check if strings are just strings

This commit is contained in:
Aria Desires 2025-08-13 20:57:49 -04:00
parent 86e9b4d337
commit a87750a027
3 changed files with 39 additions and 15 deletions

View File

@ -7,7 +7,6 @@ use std::borrow::Cow;
use crate::find_node::covering_node; use crate::find_node::covering_node;
use crate::stub_mapping::StubMapper; use crate::stub_mapping::StubMapper;
use ruff_db::Db;
use ruff_db::files::File; use ruff_db::files::File;
use ruff_db::parsed::ParsedModuleRef; use ruff_db::parsed::ParsedModuleRef;
use ruff_db::source::source_text; use ruff_db::source::source_text;
@ -280,9 +279,10 @@ impl GotoTarget<'_> {
GotoTarget::ImportModuleAlias { alias } => alias.inferred_type(model), GotoTarget::ImportModuleAlias { alias } => alias.inferred_type(model),
GotoTarget::ExceptVariable(except) => except.inferred_type(model), GotoTarget::ExceptVariable(except) => except.inferred_type(model),
GotoTarget::KeywordArgument { keyword, .. } => keyword.value.inferred_type(model), GotoTarget::KeywordArgument { keyword, .. } => keyword.value.inferred_type(model),
GotoTarget::StringAnnotationExprName { .. } => { GotoTarget::StringAnnotationExprName { string_literal, .. } => {
// TODO: make a way to ask the inference engine about a sub-expr of a string annotation // TODO: make a way to ask the inference engine about a sub-expr of a string annotation
return None; // for now we just yield the type of the entire string expression
string_literal.inferred_type(model)
} }
// TODO: Support identifier targets // TODO: Support identifier targets
GotoTarget::PatternMatchRest(_) GotoTarget::PatternMatchRest(_)
@ -528,7 +528,7 @@ impl GotoTarget<'_> {
/// Creates a `GotoTarget` from a `CoveringNode` and an offset within the node /// Creates a `GotoTarget` from a `CoveringNode` and an offset within the node
pub(crate) fn from_covering_node<'a>( pub(crate) fn from_covering_node<'a>(
db: &dyn Db, db: &dyn crate::Db,
file: File, file: File,
covering_node: &crate::find_node::CoveringNode<'a>, covering_node: &crate::find_node::CoveringNode<'a>,
offset: TextSize, offset: TextSize,
@ -680,13 +680,20 @@ impl GotoTarget<'_> {
} }
}, },
AnyNodeRef::ExprStringLiteral(string_literal) => { node @ AnyNodeRef::ExprStringLiteral(string_literal) => {
// If we encounter a string literal, blindly assume it's a string annotation // If we encounter a string literal, try to figure out if it's actually
// like `x: "str | int"` by parsing it as a sub-AST. // a string annotation by asking the type checker if its type is
// // a StringLiteral equal to itself
// TODO: we *really* should be asking the semantic analysis if this is actually let model = SemanticModel::new(db, file);
// a string annotation, because this behaviour will effect any random string let string_ty = string_literal.inferred_type(&model);
// literal that looks like a type..! if let Type::StringLiteral(string) = string_ty {
if string.value(db) == string_literal.value.to_str() {
return node.as_expr_ref().map(GotoTarget::Expression);
}
}
// Ok it has a different type, that means it's some kind of string annotation,
// so try to parse it as a sub-AST
let source = source_text(db, file); let source = source_text(db, file);
let sub_ast = parse_string_annotation( let sub_ast = parse_string_annotation(
source.as_str(), source.as_str(),
@ -814,7 +821,7 @@ fn definitions_to_navigation_targets<'db>(
} }
pub(crate) fn find_goto_target<'a>( pub(crate) fn find_goto_target<'a>(
db: &dyn Db, db: &dyn crate::Db,
file: File, file: File,
parsed: &'a ParsedModuleRef, parsed: &'a ParsedModuleRef,
offset: TextSize, offset: TextSize,
@ -823,7 +830,7 @@ pub(crate) fn find_goto_target<'a>(
} }
fn find_goto_target_impl<'a>( fn find_goto_target_impl<'a>(
db: &dyn Db, db: &dyn crate::Db,
file: File, file: File,
tokens: &'a Tokens, tokens: &'a Tokens,
root: AnyNodeRef<'a>, root: AnyNodeRef<'a>,

View File

@ -225,7 +225,24 @@ mod tests {
"#, "#,
); );
assert_snapshot!(test.goto_type_definition(), @"No goto target found"); assert_snapshot!(test.goto_type_definition(), @r#"
info[goto-type-definition]: Type definition
--> stdlib/builtins.pyi:892:7
|
890 | def __getitem__(self, key: int, /) -> str | int | None: ...
891 |
892 | class str(Sequence[str]):
| ^^^
893 | """str(object='') -> str
894 | str(bytes_or_buffer[, encoding[, errors]]) -> str
|
info: Source
--> main.py:2:22
|
2 | a: str = "test"
| ^^^^^^
|
"#);
} }
#[test] #[test]

View File

@ -9942,7 +9942,7 @@ impl<'db> IntersectionType<'db> {
#[derive(PartialOrd, Ord)] #[derive(PartialOrd, Ord)]
pub struct StringLiteralType<'db> { pub struct StringLiteralType<'db> {
#[returns(deref)] #[returns(deref)]
value: Box<str>, pub value: Box<str>,
} }
// The Salsa heap is tracked separately. // The Salsa heap is tracked separately.