mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-28 12:55:05 +00:00
[ty] Fix attribute writes to unions/intersections including modules (#18313)
## Summary Fix a bug that involved writes to attributes on union/intersection types that included modules as elements. This is a prerequisite to avoid some ecosystem false positives in https://github.com/astral-sh/ruff/pull/18312 ## Test Plan Added regression test
This commit is contained in:
parent
fbaf826a9d
commit
7eca6f96e3
2 changed files with 53 additions and 16 deletions
|
@ -1698,6 +1698,36 @@ reveal_type(outer.nested.inner.Outer.Nested.Inner.attr) # revealed: int
|
||||||
outer.nested.inner.Outer.Nested.Inner.attr = "a"
|
outer.nested.inner.Outer.Nested.Inner.attr = "a"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Unions of module attributes
|
||||||
|
|
||||||
|
`mod1.py`:
|
||||||
|
|
||||||
|
```py
|
||||||
|
global_symbol: str = "a"
|
||||||
|
```
|
||||||
|
|
||||||
|
`mod2.py`:
|
||||||
|
|
||||||
|
```py
|
||||||
|
global_symbol: str = "a"
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
import mod1
|
||||||
|
import mod2
|
||||||
|
|
||||||
|
def _(flag: bool):
|
||||||
|
if flag:
|
||||||
|
mod = mod1
|
||||||
|
else:
|
||||||
|
mod = mod2
|
||||||
|
|
||||||
|
mod.global_symbol = "b"
|
||||||
|
|
||||||
|
# error: [invalid-assignment] "Object of type `Literal[1]` is not assignable to attribute `global_symbol` on type `<module 'mod1'> | <module 'mod2'>`"
|
||||||
|
mod.global_symbol = 1
|
||||||
|
```
|
||||||
|
|
||||||
## Literal types
|
## Literal types
|
||||||
|
|
||||||
### Function-literal attributes
|
### Function-literal attributes
|
||||||
|
|
|
@ -3406,24 +3406,31 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
Type::ModuleLiteral(module) => {
|
Type::ModuleLiteral(module) => {
|
||||||
if let Symbol::Type(attr_ty, _) = module.static_member(db, attribute) {
|
if let Symbol::Type(attr_ty, _) = module.static_member(db, attribute) {
|
||||||
let assignable = value_ty.is_assignable_to(db, attr_ty);
|
let assignable = value_ty.is_assignable_to(db, attr_ty);
|
||||||
if !assignable {
|
if assignable {
|
||||||
report_invalid_attribute_assignment(
|
true
|
||||||
&self.context,
|
} else {
|
||||||
target.into(),
|
if emit_diagnostics {
|
||||||
attr_ty,
|
report_invalid_attribute_assignment(
|
||||||
value_ty,
|
&self.context,
|
||||||
attribute,
|
target.into(),
|
||||||
);
|
attr_ty,
|
||||||
|
value_ty,
|
||||||
|
attribute,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
|
||||||
} else {
|
} else {
|
||||||
if let Some(builder) = self.context.report_lint(&UNRESOLVED_ATTRIBUTE, target) {
|
if emit_diagnostics {
|
||||||
builder.into_diagnostic(format_args!(
|
if let Some(builder) =
|
||||||
"Unresolved attribute `{}` on type `{}`.",
|
self.context.report_lint(&UNRESOLVED_ATTRIBUTE, target)
|
||||||
attribute,
|
{
|
||||||
object_ty.display(db)
|
builder.into_diagnostic(format_args!(
|
||||||
));
|
"Unresolved attribute `{}` on type `{}`.",
|
||||||
|
attribute,
|
||||||
|
object_ty.display(db)
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue