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::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>,

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]

View File

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