@@ -90,6 +90,8 @@ pub(super) struct SemanticIndexBuilder<'db, 'ast> {
90
90
91
91
/// Flags about the file's global scope
92
92
has_future_annotations : bool ,
93
+ /// Whether we are currently visiting an `if TYPE_CHECKING` block.
94
+ in_type_checking_block : bool ,
93
95
94
96
// Used for checking semantic syntax errors
95
97
python_version : PythonVersion ,
@@ -130,6 +132,7 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
130
132
try_node_context_stack_manager : TryNodeContextStackManager :: default ( ) ,
131
133
132
134
has_future_annotations : false ,
135
+ in_type_checking_block : false ,
133
136
134
137
scopes : IndexVec :: new ( ) ,
135
138
place_tables : IndexVec :: new ( ) ,
@@ -248,6 +251,7 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
248
251
node_with_kind,
249
252
children_start..children_start,
250
253
reachability,
254
+ self . in_type_checking_block ,
251
255
) ;
252
256
let is_class_scope = scope. kind ( ) . is_class ( ) ;
253
257
self . try_node_context_stack_manager . enter_nested_scope ( ) ;
@@ -719,7 +723,7 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
719
723
// since its the pattern that introduces any constraints, not the body.) Ideally, that
720
724
// standalone expression would wrap the match arm's pattern as a whole. But a standalone
721
725
// expression can currently only wrap an ast::Expr, which patterns are not. So, we need to
722
- // choose an Expr that can “ stand in” for the pattern, which we can wrap in a standalone
726
+ // choose an Expr that can " stand in" for the pattern, which we can wrap in a standalone
723
727
// expression.
724
728
//
725
729
// See the comment in TypeInferenceBuilder::infer_match_pattern for more details.
@@ -1498,6 +1502,17 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
1498
1502
let mut last_predicate = self . record_expression_narrowing_constraint ( & node. test ) ;
1499
1503
let mut last_reachability_constraint =
1500
1504
self . record_reachability_constraint ( last_predicate) ;
1505
+
1506
+ let is_outer_block_in_type_checking = self . in_type_checking_block ;
1507
+
1508
+ let if_block_in_type_checking = is_if_type_checking ( & node. test ) ;
1509
+
1510
+ // Track if we're in a chain that started with "not TYPE_CHECKING"
1511
+ let mut is_in_not_type_checking_chain = is_if_not_type_checking ( & node. test ) ;
1512
+
1513
+ self . in_type_checking_block =
1514
+ if_block_in_type_checking || is_outer_block_in_type_checking;
1515
+
1501
1516
self . visit_body ( & node. body ) ;
1502
1517
1503
1518
let mut post_clauses: Vec < FlowSnapshot > = vec ! [ ] ;
@@ -1516,6 +1531,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
1516
1531
// if there's no `else` branch, we should add a no-op `else` branch
1517
1532
Some ( ( None , Default :: default ( ) ) )
1518
1533
} ) ;
1534
+
1519
1535
for ( clause_test, clause_body) in elif_else_clauses {
1520
1536
// snapshot after every block except the last; the last one will just become
1521
1537
// the state that we merge the other snapshots into
@@ -1538,12 +1554,34 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
1538
1554
self . record_reachability_constraint ( last_predicate) ;
1539
1555
}
1540
1556
1557
+ // Determine if this clause is in type checking context
1558
+ let clause_in_type_checking = if let Some ( elif_test) = clause_test {
1559
+ if is_if_type_checking ( elif_test) {
1560
+ // This block has "TYPE_CHECKING" condition
1561
+ true
1562
+ } else if is_if_not_type_checking ( elif_test) {
1563
+ // This block has "not TYPE_CHECKING" condition so we update the chain state for future blocks
1564
+ is_in_not_type_checking_chain = true ;
1565
+ false
1566
+ } else {
1567
+ // This block has some other condition
1568
+ // It's in type checking only if we're in a "not TYPE_CHECKING" chain
1569
+ is_in_not_type_checking_chain
1570
+ }
1571
+ } else {
1572
+ is_in_not_type_checking_chain
1573
+ } ;
1574
+
1575
+ self . in_type_checking_block = clause_in_type_checking;
1576
+
1541
1577
self . visit_body ( clause_body) ;
1542
1578
}
1543
1579
1544
1580
for post_clause_state in post_clauses {
1545
1581
self . flow_merge ( post_clause_state) ;
1546
1582
}
1583
+
1584
+ self . in_type_checking_block = is_outer_block_in_type_checking;
1547
1585
}
1548
1586
ast:: Stmt :: While ( ast:: StmtWhile {
1549
1587
test,
@@ -2711,3 +2749,18 @@ impl ExpressionsScopeMapBuilder {
2711
2749
ExpressionsScopeMap ( interval_map. into_boxed_slice ( ) )
2712
2750
}
2713
2751
}
2752
+
2753
+ /// Returns if the expression is a `TYPE_CHECKING` expression.
2754
+ fn is_if_type_checking ( expr : & ast:: Expr ) -> bool {
2755
+ matches ! ( expr, ast:: Expr :: Name ( ast:: ExprName { id, .. } ) if id == "TYPE_CHECKING" )
2756
+ }
2757
+
2758
+ /// Returns if the expression is a `not TYPE_CHECKING` expression.
2759
+ fn is_if_not_type_checking ( expr : & ast:: Expr ) -> bool {
2760
+ matches ! ( expr, ast:: Expr :: UnaryOp ( ast:: ExprUnaryOp { op, operand, .. } ) if * op == ruff_python_ast:: UnaryOp :: Not
2761
+ && matches!(
2762
+ & * * operand,
2763
+ ast:: Expr :: Name ( ast:: ExprName { id, .. } ) if id == "TYPE_CHECKING"
2764
+ )
2765
+ )
2766
+ }
0 commit comments