Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -222,10 +222,10 @@ reveal_type(r) # revealed: dict[int | str, int | str]
## Incorrect collection literal assignments are complained about

```py
# error: [invalid-assignment] "Object of type `list[Unknown | int]` is not assignable to `list[str]`"
# error: [invalid-assignment] "Object of type `list[str | int]` is not assignable to `list[str]`"
a: list[str] = [1, 2, 3]

# error: [invalid-assignment] "Object of type `set[Unknown | int | str]` is not assignable to `set[int]`"
# error: [invalid-assignment] "Object of type `set[int | str]` is not assignable to `set[int]`"
b: set[int] = {1, 2, "3"}
```

Expand Down Expand Up @@ -439,7 +439,7 @@ reveal_type(d) # revealed: list[tuple[int, int] | int]
e: list[int] = f(True)
reveal_type(e) # revealed: list[int]

# error: [invalid-assignment] "Object of type `list[Literal["a"]]` is not assignable to `list[int]`"
# error: [invalid-assignment] "Object of type `list[Literal["a"] | int]` is not assignable to `list[int]`"
g: list[int] = f("a")

# error: [invalid-assignment] "Object of type `list[Literal["a"]]` is not assignable to `tuple[int]`"
Expand Down
4 changes: 2 additions & 2 deletions crates/ty_python_semantic/resources/mdtest/bidirectional.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,9 +275,9 @@

A(f(1))

# error: [invalid-argument-type] "Argument to function `__new__` is incorrect: Expected `list[int | str]`, found `list[list[Unknown]]`"
# error: [invalid-argument-type] "Argument to bound method `__init__` is incorrect: Expected `list[int | None]`, found `list[list[Unknown]]`"
# error: [invalid-argument-type] "Argument to function `__new__` is incorrect: Expected `list[int | str]`, found `list[list[Unknown]] & list[list[Unknown] | int | str] & list[list[Unknown] | int | None]`"
# error: [invalid-argument-type] "Argument to bound method `__init__` is incorrect: Expected `list[int | None]`, found `list[list[Unknown]] & list[list[Unknown] | int | str] & list[list[Unknown] | int | None]`"
A(f([]))

Check failure on line 280 in crates/ty_python_semantic/resources/mdtest/bidirectional.md

View workflow job for this annotation

GitHub Actions / cargo test (linux)

unexpected error: 3 [invalid-argument-type] "Argument to bound method `__init__` is incorrect: Expected `list[int | None]`, found `list[list[Unknown]] & list[list[Unknown] | int | None] & list[list[Unknown] | int | str]`"

Check failure on line 280 in crates/ty_python_semantic/resources/mdtest/bidirectional.md

View workflow job for this annotation

GitHub Actions / cargo test (linux)

unexpected error: 3 [invalid-argument-type] "Argument to function `__new__` is incorrect: Expected `list[int | str]`, found `list[list[Unknown]] & list[list[Unknown] | int | None] & list[list[Unknown] | int | str]`"

Check failure on line 280 in crates/ty_python_semantic/resources/mdtest/bidirectional.md

View workflow job for this annotation

GitHub Actions / cargo test (linux)

unmatched assertion: error: [invalid-argument-type] "Argument to bound method `__init__` is incorrect: Expected `list[int | None]`, found `list[list[Unknown]] & list[list[Unknown] | int | str] & list[list[Unknown] | int | None]`"

Check failure on line 280 in crates/ty_python_semantic/resources/mdtest/bidirectional.md

View workflow job for this annotation

GitHub Actions / cargo test (linux)

