Skip to content

Commit 77da466

Browse files
committed
[red-knot] Silence unresolved-import in unreachable code
1 parent 5b6e949 commit 77da466

File tree

6 files changed

+114
-78
lines changed

6 files changed

+114
-78
lines changed

crates/red_knot_python_semantic/resources/mdtest/unreachable.md

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,6 @@ python-version = "3.10"
117117
import sys
118118

119119
if sys.version_info >= (3, 11):
120-
# TODO: we should not emit an error here
121-
# error: [unresolved-import]
122120
from typing import Self
123121
```
124122

@@ -391,22 +389,14 @@ diagnostics:
391389
import sys
392390

393391
if sys.version_info >= (3, 11):
394-
# TODO
395-
# error: [unresolved-import]
396392
from builtins import ExceptionGroup
397393

398-
# TODO
399-
# error: [unresolved-import]
400394
import builtins.ExceptionGroup
401395

402396
# See https://docs.python.org/3/whatsnew/3.11.html#new-modules
403397

404-
# TODO
405-
# error: [unresolved-import]
406398
import tomllib
407399

408-
# TODO
409-
# error: [unresolved-import]
410400
import wsgiref.types
411401
```
412402

@@ -435,8 +425,6 @@ import sys
435425
import typing
436426

437427
if sys.version_info >= (3, 11):
438-
# TODO (silence diagnostics for imports, see above)
439-
# error: [unresolved-import]
440428
from typing import Self
441429

442430
class C:

crates/red_knot_python_semantic/src/semantic_index.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ use salsa::plumbing::AsId;
1010
use salsa::Update;
1111

1212
use crate::module_name::ModuleName;
13+
use crate::node_key::NodeKey;
1314
use crate::semantic_index::ast_ids::node_key::ExpressionNodeKey;
14-
use crate::semantic_index::ast_ids::{AstIds, ScopedExpressionId};
15+
use crate::semantic_index::ast_ids::AstIds;
1516
use crate::semantic_index::attribute_assignment::AttributeAssignments;
1617
use crate::semantic_index::builder::SemanticIndexBuilder;
1718
use crate::semantic_index::definition::{Definition, DefinitionNodeKey, Definitions};
@@ -254,7 +255,7 @@ impl<'db> SemanticIndex<'db> {
254255
})
255256
}
256257

