Compare commits
3 Commits
david/fix-
...
david/fix-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fee980a1ba | ||
|
|
f25b3e2254 | ||
|
|
c6b661f374 |
@@ -19,12 +19,6 @@ class Identity:
|
||||
reveal_type(Identity[0]) # revealed: str
|
||||
```
|
||||
|
||||
`__class_getitem__` is implicitly a classmethod, so it can be called like this:
|
||||
|
||||
```py
|
||||
reveal_type(Identity.__class_getitem__(0)) # revealed: str
|
||||
```
|
||||
|
||||
## Class getitem union
|
||||
|
||||
```py
|
||||
|
||||
@@ -182,7 +182,7 @@ impl<'db> DunderAllNamesCollector<'db> {
|
||||
///
|
||||
/// This function panics if `expr` was not marked as a standalone expression during semantic indexing.
|
||||
fn standalone_expression_type(&self, expr: &ast::Expr) -> Type<'db> {
|
||||
infer_expression_types(self.db, self.index.expression(expr), false).expression_type(expr)
|
||||
infer_expression_types(self.db, self.index.expression(expr)).expression_type(expr)
|
||||
}
|
||||
|
||||
/// Evaluate the given expression and return its truthiness.
|
||||
|
||||
@@ -42,7 +42,7 @@ fn ast_ids<'db>(db: &'db dyn Db, scope: ScopeId) -> &'db AstIds {
|
||||
|
||||
/// Uniquely identifies a use of a name in a [`crate::semantic_index::FileScopeId`].
|
||||
#[newtype_index]
|
||||
#[derive(get_size2::GetSize)]
|
||||
#[derive(get_size2::GetSize, salsa::Update)]
|
||||
pub struct ScopedUseId;
|
||||
|
||||
pub trait HasScopedUseId {
|
||||
|
||||
@@ -328,10 +328,10 @@ fn singleton_to_type(db: &dyn Db, singleton: ruff_python_ast::Singleton) -> Type
|
||||
fn pattern_kind_to_type<'db>(db: &'db dyn Db, kind: &PatternPredicateKind<'db>) -> Type<'db> {
|
||||
match kind {
|
||||
PatternPredicateKind::Singleton(singleton) => singleton_to_type(db, *singleton),
|
||||
PatternPredicateKind::Value(value) => infer_expression_type(db, *value, false),
|
||||
PatternPredicateKind::Value(value) => infer_expression_type(db, *value),
|
||||
PatternPredicateKind::Class(class_expr, kind) => {
|
||||
if kind.is_irrefutable() {
|
||||
infer_expression_type(db, *class_expr, false)
|
||||
infer_expression_type(db, *class_expr)
|
||||
.to_instance(db)
|
||||
.unwrap_or(Type::Never)
|
||||
} else {
|
||||
@@ -718,7 +718,7 @@ impl ReachabilityConstraints {
|
||||
) -> Truthiness {
|
||||
match predicate_kind {
|
||||
PatternPredicateKind::Value(value) => {
|
||||
let value_ty = infer_expression_type(db, *value, false);
|
||||
let value_ty = infer_expression_type(db, *value);
|
||||
|
||||
if subject_ty.is_single_valued(db) {
|
||||
Truthiness::from(subject_ty.is_equivalent_to(db, value_ty))
|
||||
@@ -769,7 +769,7 @@ impl ReachabilityConstraints {
|
||||
truthiness
|
||||
}
|
||||
PatternPredicateKind::Class(class_expr, kind) => {
|
||||
let class_ty = infer_expression_type(db, *class_expr, false).to_instance(db);
|
||||
let class_ty = infer_expression_type(db, *class_expr).to_instance(db);
|
||||
|
||||
class_ty.map_or(Truthiness::Ambiguous, |class_ty| {
|
||||
if subject_ty.is_subtype_of(db, class_ty) {
|
||||
@@ -797,7 +797,7 @@ impl ReachabilityConstraints {
|
||||
}
|
||||
|
||||
fn analyze_single_pattern_predicate(db: &dyn Db, predicate: PatternPredicate) -> Truthiness {
|
||||
let subject_ty = infer_expression_type(db, predicate.subject(db), false);
|
||||
let subject_ty = infer_expression_type(db, predicate.subject(db));
|
||||
|
||||
let narrowed_subject_ty = IntersectionBuilder::new(db)
|
||||
.add_positive(subject_ty)
|
||||
@@ -837,7 +837,7 @@ impl ReachabilityConstraints {
|
||||
// selection algorithm).
|
||||
// Avoiding this on the happy-path is important because these constraints can be
|
||||
// very large in number, since we add them on all statement level function calls.
|
||||
let ty = infer_expression_type(db, callable, false);
|
||||
let ty = infer_expression_type(db, callable);
|
||||
|
||||
// Short-circuit for well known types that are known not to return `Never` when called.
|
||||
// Without the short-circuit, we've seen that threads keep blocking each other
|
||||
@@ -875,7 +875,7 @@ impl ReachabilityConstraints {
|
||||
} else if all_overloads_return_never {
|
||||
Truthiness::AlwaysTrue
|
||||
} else {
|
||||
let call_expr_ty = infer_expression_type(db, call_expr, false);
|
||||
let call_expr_ty = infer_expression_type(db, call_expr);
|
||||
if call_expr_ty.is_equivalent_to(db, Type::Never) {
|
||||
Truthiness::AlwaysTrue
|
||||
} else {
|
||||
|
||||
@@ -3337,12 +3337,7 @@ impl<'db> Type<'db> {
|
||||
name: Name,
|
||||
policy: MemberLookupPolicy,
|
||||
) -> PlaceAndQualifiers<'db> {
|
||||
let _span = tracing::trace_span!(
|
||||
"member_lookup_with_policy",
|
||||
ty = self.display(db).to_string(),
|
||||
?name
|
||||
)
|
||||
.entered();
|
||||
let _span = tracing::trace_span!("member_lookup_with_policy: {}", ?name).entered();
|
||||
if name == "__class__" {
|
||||
return Place::bound(self.dunder_class(db)).into();
|
||||
}
|
||||
@@ -10428,7 +10423,11 @@ static_assertions::assert_eq_size!(Type, [u8; 16]);
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::db::tests::{TestDbBuilder, setup_db};
|
||||
use crate::place::{typing_extensions_symbol, typing_symbol};
|
||||
use crate::place::{global_symbol, typing_extensions_symbol, typing_symbol};
|
||||
use ruff_db::files::system_path_to_file;
|
||||
use ruff_db::parsed::parsed_module;
|
||||
use ruff_db::system::DbWithWritableSystem as _;
|
||||
use ruff_db::testing::assert_function_query_was_not_run;
|
||||
use ruff_python_ast::PythonVersion;
|
||||
use test_case::test_case;
|
||||
|
||||
@@ -10469,62 +10468,62 @@ pub(crate) mod tests {
|
||||
|
||||
/// Inferring the result of a call-expression shouldn't need to re-run after
|
||||
/// a trivial change to the function's file (e.g. by adding a docstring to the function).
|
||||
// #[test]
|
||||
// fn call_type_doesnt_rerun_when_only_callee_changed() -> anyhow::Result<()> {
|
||||
// let mut db = setup_db();
|
||||
#[test]
|
||||
fn call_type_doesnt_rerun_when_only_callee_changed() -> anyhow::Result<()> {
|
||||
let mut db = setup_db();
|
||||
|
||||
// db.write_dedented(
|
||||
// "src/foo.py",
|
||||
// r#"
|
||||
// def foo() -> int:
|
||||
// return 5
|
||||
// "#,
|
||||
// )?;
|
||||
// db.write_dedented(
|
||||
// "src/bar.py",
|
||||
// r#"
|
||||
// from foo import foo
|
||||
db.write_dedented(
|
||||
"src/foo.py",
|
||||
r#"
|
||||
def foo() -> int:
|
||||
return 5
|
||||
"#,
|
||||
)?;
|
||||
db.write_dedented(
|
||||
"src/bar.py",
|
||||
r#"
|
||||
from foo import foo
|
||||
|
||||
// a = foo()
|
||||
// "#,
|
||||
// )?;
|
||||
a = foo()
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// let bar = system_path_to_file(&db, "src/bar.py")?;
|
||||
// let a = global_symbol(&db, bar, "a").place;
|
||||
let bar = system_path_to_file(&db, "src/bar.py")?;
|
||||
let a = global_symbol(&db, bar, "a").place;
|
||||
|
||||
// assert_eq!(
|
||||
// a.expect_type(),
|
||||
// UnionType::from_elements(&db, [Type::unknown(), KnownClass::Int.to_instance(&db)])
|
||||
// );
|
||||
assert_eq!(
|
||||
a.expect_type(),
|
||||
UnionType::from_elements(&db, [Type::unknown(), KnownClass::Int.to_instance(&db)])
|
||||
);
|
||||
|
||||
// // Add a docstring to foo to trigger a re-run.
|
||||
// // The bar-call site of foo should not be re-run because of that
|
||||
// db.write_dedented(
|
||||
// "src/foo.py",
|
||||
// r#"
|
||||
// def foo() -> int:
|
||||
// "Computes a value"
|
||||
// return 5
|
||||
// "#,
|
||||
// )?;
|
||||
// db.clear_salsa_events();
|
||||
// Add a docstring to foo to trigger a re-run.
|
||||
// The bar-call site of foo should not be re-run because of that
|
||||
db.write_dedented(
|
||||
"src/foo.py",
|
||||
r#"
|
||||
def foo() -> int:
|
||||
"Computes a value"
|
||||
return 5
|
||||
"#,
|
||||
)?;
|
||||
db.clear_salsa_events();
|
||||
|
||||
// let a = global_symbol(&db, bar, "a").place;
|
||||
let a = global_symbol(&db, bar, "a").place;
|
||||
|
||||
// assert_eq!(
|
||||
// a.expect_type(),
|
||||
// UnionType::from_elements(&db, [Type::unknown(), KnownClass::Int.to_instance(&db)])
|
||||
// );
|
||||
// let events = db.take_salsa_events();
|
||||
assert_eq!(
|
||||
a.expect_type(),
|
||||
UnionType::from_elements(&db, [Type::unknown(), KnownClass::Int.to_instance(&db)])
|
||||
);
|
||||
let events = db.take_salsa_events();
|
||||
|
||||
// let module = parsed_module(&db, bar).load(&db);
|
||||
// let call = &*module.syntax().body[1].as_assign_stmt().unwrap().value;
|
||||
// let foo_call = semantic_index(&db, bar).expression(call);
|
||||
let module = parsed_module(&db, bar).load(&db);
|
||||
let call = &*module.syntax().body[1].as_assign_stmt().unwrap().value;
|
||||
let foo_call = semantic_index(&db, bar).expression(call);
|
||||
|
||||
// assert_function_query_was_not_run(&db, infer_expression_types, foo_call, &events, false);
|
||||
assert_function_query_was_not_run(&db, infer_expression_types, foo_call, &events);
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// All other tests also make sure that `Type::Todo` works as expected. This particular
|
||||
/// test makes sure that we handle `Todo` types correctly, even if they originate from
|
||||
|
||||
@@ -2816,6 +2816,8 @@ impl<'db> ClassLiteral<'db> {
|
||||
name: String,
|
||||
target_method_decorator: MethodDecorator,
|
||||
) -> PlaceAndQualifiers<'db> {
|
||||
let _span =
|
||||
tracing::trace_span!("implicit_attribute_inner", ?class_body_scope, ?name).entered();
|
||||
// If we do not see any declarations of an attribute, neither in the class body nor in
|
||||
// any method, we build a union of `Unknown` with the inferred types of all bindings of
|
||||
// that attribute. We include `Unknown` in that union to account for the fact that the
|
||||
@@ -2882,7 +2884,7 @@ impl<'db> ClassLiteral<'db> {
|
||||
// `self.SOME_CONSTANT: Final = 1`, infer the type from the value
|
||||
// on the right-hand side.
|
||||
|
||||
let inferred_ty = infer_expression_type(db, index.expression(value), true);
|
||||
let inferred_ty = infer_expression_type(db, index.expression(value));
|
||||
return Place::bound(inferred_ty).with_qualifiers(all_qualifiers);
|
||||
}
|
||||
|
||||
@@ -2968,7 +2970,6 @@ impl<'db> ClassLiteral<'db> {
|
||||
let inferred_ty = infer_expression_type(
|
||||
db,
|
||||
index.expression(assign.value(&module)),
|
||||
true,
|
||||
);
|
||||
|
||||
union_of_inferred_types = union_of_inferred_types.add(inferred_ty);
|
||||
@@ -2996,7 +2997,6 @@ impl<'db> ClassLiteral<'db> {
|
||||
let iterable_ty = infer_expression_type(
|
||||
db,
|
||||
index.expression(for_stmt.iterable(&module)),
|
||||
true,
|
||||
);
|
||||
// TODO: Potential diagnostics resulting from the iterable are currently not reported.
|
||||
let inferred_ty =
|
||||
@@ -3027,7 +3027,6 @@ impl<'db> ClassLiteral<'db> {
|
||||
let context_ty = infer_expression_type(
|
||||
db,
|
||||
index.expression(with_item.context_expr(&module)),
|
||||
true,
|
||||
);
|
||||
let inferred_ty = if with_item.is_async() {
|
||||
context_ty.aenter(db)
|
||||
@@ -3061,7 +3060,6 @@ impl<'db> ClassLiteral<'db> {
|
||||
let iterable_ty = infer_expression_type(
|
||||
db,
|
||||
index.expression(comprehension.iterable(&module)),
|
||||
true,
|
||||
);
|
||||
// TODO: Potential diagnostics resulting from the iterable are currently not reported.
|
||||
let inferred_ty =
|
||||
|
||||
@@ -725,10 +725,7 @@ impl<'db> FunctionType<'db> {
|
||||
/// classmethod.
|
||||
pub(crate) fn is_classmethod(self, db: &'db dyn Db) -> bool {
|
||||
self.has_known_decorator(db, FunctionDecorators::CLASSMETHOD)
|
||||
|| matches!(
|
||||
self.name(db).as_str(),
|
||||
"__init_subclass__" | "__class_getitem__"
|
||||
)
|
||||
|| self.name(db) == "__init_subclass__"
|
||||
}
|
||||
|
||||
/// If the implementation of this function is deprecated, returns the `@warnings.deprecated`.
|
||||
|
||||
@@ -83,7 +83,7 @@ use crate::semantic_index::definition::{
|
||||
};
|
||||
use crate::semantic_index::expression::{Expression, ExpressionKind};
|
||||
use crate::semantic_index::narrowing_constraints::ConstraintKey;
|
||||
use crate::semantic_index::place::{PlaceExpr, PlaceExprRef};
|
||||
use crate::semantic_index::place::{PlaceExpr, PlaceExprRef, ScopedPlaceId};
|
||||
use crate::semantic_index::scope::{
|
||||
FileScopeId, NodeWithScopeKind, NodeWithScopeRef, ScopeId, ScopeKind,
|
||||
};
|
||||
@@ -137,7 +137,7 @@ use crate::types::{
|
||||
use crate::unpack::{EvaluationMode, Unpack, UnpackPosition};
|
||||
use crate::util::diagnostics::format_enumeration;
|
||||
use crate::util::subscript::{PyIndex, PySlice};
|
||||
use crate::{Db, FxOrderSet, Program};
|
||||
use crate::{Db, FxOrderSet, Program, db};
|
||||
|
||||
/// Infer all types for a [`ScopeId`], including all definitions and expressions in that scope.
|
||||
/// Use when checking a scope, or needing to provide a type for an arbitrary expression in the
|
||||
@@ -171,7 +171,6 @@ fn scope_cycle_initial<'db>(_db: &'db dyn Db, scope: ScopeId<'db>) -> ScopeInfer
|
||||
|
||||
/// Infer all types for a [`Definition`] (including sub-expressions).
|
||||
/// Use when resolving a place use or public type of a place.
|
||||
#[salsa::tracked(returns(ref), cycle_fn=definition_cycle_recover, cycle_initial=definition_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
|
||||
pub(crate) fn infer_definition_types<'db>(
|
||||
db: &'db dyn Db,
|
||||
definition: Definition<'db>,
|
||||
@@ -252,11 +251,9 @@ fn deferred_cycle_initial<'db>(
|
||||
/// Use rarely; only for cases where we'd otherwise risk double-inferring an expression: RHS of an
|
||||
/// assignment, which might be unpacking/multi-target and thus part of multiple definitions, or a
|
||||
/// type narrowing guard expression (e.g. if statement test node).
|
||||
#[salsa::tracked(returns(ref), cycle_fn=expression_cycle_recover, cycle_initial=expression_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
|
||||
pub(crate) fn infer_expression_types<'db>(
|
||||
db: &'db dyn Db,
|
||||
expression: Expression<'db>,
|
||||
_break_cycle: bool,
|
||||
) -> ExpressionInference<'db> {
|
||||
let file = expression.file(db);
|
||||
let module = parsed_module(db, file).load(db);
|
||||
@@ -279,7 +276,6 @@ fn expression_cycle_recover<'db>(
|
||||
_value: &ExpressionInference<'db>,
|
||||
_count: u32,
|
||||
_expression: Expression<'db>,
|
||||
_break_cycle: bool,
|
||||
) -> salsa::CycleRecoveryAction<ExpressionInference<'db>> {
|
||||
salsa::CycleRecoveryAction::Iterate
|
||||
}
|
||||
@@ -287,7 +283,6 @@ fn expression_cycle_recover<'db>(
|
||||
fn expression_cycle_initial<'db>(
|
||||
db: &'db dyn Db,
|
||||
expression: Expression<'db>,
|
||||
_break_cycle: bool,
|
||||
) -> ExpressionInference<'db> {
|
||||
ExpressionInference::cycle_fallback(expression.scope(db))
|
||||
}
|
||||
@@ -301,9 +296,8 @@ pub(super) fn infer_same_file_expression_type<'db>(
|
||||
db: &'db dyn Db,
|
||||
expression: Expression<'db>,
|
||||
parsed: &ParsedModuleRef,
|
||||
break_cycle: bool,
|
||||
) -> Type<'db> {
|
||||
let inference = infer_expression_types(db, expression, break_cycle);
|
||||
let inference = infer_expression_types(db, expression);
|
||||
inference.expression_type(expression.node_ref(db, parsed))
|
||||
}
|
||||
|
||||
@@ -318,33 +312,19 @@ pub(super) fn infer_same_file_expression_type<'db>(
|
||||
pub(crate) fn infer_expression_type<'db>(
|
||||
db: &'db dyn Db,
|
||||
expression: Expression<'db>,
|
||||
break_cycle: bool,
|
||||
) -> Type<'db> {
|
||||
let file = expression.file(db);
|
||||
let module = parsed_module(db, file).load(db);
|
||||
|
||||
// It's okay to call the "same file" version here because we're inside a salsa query.
|
||||
infer_same_file_expression_type(db, expression, &module, break_cycle)
|
||||
infer_same_file_expression_type(db, expression, &module)
|
||||
}
|
||||
|
||||
// #[salsa::tracked(cycle_fn=single_expression_cycle_recover, cycle_initial=single_expression_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
|
||||
// pub(crate) fn infer_expression_type_query<'db>(
|
||||
// db: &'db dyn Db,
|
||||
// expression: Expression<'db>,
|
||||
// ) -> Type<'db> {
|
||||
// let file = expression.file(db);
|
||||
// let module = parsed_module(db, file).load(db);
|
||||
|
||||
// // It's okay to call the "same file" version here because we're inside a salsa query.
|
||||
// infer_same_file_expression_type(db, expression, &module)
|
||||
// }
|
||||
|
||||
fn single_expression_cycle_recover<'db>(
|
||||
_db: &'db dyn Db,
|
||||
_value: &Type<'db>,
|
||||
_count: u32,
|
||||
_expression: Expression<'db>,
|
||||
_break_cycle: bool,
|
||||
) -> salsa::CycleRecoveryAction<Type<'db>> {
|
||||
salsa::CycleRecoveryAction::Iterate
|
||||
}
|
||||
@@ -352,7 +332,6 @@ fn single_expression_cycle_recover<'db>(
|
||||
fn single_expression_cycle_initial<'db>(
|
||||
_db: &'db dyn Db,
|
||||
_expression: Expression<'db>,
|
||||
_break_cycle: bool,
|
||||
) -> Type<'db> {
|
||||
Type::Never
|
||||
}
|
||||
@@ -366,7 +345,7 @@ pub(crate) fn static_expression_truthiness<'db>(
|
||||
db: &'db dyn Db,
|
||||
expression: Expression<'db>,
|
||||
) -> Truthiness {
|
||||
let inference = infer_expression_types(db, expression, false);
|
||||
let inference = infer_expression_types(db, expression);
|
||||
|
||||
if !inference.all_places_definitely_bound() {
|
||||
return Truthiness::Ambiguous;
|
||||
@@ -5654,8 +5633,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
expression: &ast::Expr,
|
||||
standalone_expression: Expression<'db>,
|
||||
) -> Type<'db> {
|
||||
let types = infer_expression_types(self.db(), standalone_expression, false);
|
||||
self.extend_expression(types);
|
||||
let types = infer_expression_types(self.db(), standalone_expression);
|
||||
self.extend_expression(&types);
|
||||
|
||||
// Instead of calling `self.expression_type(expr)` after extending here, we get
|
||||
// the result from `types` directly because we might be in cycle recovery where
|
||||
@@ -6088,7 +6067,6 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
builder.db(),
|
||||
builder.index.expression(iter_expr),
|
||||
builder.module(),
|
||||
false,
|
||||
)
|
||||
} else {
|
||||
builder.infer_standalone_expression(iter_expr)
|
||||
@@ -6111,7 +6089,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
|
||||
let mut infer_iterable_type = || {
|
||||
let expression = self.index.expression(iterable);
|
||||
let result = infer_expression_types(self.db(), expression, false);
|
||||
let result = infer_expression_types(self.db(), expression);
|
||||
|
||||
// Two things are different if it's the first comprehension:
|
||||
// (1) We must lookup the `ScopedExpressionId` of the iterable expression in the outer scope,
|
||||
@@ -6122,7 +6100,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
if comprehension.is_first() && target.is_name_expr() {
|
||||
result.expression_type(iterable)
|
||||
} else {
|
||||
self.extend_expression_unchecked(result);
|
||||
self.extend_expression_unchecked(&result);
|
||||
result.expression_type(iterable)
|
||||
}
|
||||
};
|
||||
@@ -6817,37 +6795,27 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
expr: PlaceExprRef,
|
||||
expr_ref: ast::ExprRef,
|
||||
) -> (Place<'db>, Option<ScopedUseId>) {
|
||||
if expr_ref
|
||||
.as_name_expr()
|
||||
.is_some_and(|name| name.is_invalid())
|
||||
{
|
||||
return (Place::Unbound, None);
|
||||
}
|
||||
|
||||
let db = self.db();
|
||||
let scope = self.scope();
|
||||
let file_scope_id = scope.file_scope_id(db);
|
||||
let place_table = self.index.place_table(file_scope_id);
|
||||
let use_def = self.index.use_def_map(file_scope_id);
|
||||
|
||||
// If we're inferring types of deferred expressions, look them up from end-of-scope.
|
||||
if self.is_deferred() {
|
||||
let place = if let Some(place_id) = place_table.place_id(expr) {
|
||||
place_from_bindings(db, use_def.all_reachable_bindings(place_id))
|
||||
} else {
|
||||
assert!(
|
||||
self.deferred_state.in_string_annotation(),
|
||||
"Expected the place table to create a place for every valid PlaceExpr node"
|
||||
);
|
||||
Place::Unbound
|
||||
};
|
||||
(place, None)
|
||||
let is_deferred = self.is_deferred();
|
||||
let deferred_state = self.deferred_state;
|
||||
let use_id = if is_deferred {
|
||||
None
|
||||
} else {
|
||||
if expr_ref
|
||||
.as_name_expr()
|
||||
.is_some_and(|name| name.is_invalid())
|
||||
{
|
||||
return (Place::Unbound, None);
|
||||
}
|
||||
Some(expr_ref.scoped_use_id(db, scope))
|
||||
};
|
||||
|
||||
let use_id = expr_ref.scoped_use_id(db, scope);
|
||||
let place = place_from_bindings(db, use_def.bindings_at_use(use_id));
|
||||
let place_table = self.index.place_table(scope.file_scope_id(db));
|
||||
let place_id = place_table.place_id(expr);
|
||||
|
||||
(place, Some(use_id))
|
||||
}
|
||||
infer_local_place_load(db, scope, deferred_state, place_id, use_id)
|
||||
}
|
||||
|
||||
/// Infer the type of a place expression from definitions, assuming a load context.
|
||||
@@ -7361,6 +7329,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
}
|
||||
|
||||
fn infer_attribute_expression(&mut self, attribute: &ast::ExprAttribute) -> Type<'db> {
|
||||
let _span = tracing::debug_span!("infer_attribute_expression", ?attribute).entered();
|
||||
|
||||
let ast::ExprAttribute {
|
||||
value,
|
||||
attr: _,
|
||||
@@ -9223,7 +9193,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
}
|
||||
}
|
||||
|
||||
match ty.try_call(db, &CallArguments::positional([slice_ty])) {
|
||||
match ty.try_call(db, &CallArguments::positional([value_ty, slice_ty])) {
|
||||
Ok(bindings) => return bindings.return_type(db),
|
||||
Err(CallError(_, bindings)) => {
|
||||
if let Some(builder) =
|
||||
@@ -11316,6 +11286,60 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn infer_local_place_load_cycle_recover<'db>(
|
||||
_db: &'db dyn Db,
|
||||
_value: &(Place<'db>, Option<ScopedUseId>),
|
||||
_count: u32,
|
||||
_scope: ScopeId<'db>,
|
||||
_deferred_state: DeferredExpressionState,
|
||||
_place_id: Option<ScopedPlaceId>,
|
||||
_use_id: Option<ScopedUseId>,
|
||||
) -> salsa::CycleRecoveryAction<(Place<'db>, Option<ScopedUseId>)> {
|
||||
salsa::CycleRecoveryAction::Iterate
|
||||
}
|
||||
|
||||
fn infer_local_place_load_cycle_initial<'db>(
|
||||
_db: &'db dyn Db,
|
||||
_scope: ScopeId<'db>,
|
||||
_deferred_state: DeferredExpressionState,
|
||||
_place_id: Option<ScopedPlaceId>,
|
||||
_use_id: Option<ScopedUseId>,
|
||||
) -> (Place<'db>, Option<ScopedUseId>) {
|
||||
(Place::Unbound, None)
|
||||
}
|
||||
|
||||
#[salsa::tracked(cycle_fn=infer_local_place_load_cycle_recover, cycle_initial=infer_local_place_load_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
|
||||
fn infer_local_place_load<'db>(
|
||||
db: &'db dyn Db,
|
||||
scope: ScopeId<'db>,
|
||||
deferred_state: DeferredExpressionState,
|
||||
place_id: Option<ScopedPlaceId>,
|
||||
use_id: Option<ScopedUseId>,
|
||||
) -> (Place<'db>, Option<ScopedUseId>) {
|
||||
let file_scope_id = scope.file_scope_id(db);
|
||||
let index = semantic_index(db, scope.file(db));
|
||||
let use_def = index.use_def_map(file_scope_id);
|
||||
|
||||
if let Some(use_id) = use_id {
|
||||
// Non-deferred load
|
||||
let place = place_from_bindings(db, use_def.bindings_at_use(use_id));
|
||||
|
||||
(place, Some(use_id))
|
||||
} else {
|
||||
// If we're inferring types of deferred expressions, look them up from end-of-scope.
|
||||
let place = if let Some(place_id) = place_id {
|
||||
place_from_bindings(db, use_def.all_reachable_bindings(place_id))
|
||||
} else {
|
||||
assert!(
|
||||
deferred_state.in_string_annotation(),
|
||||
"Expected the place table to create a place for every valid PlaceExpr node"
|
||||
);
|
||||
Place::Unbound
|
||||
};
|
||||
(place, None)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum GenericContextError {
|
||||
/// It's invalid to subscript `Generic` or `Protocol` with this type
|
||||
@@ -11340,7 +11364,7 @@ impl GenericContextError {
|
||||
}
|
||||
|
||||
/// The deferred state of a specific expression in an inference region.
|
||||
#[derive(Default, Debug, Clone, Copy)]
|
||||
#[derive(Default, Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||
enum DeferredExpressionState {
|
||||
/// The expression is not deferred.
|
||||
#[default]
|
||||
@@ -11687,7 +11711,7 @@ mod tests {
|
||||
use ruff_db::diagnostic::Diagnostic;
|
||||
use ruff_db::files::{File, system_path_to_file};
|
||||
use ruff_db::system::DbWithWritableSystem as _;
|
||||
use ruff_db::testing::assert_function_query_was_not_run;
|
||||
use ruff_db::testing::{assert_function_query_was_not_run, assert_function_query_was_run};
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -12069,267 +12093,267 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn dependency_implicit_instance_attribute() -> anyhow::Result<()> {
|
||||
// fn x_rhs_expression(db: &TestDb) -> Expression<'_> {
|
||||
// let file_main = system_path_to_file(db, "/src/main.py").unwrap();
|
||||
// let ast = parsed_module(db, file_main).load(db);
|
||||
// // Get the second statement in `main.py` (x = …) and extract the expression
|
||||
// // node on the right-hand side:
|
||||
// let x_rhs_node = &ast.syntax().body[1].as_assign_stmt().unwrap().value;
|
||||
#[test]
|
||||
fn dependency_implicit_instance_attribute() -> anyhow::Result<()> {
|
||||
fn x_rhs_expression(db: &TestDb) -> Expression<'_> {
|
||||
let file_main = system_path_to_file(db, "/src/main.py").unwrap();
|
||||
let ast = parsed_module(db, file_main).load(db);
|
||||
// Get the second statement in `main.py` (x = …) and extract the expression
|
||||
// node on the right-hand side:
|
||||
let x_rhs_node = &ast.syntax().body[1].as_assign_stmt().unwrap().value;
|
||||
|
||||
// let index = semantic_index(db, file_main);
|
||||
// index.expression(x_rhs_node.as_ref())
|
||||
// }
|
||||
let index = semantic_index(db, file_main);
|
||||
index.expression(x_rhs_node.as_ref())
|
||||
}
|
||||
|
||||
// let mut db = setup_db();
|
||||
let mut db = setup_db();
|
||||
|
||||
// db.write_dedented(
|
||||
// "/src/mod.py",
|
||||
// r#"
|
||||
// class C:
|
||||
// def f(self):
|
||||
// self.attr: int | None = None
|
||||
// "#,
|
||||
// )?;
|
||||
// db.write_dedented(
|
||||
// "/src/main.py",
|
||||
// r#"
|
||||
// from mod import C
|
||||
// x = C().attr
|
||||
// "#,
|
||||
// )?;
|
||||
db.write_dedented(
|
||||
"/src/mod.py",
|
||||
r#"
|
||||
class C:
|
||||
def f(self):
|
||||
self.attr: int | None = None
|
||||
"#,
|
||||
)?;
|
||||
db.write_dedented(
|
||||
"/src/main.py",
|
||||
r#"
|
||||
from mod import C
|
||||
x = C().attr
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// let file_main = system_path_to_file(&db, "/src/main.py").unwrap();
|
||||
// let attr_ty = global_symbol(&db, file_main, "x").place.expect_type();
|
||||
// assert_eq!(attr_ty.display(&db).to_string(), "Unknown | int | None");
|
||||
let file_main = system_path_to_file(&db, "/src/main.py").unwrap();
|
||||
let attr_ty = global_symbol(&db, file_main, "x").place.expect_type();
|
||||
assert_eq!(attr_ty.display(&db).to_string(), "Unknown | int | None");
|
||||
|
||||
// // Change the type of `attr` to `str | None`; this should trigger the type of `x` to be re-inferred
|
||||
// db.write_dedented(
|
||||
// "/src/mod.py",
|
||||
// r#"
|
||||
// class C:
|
||||
// def f(self):
|
||||
// self.attr: str | None = None
|
||||
// "#,
|
||||
// )?;
|
||||
// Change the type of `attr` to `str | None`; this should trigger the type of `x` to be re-inferred
|
||||
db.write_dedented(
|
||||
"/src/mod.py",
|
||||
r#"
|
||||
class C:
|
||||
def f(self):
|
||||
self.attr: str | None = None
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// let events = {
|
||||
// db.clear_salsa_events();
|
||||
// let attr_ty = global_symbol(&db, file_main, "x").place.expect_type();
|
||||
// assert_eq!(attr_ty.display(&db).to_string(), "Unknown | str | None");
|
||||
// db.take_salsa_events()
|
||||
// };
|
||||
// assert_function_query_was_run(&db, infer_expression_types, x_rhs_expression(&db), &events);
|
||||
let events = {
|
||||
db.clear_salsa_events();
|
||||
let attr_ty = global_symbol(&db, file_main, "x").place.expect_type();
|
||||
assert_eq!(attr_ty.display(&db).to_string(), "Unknown | str | None");
|
||||
db.take_salsa_events()
|
||||
};
|
||||
assert_function_query_was_run(&db, infer_expression_types, x_rhs_expression(&db), &events);
|
||||
|
||||
// // Add a comment; this should not trigger the type of `x` to be re-inferred
|
||||
// db.write_dedented(
|
||||
// "/src/mod.py",
|
||||
// r#"
|
||||
// class C:
|
||||
// def f(self):
|
||||
// # a comment!
|
||||
// self.attr: str | None = None
|
||||
// "#,
|
||||
// )?;
|
||||
// Add a comment; this should not trigger the type of `x` to be re-inferred
|
||||
db.write_dedented(
|
||||
"/src/mod.py",
|
||||
r#"
|
||||
class C:
|
||||
def f(self):
|
||||
# a comment!
|
||||
self.attr: str | None = None
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// let events = {
|
||||
// db.clear_salsa_events();
|
||||
// let attr_ty = global_symbol(&db, file_main, "x").place.expect_type();
|
||||
// assert_eq!(attr_ty.display(&db).to_string(), "Unknown | str | None");
|
||||
// db.take_salsa_events()
|
||||
// };
|
||||
let events = {
|
||||
db.clear_salsa_events();
|
||||
let attr_ty = global_symbol(&db, file_main, "x").place.expect_type();
|
||||
assert_eq!(attr_ty.display(&db).to_string(), "Unknown | str | None");
|
||||
db.take_salsa_events()
|
||||
};
|
||||
|
||||
// assert_function_query_was_not_run(
|
||||
// &db,
|
||||
// infer_expression_types,
|
||||
// x_rhs_expression(&db),
|
||||
// &events,
|
||||
// );
|
||||
assert_function_query_was_not_run(
|
||||
&db,
|
||||
infer_expression_types,
|
||||
x_rhs_expression(&db),
|
||||
&events,
|
||||
);
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// /// This test verifies that changing a class's declaration in a non-meaningful way (e.g. by adding a comment)
|
||||
// /// doesn't trigger type inference for expressions that depend on the class's members.
|
||||
// #[test]
|
||||
// fn dependency_own_instance_member() -> anyhow::Result<()> {
|
||||
// fn x_rhs_expression(db: &TestDb) -> Expression<'_> {
|
||||
// let file_main = system_path_to_file(db, "/src/main.py").unwrap();
|
||||
// let ast = parsed_module(db, file_main).load(db);
|
||||
// // Get the second statement in `main.py` (x = …) and extract the expression
|
||||
// // node on the right-hand side:
|
||||
// let x_rhs_node = &ast.syntax().body[1].as_assign_stmt().unwrap().value;
|
||||
/// This test verifies that changing a class's declaration in a non-meaningful way (e.g. by adding a comment)
|
||||
/// doesn't trigger type inference for expressions that depend on the class's members.
|
||||
#[test]
|
||||
fn dependency_own_instance_member() -> anyhow::Result<()> {
|
||||
fn x_rhs_expression(db: &TestDb) -> Expression<'_> {
|
||||
let file_main = system_path_to_file(db, "/src/main.py").unwrap();
|
||||
let ast = parsed_module(db, file_main).load(db);
|
||||
// Get the second statement in `main.py` (x = …) and extract the expression
|
||||
// node on the right-hand side:
|
||||
let x_rhs_node = &ast.syntax().body[1].as_assign_stmt().unwrap().value;
|
||||
|
||||
// let index = semantic_index(db, file_main);
|
||||
// index.expression(x_rhs_node.as_ref())
|
||||
// }
|
||||
let index = semantic_index(db, file_main);
|
||||
index.expression(x_rhs_node.as_ref())
|
||||
}
|
||||
|
||||
// let mut db = setup_db();
|
||||
let mut db = setup_db();
|
||||
|
||||
// db.write_dedented(
|
||||
// "/src/mod.py",
|
||||
// r#"
|
||||
// class C:
|
||||
// if random.choice([True, False]):
|
||||
// attr: int = 42
|
||||
// else:
|
||||
// attr: None = None
|
||||
// "#,
|
||||
// )?;
|
||||
// db.write_dedented(
|
||||
// "/src/main.py",
|
||||
// r#"
|
||||
// from mod import C
|
||||
// x = C().attr
|
||||
// "#,
|
||||
// )?;
|
||||
db.write_dedented(
|
||||
"/src/mod.py",
|
||||
r#"
|
||||
class C:
|
||||
if random.choice([True, False]):
|
||||
attr: int = 42
|
||||
else:
|
||||
attr: None = None
|
||||
"#,
|
||||
)?;
|
||||
db.write_dedented(
|
||||
"/src/main.py",
|
||||
r#"
|
||||
from mod import C
|
||||
x = C().attr
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// let file_main = system_path_to_file(&db, "/src/main.py").unwrap();
|
||||
// let attr_ty = global_symbol(&db, file_main, "x").place.expect_type();
|
||||
// assert_eq!(attr_ty.display(&db).to_string(), "Unknown | int | None");
|
||||
let file_main = system_path_to_file(&db, "/src/main.py").unwrap();
|
||||
let attr_ty = global_symbol(&db, file_main, "x").place.expect_type();
|
||||
assert_eq!(attr_ty.display(&db).to_string(), "Unknown | int | None");
|
||||
|
||||
// // Change the type of `attr` to `str | None`; this should trigger the type of `x` to be re-inferred
|
||||
// db.write_dedented(
|
||||
// "/src/mod.py",
|
||||
// r#"
|
||||
// class C:
|
||||
// if random.choice([True, False]):
|
||||
// attr: str = "42"
|
||||
// else:
|
||||
// attr: None = None
|
||||
// "#,
|
||||
// )?;
|
||||
// Change the type of `attr` to `str | None`; this should trigger the type of `x` to be re-inferred
|
||||
db.write_dedented(
|
||||
"/src/mod.py",
|
||||
r#"
|
||||
class C:
|
||||
if random.choice([True, False]):
|
||||
attr: str = "42"
|
||||
else:
|
||||
attr: None = None
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// let events = {
|
||||
// db.clear_salsa_events();
|
||||
// let attr_ty = global_symbol(&db, file_main, "x").place.expect_type();
|
||||
// assert_eq!(attr_ty.display(&db).to_string(), "Unknown | str | None");
|
||||
// db.take_salsa_events()
|
||||
// };
|
||||
// assert_function_query_was_run(&db, infer_expression_types, x_rhs_expression(&db), &events);
|
||||
let events = {
|
||||
db.clear_salsa_events();
|
||||
let attr_ty = global_symbol(&db, file_main, "x").place.expect_type();
|
||||
assert_eq!(attr_ty.display(&db).to_string(), "Unknown | str | None");
|
||||
db.take_salsa_events()
|
||||
};
|
||||
assert_function_query_was_run(&db, infer_expression_types, x_rhs_expression(&db), &events);
|
||||
|
||||
// // Add a comment; this should not trigger the type of `x` to be re-inferred
|
||||
// db.write_dedented(
|
||||
// "/src/mod.py",
|
||||
// r#"
|
||||
// class C:
|
||||
// # comment
|
||||
// if random.choice([True, False]):
|
||||
// attr: str = "42"
|
||||
// else:
|
||||
// attr: None = None
|
||||
// "#,
|
||||
// )?;
|
||||
// Add a comment; this should not trigger the type of `x` to be re-inferred
|
||||
db.write_dedented(
|
||||
"/src/mod.py",
|
||||
r#"
|
||||
class C:
|
||||
# comment
|
||||
if random.choice([True, False]):
|
||||
attr: str = "42"
|
||||
else:
|
||||
attr: None = None
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// let events = {
|
||||
// db.clear_salsa_events();
|
||||
// let attr_ty = global_symbol(&db, file_main, "x").place.expect_type();
|
||||
// assert_eq!(attr_ty.display(&db).to_string(), "Unknown | str | None");
|
||||
// db.take_salsa_events()
|
||||
// };
|
||||
let events = {
|
||||
db.clear_salsa_events();
|
||||
let attr_ty = global_symbol(&db, file_main, "x").place.expect_type();
|
||||
assert_eq!(attr_ty.display(&db).to_string(), "Unknown | str | None");
|
||||
db.take_salsa_events()
|
||||
};
|
||||
|
||||
// assert_function_query_was_not_run(
|
||||
// &db,
|
||||
// infer_expression_types,
|
||||
// x_rhs_expression(&db),
|
||||
// &events,
|
||||
// );
|
||||
assert_function_query_was_not_run(
|
||||
&db,
|
||||
infer_expression_types,
|
||||
x_rhs_expression(&db),
|
||||
&events,
|
||||
);
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn dependency_implicit_class_member() -> anyhow::Result<()> {
|
||||
// fn x_rhs_expression(db: &TestDb) -> Expression<'_> {
|
||||
// let file_main = system_path_to_file(db, "/src/main.py").unwrap();
|
||||
// let ast = parsed_module(db, file_main).load(db);
|
||||
// // Get the third statement in `main.py` (x = …) and extract the expression
|
||||
// // node on the right-hand side:
|
||||
// let x_rhs_node = &ast.syntax().body[2].as_assign_stmt().unwrap().value;
|
||||
#[test]
|
||||
fn dependency_implicit_class_member() -> anyhow::Result<()> {
|
||||
fn x_rhs_expression(db: &TestDb) -> Expression<'_> {
|
||||
let file_main = system_path_to_file(db, "/src/main.py").unwrap();
|
||||
let ast = parsed_module(db, file_main).load(db);
|
||||
// Get the third statement in `main.py` (x = …) and extract the expression
|
||||
// node on the right-hand side:
|
||||
let x_rhs_node = &ast.syntax().body[2].as_assign_stmt().unwrap().value;
|
||||
|
||||
// let index = semantic_index(db, file_main);
|
||||
// index.expression(x_rhs_node.as_ref())
|
||||
// }
|
||||
let index = semantic_index(db, file_main);
|
||||
index.expression(x_rhs_node.as_ref())
|
||||
}
|
||||
|
||||
// let mut db = setup_db();
|
||||
let mut db = setup_db();
|
||||
|
||||
// db.write_dedented(
|
||||
// "/src/mod.py",
|
||||
// r#"
|
||||
// class C:
|
||||
// def __init__(self):
|
||||
// self.instance_attr: str = "24"
|
||||
db.write_dedented(
|
||||
"/src/mod.py",
|
||||
r#"
|
||||
class C:
|
||||
def __init__(self):
|
||||
self.instance_attr: str = "24"
|
||||
|
||||
// @classmethod
|
||||
// def method(cls):
|
||||
// cls.class_attr: int = 42
|
||||
// "#,
|
||||
// )?;
|
||||
// db.write_dedented(
|
||||
// "/src/main.py",
|
||||
// r#"
|
||||
// from mod import C
|
||||
// C.method()
|
||||
// x = C().class_attr
|
||||
// "#,
|
||||
// )?;
|
||||
@classmethod
|
||||
def method(cls):
|
||||
cls.class_attr: int = 42
|
||||
"#,
|
||||
)?;
|
||||
db.write_dedented(
|
||||
"/src/main.py",
|
||||
r#"
|
||||
from mod import C
|
||||
C.method()
|
||||
x = C().class_attr
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// let file_main = system_path_to_file(&db, "/src/main.py").unwrap();
|
||||
// let attr_ty = global_symbol(&db, file_main, "x").place.expect_type();
|
||||
// assert_eq!(attr_ty.display(&db).to_string(), "Unknown | int");
|
||||
let file_main = system_path_to_file(&db, "/src/main.py").unwrap();
|
||||
let attr_ty = global_symbol(&db, file_main, "x").place.expect_type();
|
||||
assert_eq!(attr_ty.display(&db).to_string(), "Unknown | int");
|
||||
|
||||
// // Change the type of `class_attr` to `str`; this should trigger the type of `x` to be re-inferred
|
||||
// db.write_dedented(
|
||||
// "/src/mod.py",
|
||||
// r#"
|
||||
// class C:
|
||||
// def __init__(self):
|
||||
// self.instance_attr: str = "24"
|
||||
// Change the type of `class_attr` to `str`; this should trigger the type of `x` to be re-inferred
|
||||
db.write_dedented(
|
||||
"/src/mod.py",
|
||||
r#"
|
||||
class C:
|
||||
def __init__(self):
|
||||
self.instance_attr: str = "24"
|
||||
|
||||
// @classmethod
|
||||
// def method(cls):
|
||||
// cls.class_attr: str = "42"
|
||||
// "#,
|
||||
// )?;
|
||||
@classmethod
|
||||
def method(cls):
|
||||
cls.class_attr: str = "42"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// let events = {
|
||||
// db.clear_salsa_events();
|
||||
// let attr_ty = global_symbol(&db, file_main, "x").place.expect_type();
|
||||
// assert_eq!(attr_ty.display(&db).to_string(), "Unknown | str");
|
||||
// db.take_salsa_events()
|
||||
// };
|
||||
// assert_function_query_was_run(&db, infer_expression_types, x_rhs_expression(&db), &events);
|
||||
let events = {
|
||||
db.clear_salsa_events();
|
||||
let attr_ty = global_symbol(&db, file_main, "x").place.expect_type();
|
||||
assert_eq!(attr_ty.display(&db).to_string(), "Unknown | str");
|
||||
db.take_salsa_events()
|
||||
};
|
||||
assert_function_query_was_run(&db, infer_expression_types, x_rhs_expression(&db), &events);
|
||||
|
||||
// // Add a comment; this should not trigger the type of `x` to be re-inferred
|
||||
// db.write_dedented(
|
||||
// "/src/mod.py",
|
||||
// r#"
|
||||
// class C:
|
||||
// def __init__(self):
|
||||
// self.instance_attr: str = "24"
|
||||
// Add a comment; this should not trigger the type of `x` to be re-inferred
|
||||
db.write_dedented(
|
||||
"/src/mod.py",
|
||||
r#"
|
||||
class C:
|
||||
def __init__(self):
|
||||
self.instance_attr: str = "24"
|
||||
|
||||
// @classmethod
|
||||
// def method(cls):
|
||||
// # comment
|
||||
// cls.class_attr: str = "42"
|
||||
// "#,
|
||||
// )?;
|
||||
@classmethod
|
||||
def method(cls):
|
||||
# comment
|
||||
cls.class_attr: str = "42"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// let events = {
|
||||
// db.clear_salsa_events();
|
||||
// let attr_ty = global_symbol(&db, file_main, "x").place.expect_type();
|
||||
// assert_eq!(attr_ty.display(&db).to_string(), "Unknown | str");
|
||||
// db.take_salsa_events()
|
||||
// };
|
||||
let events = {
|
||||
db.clear_salsa_events();
|
||||
let attr_ty = global_symbol(&db, file_main, "x").place.expect_type();
|
||||
assert_eq!(attr_ty.display(&db).to_string(), "Unknown | str");
|
||||
db.take_salsa_events()
|
||||
};
|
||||
|
||||
// assert_function_query_was_not_run(
|
||||
// &db,
|
||||
// infer_expression_types,
|
||||
// x_rhs_expression(&db),
|
||||
// &events,
|
||||
// );
|
||||
assert_function_query_was_not_run(
|
||||
&db,
|
||||
infer_expression_types,
|
||||
x_rhs_expression(&db),
|
||||
&events,
|
||||
);
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -709,7 +709,7 @@ impl<'db, 'ast> NarrowingConstraintsBuilder<'db, 'ast> {
|
||||
return None;
|
||||
}
|
||||
|
||||
let inference = infer_expression_types(self.db, expression, false);
|
||||
let inference = infer_expression_types(self.db, expression);
|
||||
|
||||
let comparator_tuples = std::iter::once(&**left)
|
||||
.chain(comparators)
|
||||
@@ -799,7 +799,7 @@ impl<'db, 'ast> NarrowingConstraintsBuilder<'db, 'ast> {
|
||||
expression: Expression<'db>,
|
||||
is_positive: bool,
|
||||
) -> Option<NarrowingConstraints<'db>> {
|
||||
let inference = infer_expression_types(self.db, expression, false);
|
||||
let inference = infer_expression_types(self.db, expression);
|
||||
|
||||
let callable_ty = inference.expression_type(&*expr_call.func);
|
||||
|
||||
@@ -921,8 +921,7 @@ impl<'db, 'ast> NarrowingConstraintsBuilder<'db, 'ast> {
|
||||
let subject = place_expr(subject.node_ref(self.db, self.module))?;
|
||||
let place = self.expect_place(&subject);
|
||||
|
||||
let ty = infer_same_file_expression_type(self.db, cls, self.module, false)
|
||||
.to_instance(self.db)?;
|
||||
let ty = infer_same_file_expression_type(self.db, cls, self.module).to_instance(self.db)?;
|
||||
|
||||
Some(NarrowingConstraints::from_iter([(place, ty)]))
|
||||
}
|
||||
@@ -935,7 +934,7 @@ impl<'db, 'ast> NarrowingConstraintsBuilder<'db, 'ast> {
|
||||
let subject = place_expr(subject.node_ref(self.db, self.module))?;
|
||||
let place = self.expect_place(&subject);
|
||||
|
||||
let ty = infer_same_file_expression_type(self.db, value, self.module, false);
|
||||
let ty = infer_same_file_expression_type(self.db, value, self.module);
|
||||
Some(NarrowingConstraints::from_iter([(place, ty)]))
|
||||
}
|
||||
|
||||
@@ -964,7 +963,7 @@ impl<'db, 'ast> NarrowingConstraintsBuilder<'db, 'ast> {
|
||||
expression: Expression<'db>,
|
||||
is_positive: bool,
|
||||
) -> Option<NarrowingConstraints<'db>> {
|
||||
let inference = infer_expression_types(self.db, expression, false);
|
||||
let inference = infer_expression_types(self.db, expression);
|
||||
let mut sub_constraints = expr_bool_op
|
||||
.values
|
||||
.iter()
|
||||
|
||||
@@ -48,7 +48,7 @@ impl<'db, 'ast> Unpacker<'db, 'ast> {
|
||||
"Unpacking target must be a list or tuple expression"
|
||||
);
|
||||
|
||||
let value_type = infer_expression_types(self.db(), value.expression(), false)
|
||||
let value_type = infer_expression_types(self.db(), value.expression())
|
||||
.expression_type(value.expression().node_ref(self.db(), self.module()));
|
||||
|
||||
let value_type = match value.kind() {
|
||||
|
||||
Reference in New Issue
Block a user