unmatched assertion: error: [invalid-argument-type] "Argument to function `__new__` is incorrect: Expected `list[int | str]`, found `list[list[Unknown]] & list[list[Unknown] | int | str] & list[list[Unknown] | int | None]`"
```

## Multi-inference diagnostics
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ type("Foo", Base, {})
# error: [invalid-argument-type] "Argument to class `type` is incorrect: Expected `tuple[type, ...]`, found `tuple[Literal[1], Literal[2]]`"
type("Foo", (1, 2), {})

# error: [invalid-argument-type] "Argument to class `type` is incorrect: Expected `dict[str, Any]`, found `dict[Unknown | bytes, Unknown | int]`"
# error: [invalid-argument-type] "Argument to class `type` is incorrect: Expected `dict[str, Any]`, found `dict[str | bytes, Any]`"
type("Foo", (Base,), {b"attr": 1})
```

Expand Down
14 changes: 6 additions & 8 deletions crates/ty_python_semantic/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ pub(crate) use self::diagnostic::register_lints;
pub use self::diagnostic::{TypeCheckDiagnostics, UNDEFINED_REVEAL};
pub(crate) use self::infer::{
TypeContext, infer_deferred_types, infer_definition_types, infer_expression_type,
infer_expression_types, infer_isolated_expression, infer_scope_types,
static_expression_truthiness,
infer_expression_types, infer_scope_types, static_expression_truthiness,
};
pub(crate) use self::signatures::{CallableSignature, Parameter, Parameters, Signature};
pub(crate) use self::subclass_of::{SubclassOfInner, SubclassOfType};
Expand Down Expand Up @@ -1310,14 +1309,13 @@ impl<'db> Type<'db> {
};

// Avoid literal promotion if it leads to an unassignable type.
if tcx
.annotation
.is_none_or(|annotation| promoted.is_assignable_to(db, annotation))
{
return promoted;
if tcx.annotation.is_some_and(|annotation| {
self.is_assignable_to(db, annotation) && !promoted.is_assignable_to(db, annotation)
}) {
return self;
}

self
promoted
}

/// Return a "normalized" version of `self` that ensures that equivalent types have the same Salsa ID.
Expand Down
20 changes: 1 addition & 19 deletions crates/ty_python_semantic/src/types/call/bind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ use crate::types::{
BoundMethodType, ClassLiteral, DataclassFlags, DataclassParams, FieldInstance,
KnownBoundMethodType, KnownClass, KnownInstanceType, MemberLookupPolicy, NominalInstanceType,
PropertyInstanceType, SpecialFormType, TrackedConstraintSet, TypeAliasType, TypeContext,
UnionBuilder, UnionType, WrapperDescriptorKind, enums, ide_support, infer_isolated_expression,
todo_type,
UnionBuilder, UnionType, WrapperDescriptorKind, enums, ide_support, todo_type,
};
use ruff_db::diagnostic::{Annotation, Diagnostic, SubDiagnostic, SubDiagnosticSeverity};
use ruff_python_ast::{self as ast, ArgOrKeyword, PythonVersion};
Expand Down Expand Up @@ -3587,23 +3586,6 @@ impl<'db> BindingError<'db> {
return;
};

// Re-infer the argument type of call expressions, ignoring the type context for more
// precise error messages.
let provided_ty = match Self::get_argument_node(node, *argument_index) {
None => *provided_ty,

// Ignore starred arguments, as those are difficult to re-infer.
Some(
ast::ArgOrKeyword::Arg(ast::Expr::Starred(_))
| ast::ArgOrKeyword::Keyword(ast::Keyword { arg: None, .. }),
) => *provided_ty,

Some(
ast::ArgOrKeyword::Arg(value)
| ast::ArgOrKeyword::Keyword(ast::Keyword { value, .. }),
) => infer_isolated_expression(context.db(), context.scope(), value),
};

let provided_ty_display = provided_ty.display(context.db());
let expected_ty_display = expected_ty.display(context.db());

