mirror of https://github.com/astral-sh/ruff
[syntax-error] Default type parameter followed by non-default type parameter (#21657)
## Summary This PR implements syntax error where a default type parameter is followed by a non-default type parameter. https://github.com/astral-sh/ruff/issues/17412#issuecomment-3584088217 ## Test Plan I have written inline tests as directed in #17412 --------- Signed-off-by: 11happy <bhuminjaysoni@gmail.com> Signed-off-by: 11happy <soni5happy@gmail.com>
This commit is contained in:
parent
abaa49f552
commit
f68080b55e
|
|
@ -747,6 +747,7 @@ impl SemanticSyntaxContext for Checker<'_> {
|
||||||
| SemanticSyntaxErrorKind::LoadBeforeNonlocalDeclaration { .. }
|
| SemanticSyntaxErrorKind::LoadBeforeNonlocalDeclaration { .. }
|
||||||
| SemanticSyntaxErrorKind::NonlocalAndGlobal(_)
|
| SemanticSyntaxErrorKind::NonlocalAndGlobal(_)
|
||||||
| SemanticSyntaxErrorKind::AnnotatedGlobal(_)
|
| SemanticSyntaxErrorKind::AnnotatedGlobal(_)
|
||||||
|
| SemanticSyntaxErrorKind::TypeParameterDefaultOrder(_)
|
||||||
| SemanticSyntaxErrorKind::AnnotatedNonlocal(_) => {
|
| SemanticSyntaxErrorKind::AnnotatedNonlocal(_) => {
|
||||||
self.semantic_errors.borrow_mut().push(error);
|
self.semantic_errors.borrow_mut().push(error);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
class C[T = int, U]: ...
|
||||||
|
class C[T1, T2 = int, T3, T4]: ...
|
||||||
|
type Alias[T = int, U] = ...
|
||||||
|
|
@ -144,11 +144,16 @@ impl SemanticSyntaxChecker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stmt::ClassDef(ast::StmtClassDef { type_params, .. })
|
Stmt::ClassDef(ast::StmtClassDef {
|
||||||
| Stmt::TypeAlias(ast::StmtTypeAlias { type_params, .. }) => {
|
type_params: Some(type_params),
|
||||||
if let Some(type_params) = type_params {
|
..
|
||||||
|
})
|
||||||
|
| Stmt::TypeAlias(ast::StmtTypeAlias {
|
||||||
|
type_params: Some(type_params),
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
Self::duplicate_type_parameter_name(type_params, ctx);
|
Self::duplicate_type_parameter_name(type_params, ctx);
|
||||||
}
|
Self::type_parameter_default_order(type_params, ctx);
|
||||||
}
|
}
|
||||||
Stmt::Assign(ast::StmtAssign { targets, value, .. }) => {
|
Stmt::Assign(ast::StmtAssign { targets, value, .. }) => {
|
||||||
if let [Expr::Starred(ast::ExprStarred { range, .. })] = targets.as_slice() {
|
if let [Expr::Starred(ast::ExprStarred { range, .. })] = targets.as_slice() {
|
||||||
|
|
@ -611,6 +616,39 @@ impl SemanticSyntaxChecker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn type_parameter_default_order<Ctx: SemanticSyntaxContext>(
|
||||||
|
type_params: &ast::TypeParams,
|
||||||
|
ctx: &Ctx,
|
||||||
|
) {
|
||||||
|
let mut seen_default = false;
|
||||||
|
for type_param in type_params.iter() {
|
||||||
|
let has_default = match type_param {
|
||||||
|
ast::TypeParam::TypeVar(ast::TypeParamTypeVar { default, .. })
|
||||||
|
| ast::TypeParam::TypeVarTuple(ast::TypeParamTypeVarTuple { default, .. })
|
||||||
|
| ast::TypeParam::ParamSpec(ast::TypeParamParamSpec { default, .. }) => {
|
||||||
|
default.is_some()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if seen_default && !has_default {
|
||||||
|
// test_err type_parameter_default_order
|
||||||
|
// class C[T = int, U]: ...
|
||||||
|
// class C[T1, T2 = int, T3, T4]: ...
|
||||||
|
// type Alias[T = int, U] = ...
|
||||||
|
Self::add_error(
|
||||||
|
ctx,
|
||||||
|
SemanticSyntaxErrorKind::TypeParameterDefaultOrder(
|
||||||
|
type_param.name().id.to_string(),
|
||||||
|
),
|
||||||
|
type_param.range(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if has_default {
|
||||||
|
seen_default = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn duplicate_parameter_name<Ctx: SemanticSyntaxContext>(
|
fn duplicate_parameter_name<Ctx: SemanticSyntaxContext>(
|
||||||
parameters: &ast::Parameters,
|
parameters: &ast::Parameters,
|
||||||
ctx: &Ctx,
|
ctx: &Ctx,
|
||||||
|
|
@ -1066,6 +1104,12 @@ impl Display for SemanticSyntaxError {
|
||||||
SemanticSyntaxErrorKind::DuplicateTypeParameter => {
|
SemanticSyntaxErrorKind::DuplicateTypeParameter => {
|
||||||
f.write_str("duplicate type parameter")
|
f.write_str("duplicate type parameter")
|
||||||
}
|
}
|
||||||
|
SemanticSyntaxErrorKind::TypeParameterDefaultOrder(name) => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"non default type parameter `{name}` follows default type parameter"
|
||||||
|
)
|
||||||
|
}
|
||||||
SemanticSyntaxErrorKind::MultipleCaseAssignment(name) => {
|
SemanticSyntaxErrorKind::MultipleCaseAssignment(name) => {
|
||||||
write!(f, "multiple assignments to name `{name}` in pattern")
|
write!(f, "multiple assignments to name `{name}` in pattern")
|
||||||
}
|
}
|
||||||
|
|
@ -1572,6 +1616,9 @@ pub enum SemanticSyntaxErrorKind {
|
||||||
|
|
||||||
/// Represents a nonlocal statement for a name that has no binding in an enclosing scope.
|
/// Represents a nonlocal statement for a name that has no binding in an enclosing scope.
|
||||||
NonlocalWithoutBinding(String),
|
NonlocalWithoutBinding(String),
|
||||||
|
|
||||||
|
/// Represents a default type parameter followed by a non-default type parameter.
|
||||||
|
TypeParameterDefaultOrder(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)]
|
||||||
|
|
|
||||||
|
|
@ -375,3 +375,12 @@ Module(
|
||||||
4 | type X[**P = x := int] = int
|
4 | type X[**P = x := int] = int
|
||||||
5 | type X[**P = *int] = int
|
5 | type X[**P = *int] = int
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
2 | type X[**P = yield x] = int
|
||||||
|
3 | type X[**P = yield from x] = int
|
||||||
|
4 | type X[**P = x := int] = int
|
||||||
|
| ^^^ Syntax Error: non default type parameter `int` follows default type parameter
|
||||||
|
5 | type X[**P = *int] = int
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
@ -459,3 +459,12 @@ Module(
|
||||||
5 | type X[T = x := int] = int
|
5 | type X[T = x := int] = int
|
||||||
6 | type X[T: int = *int] = int
|
6 | type X[T: int = *int] = int
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
3 | type X[T = (yield x)] = int
|
||||||
|
4 | type X[T = yield from x] = int
|
||||||
|
5 | type X[T = x := int] = int
|
||||||
|
| ^^^ Syntax Error: non default type parameter `int` follows default type parameter
|
||||||
|
6 | type X[T: int = *int] = int
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
@ -384,3 +384,11 @@ Module(
|
||||||
| ^^^^^^^^^^^^ Syntax Error: yield expression cannot be used within a TypeVarTuple default
|
| ^^^^^^^^^^^^ Syntax Error: yield expression cannot be used within a TypeVarTuple default
|
||||||
5 | type X[*Ts = x := int] = int
|
5 | type X[*Ts = x := int] = int
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
3 | type X[*Ts = yield x] = int
|
||||||
|
4 | type X[*Ts = yield from x] = int
|
||||||
|
5 | type X[*Ts = x := int] = int
|
||||||
|
| ^^^ Syntax Error: non default type parameter `int` follows default type parameter
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,277 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_parser/tests/fixtures.rs
|
||||||
|
input_file: crates/ruff_python_parser/resources/inline/err/type_parameter_default_order.py
|
||||||
|
---
|
||||||
|
## AST
|
||||||
|
|
||||||
|
```
|
||||||
|
Module(
|
||||||
|
ModModule {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 0..89,
|
||||||
|
body: [
|
||||||
|
ClassDef(
|
||||||
|
StmtClassDef {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 0..24,
|
||||||
|
decorator_list: [],
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("C"),
|
||||||
|
range: 6..7,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
},
|
||||||
|
type_params: Some(
|
||||||
|
TypeParams {
|
||||||
|
range: 7..19,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
type_params: [
|
||||||
|
TypeVar(
|
||||||
|
TypeParamTypeVar {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 8..15,
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("T"),
|
||||||
|
range: 8..9,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
},
|
||||||
|
bound: None,
|
||||||
|
default: Some(
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 12..15,
|
||||||
|
id: Name("int"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TypeVar(
|
||||||
|
TypeParamTypeVar {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 17..18,
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("U"),
|
||||||
|
range: 17..18,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
},
|
||||||
|
bound: None,
|
||||||
|
default: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
arguments: None,
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 21..24,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 21..24,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ClassDef(
|
||||||
|
StmtClassDef {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 25..59,
|
||||||
|
decorator_list: [],
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("C"),
|
||||||
|
range: 31..32,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
},
|
||||||
|
type_params: Some(
|
||||||
|
TypeParams {
|
||||||
|
range: 32..54,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
type_params: [
|
||||||
|
TypeVar(
|
||||||
|
TypeParamTypeVar {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 33..35,
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("T1"),
|
||||||
|
range: 33..35,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
},
|
||||||
|
bound: None,
|
||||||
|
default: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TypeVar(
|
||||||
|
TypeParamTypeVar {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 37..45,
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("T2"),
|
||||||
|
range: 37..39,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
},
|
||||||
|
bound: None,
|
||||||
|
default: Some(
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 42..45,
|
||||||
|
id: Name("int"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TypeVar(
|
||||||
|
TypeParamTypeVar {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 47..49,
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("T3"),
|
||||||
|
range: 47..49,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
},
|
||||||
|
bound: None,
|
||||||
|
default: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TypeVar(
|
||||||
|
TypeParamTypeVar {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 51..53,
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("T4"),
|
||||||
|
range: 51..53,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
},
|
||||||
|
bound: None,
|
||||||
|
default: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
arguments: None,
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 56..59,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 56..59,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TypeAlias(
|
||||||
|
StmtTypeAlias {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 60..88,
|
||||||
|
name: Name(
|
||||||
|
ExprName {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 65..70,
|
||||||
|
id: Name("Alias"),
|
||||||
|
ctx: Store,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
type_params: Some(
|
||||||
|
TypeParams {
|
||||||
|
range: 70..82,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
type_params: [
|
||||||
|
TypeVar(
|
||||||
|
TypeParamTypeVar {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 71..78,
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("T"),
|
||||||
|
range: 71..72,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
},
|
||||||
|
bound: None,
|
||||||
|
default: Some(
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 75..78,
|
||||||
|
id: Name("int"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TypeVar(
|
||||||
|
TypeParamTypeVar {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 80..81,
|
||||||
|
name: Identifier {
|
||||||
|
id: Name("U"),
|
||||||
|
range: 80..81,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
},
|
||||||
|
bound: None,
|
||||||
|
default: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 85..88,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
```
|
||||||
|
## Semantic Syntax Errors
|
||||||
|
|
||||||
|
|
|
||||||
|
1 | class C[T = int, U]: ...
|
||||||
|
| ^ Syntax Error: non default type parameter `U` follows default type parameter
|
||||||
|
2 | class C[T1, T2 = int, T3, T4]: ...
|
||||||
|
3 | type Alias[T = int, U] = ...
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
1 | class C[T = int, U]: ...
|
||||||
|
2 | class C[T1, T2 = int, T3, T4]: ...
|
||||||
|
| ^^ Syntax Error: non default type parameter `T3` follows default type parameter
|
||||||
|
3 | type Alias[T = int, U] = ...
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
1 | class C[T = int, U]: ...
|
||||||
|
2 | class C[T1, T2 = int, T3, T4]: ...
|
||||||
|
| ^^ Syntax Error: non default type parameter `T4` follows default type parameter
|
||||||
|
3 | type Alias[T = int, U] = ...
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
1 | class C[T = int, U]: ...
|
||||||
|
2 | class C[T1, T2 = int, T3, T4]: ...
|
||||||
|
3 | type Alias[T = int, U] = ...
|
||||||
|
| ^ Syntax Error: non default type parameter `U` follows default type parameter
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue