diff --git a/crates/ty/docs/rules.md b/crates/ty/docs/rules.md
index 0cb9f447e26d0..3da35cb8b90a5 100644
--- a/crates/ty/docs/rules.md
+++ b/crates/ty/docs/rules.md
@@ -8,7 +8,7 @@
Default level: error ·
Added in 0.0.13 ·
Related issues ·
-View source
+View source
@@ -49,7 +49,7 @@ class Derived(Base): # Error: `Derived` does not implement `method`
Default level: warn ·
Added in 0.0.1-alpha.20 ·
Related issues ·
-View source
+View source
@@ -90,7 +90,7 @@ class SubProto(BaseProto, Protocol):
Default level: error ·
Added in 0.0.14 ·
Related issues ·
-View source
+View source
@@ -157,7 +157,7 @@ def test(): -> "int":
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -181,7 +181,7 @@ Calling a non-callable object will raise a `TypeError` at runtime.
Default level: error ·
Added in 0.0.7 ·
Related issues ·
-View source
+View source
@@ -212,7 +212,7 @@ def f(x: object):
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -244,7 +244,7 @@ f(int) # error
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -275,7 +275,7 @@ a = 1
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -307,7 +307,7 @@ class C(A, B): ...
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -339,7 +339,7 @@ class B(A): ...
Default level: error ·
Added in 0.0.1-alpha.29 ·
Related issues ·
-View source
+View source
@@ -367,7 +367,7 @@ type B = A
Default level: warn ·
Added in 0.0.1-alpha.16 ·
Related issues ·
-View source
+View source
@@ -394,7 +394,7 @@ old_func() # emits [deprecated] diagnostic
Default level: ignore ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -423,7 +423,7 @@ false positives it can produce.
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -450,7 +450,7 @@ class B(A, A): ...
Default level: error ·
Added in 0.0.1-alpha.12 ·
Related issues ·
-View source
+View source
@@ -488,7 +488,7 @@ class A: # Crash at runtime
Default level: error ·
Added in 0.0.14 ·
Related issues ·
-View source
+View source
@@ -651,7 +651,7 @@ def test(): -> "Literal[5]":
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -681,7 +681,7 @@ class C(A, B): ...
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -707,7 +707,7 @@ t[3] # IndexError: tuple index out of range
Default level: warn ·
Added in 0.0.1-alpha.33 ·
Related issues ·
-View source
+View source
@@ -741,7 +741,7 @@ class MyClass: ...
Default level: error ·
Added in 0.0.1-alpha.12 ·
Related issues ·
-View source
+View source
@@ -830,7 +830,7 @@ an atypical memory layout.
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -857,7 +857,7 @@ func("foo") # error: [invalid-argument-type]
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -885,7 +885,7 @@ a: int = ''
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -919,7 +919,7 @@ C.instance_var = 3 # error: Cannot assign to instance variable
Default level: error ·
Added in 0.0.1-alpha.19 ·
Related issues ·
-View source
+View source
@@ -955,7 +955,7 @@ asyncio.run(main())
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -979,7 +979,7 @@ class A(42): ... # error: [invalid-base]
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1006,7 +1006,7 @@ with 1:
Default level: error ·
Added in 0.0.12 ·
Related issues ·
-View source
+View source
@@ -1043,7 +1043,7 @@ class Foo(NamedTuple):
Default level: error ·
Added in 0.0.13 ·
Related issues ·
-View source
+View source
@@ -1075,7 +1075,7 @@ class A:
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1104,7 +1104,7 @@ a: str
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1148,7 +1148,7 @@ except ZeroDivisionError:
Default level: error ·
Added in 0.0.1-alpha.28 ·
Related issues ·
-View source
+View source
@@ -1190,7 +1190,7 @@ class D(A):
Default level: error ·
Added in 0.0.1-alpha.35 ·
Related issues ·
-View source
+View source
@@ -1234,7 +1234,7 @@ class NonFrozenChild(FrozenBase): # Error raised here
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1272,7 +1272,7 @@ class D(Generic[U, T]): ...
Default level: error ·
Added in 0.0.12 ·
Related issues ·
-View source
+View source
@@ -1351,7 +1351,7 @@ a = 20 / 0 # type: ignore
Default level: error ·
Added in 0.0.1-alpha.17 ·
Related issues ·
-View source
+View source
@@ -1390,7 +1390,7 @@ carol = Person(name="Carol", age=25) # typo!
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1425,7 +1425,7 @@ def f(t: TypeVar("U")): ...
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1459,7 +1459,7 @@ class B(metaclass=f): ...
Default level: error ·
Added in 0.0.1-alpha.20 ·
Related issues ·
-View source
+View source
@@ -1566,7 +1566,7 @@ Correct use of `@override` is enforced by ty's `invalid-explicit-override` rule.
Default level: error ·
Added in 0.0.1-alpha.19 ·
Related issues ·
-View source
+View source
@@ -1620,7 +1620,7 @@ AttributeError: Cannot overwrite NamedTuple attribute _asdict
Default level: error ·
Added in 0.0.1-alpha.27 ·
Related issues ·
-View source
+View source
@@ -1650,7 +1650,7 @@ Baz = NewType("Baz", int | str) # error: invalid base for `typing.NewType`
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1700,7 +1700,7 @@ def foo(x: int) -> int: ...
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1726,7 +1726,7 @@ def f(a: int = ''): ...
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1757,7 +1757,7 @@ P2 = ParamSpec("S2") # error: ParamSpec name must match the variable it's assig
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1791,7 +1791,7 @@ TypeError: Protocols can only inherit from other protocols, got
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1840,7 +1840,7 @@ def g():
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1869,7 +1869,7 @@ def func() -> int:
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1965,7 +1965,7 @@ class C: ...
Default level: error ·
Added in 0.0.10 ·
Related issues ·
-View source
+View source
@@ -2011,7 +2011,7 @@ class MyClass:
Default level: error ·
Added in 0.0.1-alpha.6 ·
Related issues ·
-View source
+View source
@@ -2038,7 +2038,7 @@ NewAlias = TypeAliasType(get_name(), int) # error: TypeAliasType name mus
Default level: error ·
Added in 0.0.1-alpha.29 ·
Related issues ·
-View source
+View source
@@ -2085,7 +2085,7 @@ Bar[int] # error: too few arguments
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2115,7 +2115,7 @@ TYPE_CHECKING = ''
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2145,7 +2145,7 @@ b: Annotated[int] # `Annotated` expects at least two arguments
Default level: error ·
Added in 0.0.1-alpha.11 ·
Related issues ·
-View source
+View source
@@ -2179,7 +2179,7 @@ f(10) # Error
Default level: error ·
Added in 0.0.1-alpha.11 ·
Related issues ·
-View source
+View source
@@ -2213,7 +2213,7 @@ class C:
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2248,7 +2248,7 @@ T = TypeVar('T', bound=str) # valid bound TypeVar
Default level: error ·
Added in 0.0.14 ·
Related issues ·
-View source
+View source
@@ -2283,7 +2283,7 @@ def f(x: dict):
Default level: error ·
Added in 0.0.9 ·
Related issues ·
-View source
+View source
@@ -2314,7 +2314,7 @@ class Foo(TypedDict):
Default level: error ·
Added in 0.0.14 ·
Related issues ·
-View source
+View source
@@ -2369,7 +2369,7 @@ def h(arg2: type):
Default level: error ·
Added in 0.0.15 ·
Related issues ·
-View source
+View source
@@ -2412,7 +2412,7 @@ def g(arg: object):
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2437,7 +2437,7 @@ func() # TypeError: func() missing 1 required positional argument: 'x'
Default level: error ·
Added in 0.0.1-alpha.20 ·
Related issues ·
-View source
+View source
@@ -2470,7 +2470,7 @@ alice["age"] # KeyError
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2499,7 +2499,7 @@ func("string") # error: [no-matching-overload]
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2525,7 +2525,7 @@ for i in 34: # TypeError: 'int' object is not iterable
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2549,7 +2549,7 @@ Subscripting an object that does not support it will raise a `TypeError` at runt
Default level: error ·
Added in 0.0.1-alpha.29 ·
Related issues ·
-View source
+View source
@@ -2582,7 +2582,7 @@ class B(A):
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2609,7 +2609,7 @@ f(1, x=2) # Error raised here
Default level: error ·
Added in 0.0.1-alpha.22 ·
Related issues ·
-View source
+View source
@@ -2636,7 +2636,7 @@ f(x=1) # Error raised here
Default level: warn ·
Added in 0.0.1-alpha.22 ·
Related issues ·
-View source
+View source
@@ -2664,7 +2664,7 @@ A.c # AttributeError: type object 'A' has no attribute 'c'
Default level: warn ·
Added in 0.0.1-alpha.22 ·
Related issues ·
-View source
+View source
@@ -2696,7 +2696,7 @@ A()[0] # TypeError: 'A' object is not subscriptable
Default level: ignore ·
Added in 0.0.1-alpha.22 ·
Related issues ·
-View source
+View source
@@ -2733,7 +2733,7 @@ from module import a # ImportError: cannot import name 'a' from 'module'
Default level: ignore ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2797,7 +2797,7 @@ def test(): -> "int":
Default level: warn ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2824,7 +2824,7 @@ cast(int, f()) # Redundant
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2854,7 +2854,7 @@ static_assert(int(2.0 * 3.0) == 6) # error: does not have a statically known tr
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2883,7 +2883,7 @@ class B(A): ... # Error raised here
Default level: error ·
Added in 0.0.1-alpha.30 ·
Related issues ·
-View source
+View source
@@ -2917,7 +2917,7 @@ class F(NamedTuple):
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2944,7 +2944,7 @@ f("foo") # Error raised here
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2972,7 +2972,7 @@ def _(x: int):
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3018,7 +3018,7 @@ class A:
Default level: warn ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3042,7 +3042,7 @@ reveal_type(1) # NameError: name 'reveal_type' is not defined
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3069,7 +3069,7 @@ f(x=1, y=2) # Error raised here
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3097,7 +3097,7 @@ A().foo # AttributeError: 'A' object has no attribute 'foo'
Default level: warn ·
Added in 0.0.1-alpha.15 ·
Related issues ·
-View source
+View source
@@ -3155,7 +3155,7 @@ def g():
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3180,7 +3180,7 @@ import foo # ModuleNotFoundError: No module named 'foo'
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3205,7 +3205,7 @@ print(x) # NameError: name 'x' is not defined
Default level: warn ·
Added in 0.0.1-alpha.7 ·
Related issues ·
-View source
+View source
@@ -3244,7 +3244,7 @@ class D(C): ... # error: [unsupported-base]
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3281,7 +3281,7 @@ b1 < b2 < b1 # exception raised here
Default level: ignore ·
Added in 0.0.12 ·
Related issues ·
-View source
+View source
@@ -3322,7 +3322,7 @@ def factory(base: type[Base]) -> type:
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3423,7 +3423,7 @@ to `false`.
Default level: warn ·
Added in 0.0.1-alpha.22 ·
Related issues ·
-View source
+View source
@@ -3486,7 +3486,7 @@ def foo(x: int | str) -> int | str:
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
diff --git a/crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_argument_type.md b/crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_argument_type.md
index 8d4761dbde51c..663d84c0c77a1 100644
--- a/crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_argument_type.md
+++ b/crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_argument_type.md
@@ -244,3 +244,16 @@ def f[T: Foo](x: T) -> T:
needs_a_foo(x) # error: [invalid-argument-type]
return x
```
+
+## Numbers special case
+
+```py
+from numbers import Number
+
+def f(x: Number): ...
+
+f(5) # error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `Number`, found `Literal[5]`"
+
+def g(x: float):
+ f(x) # error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `Number`, found `int | float`"
+```
diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_argument_typ\342\200\246_-_Invalid_argument_typ\342\200\246_-_Numbers_special_case_(6d84dc3231c49ace).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_argument_typ\342\200\246_-_Invalid_argument_typ\342\200\246_-_Numbers_special_case_(6d84dc3231c49ace).snap"
new file mode 100644
index 0000000000000..44d326f003247
--- /dev/null
+++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_argument_typ\342\200\246_-_Invalid_argument_typ\342\200\246_-_Numbers_special_case_(6d84dc3231c49ace).snap"
@@ -0,0 +1,77 @@
+---
+source: crates/ty_test/src/lib.rs
+expression: snapshot
+---
+
+---
+mdtest name: invalid_argument_type.md - Invalid argument type diagnostics - Numbers special case
+mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_argument_type.md
+---
+
+# Python source files
+
+## mdtest_snippet.py
+
+```
+1 | from numbers import Number
+2 |
+3 | def f(x: Number): ...
+4 |
+5 | f(5) # error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `Number`, found `Literal[5]`"
+6 |
+7 | def g(x: float):
+8 | f(x) # error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `Number`, found `int | float`"
+```
+
+# Diagnostics
+
+```
+error[invalid-argument-type]: Argument to function `f` is incorrect
+ --> src/mdtest_snippet.py:5:3
+ |
+3 | def f(x: Number): ...
+4 |
+5 | f(5) # error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `Number`, found `Literal[5]`"
+ | ^ Expected `Number`, found `Literal[5]`
+6 |
+7 | def g(x: float):
+ |
+info: Function defined here
+ --> src/mdtest_snippet.py:3:5
+ |
+1 | from numbers import Number
+2 |
+3 | def f(x: Number): ...
+ | ^ --------- Parameter declared here
+4 |
+5 | f(5) # error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `Number`, found `Literal[5]`"
+ |
+info: Types from the `numbers` module aren't supported for static type checking
+help: Consider using a protocol instead, such as `typing.SupportsFloat`
+info: rule `invalid-argument-type` is enabled by default
+
+```
+
+```
+error[invalid-argument-type]: Argument to function `f` is incorrect
+ --> src/mdtest_snippet.py:8:7
+ |
+7 | def g(x: float):
+8 | f(x) # error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `Number`, found `int | float`"
+ | ^ Expected `Number`, found `int | float`
+ |
+info: Function defined here
+ --> src/mdtest_snippet.py:3:5
+ |
+1 | from numbers import Number
+2 |
+3 | def f(x: Number): ...
+ | ^ --------- Parameter declared here
+4 |
+5 | f(5) # error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `Number`, found `Literal[5]`"
+ |
+info: Types from the `numbers` module aren't supported for static type checking
+help: Consider using a protocol instead, such as `typing.SupportsFloat`
+info: rule `invalid-argument-type` is enabled by default
+
+```
diff --git a/crates/ty_python_semantic/src/types/call/bind.rs b/crates/ty_python_semantic/src/types/call/bind.rs
index 16622e5238bbc..657fa2f2045bb 100644
--- a/crates/ty_python_semantic/src/types/call/bind.rs
+++ b/crates/ty_python_semantic/src/types/call/bind.rs
@@ -30,6 +30,7 @@ use crate::types::diagnostic::{
CALL_NON_CALLABLE, CALL_TOP_CALLABLE, CONFLICTING_ARGUMENT_FORMS, INVALID_ARGUMENT_TYPE,
INVALID_DATACLASS, MISSING_ARGUMENT, NO_MATCHING_OVERLOAD, PARAMETER_ALREADY_ASSIGNED,
POSITIONAL_ONLY_PARAMETER_AS_KWARG, TOO_MANY_POSITIONAL_ARGUMENTS, UNKNOWN_ARGUMENT,
+ note_numbers_module_not_supported,
};
use crate::types::enums::is_enum_class;
use crate::types::function::{
@@ -4515,6 +4516,21 @@ impl<'db> BindingError<'db> {
if let Some(union_diag) = union_diag {
union_diag.add_union_context(context.db(), &mut diag);
}
+
+ // If the type comes from first-party code, the user may have some control over
+ // the parameter annotation; provide additional context to help them fix it.
+ if callable_ty
+ .definition(context.db())
+ .and_then(|definition| definition.file(context.db()))
+ .is_some_and(|file| context.db().should_check_file(file))
+ {
+ note_numbers_module_not_supported(
+ context.db(),
+ &mut diag,
+ *expected_ty,
+ *provided_ty,
+ );
+ }
}
Self::InvalidKeyType {
diff --git a/crates/ty_python_semantic/src/types/definition.rs b/crates/ty_python_semantic/src/types/definition.rs
index ffe65ec71d0ea..b16e891201b79 100644
--- a/crates/ty_python_semantic/src/types/definition.rs
+++ b/crates/ty_python_semantic/src/types/definition.rs
@@ -1,6 +1,6 @@
use crate::Db;
use crate::semantic_index::definition::Definition;
-use ruff_db::files::FileRange;
+use ruff_db::files::{File, FileRange};
use ruff_db::parsed::parsed_module;
use ruff_db::source::source_text;
use ruff_text_size::{TextLen, TextRange};
@@ -56,4 +56,17 @@ impl TypeDefinition<'_> {
}
}
}
+
+ pub(super) fn file(&self, db: &dyn Db) -> Option {
+ match self {
+ Self::Module(module) => module.file(db),
+ Self::StaticClass(definition)
+ | Self::DynamicClass(definition)
+ | Self::Function(definition)
+ | Self::TypeVar(definition)
+ | Self::TypeAlias(definition)
+ | Self::SpecialForm(definition)
+ | Self::NewType(definition) => Some(definition.file(db)),
+ }
+ }
}
diff --git a/crates/ty_python_semantic/src/types/diagnostic.rs b/crates/ty_python_semantic/src/types/diagnostic.rs
index 7a0c147312d0f..9899db9c4d623 100644
--- a/crates/ty_python_semantic/src/types/diagnostic.rs
+++ b/crates/ty_python_semantic/src/types/diagnostic.rs
@@ -31,7 +31,9 @@ use crate::types::{
ProtocolInstanceType, SpecialFormType, SubclassOfInner, Type, TypeContext, binding_type,
protocol_class::ProtocolClass,
};
-use crate::types::{DataclassFlags, KnownInstanceType, MemberLookupPolicy, TypeVarInstance};
+use crate::types::{
+ DataclassFlags, KnownInstanceType, MemberLookupPolicy, TypeVarInstance, UnionType,
+};
use crate::{Db, DisplaySettings, FxIndexMap, Program, declare_lint};
use itertools::Itertools;
use ruff_db::{
@@ -2937,6 +2939,35 @@ fn report_invalid_assignment_with_message<'db, 'ctx: 'db, T: Ranged>(
Some(diag)
}
+pub(super) fn note_numbers_module_not_supported<'db>(
+ db: &'db dyn Db,
+ diag: &mut Diagnostic,
+ target_ty: Type<'db>,
+ value_ty: Type<'db>,
+) {
+ const BUILTIN_NUMBERS: [KnownClass; 3] =
+ [KnownClass::Int, KnownClass::Float, KnownClass::Complex];
+
+ if let Type::NominalInstance(target_instance) = target_ty {
+ let file = target_instance.class(db).class_literal(db).file(db);
+ if let Some(module) = file_to_module(db, file)
+ && module.is_known(db, KnownModule::Numbers)
+ {
+ let is_numeric = value_ty.is_subtype_of(
+ db,
+ UnionType::from_elements(db, BUILTIN_NUMBERS.iter().map(|cls| cls.to_instance(db))),
+ );
+
+ if is_numeric {
+ diag.info(
+ "Types from the `numbers` module aren't supported for static type checking",
+ );
+ diag.help("Consider using a protocol instead, such as `typing.SupportsFloat`");
+ }
+ }
+ }
+}
+
pub(super) fn report_invalid_assignment<'db>(
context: &InferContext<'db, '_>,
target_node: AnyNodeRef,
@@ -3020,24 +3051,7 @@ pub(super) fn report_invalid_assignment<'db>(
}
// special case message
- if let Type::NominalInstance(target_instance) = target_ty {
- let db = context.db();
- let file = target_instance.class(db).class_literal(db).file(db);
- if let Some(module) = file_to_module(db, file)
- && module.is_known(db, KnownModule::Numbers)
- {
- let is_numeric = [KnownClass::Int, KnownClass::Float, KnownClass::Complex]
- .iter()
- .any(|numeric| value_ty.is_subtype_of(db, numeric.to_instance(db)));
-
- if is_numeric {
- diag.info(
- "Types from the `numbers` module aren't supported for static type checking",
- );
- diag.help("Consider using a protocol instead, such as `typing.SupportsFloat`");
- }
- }
- }
+ note_numbers_module_not_supported(context.db(), &mut diag, target_ty, value_ty);
}
pub(super) fn report_invalid_attribute_assignment(