257-
/// Returns true if a given expression is reachable from the start of the scope. For example,
258+
/// Returns true if a given AST node is reachable from the start of the scope. For example,
258259
/// in the following code, expression `2` is reachable, but expressions `1` and `3` are not:
259260
/// ```py
260261
/// def f():
@@ -265,16 +266,14 @@ impl<'db> SemanticIndex<'db> {
265266
/// return
266267
/// x # 3
267268
/// ```
268-
pub(crate) fn is_expression_reachable(
269+
pub(crate) fn is_node_reachable(
269270
&self,
270271
db: &'db dyn crate::Db,
271272
scope_id: FileScopeId,
272-
expression_id: ScopedExpressionId,
273+
node_key: NodeKey,
273274
) -> bool {
274275
self.is_scope_reachable(db, scope_id)
275-
&& self
276-
.use_def_map(scope_id)
277-
.is_expression_reachable(db, expression_id)
276+
&& self.use_def_map(scope_id).is_node_reachable(db, node_key)
278277
}
279278

280279
/// Returns an iterator over the descendent scopes of `scope`.

crates/red_knot_python_semantic/src/semantic_index/builder.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use ruff_python_ast::{self as ast};
1313
use crate::ast_node_ref::AstNodeRef;
1414
use crate::module_name::ModuleName;
1515
use crate::module_resolver::resolve_module;
16+
use crate::node_key::NodeKey;
1617
use crate::semantic_index::ast_ids::node_key::ExpressionNodeKey;
1718
use crate::semantic_index::ast_ids::AstIdsBuilder;
1819
use crate::semantic_index::attribute_assignment::{AttributeAssignment, AttributeAssignments};
@@ -1139,7 +1140,10 @@ where
11391140
);
11401141
}
11411142
ast::Stmt::Import(node) => {
1142-
for alias in &node.names {
1143+
self.current_use_def_map_mut()
1144+
.record_node_reachability(NodeKey::from_node(node));
1145+
1146+
for (alias_index, alias) in node.names.iter().enumerate() {
11431147
// Mark the imported module, and all of its parents, as being imported in this
11441148
// file.
11451149
if let Some(module_name) = ModuleName::new(&alias.name) {
@@ -1156,13 +1160,17 @@ where
11561160
self.add_definition(
11571161
symbol,
11581162
ImportDefinitionNodeRef {
1159-
alias,
1163+
node,
1164+
alias_index,
11601165
is_reexported,
11611166
},
11621167
);
11631168
}
11641169
}
11651170
ast::Stmt::ImportFrom(node) => {
1171+
self.current_use_def_map_mut()
1172+
.record_node_reachability(NodeKey::from_node(node));
1173+
11661174
let mut found_star = false;
11671175
for (alias_index, alias) in node.names.iter().enumerate() {
11681176
if &alias.name == "*" {
@@ -1753,6 +1761,8 @@ where
17531761
.insert(expr.into(), self.current_scope());
17541762
let expression_id = self.current_ast_ids().record_expression(expr);
17551763

1764+
let node_key = NodeKey::from_node(expr);
1765+
17561766
match expr {
17571767
ast::Expr::Name(name_node @ ast::ExprName { id, ctx, .. }) => {
17581768
let (is_use, is_definition) = match (ctx, self.current_assignment()) {
@@ -1771,7 +1781,7 @@ where
17711781
self.mark_symbol_used(symbol);
17721782
let use_id = self.current_ast_ids().record_use(expr);
17731783
self.current_use_def_map_mut()
1774-
.record_use(symbol, use_id, expression_id);
1784+
.record_use(symbol, use_id, node_key);
17751785
}
17761786

17771787
if is_definition {
@@ -2035,15 +2045,15 @@ where
20352045
// Track reachability of attribute expressions to silence `unresolved-attribute`
20362046
// diagnostics in unreachable code.
20372047
self.current_use_def_map_mut()
2038-
.record_expression_reachability(expression_id);
2048+
.record_node_reachability(node_key);
20392049

20402050
walk_expr(self, expr);
20412051
}
20422052
ast::Expr::StringLiteral(_) => {
20432053
// Track reachability of string literals, as they could be a stringified annotation
20442054
// with child expressions whose reachability we are interested in.
20452055
self.current_use_def_map_mut()
2046-
.record_expression_reachability(expression_id);
2056+
.record_node_reachability(node_key);
20472057

20482058
walk_expr(self, expr);
20492059
}

crates/red_knot_python_semantic/src/semantic_index/definition.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,8 @@ impl<'a> From<StarImportDefinitionNodeRef<'a>> for DefinitionNodeRef<'a> {
224224

225225
#[derive(Copy, Clone, Debug)]
226226
pub(crate) struct ImportDefinitionNodeRef<'a> {
227-
pub(crate) alias: &'a ast::Alias,
227+
pub(crate) node: &'a ast::StmtImport,
228+
pub(crate) alias_index: usize,
228229
pub(crate) is_reexported: bool,
229230
}
230231

@@ -294,10 +295,12 @@ impl<'db> DefinitionNodeRef<'db> {
294295
pub(super) unsafe fn into_owned(self, parsed: ParsedModule) -> DefinitionKind<'db> {
295296
match self {
296297
DefinitionNodeRef::Import(ImportDefinitionNodeRef {
297-
alias,
298+
node,
299+
alias_index,
298300
is_reexported,
299301
}) => DefinitionKind::Import(ImportDefinitionKind {
300-
alias: AstNodeRef::new(parsed, alias),
302+
node: AstNodeRef::new(parsed, node),
303+
alias_index,
301304
is_reexported,
302305
}),
303306

@@ -417,9 +420,10 @@ impl<'db> DefinitionNodeRef<'db> {
417420
pub(super) fn key(self) -> DefinitionNodeKey {
418421
match self {
419422
Self::Import(ImportDefinitionNodeRef {
420-
alias,
423+
node,
424+
alias_index,
421425
is_reexported: _,
422-
}) => alias.into(),
426+
}) => (&node.names[alias_index]).into(),
423427
Self::ImportFrom(ImportFromDefinitionNodeRef {
424428
node,
425429
alias_index,
@@ -757,13 +761,18 @@ impl ComprehensionDefinitionKind {
757761

758762
#[derive(Clone, Debug)]
759763
pub struct ImportDefinitionKind {
760-
alias: AstNodeRef<ast::Alias>,
764+
node: AstNodeRef<ast::StmtImport>,
765+
alias_index: usize,
761766
is_reexported: bool,
762767
}
763768

764769
impl ImportDefinitionKind {
770+
pub(crate) fn import(&self) -> &ast::StmtImport {
771+
self.node.node()
772+
}
773+
765774
pub(crate) fn alias(&self) -> &ast::Alias {
766-
self.alias.node()
775+
&self.node.node().names[self.alias_index]
767776
}
768777

769778
pub(crate) fn is_reexported(&self) -> bool {

crates/red_knot_python_semantic/src/semantic_index/use_def.rs

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,8 @@ use self::symbol_state::{
263263
LiveBindingsIterator, LiveDeclaration, LiveDeclarationsIterator, ScopedDefinitionId,
264264
SymbolBindings, SymbolDeclarations, SymbolState,
265265
};
266-
use crate::semantic_index::ast_ids::{ScopedExpressionId, ScopedUseId};
266+
use crate::node_key::NodeKey;
267+
use crate::semantic_index::ast_ids::ScopedUseId;
267268
use crate::semantic_index::definition::Definition;
268269
use crate::semantic_index::narrowing_constraints::{
269270
NarrowingConstraints, NarrowingConstraintsBuilder, NarrowingConstraintsIterator,
@@ -297,8 +298,8 @@ pub(crate) struct UseDefMap<'db> {
297298
/// [`SymbolBindings`] reaching a [`ScopedUseId`].
298299
bindings_by_use: IndexVec<ScopedUseId, SymbolBindings>,
299300

300-
/// Tracks whether or not a given expression is reachable from the start of the scope.
301-
expression_reachability: FxHashMap<ScopedExpressionId, ScopedVisibilityConstraintId>,
301+
/// Tracks whether or not a given AST node is reachable from the start of the scope.
302+
node_reachability: FxHashMap<NodeKey, ScopedVisibilityConstraintId>,
302303

303304
/// If the definition is a binding (only) -- `x = 1` for example -- then we need
304305
/// [`SymbolDeclarations`] to know whether this binding is permitted by the live declarations.
@@ -364,20 +365,16 @@ impl<'db> UseDefMap<'db> {
364365
/// be unreachable. Use [`super::SemanticIndex::is_expression_reachable`] for the global
365366
/// analysis.
366367
#[track_caller]
367-
pub(super) fn is_expression_reachable(
368-
&self,
369-
db: &dyn crate::Db,
370-
expression_id: ScopedExpressionId,
371-
) -> bool {
368+
pub(super) fn is_node_reachable(&self, db: &dyn crate::Db, node_key: NodeKey) -> bool {
372369
!self
373370
.visibility_constraints
374371
.evaluate(
375372
db,
376373
&self.predicates,
377374
*self
378-
.expression_reachability
379-
.get(&expression_id)
380-
.expect("`is_expression_reachable` should only be called on expressions with recorded reachability"),
375+
.node_reachability
376+
.get(&node_key)
377+
.expect("`is_node_reachable` should only be called on AST nodes with recorded reachability"),
381378
)
382379
.is_always_false()
383380
}
@@ -636,8 +633,8 @@ pub(super) struct UseDefMapBuilder<'db> {
636633
/// The use of `x` is recorded with a reachability constraint of `[test]`.
637634
pub(super) reachability: ScopedVisibilityConstraintId,
638635

639-
/// Tracks whether or not a given expression is reachable from the start of the scope.
640-
expression_reachability: FxHashMap<ScopedExpressionId, ScopedVisibilityConstraintId>,
636+
/// Tracks whether or not a given AST node is reachable from the start of the scope.
637+
node_reachability: FxHashMap<NodeKey, ScopedVisibilityConstraintId>,
641638

642639
/// Live declarations for each so-far-recorded binding.
643640
declarations_by_binding: FxHashMap<Definition<'db>, SymbolDeclarations>,
@@ -663,7 +660,7 @@ impl Default for UseDefMapBuilder<'_> {
663660
scope_start_visibility: ScopedVisibilityConstraintId::ALWAYS_TRUE,
664661
bindings_by_use: IndexVec::new(),
665662
reachability: ScopedVisibilityConstraintId::ALWAYS_TRUE,
666-
expression_reachability: FxHashMap::default(),
663+
node_reachability: FxHashMap::default(),
667664
declarations_by_binding: FxHashMap::default(),
668665
bindings_by_declaration: FxHashMap::default(),
669666
symbol_states: IndexVec::new(),
@@ -822,7 +819,7 @@ impl<'db> UseDefMapBuilder<'db> {
822819
&mut self,
823820
symbol: ScopedSymbolId,
824821
use_id: ScopedUseId,
825-
expression_id: ScopedExpressionId,
822+
node_key: NodeKey,
826823
) {
827824
// We have a use of a symbol; clone the current bindings for that symbol, and record them
828825
// as the live bindings for this use.
@@ -833,12 +830,11 @@ impl<'db> UseDefMapBuilder<'db> {
833830

834831
// Track reachability of all uses of symbols to silence `unresolved-reference`
835832
// diagnostics in unreachable code.
836-
self.record_expression_reachability(expression_id);
833+
self.record_node_reachability(node_key);
837834
}
838835

839-
pub(super) fn record_expression_reachability(&mut self, expression_id: ScopedExpressionId) {
840-
self.expression_reachability
841-
.insert(expression_id, self.reachability);
836+
pub(super) fn record_node_reachability(&mut self, node_key: NodeKey) {
837+
self.node_reachability.insert(node_key, self.reachability);
842838
}
843839

844840
pub(super) fn snapshot_eager_bindings(
@@ -935,7 +931,7 @@ impl<'db> UseDefMapBuilder<'db> {
935931
self.all_definitions.shrink_to_fit();
936932
self.symbol_states.shrink_to_fit();
937933
self.bindings_by_use.shrink_to_fit();
938-
self.expression_reachability.shrink_to_fit();
934+
self.node_reachability.shrink_to_fit();
939935
self.declarations_by_binding.shrink_to_fit();
940936
self.bindings_by_declaration.shrink_to_fit();
941937
self.eager_bindings.shrink_to_fit();
@@ -946,7 +942,7 @@ impl<'db> UseDefMapBuilder<'db> {
946942
narrowing_constraints: self.narrowing_constraints.build(),
947943
visibility_constraints: self.visibility_constraints.build(),
948944
bindings_by_use: self.bindings_by_use,
949-
expression_reachability: self.expression_reachability,
945+
node_reachability: self.node_reachability,
950946
public_symbols: self.symbol_states,
951947
declarations_by_binding: self.declarations_by_binding,
952948
bindings_by_declaration: self.bindings_by_declaration,

0 commit comments

Comments
 (0)