Skip to content

Commit 11890cb

Browse files
for imported type, ignore ReturnsNever constraints
1 parent 68efb2b commit 11890cb

File tree

4 files changed

+89
-22
lines changed

4 files changed

+89
-22
lines changed

crates/ty_python_semantic/src/place.rs

Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ pub(crate) fn symbol<'db>(
206206
name,
207207
RequiresExplicitReExport::No,
208208
considered_definitions,
209+
false,
209210
)
210211
}
211212

@@ -242,6 +243,7 @@ pub(crate) fn class_symbol<'db>(
242243
place,
243244
RequiresExplicitReExport::No,
244245
ConsideredDefinitions::EndOfScope,
246+
false,
245247
);
246248

247249
if !place_and_quals.place.is_unbound() {
@@ -257,7 +259,8 @@ pub(crate) fn class_symbol<'db>(
257259
// Otherwise, we need to check if the symbol has bindings
258260
let use_def = use_def_map(db, scope);
259261
let bindings = use_def.end_of_scope_bindings(place);
260-
let inferred = place_from_bindings_impl(db, bindings, RequiresExplicitReExport::No);
262+
let inferred =
263+
place_from_bindings_impl(db, bindings, RequiresExplicitReExport::No, false);
261264

262265
// TODO: we should not need to calculate inferred type second time. This is a temporary
263266
// solution until the notion of Boundness and Declaredness is split. See #16036, #16264
@@ -293,6 +296,7 @@ pub(crate) fn explicit_global_symbol<'db>(
293296
name,
294297
RequiresExplicitReExport::No,
295298
ConsideredDefinitions::AllReachable,
299+
false,
296300
)
297301
}
298302

@@ -352,6 +356,7 @@ pub(crate) fn imported_symbol<'db>(
352356
name,
353357
requires_explicit_reexport,
354358
ConsideredDefinitions::EndOfScope,
359+
true,
355360
)
356361
.or_fall_back_to(db, || {
357362
if name == "__getattr__" {
@@ -382,6 +387,7 @@ pub(crate) fn builtins_symbol<'db>(db: &'db dyn Db, symbol: &str) -> PlaceAndQua
382387
symbol,
383388
RequiresExplicitReExport::Yes,
384389
ConsideredDefinitions::EndOfScope,
390+
false,
385391
)
386392
.or_fall_back_to(db, || {
387393
// We're looking up in the builtins namespace and not the module, so we should
@@ -453,7 +459,12 @@ pub(super) fn place_from_bindings<'db>(
453459
db: &'db dyn Db,
454460
bindings_with_constraints: BindingWithConstraintsIterator<'_, 'db>,
455461
) -> Place<'db> {
456-
place_from_bindings_impl(db, bindings_with_constraints, RequiresExplicitReExport::No)
462+
place_from_bindings_impl(
463+
db,
464+
bindings_with_constraints,
465+
RequiresExplicitReExport::No,
466+
false,
467+
)
457468
}
458469

459470
/// Build a declared type from a [`DeclarationsIterator`].
@@ -468,7 +479,7 @@ pub(crate) fn place_from_declarations<'db>(
468479
db: &'db dyn Db,
469480
declarations: DeclarationsIterator<'_, 'db>,
470481
) -> PlaceFromDeclarationsResult<'db> {
471-
place_from_declarations_impl(db, declarations, RequiresExplicitReExport::No)
482+
place_from_declarations_impl(db, declarations, RequiresExplicitReExport::No, false)
472483
}
473484

474485
pub(crate) type DeclaredTypeAndConflictingTypes<'db> =
@@ -598,6 +609,7 @@ impl<'db> From<Place<'db>> for PlaceAndQualifiers<'db> {
598609
}
599610
}
600611

