Skip to content

Commit af6215a

Browse files
committed
Semantic tokens: consistently add the DEFINITION modifier
There are a few more statements potentially containing definitions than what is currently covered. This commit reviews all the statements and completes the list of those requiring a custom visitor to add the `DEFINITION` modifier for the appropriate tokens.
1 parent ffce0de commit af6215a

File tree

1 file changed

+139
-4
lines changed

1 file changed

+139
-4
lines changed

crates/ty_ide/src/semantic_tokens.rs

Lines changed: 139 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,23 @@ impl SourceOrderVisitor<'_> for SemanticTokenVisitor<'_> {
673673
self.visit_body(&class.body);
674674
self.in_class_scope = prev_in_class;
675675
}
676+
ast::Stmt::TypeAlias(type_alias) => {
677+
// Type alias name
678+
self.add_token(
679+
type_alias.name.range(),
680+
SemanticTokenType::Class,
681+
SemanticTokenModifier::DEFINITION,
682+
);
683+
684+
// Type parameters (Python 3.12+ syntax)
685+
if let Some(type_params) = &type_alias.type_params {
686+
for type_param in &type_params.type_params {
687+
self.visit_type_param(type_param);
688+
}
689+
}
690+
691+
self.visit_expr(&type_alias.value);
692+
}
676693
ast::Stmt::Import(import) => {
677694
for alias in &import.names {
678695
if let Some(asname) = &alias.asname {
@@ -747,6 +764,49 @@ impl SourceOrderVisitor<'_> for SemanticTokenVisitor<'_> {
747764
self.visit_expr(value);
748765
}
749766
}
767+
ast::Stmt::For(for_stmt) => {
768+
self.in_target_creating_definition = true;
769+
self.visit_expr(&for_stmt.target);
770+
self.in_target_creating_definition = false;
771+
772+
self.visit_expr(&for_stmt.iter);
773+
self.visit_body(&for_stmt.body);
774+
self.visit_body(&for_stmt.orelse);
775+
}
776+
ast::Stmt::With(with_stmt) => {
777+
for item in &with_stmt.items {
778+
self.visit_expr(&item.context_expr);
779+
if let Some(expr) = &item.optional_vars {
780+
self.in_target_creating_definition = true;
781+
self.visit_expr(expr);
782+
self.in_target_creating_definition = false;
783+
}
784+
}
785+
786+
self.visit_body(&with_stmt.body);
787+
}
788+
ast::Stmt::Try(try_stmt) => {
789+
self.visit_body(&try_stmt.body);
790+
for handler in &try_stmt.handlers {
791+
match handler {
792+
ast::ExceptHandler::ExceptHandler(except_handler) => {
793+
if let Some(expr) = &except_handler.type_ {
794+
self.visit_expr(expr);
795+
}
796+
if let Some(name) = &except_handler.name {
797+
self.add_token(
798+
name.range(),
799+
SemanticTokenType::Variable,
800+
SemanticTokenModifier::DEFINITION,
801+
);
802+
}
803+
self.visit_body(&except_handler.body);
804+
}
805+
}
806+
}
807+
self.visit_body(&try_stmt.orelse);
808+
self.visit_body(&try_stmt.finalbody);
809+
}
750810

751811
_ => {
752812
// For all other statement types, let the default visitor handle them
@@ -1304,7 +1364,7 @@ result = check(None)
13041364
);
13051365

13061366
assert_snapshot!(test.to_snapshot(&test.highlight_file()), @r#"
1307-
"U" @ 6..7: TypeParameter
1367+
"U" @ 6..7: Class [definition]
13081368
"str" @ 10..13: Class
13091369
"int" @ 16..19: Class
13101370
"Test" @ 27..31: Class [definition]
@@ -2405,16 +2465,16 @@ finally:
24052465
"1" @ 14..15: Number
24062466
"0" @ 18..19: Number
24072467
"ValueError" @ 27..37: Class
2408-
"ve" @ 41..43: Variable
2468+
"ve" @ 41..43: Variable [definition]
24092469
"print" @ 49..54: Function
24102470
"ve" @ 55..57: Variable
24112471
"TypeError" @ 67..76: Class
24122472
"RuntimeError" @ 78..90: Class
2413-
"re" @ 95..97: Variable
2473+
"re" @ 95..97: Variable [definition]
24142474
"print" @ 103..108: Function
24152475
"re" @ 109..111: Variable
24162476
"Exception" @ 120..129: Class
2417-
"e" @ 133..134: Variable
2477+
"e" @ 133..134: Variable [definition]
24182478
"print" @ 140..145: Function
24192479
"e" @ 146..147: Variable
24202480
"#);
@@ -2460,6 +2520,81 @@ class C:
24602520
"#);
24612521
}
24622522

2523+
#[test]
2524+
fn test_augmented_assignment() {
2525+
let test = SemanticTokenTest::new(
2526+
r#"
2527+
x = 0
2528+
x += 1
2529+
"#,
2530+
);
2531+
2532+
let tokens = test.highlight_file();
2533+
2534+
assert_snapshot!(test.to_snapshot(&tokens), @r#"
2535+
"x" @ 1..2: Variable [definition]
2536+
"0" @ 5..6: Number
2537+
"x" @ 7..8: Variable
2538+
"1" @ 12..13: Number
2539+
"#);
2540+
}
2541+
2542+
#[test]
2543+
fn test_type_alias() {
2544+
let test = SemanticTokenTest::new("type MyList[T] = list[T]");
2545+
2546+
let tokens = test.highlight_file();
2547+
2548+
assert_snapshot!(test.to_snapshot(&tokens), @r#"
2549+
"MyList" @ 5..11: Class [definition]
2550+
"T" @ 12..13: TypeParameter [definition]
2551+
"list" @ 17..21: Class
2552+
"T" @ 22..23: TypeParameter
2553+
"#);
2554+
}
2555+
2556+
#[test]
2557+
fn test_for_stmt() {
2558+
let test = SemanticTokenTest::new(
2559+
r#"
2560+
for item in []:
2561+
print(item)
2562+
else:
2563+
print(0)
2564+
"#,
2565+
);
2566+
2567+
let tokens = test.highlight_file();
2568+
2569+
assert_snapshot!(test.to_snapshot(&tokens), @r#"
2570+
"item" @ 5..9: Variable [definition]
2571+
"print" @ 21..26: Function
2572+
"item" @ 27..31: Variable
2573+
"print" @ 43..48: Function
2574+
"0" @ 49..50: Number
2575+
"#);
2576+
}
2577+
2578+
#[test]
2579+
fn test_with_stmt() {
2580+
let test = SemanticTokenTest::new(
2581+
r#"
2582+
with open("file.txt") as f:
2583+
f.read()
2584+
"#,
2585+
);
2586+
2587+
let tokens = test.highlight_file();
2588+
2589+
assert_snapshot!(test.to_snapshot(&tokens), @r#"
2590+
"open" @ 6..10: Function
2591+
"\"file.txt\"" @ 11..21: String
2592+
"f" @ 26..27: Variable [definition]
2593+
"f" @ 33..34: Variable
2594+
"read" @ 35..39: Method
2595+
"#);
2596+
}
2597+
24632598
/// Regression test for <https://github.com/astral-sh/ty/issues/1406>
24642599
#[test]
24652600
fn test_invalid_kwargs() {

0 commit comments

Comments
 (0)