Skip to content
Merged
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
2 changes: 2 additions & 0 deletions crates/ty_module_resolver/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ pub enum KnownModule {
UnittestMock,
Uuid,
Warnings,
Numbers,
}

impl KnownModule {
Expand Down Expand Up @@ -362,6 +363,7 @@ impl KnownModule {
Self::UnittestMock => "unittest.mock",
Self::Uuid => "uuid",
Self::Templatelib => "string.templatelib",
Self::Numbers => "numbers",
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ reveal_type(y) # revealed: Literal[1]
x: int = "foo" # error: [invalid-assignment] "Object of type `Literal["foo"]` is not assignable to `int`"
```

## Numbers special case

<!-- snapshot-diagnostics -->

```py
from numbers import Number

a: Number = 1 # error: [invalid-assignment] "Object of type `Literal[1]` is not assignable to `Number`"
```

## Violates previous annotation

```py
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
source: crates/ty_test/src/lib.rs
expression: snapshot
---

---
mdtest name: annotations.md - Assignment with annotations - Numbers special case
mdtest path: crates/ty_python_semantic/resources/mdtest/assignment/annotations.md
---

# Python source files

## mdtest_snippet.py

```
1 | from numbers import Number
2 |
3 | a: Number = 1 # error: [invalid-assignment] "Object of type `Literal[1]` is not assignable to `Number`"
```

# Diagnostics

```
error[invalid-assignment]: Object of type `Literal[1]` is not assignable to `Number`
--> src/mdtest_snippet.py:3:4
|
1 | from numbers import Number
2 |
3 | a: Number = 1 # error: [invalid-assignment] "Object of type `Literal[1]` is not assignable to `Number`"
| ------ ^ Incompatible value of type `Literal[1]`
| |
| Declared type
|
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-assignment` is enabled by default

```
22 changes: 21 additions & 1 deletion crates/ty_python_semantic/src/types/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ use ruff_python_ast::{self as ast, AnyNodeRef, PythonVersion, StringFlags};
use ruff_text_size::{Ranged, TextRange};
use rustc_hash::FxHashSet;
use std::fmt::{self, Formatter};
use ty_module_resolver::{Module, ModuleName};
use ty_module_resolver::{KnownModule, Module, ModuleName, file_to_module};

const RUNTIME_CHECKABLE_DOCS_URL: &str =
"https://docs.python.org/3/library/typing.html#typing.runtime_checkable";
Expand Down Expand Up @@ -2968,6 +2968,26 @@ pub(super) fn report_invalid_assignment<'db>(
let message = diag.primary_message().to_string();
diag.set_concise_message(message);
}

// 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`");
}
}
}
}

pub(super) fn report_invalid_attribute_assignment(
Expand Down