Expand Down
15 changes: 3 additions & 12 deletions crates/ty_python_semantic/src/types/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use super::{
use crate::diagnostic::did_you_mean;
use crate::diagnostic::format_enumeration;
use crate::lint::{Level, LintRegistryBuilder, LintStatus};
use crate::semantic_index::definition::{Definition, DefinitionKind};
use crate::semantic_index::definition::Definition;
use crate::semantic_index::place::{PlaceTable, ScopedPlaceId};
use crate::semantic_index::{global_scope, place_table};
use crate::suppression::FileSuppressionId;
Expand All @@ -23,7 +23,7 @@ use crate::types::string_annotation::{
use crate::types::{
BoundTypeVarInstance, ClassType, DynamicType, LintDiagnosticGuard, Protocol,
ProtocolInstanceType, SpecialFormType, SubclassOfInner, Type, TypeContext, binding_type,
infer_isolated_expression, protocol_class::ProtocolClass,
protocol_class::ProtocolClass,
};
use crate::{
Db, DisplaySettings, FxIndexMap, FxOrderMap, Module, ModuleName, Program, declare_lint,
Expand Down Expand Up @@ -2036,21 +2036,12 @@ fn report_invalid_assignment_with_message(
pub(super) fn report_invalid_assignment<'db>(
context: &InferContext<'db, '_>,
node: AnyNodeRef,
definition: Definition<'db>,
target_ty: Type,
mut source_ty: Type<'db>,
source_ty: Type<'db>,
) {
let settings =
DisplaySettings::from_possibly_ambiguous_type_pair(context.db(), target_ty, source_ty);

if let DefinitionKind::AnnotatedAssignment(annotated_assignment) = definition.kind(context.db())
&& let Some(value) = annotated_assignment.value(context.module())
{
// Re-infer the RHS of the annotated assignment, ignoring the type context for more precise
// error messages.
source_ty = infer_isolated_expression(context.db(), definition.scope(context.db()), value);
}

report_invalid_assignment_with_message(
context,
node,
Expand Down
19 changes: 0 additions & 19 deletions crates/ty_python_semantic/src/types/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
//! be considered a bug.)

use ruff_db::parsed::{ParsedModuleRef, parsed_module};
use ruff_python_ast as ast;
use ruff_text_size::Ranged;
use rustc_hash::FxHashMap;
use salsa;
Expand Down Expand Up @@ -209,24 +208,6 @@ fn infer_expression_types_impl<'db>(
.finish_expression()
}

/// Infer the type of an expression in isolation.
///
/// The type returned by this function may be different than the type of the expression
/// if it was inferred within its region, as it does not account for surrounding type context.
/// This can be useful to re-infer the type of an expression for diagnostics.
pub(crate) fn infer_isolated_expression<'db>(
db: &'db dyn Db,
scope: ScopeId<'db>,
expr: &ast::Expr,
) -> Type<'db> {
let file = scope.file(db);
let module = parsed_module(db, file).load(db);
let index = semantic_index(db, file);

TypeInferenceBuilder::new(db, InferenceRegion::Scope(scope), index, &module)
.infer_isolated_expression(expr)
}

fn expression_cycle_recover<'db>(
db: &'db dyn Db,
_id: salsa::Id,
Expand Down
10 changes: 1 addition & 9 deletions crates/ty_python_semantic/src/types/infer/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1602,7 +1602,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
}

if !bound_ty.is_assignable_to(db, declared_ty) {
report_invalid_assignment(&self.context, node, binding, declared_ty, bound_ty);
report_invalid_assignment(&self.context, node, declared_ty, bound_ty);

// Allow declarations to override inference in case of invalid assignment.
bound_ty = declared_ty;
Expand Down Expand Up @@ -1762,7 +1762,6 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
report_invalid_assignment(
&self.context,
node,
definition,
declared_ty.inner_type(),
inferred_ty,
);
Expand Down Expand Up @@ -10188,13 +10187,6 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
}
}

/// Infer the type of the given expression in isolation, ignoring the surrounding region.
pub(super) fn infer_isolated_expression(mut self, expr: &ast::Expr) -> Type<'db> {
let expr_ty = self.infer_expression_impl(expr, TypeContext::default());
let _ = self.context.finish();
expr_ty
}

pub(super) fn finish_expression(mut self) -> ExpressionInference<'db> {
self.infer_region();

Expand Down
Loading