From a87750a02771b491f3b8aef72088690cd8bae05a Mon Sep 17 00:00:00 2001 From: Aria Desires Date: Wed, 13 Aug 2025 20:57:49 -0400 Subject: [PATCH] check if strings are just strings --- crates/ty_ide/src/goto.rs | 33 ++++++++++++++--------- crates/ty_ide/src/goto_type_definition.rs | 19 ++++++++++++- crates/ty_python_semantic/src/types.rs | 2 +- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/crates/ty_ide/src/goto.rs b/crates/ty_ide/src/goto.rs index 6ca264d76c..85666bb35b 100644 --- a/crates/ty_ide/src/goto.rs +++ b/crates/ty_ide/src/goto.rs @@ -7,7 +7,6 @@ use std::borrow::Cow; use crate::find_node::covering_node; use crate::stub_mapping::StubMapper; -use ruff_db::Db; use ruff_db::files::File; use ruff_db::parsed::ParsedModuleRef; use ruff_db::source::source_text; @@ -280,9 +279,10 @@ impl GotoTarget<'_> { GotoTarget::ImportModuleAlias { alias } => alias.inferred_type(model), GotoTarget::ExceptVariable(except) => except.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 - return None; + // for now we just yield the type of the entire string expression + string_literal.inferred_type(model) } // TODO: Support identifier targets GotoTarget::PatternMatchRest(_) @@ -528,7 +528,7 @@ impl GotoTarget<'_> { /// Creates a `GotoTarget` from a `CoveringNode` and an offset within the node pub(crate) fn from_covering_node<'a>( - db: &dyn Db, + db: &dyn crate::Db, file: File, covering_node: &crate::find_node::CoveringNode<'a>, offset: TextSize, @@ -680,13 +680,20 @@ impl GotoTarget<'_> { } }, - AnyNodeRef::ExprStringLiteral(string_literal) => { - // If we encounter a string literal, blindly assume it's a string annotation - // like `x: "str | int"` by parsing it as a sub-AST. - // - // TODO: we *really* should be asking the semantic analysis if this is actually - // a string annotation, because this behaviour will effect any random string - // literal that looks like a type..! + node @ AnyNodeRef::ExprStringLiteral(string_literal) => { + // If we encounter a string literal, try to figure out if it's actually + // a string annotation by asking the type checker if its type is + // a StringLiteral equal to itself + let model = SemanticModel::new(db, file); + let string_ty = string_literal.inferred_type(&model); + 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 sub_ast = parse_string_annotation( source.as_str(), @@ -814,7 +821,7 @@ fn definitions_to_navigation_targets<'db>( } pub(crate) fn find_goto_target<'a>( - db: &dyn Db, + db: &dyn crate::Db, file: File, parsed: &'a ParsedModuleRef, offset: TextSize, @@ -823,7 +830,7 @@ pub(crate) fn find_goto_target<'a>( } fn find_goto_target_impl<'a>( - db: &dyn Db, + db: &dyn crate::Db, file: File, tokens: &'a Tokens, root: AnyNodeRef<'a>, diff --git a/crates/ty_ide/src/goto_type_definition.rs b/crates/ty_ide/src/goto_type_definition.rs index 026013476e..e4a353a926 100644 --- a/crates/ty_ide/src/goto_type_definition.rs +++ b/crates/ty_ide/src/goto_type_definition.rs @@ -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] diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 23f4422867..24020175f1 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -9942,7 +9942,7 @@ impl<'db> IntersectionType<'db> { #[derive(PartialOrd, Ord)] pub struct StringLiteralType<'db> { #[returns(deref)] - value: Box, + pub value: Box, } // The Salsa heap is tracked separately.