Skip to content

Comments

[syntax-errors] Duplicate type parameter names#16858

Merged
ntBre merged 8 commits intomainfrom
brent/syn-duplicate-type-params
Mar 21, 2025
Merged

[syntax-errors] Duplicate type parameter names#16858
ntBre merged 8 commits intomainfrom
brent/syn-duplicate-type-params

Conversation

@ntBre
Copy link
Contributor

@ntBre ntBre commented Mar 19, 2025

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. (That's the reason for the red-knot code
owner review requests, sorry!)

Test Plan

New ruff_python_syntax_errors unit tests.

Fixes #11119.

@ntBre ntBre added rule Implementing or modifying a lint rule preview Related to preview mode features labels Mar 19, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Mar 19, 2025

mypy_primer results

No ecosystem changes detected ✅

@github-actions
Copy link
Contributor

github-actions bot commented Mar 19, 2025

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

Formatter (stable)

✅ ecosystem check detected no format changes.

Formatter (preview)

✅ ecosystem check detected no format changes.

@carljm
Copy link
Contributor

carljm commented Mar 19, 2025

(That's the reason for the red-knot code
owner review requests, sorry!)

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.

@carljm carljm removed their request for review March 19, 2025 23:21
Comment on lines 127 to 136
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;
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not saying that we shouldn't change the definition but you could have written this as

Suggested change
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,
};

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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());
	}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines 305 to 308
#[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(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a few examples where:

  • With more than two duplicate type params
  • With non duplicate type params

Copy link
Member

@dhruvmanila dhruvmanila left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good

@ntBre ntBre force-pushed the brent/syn-duplicate-type-params branch from c95ecde to 9a2c750 Compare March 21, 2025 15:09
@ntBre
Copy link
Contributor Author

ntBre commented Mar 21, 2025

I rebased this onto #16106, added more test cases (and converted them to inline tests), returned early with <2 parameters, and removed itertools.

Base automatically changed from brent/syntax-error-source-order to main March 21, 2025 18:45
ntBre added 8 commits March 21, 2025 14:48
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.
@ntBre ntBre force-pushed the brent/syn-duplicate-type-params branch from 9a2c750 to 1a63839 Compare March 21, 2025 18:49
@ntBre ntBre merged commit e4f5fe8 into main Mar 21, 2025
23 checks passed
@ntBre ntBre deleted the brent/syn-duplicate-type-params branch March 21, 2025 19:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

preview Related to preview mode features rule Implementing or modifying a lint rule

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ruff fails to detect duplicate type parameter names

4 participants