612+
#[allow(clippy::too_many_arguments)]
601613
fn place_cycle_recover<'db>(
602614
_db: &'db dyn Db,
603615
_value: &PlaceAndQualifiers<'db>,
@@ -606,6 +618,7 @@ fn place_cycle_recover<'db>(
606618
_place_id: ScopedPlaceId,
607619
_requires_explicit_reexport: RequiresExplicitReExport,
608620
_considered_definitions: ConsideredDefinitions,
621+
_ignore_never_constraints: bool,
609622
) -> salsa::CycleRecoveryAction<PlaceAndQualifiers<'db>> {
610623
salsa::CycleRecoveryAction::Iterate
611624
}
@@ -616,6 +629,7 @@ fn place_cycle_initial<'db>(
616629
_place_id: ScopedPlaceId,
617630
_requires_explicit_reexport: RequiresExplicitReExport,
618631
_considered_definitions: ConsideredDefinitions,
632+
_ignore_never_constraints: bool,
619633
) -> PlaceAndQualifiers<'db> {
620634
Place::bound(Type::Never).into()
621635
}
@@ -627,6 +641,7 @@ fn place_by_id<'db>(
627641
place_id: ScopedPlaceId,
628642
requires_explicit_reexport: RequiresExplicitReExport,
629643
considered_definitions: ConsideredDefinitions,
644+
ignore_never_constraints: bool,
630645
) -> PlaceAndQualifiers<'db> {
631646
let use_def = use_def_map(db, scope);
632647

@@ -638,7 +653,12 @@ fn place_by_id<'db>(
638653
ConsideredDefinitions::AllReachable => use_def.all_reachable_declarations(place_id),
639654
};
640655

641-
let declared = place_from_declarations_impl(db, declarations, requires_explicit_reexport);
656+
let declared = place_from_declarations_impl(
657+
db,
658+
declarations,
659+
requires_explicit_reexport,
660+
ignore_never_constraints,
661+
);
642662

643663
let all_considered_bindings = || match considered_definitions {
644664
ConsideredDefinitions::EndOfScope => use_def.end_of_scope_bindings(place_id),
@@ -660,7 +680,12 @@ fn place_by_id<'db>(
660680
}) => {
661681
let bindings = all_considered_bindings();
662682
let boundness_analysis = bindings.boundness_analysis;
663-
let inferred = place_from_bindings_impl(db, bindings, requires_explicit_reexport);
683+
let inferred = place_from_bindings_impl(
684+
db,
685+
bindings,
686+
requires_explicit_reexport,
687+
ignore_never_constraints,
688+
);
664689

665690
let place = match inferred {
666691
// Place is possibly undeclared and definitely unbound
@@ -690,7 +715,12 @@ fn place_by_id<'db>(
690715
}) => {
691716
let bindings = all_considered_bindings();
692717
let boundness_analysis = bindings.boundness_analysis;
693-
let mut inferred = place_from_bindings_impl(db, bindings, requires_explicit_reexport);
718+
let mut inferred = place_from_bindings_impl(
719+
db,
720+
bindings,
721+
requires_explicit_reexport,
722+
ignore_never_constraints,
723+
);
694724

695725
if boundness_analysis == BoundnessAnalysis::AssumeBound {
696726
if let Place::Type(ty, Boundness::PossiblyUnbound) = inferred {
@@ -756,6 +786,7 @@ fn symbol_impl<'db>(
756786
name: &str,
757787
requires_explicit_reexport: RequiresExplicitReExport,
758788
considered_definitions: ConsideredDefinitions,
789+
ignore_never_constraints: bool,
759790
) -> PlaceAndQualifiers<'db> {
760791
let _span = tracing::trace_span!("symbol", ?name).entered();
761792

@@ -782,6 +813,7 @@ fn symbol_impl<'db>(
782813
symbol,
783814
requires_explicit_reexport,
784815
considered_definitions,
816+
ignore_never_constraints,
785817
)
786818
})
787819
.unwrap_or_default()
@@ -806,6 +838,7 @@ fn place_impl<'db>(
806838
place,
807839
requires_explicit_reexport,
808840
considered_definitions,
841+
false,
809842
)
810843
})
811844
.unwrap_or_default()
@@ -820,6 +853,7 @@ fn place_from_bindings_impl<'db>(
820853
db: &'db dyn Db,
821854
bindings_with_constraints: BindingWithConstraintsIterator<'_, 'db>,
822855
requires_explicit_reexport: RequiresExplicitReExport,
856+
ignore_never_constraints: bool,
823857
) -> Place<'db> {
824858
let predicates = bindings_with_constraints.predicates;
825859
let reachability_constraints = bindings_with_constraints.reachability_constraints;
@@ -845,7 +879,12 @@ fn place_from_bindings_impl<'db>(
845879
// expressions, which is extra work and can lead to cycles.
846880
let unbound_visibility = || {
847881
unbound_reachability_constraint.map(|reachability_constraint| {
848-
reachability_constraints.evaluate(db, predicates, reachability_constraint)
882+
reachability_constraints.evaluate(
883+
db,
884+
predicates,
885+
reachability_constraint,
886+
ignore_never_constraints,
887+
)
849888
})
850889
};
851890

