mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:24:57 +00:00
[ty] Keep track of type qualifiers in stub declarations without right-hand side (#19756)
Some checks are pending
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks-instrumented (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
Some checks are pending
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks-instrumented (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
## Summary closes https://github.com/astral-sh/ty/issues/937 ## Test Plan Regression test
This commit is contained in:
parent
2d2841e20d
commit
7df7be5c7d
2 changed files with 48 additions and 17 deletions
|
@ -38,6 +38,29 @@ c.d = 2
|
|||
c.e = 2
|
||||
```
|
||||
|
||||
## From stubs
|
||||
|
||||
This is a regression test for a bug where we did not properly keep track of type qualifiers when
|
||||
accessed from stub files.
|
||||
|
||||
`module.pyi`:
|
||||
|
||||
```pyi
|
||||
from typing import ClassVar
|
||||
|
||||
class C:
|
||||
a: ClassVar[int]
|
||||
```
|
||||
|
||||
`main.py`:
|
||||
|
||||
```py
|
||||
from module import C
|
||||
|
||||
c = C()
|
||||
c.a = 2 # error: [invalid-attribute-access]
|
||||
```
|
||||
|
||||
## Conflicting type qualifiers
|
||||
|
||||
We currently ignore conflicting qualifiers and simply union them, which is more conservative than
|
||||
|
|
|
@ -694,7 +694,7 @@ enum IntersectionOn {
|
|||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
enum DeclaredAndInferredType<'db> {
|
||||
/// We know that both the declared and inferred types are the same.
|
||||
AreTheSame(Type<'db>),
|
||||
AreTheSame(TypeAndQualifiers<'db>),
|
||||
/// Declared and inferred types might be different, we need to check assignability.
|
||||
MightBeDifferent {
|
||||
declared_ty: TypeAndQualifiers<'db>,
|
||||
|
@ -702,6 +702,12 @@ enum DeclaredAndInferredType<'db> {
|
|||
},
|
||||
}
|
||||
|
||||
impl<'db> DeclaredAndInferredType<'db> {
|
||||
fn are_the_same_type(ty: Type<'db>) -> Self {
|
||||
Self::AreTheSame(ty.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder to infer all types in a region.
|
||||
///
|
||||
/// A builder is used by creating it with [`new()`](TypeInferenceBuilder::new), and then calling
|
||||
|
@ -2132,7 +2138,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
);
|
||||
|
||||
let (declared_ty, inferred_ty) = match *declared_and_inferred_ty {
|
||||
DeclaredAndInferredType::AreTheSame(ty) => (ty.into(), ty),
|
||||
DeclaredAndInferredType::AreTheSame(type_and_qualifiers) => {
|
||||
(type_and_qualifiers, type_and_qualifiers.inner_type())
|
||||
}
|
||||
DeclaredAndInferredType::MightBeDifferent {
|
||||
declared_ty,
|
||||
inferred_ty,
|
||||
|
@ -2191,7 +2199,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
self.add_declaration_with_binding(
|
||||
node,
|
||||
definition,
|
||||
&DeclaredAndInferredType::AreTheSame(Type::unknown()),
|
||||
&DeclaredAndInferredType::are_the_same_type(Type::unknown()),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2658,7 +2666,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
self.add_declaration_with_binding(
|
||||
function.into(),
|
||||
definition,
|
||||
&DeclaredAndInferredType::AreTheSame(inferred_ty),
|
||||
&DeclaredAndInferredType::are_the_same_type(inferred_ty),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2818,7 +2826,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
.as_ref()
|
||||
.is_some_and(|d| d.is_ellipsis_literal_expr())
|
||||
{
|
||||
DeclaredAndInferredType::AreTheSame(declared_ty)
|
||||
DeclaredAndInferredType::are_the_same_type(declared_ty)
|
||||
} else {
|
||||
if let Some(builder) = self
|
||||
.context
|
||||
|
@ -2831,10 +2839,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
declared_ty.display(self.db())
|
||||
));
|
||||
}
|
||||
DeclaredAndInferredType::AreTheSame(declared_ty)
|
||||
DeclaredAndInferredType::are_the_same_type(declared_ty)
|
||||
}
|
||||
} else {
|
||||
DeclaredAndInferredType::AreTheSame(declared_ty)
|
||||
DeclaredAndInferredType::are_the_same_type(declared_ty)
|
||||
};
|
||||
self.add_declaration_with_binding(
|
||||
parameter.into(),
|
||||
|
@ -2874,7 +2882,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
self.add_declaration_with_binding(
|
||||
parameter.into(),
|
||||
definition,
|
||||
&DeclaredAndInferredType::AreTheSame(ty),
|
||||
&DeclaredAndInferredType::are_the_same_type(ty),
|
||||
);
|
||||
} else {
|
||||
self.add_binding(
|
||||
|
@ -2906,7 +2914,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
self.add_declaration_with_binding(
|
||||
parameter.into(),
|
||||
definition,
|
||||
&DeclaredAndInferredType::AreTheSame(ty),
|
||||
&DeclaredAndInferredType::are_the_same_type(ty),
|
||||
);
|
||||
} else {
|
||||
self.add_binding(
|
||||
|
@ -3004,7 +3012,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
self.add_declaration_with_binding(
|
||||
class_node.into(),
|
||||
definition,
|
||||
&DeclaredAndInferredType::AreTheSame(class_ty),
|
||||
&DeclaredAndInferredType::are_the_same_type(class_ty),
|
||||
);
|
||||
|
||||
// if there are type parameters, then the keywords and bases are within that scope
|
||||
|
@ -3078,7 +3086,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
self.add_declaration_with_binding(
|
||||
type_alias.into(),
|
||||
definition,
|
||||
&DeclaredAndInferredType::AreTheSame(type_alias_ty),
|
||||
&DeclaredAndInferredType::are_the_same_type(type_alias_ty),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3433,7 +3441,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
self.add_declaration_with_binding(
|
||||
node.into(),
|
||||
definition,
|
||||
&DeclaredAndInferredType::AreTheSame(ty),
|
||||
&DeclaredAndInferredType::are_the_same_type(ty),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3453,7 +3461,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
self.add_declaration_with_binding(
|
||||
node.into(),
|
||||
definition,
|
||||
&DeclaredAndInferredType::AreTheSame(pep_695_todo),
|
||||
&DeclaredAndInferredType::are_the_same_type(pep_695_todo),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3473,7 +3481,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
self.add_declaration_with_binding(
|
||||
node.into(),
|
||||
definition,
|
||||
&DeclaredAndInferredType::AreTheSame(pep_695_todo),
|
||||
&DeclaredAndInferredType::are_the_same_type(pep_695_todo),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -4558,7 +4566,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
self.add_declaration_with_binding(
|
||||
target.into(),
|
||||
definition,
|
||||
&DeclaredAndInferredType::AreTheSame(declared.inner_type()),
|
||||
&DeclaredAndInferredType::AreTheSame(declared),
|
||||
);
|
||||
} else {
|
||||
self.add_declaration(target.into(), definition, declared);
|
||||
|
@ -4876,7 +4884,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
self.add_declaration_with_binding(
|
||||
alias.into(),
|
||||
definition,
|
||||
&DeclaredAndInferredType::AreTheSame(binding_ty),
|
||||
&DeclaredAndInferredType::are_the_same_type(binding_ty),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -5125,7 +5133,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
self.add_declaration_with_binding(
|
||||
alias.into(),
|
||||
definition,
|
||||
&DeclaredAndInferredType::AreTheSame(submodule_type),
|
||||
&DeclaredAndInferredType::are_the_same_type(submodule_type),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue