[syntax-errors] Duplicate type parameter names#16858
Conversation
|
|
No worries! Please don't ever hesitate to change red-knot code for this reason, it's up to us to manage our CODEOWNERS setup and handle the consequences. |
| let (Stmt::FunctionDef(ast::StmtFunctionDef { type_params, .. }) | ||
| | Stmt::ClassDef(ast::StmtClassDef { type_params, .. }) | ||
| | Stmt::TypeAlias(ast::StmtTypeAlias { type_params, .. })) = stmt | ||
| else { | ||
| return; | ||
| }; | ||
|
|
||
| let Some(type_params) = type_params else { | ||
| return; | ||
| }; |
There was a problem hiding this comment.
Not saying that we shouldn't change the definition but you could have written this as
| let (Stmt::FunctionDef(ast::StmtFunctionDef { type_params, .. }) | |
| | Stmt::ClassDef(ast::StmtClassDef { type_params, .. }) | |
| | Stmt::TypeAlias(ast::StmtTypeAlias { type_params, .. })) = stmt | |
| else { | |
| return; | |
| }; | |
| let Some(type_params) = type_params else { | |
| return; | |
| }; | |
| let type_params = match stmt { | |
| Stmt::FunctionDef(ast::StmtFunctionDef { | |
| type_params: Some(type_params), | |
| .. | |
| }) | |
| | Stmt::ClassDef(ast::StmtClassDef { | |
| type_params: Some(type_params), | |
| .. | |
| }) => &**type_params, | |
| Stmt::TypeAlias(ast::StmtTypeAlias { | |
| type_params: Some(type_params), | |
| .. | |
| }) => type_params, | |
| _ => return, | |
| }; |
There was a problem hiding this comment.
For sure, I'm happy with that too if we'd rather not change the generated code.
| .iter() | ||
| .rev() | ||
| .sorted_by_key(|t| &t.name().id) | ||
| .dedup_by_with_count(|t1, t2| t1.name().id == t2.name().id) |
There was a problem hiding this comment.
From the documentation, it's not clear to me which element dedup_by_with_count keeps if multiple elements map to the same key: Is it the first, the last, or any?
We would want to flag all except the first, which I believe the implementation currently isn't handling (it only flags one).
In which case we probably can't use dedup_by_with_count anymore, in which case I would suggest writing this entire check without using itertools (I don't mind it but I also don't love it because it hides allocations)
if type_params.len() > 1 {
let mut sorted = type_params.clone();
sorted.sort_by_key(|t| &*t.name());
let last_name = None;
for type_param in sorted {
if last_name.is_some_and(|last| last == type_param.name()) {
// push diagnostic
}
last_name = Some(type_param.name());
}
}There was a problem hiding this comment.
Ah that's a good point. I assumed it was taking the first, which is why I reversed the list, but that doesn't handle the case with more than two duplicates. I also felt like I was going overboard with itertools here, so your implementation makes sense to me.
There was a problem hiding this comment.
I went with an inner linear search instead of sorting at all, but I'm happy to go with your approach if you prefer it.
| #[test_case("type Alias[T, T] = ...", "alias")] | ||
| #[test_case("def f[T, T](t: T): ...", "function")] | ||
| #[test_case("class C[T, T]: ...", "class")] | ||
| #[test_case( |
There was a problem hiding this comment.
Let's add a few examples where:
- With more than two duplicate type params
- With non duplicate type params
c95ecde to
9a2c750
Compare
|
I rebased this onto #16106, added more test cases (and converted them to inline tests), returned early with <2 parameters, and removed itertools. |
Summary -- Detects duplicate type parameter names in function definitions, class definitions, and type alias statements. I also boxed the `type_params` field on `StmtTypeAlias` to make it easier to `match` with functions and classes. Test Plan -- New `ruff_python_syntax_errors` unit tests.
9a2c750 to
1a63839
Compare
Summary
Detects duplicate type parameter names in function definitions, class
definitions, and type alias statements.
I also boxed the
type_paramsfield onStmtTypeAliasto make it easier tomatchwith functions and classes. (That's the reason for the red-knot codeowner review requests, sorry!)
Test Plan
New
ruff_python_syntax_errorsunit tests.Fixes #11119.