Compare commits
2 Commits
dcreager/n
...
support-py
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21561000b1 | ||
|
|
9c0772d8f0 |
@@ -56,15 +56,6 @@ class D(C[T]): ...
|
||||
|
||||
(Examples `E` and `F` from above do not have analogues in the legacy syntax.)
|
||||
|
||||
## fwomp
|
||||
|
||||
```py
|
||||
class C[T]:
|
||||
x: T
|
||||
|
||||
reveal_type(C[int]()) # revealed: C[int]
|
||||
```
|
||||
|
||||
## Specializing generic classes explicitly
|
||||
|
||||
The type parameter can be specified explicitly:
|
||||
@@ -167,7 +158,7 @@ If the type of a constructor parameter is a class typevar, we can use that to in
|
||||
parameter. The types inferred from a type context and from a constructor parameter must be
|
||||
consistent with each other.
|
||||
|
||||
### `__new__` only
|
||||
## `__new__` only
|
||||
|
||||
```py
|
||||
class C[T]:
|
||||
@@ -180,7 +171,7 @@ reveal_type(C(1)) # revealed: C[Literal[1]]
|
||||
wrong_innards: C[int] = C("five")
|
||||
```
|
||||
|
||||
### `__init__` only
|
||||
## `__init__` only
|
||||
|
||||
```py
|
||||
class C[T]:
|
||||
@@ -192,7 +183,7 @@ reveal_type(C(1)) # revealed: C[Literal[1]]
|
||||
wrong_innards: C[int] = C("five")
|
||||
```
|
||||
|
||||
### Identical `__new__` and `__init__` signatures
|
||||
## Identical `__new__` and `__init__` signatures
|
||||
|
||||
```py
|
||||
class C[T]:
|
||||
@@ -207,7 +198,7 @@ reveal_type(C(1)) # revealed: C[Literal[1]]
|
||||
wrong_innards: C[int] = C("five")
|
||||
```
|
||||
|
||||
### Compatible `__new__` and `__init__` signatures
|
||||
## Compatible `__new__` and `__init__` signatures
|
||||
|
||||
```py
|
||||
class C[T]:
|
||||
@@ -233,7 +224,7 @@ reveal_type(D(1)) # revealed: D[Literal[1]]
|
||||
wrong_innards: D[int] = D("five")
|
||||
```
|
||||
|
||||
### `__init__` is itself generic
|
||||
## `__init__` is itself generic
|
||||
|
||||
TODO: These do not currently work yet, because we don't correctly model the nested generic contexts.
|
||||
|
||||
@@ -294,33 +285,6 @@ c: C[int] = C[int]()
|
||||
reveal_type(c.method("string")) # revealed: Literal["string"]
|
||||
```
|
||||
|
||||
## Nested classes
|
||||
|
||||
```py
|
||||
class C[T]:
|
||||
class D:
|
||||
x: T
|
||||
|
||||
class E[U]:
|
||||
x: T
|
||||
y: U
|
||||
|
||||
def method1(self) -> "D":
|
||||
return self.D()
|
||||
|
||||
def method2(self) -> "E[str]":
|
||||
return self.E[str]()
|
||||
|
||||
reveal_type(C[int]().method1()) # revealed: D
|
||||
# TODO: revealed: int
|
||||
reveal_type(C[int]().method1().x) # revealed: T
|
||||
|
||||
reveal_type(C[int]().method2()) # revealed: E[str]
|
||||
# TODO: revealed: int
|
||||
reveal_type(C[int]().method2().x) # revealed: T
|
||||
reveal_type(C[int]().method2().y) # revealed: str
|
||||
```
|
||||
|
||||
## Cyclic class definition
|
||||
|
||||
A class can use itself as the type parameter of one of its superclasses. (This is also known as the
|
||||
|
||||
@@ -4651,12 +4651,6 @@ impl<'db> Type<'db> {
|
||||
Type::Callable(callable.apply_specialization(db, specialization))
|
||||
}
|
||||
|
||||
Type::ClassLiteral(ClassLiteralType::NonGeneric(class)) => {
|
||||
Type::from(class.apply_specialization(db, specialization))
|
||||
}
|
||||
|
||||
Type::ClassLiteral(ClassLiteralType::Generic(_)) => self,
|
||||
|
||||
Type::GenericAlias(generic) => {
|
||||
let specialization = generic
|
||||
.specialization(db)
|
||||
@@ -4698,6 +4692,10 @@ impl<'db> Type<'db> {
|
||||
| Type::MethodWrapper(MethodWrapperKind::StrStartswith(_))
|
||||
| Type::DataclassDecorator(_)
|
||||
| Type::ModuleLiteral(_)
|
||||
// A non-generic class never needs to be specialized. A generic class is specialized
|
||||
// explicitly (via a subscript expression) or implicitly (via a call), and not because
|
||||
// some other generic context's specialization is applied to it.
|
||||
| Type::ClassLiteral(_)
|
||||
// SubclassOf contains a ClassType, which has already been specialized if needed, like
|
||||
// above with BoundMethod's self_instance.
|
||||
| Type::SubclassOf(_)
|
||||
|
||||
@@ -134,23 +134,6 @@ impl<'db> Class<'db> {
|
||||
pub struct NonGenericClass<'db> {
|
||||
#[return_ref]
|
||||
pub(crate) class: Class<'db>,
|
||||
|
||||
pub(crate) specialization: Option<Specialization<'db>>,
|
||||
}
|
||||
|
||||
impl<'db> NonGenericClass<'db> {
|
||||
pub(crate) fn apply_specialization(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
specialization: Specialization<'db>,
|
||||
) -> Self {
|
||||
eprintln!(
|
||||
"==> specialize {} with {}",
|
||||
Type::from(self).display(db),
|
||||
specialization.display(db)
|
||||
);
|
||||
NonGenericClass::new(db, self.class(db), Some(specialization))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> From<NonGenericClass<'db>> for Type<'db> {
|
||||
@@ -240,13 +223,7 @@ impl<'db> ClassType<'db> {
|
||||
|
||||
fn specialize_type(self, db: &'db dyn Db, ty: Type<'db>) -> Type<'db> {
|
||||
match self {
|
||||
Self::NonGeneric(non_generic) => {
|
||||
if let Some(specialization) = non_generic.specialization(db) {
|
||||
ty.apply_specialization(db, specialization)
|
||||
} else {
|
||||
ty
|
||||
}
|
||||
}
|
||||
Self::NonGeneric(_) => ty,
|
||||
Self::Generic(generic) => ty.apply_specialization(db, generic.specialization(db)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1795,10 +1795,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
Some(generic_context) => {
|
||||
ClassLiteralType::Generic(GenericClass::new(self.db(), class, generic_context))
|
||||
}
|
||||
None => {
|
||||
let specialization = None;
|
||||
ClassLiteralType::NonGeneric(NonGenericClass::new(self.db(), class, specialization))
|
||||
}
|
||||
None => ClassLiteralType::NonGeneric(NonGenericClass::new(self.db(), class)),
|
||||
};
|
||||
let class_ty = Type::from(class_literal);
|
||||
|
||||
|
||||
@@ -260,3 +260,9 @@ def f():
|
||||
for i in range(5):
|
||||
if j := i:
|
||||
items.append(j)
|
||||
|
||||
def f():
|
||||
values = [1, 2, 3]
|
||||
result = list() # this should be replaced with a comprehension
|
||||
for i in values:
|
||||
result.append(i + 1) # PERF401
|
||||
|
||||
@@ -270,6 +270,15 @@ pub(crate) fn manual_list_comprehension(checker: &Checker, for_stmt: &ast::StmtF
|
||||
list_binding_value.is_some_and(|binding_value| match binding_value {
|
||||
// `value = []`
|
||||
Expr::List(list_expr) => list_expr.is_empty(),
|
||||
// `value = list()`
|
||||
// This might be linted against, but turning it into a list comprehension will also remove it
|
||||
Expr::Call(call) => {
|
||||
checker
|
||||
.semantic()
|
||||
.resolve_builtin_symbol(&call.func)
|
||||
.is_some_and(|name| name == "list")
|
||||
&& call.arguments.is_empty()
|
||||
}
|
||||
_ => false,
|
||||
});
|
||||
|
||||
|
||||
@@ -208,5 +208,16 @@ PERF401.py:262:13: PERF401 Use a list comprehension to create a transformed list
|
||||
261 | if j := i:
|
||||
262 | items.append(j)
|
||||
| ^^^^^^^^^^^^^^^ PERF401
|
||||
263 |
|
||||
264 | def f():
|
||||
|
|
||||
= help: Replace for loop with list comprehension
|
||||
|
||||
PERF401.py:268:9: PERF401 Use a list comprehension to create a transformed list
|
||||
|
|
||||
266 | result = list() # this should be replaced with a comprehension
|
||||
267 | for i in values:
|
||||
268 | result.append(i + 1) # PERF401
|
||||
| ^^^^^^^^^^^^^^^^^^^^ PERF401
|
||||
|
|
||||
= help: Replace for loop with list comprehension
|
||||
|
||||
@@ -492,6 +492,8 @@ PERF401.py:262:13: PERF401 [*] Use a list comprehension to create a transformed
|
||||
261 | if j := i:
|
||||
262 | items.append(j)
|
||||
| ^^^^^^^^^^^^^^^ PERF401
|
||||
263 |
|
||||
264 | def f():
|
||||
|
|
||||
= help: Replace for loop with list comprehension
|
||||
|
||||
@@ -505,3 +507,25 @@ PERF401.py:262:13: PERF401 [*] Use a list comprehension to create a transformed
|
||||
261 |- if j := i:
|
||||
262 |- items.append(j)
|
||||
259 |+ items = [j for i in range(5) if (j := i)]
|
||||
263 260 |
|
||||
264 261 | def f():
|
||||
265 262 | values = [1, 2, 3]
|
||||
|
||||
PERF401.py:268:9: PERF401 [*] Use a list comprehension to create a transformed list
|
||||
|
|
||||
266 | result = list() # this should be replaced with a comprehension
|
||||
267 | for i in values:
|
||||
268 | result.append(i + 1) # PERF401
|
||||
| ^^^^^^^^^^^^^^^^^^^^ PERF401
|
||||
|
|
||||
= help: Replace for loop with list comprehension
|
||||
|
||||
ℹ Unsafe fix
|
||||
263 263 |
|
||||
264 264 | def f():
|
||||
265 265 | values = [1, 2, 3]
|
||||
266 |- result = list() # this should be replaced with a comprehension
|
||||
267 |- for i in values:
|
||||
268 |- result.append(i + 1) # PERF401
|
||||
266 |+ # this should be replaced with a comprehension
|
||||
267 |+ result = [i + 1 for i in values] # PERF401
|
||||
|
||||
@@ -39,6 +39,10 @@ use crate::Locator;
|
||||
/// "{}, {}".format("Hello", "World") # "Hello, World"
|
||||
/// ```
|
||||
///
|
||||
/// This fix is marked as unsafe because:
|
||||
/// - Comments attached to arguments are not moved, which can cause comments to mismatch the actual arguments.
|
||||
/// - If arguments have side effects (e.g., print), reordering may change program behavior.
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: Format String Syntax](https://docs.python.org/3/library/string.html#format-string-syntax)
|
||||
/// - [Python documentation: `str.format`](https://docs.python.org/3/library/stdtypes.html#str.format)
|
||||
|
||||
Reference in New Issue
Block a user