@@ -861,9 +900,13 @@ fn place_from_bindings_impl<'db>(
861900
return None;
862901
}
863902
DefinitionState::Deleted => {
864-
deleted_reachability = deleted_reachability.or(
865-
reachability_constraints.evaluate(db, predicates, reachability_constraint)
866-
);
903+
deleted_reachability = deleted_reachability.or(reachability_constraints
904+
.evaluate(
905+
db,
906+
predicates,
907+
reachability_constraint,
908+
ignore_never_constraints,
909+
));
867910
return None;
868911
}
869912
};
@@ -872,8 +915,12 @@ fn place_from_bindings_impl<'db>(
872915
return None;
873916
}
874917

875-
let static_reachability =
876-
reachability_constraints.evaluate(db, predicates, reachability_constraint);
918+
let static_reachability = reachability_constraints.evaluate(
919+
db,
920+
predicates,
921+
reachability_constraint,
922+
ignore_never_constraints,
923+
);
877924

878925
if static_reachability.is_always_false() {
879926
// If the static reachability evaluates to false, the binding is either not reachable
@@ -1093,6 +1140,7 @@ fn place_from_declarations_impl<'db>(
10931140
db: &'db dyn Db,
10941141
declarations: DeclarationsIterator<'_, 'db>,
10951142
requires_explicit_reexport: RequiresExplicitReExport,
1143+
ignore_never_constraints: bool,
10961144
) -> PlaceFromDeclarationsResult<'db> {
10971145
let predicates = declarations.predicates;
10981146
let reachability_constraints = declarations.reachability_constraints;
@@ -1107,9 +1155,12 @@ fn place_from_declarations_impl<'db>(
11071155
Some(DeclarationWithConstraint {
11081156
declaration,
11091157
reachability_constraint,
1110-
}) if declaration.is_undefined_or(is_non_exported) => {
1111-
reachability_constraints.evaluate(db, predicates, *reachability_constraint)
1112-
}
1158+
}) if declaration.is_undefined_or(is_non_exported) => reachability_constraints.evaluate(
1159+
db,
1160+
predicates,
1161+
*reachability_constraint,
1162+
ignore_never_constraints,
1163+
),
11131164
_ => Truthiness::AlwaysFalse,
11141165
};
11151166

@@ -1128,8 +1179,12 @@ fn place_from_declarations_impl<'db>(
11281179
return None;
11291180
}
11301181

1131-
let static_reachability =
1132-
reachability_constraints.evaluate(db, predicates, reachability_constraint);
1182+
let static_reachability = reachability_constraints.evaluate(
1183+
db,
1184+
predicates,
1185+
reachability_constraint,
1186+
ignore_never_constraints,
1187+
);
11331188

11341189
if static_reachability.is_always_false() {
11351190
None
@@ -1370,7 +1425,7 @@ impl RequiresExplicitReExport {
13701425
/// ```py
13711426
/// def _():
13721427
/// x = 1
1373-
///
1428+
///
13741429
/// x = 2
13751430
///
13761431
/// if flag():

crates/ty_python_semantic/src/semantic_index/reachability_constraints.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,7 @@ impl ReachabilityConstraints {
572572
db: &'db dyn Db,
573573
predicates: &Predicates<'db>,
574574
mut id: ScopedReachabilityConstraintId,
575+
ignore_never_constraints: bool,
575576
) -> Truthiness {
576577
loop {
577578
let node = match id {
@@ -581,7 +582,7 @@ impl ReachabilityConstraints {
581582
_ => self.interiors[id],
582583
};
583584
let predicate = &predicates[node.atom];
584-
match Self::analyze_single(db, predicate) {
585+
match Self::analyze_single(db, predicate, ignore_never_constraints) {
585586
Truthiness::AlwaysTrue => id = node.if_true,
586587
Truthiness::Ambiguous => id = node.if_ambiguous,
587588
Truthiness::AlwaysFalse => id = node.if_false,
@@ -678,13 +679,21 @@ impl ReachabilityConstraints {
678679
}
679680
}
680681

681-
fn analyze_single(db: &dyn Db, predicate: &Predicate) -> Truthiness {
682+
fn analyze_single(
683+
db: &dyn Db,
684+
predicate: &Predicate,
685+
ignore_never_constraints: bool,
686+
) -> Truthiness {
682687
match predicate.node {
683688
PredicateNode::Expression(test_expr) => {
684689
let ty = infer_expression_type(db, test_expr);
685690
ty.bool(db).negate_if(!predicate.is_positive)
686691
}
687692
PredicateNode::ReturnsNever(test_expr) => {
693+
if ignore_never_constraints {
694+
return Truthiness::AlwaysFalse.negate_if(!predicate.is_positive);
695+
}
696+
688697
let ty = infer_expression_type(db, test_expr);
689698
if let Type::FunctionLiteral(function_literal) = ty {
690699
let returns_never =

crates/ty_python_semantic/src/semantic_index/use_def.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ impl<'db> UseDefMap<'db> {
390390
reachability: ScopedReachabilityConstraintId,
391391
) -> bool {
392392
self.reachability_constraints
393-
.evaluate(db, &self.predicates, reachability)
393+
.evaluate(db, &self.predicates, reachability, false)
394394
.may_be_true()
395395
}
396396

@@ -409,6 +409,7 @@ impl<'db> UseDefMap<'db> {
409409
.node_reachability
410410
.get(&node_key)
411411
.expect("`is_node_reachable` should only be called on AST nodes with recorded reachability"),
412+
false
412413
)
413414
.may_be_true()
414415
}
@@ -505,7 +506,7 @@ impl<'db> UseDefMap<'db> {
505506
pub(crate) fn can_implicitly_return_none(&self, db: &dyn crate::Db) -> bool {
506507
!self
507508
.reachability_constraints
508-
.evaluate(db, &self.predicates, self.end_of_scope_reachability)
509+
.evaluate(db, &self.predicates, self.end_of_scope_reachability, false)
509510
.is_always_false()
510511
}
511512

@@ -518,6 +519,7 @@ impl<'db> UseDefMap<'db> {
518519
db,
519520
&self.predicates,
520521
binding.reachability_constraint,
522+
false,
521523
)
522524
}
523525

crates/ty_python_semantic/src/types/infer.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5539,6 +5539,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
55395539
db,
55405540
predicates,
55415541
binding.reachability_constraint,
5542+
false,
55425543
);
55435544
if static_reachability.is_always_false() {
55445545
continue;

0 commit comments

Comments
 (0)