Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[syntax-errors] Type parameter lists before Python 3.12 #16479

Merged
merged 11 commits into from
Mar 5, 2025

Conversation

ntBre
Copy link
Contributor

@ntBre ntBre commented Mar 3, 2025

Summary

Another simple one, just detect type parameter lists in type statements, functions, and classes. pyright only reports the type statement in the alias case, whereas we'll currently report both diagnostics, in combination with #16478.

Test Plan

Inline tests.

Summary
--

Another simple one, just detect type parameter lists in `type` statements,
functions, and classes. [pyright] only reports the `type` statement in the alias
case, whereas we'll currently report both diagnostics, in combination with

Test Plan
--
Inline tests.

[pyright]: https://pyright-play.net/?pythonVersion=3.8&strict=true&code=C4TwDgpgBAHg2gFQLpQLxQJYDthA
@ntBre ntBre added parser Related to the parser preview Related to preview mode features labels Mar 3, 2025
Copy link
Contributor

github-actions bot commented Mar 3, 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.

@NeilGirdhar
Copy link

NeilGirdhar commented Mar 3, 2025

This is W2604 from Pylint. You may want to add it here and tick it off if you want 😄 I think your other PRs are similarly listed in Pylint.

@ntBre
Copy link
Contributor Author

ntBre commented Mar 3, 2025

Ah, thank you! I've only been tracking them in #6591. I'll update #970 too.

@MichaReiser
Copy link
Member

@@ -449,6 +449,7 @@ pub enum UnsupportedSyntaxErrorKind {
Match,
Walrus,
ExceptStar,
TypeParams,
Copy link
Member

Choose a reason for hiding this comment

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

Should this be TypeParameterList or should we rename the message to type parameters?

It might also be helpful to start documenting the variants. E.g. by adding a link to the Python documentation https://docs.python.org/3/reference/compound_stmts.html#type-parameter-lists

Copy link
Contributor Author

Choose a reason for hiding this comment

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

TypeParameterList sounds good. I'll start documenting them too, I think that's a great idea. As in #16404, I included something similar to ruff rule docs in case we want to show these to users eventually.

@dhruvmanila
Copy link
Member

dhruvmanila commented Mar 4, 2025

pyright only reports the type statement in the alias case, whereas we'll currently report both diagnostics, in combination with #16478.

I think this is because the type statement itself was added in 3.12 so maybe it might be worth not raising this error for type statement as the entire statement is invalid?

Comment on lines 3109 to 3119
// test_ok type_params_py312
// # parse_options: {"target-version": "3.12"}
// type List[T] = list | set
// def foo[T](): ...
// class Foo[T]: ...

// test_err type_params_py311
// # parse_options: {"target-version": "3.11"}
// type List[T] = list | set
// def foo[T](): ...
// class Foo[T]: ...
Copy link
Member

Choose a reason for hiding this comment

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

Can we add a case with multiple type parameters? Or, we can just mix them up by using single type parameter in function and multiple type parameters in class.

Copy link
Contributor Author

@ntBre ntBre Mar 4, 2025

Choose a reason for hiding this comment

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

I added an example from the UP046 tests with bounds and constraints, as well as an unpacked TypeVarTuple and a ParamSpec.

We just mark the whole list at once, so there aren't additional errors for each type parameter.

@ntBre
Copy link
Contributor Author

ntBre commented Mar 4, 2025

pyright only reports the type statement in the alias case, whereas we'll currently report both diagnostics, in combination with #16478.

I think this is because the type statement itself was added in 3.12 so maybe it might be worth not raising this error for type statement as the entire statement is invalid?

I think that's fair. I can just hoist the check to where try_parse_type_params is called for classes and functions instead of keeping it in parse_type_params.

Comment on lines 1798 to 1800
// Only emit the `ParseError` for an empty parameter list instead of also including an
// `UnsupportedSyntaxError`.
if !type_params.is_empty() {
Copy link
Member

Choose a reason for hiding this comment

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

What's the reason for this condition? As a user I'd find it confusing if Ruff told me to first add the type parameters and then tell me to remove the entire list. Sorry if I missed this in my initial review if it was already present.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh that's a good point... I was just trying to avoid duplicates, but if anything, it might make sense to suppress the ParseError in this case. Removing the condition is certainly easiest though.

And you didn't miss it, I added it here in the revision when I added the empty test cases.

Copy link
Member

Choose a reason for hiding this comment

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

I think I'd prefer keep it both. I don't think we should suppress any syntax errors.

I know this goes against what I said earlier in #16479 (comment) but my argument for that would be that it's a new statement altogether. Maybe for now we can just avoid suppressing any syntax errors based on other syntax errors and we can act on it based on user feedback? Happy to hear others thoughts on this.

Copy link
Member

Choose a reason for hiding this comment

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

The parser does have some logic to avoid multiple errors at the same location for invalid syntax errors.

fn inner(errors: &mut Vec<ParseError>, error: ParseErrorType, range: TextRange) {
// Avoid flagging multiple errors at the same location
let is_same_location = errors
.last()
.is_some_and(|last| last.location.start() == range.start());
if !is_same_location {
errors.push(ParseError {
error,
location: range,
});
}
}
inner(&mut self.errors, error, ranged.range());

But I agree that we should solve this holisticly because it will otherwise be very error prone to avoid all possible errors at the same location

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 nice! I'll remove these checks for now, and then maybe we can add UnsupportedSyntaxErrors to that check or a similar one later.

Comment on lines 1931 to 1933
// Only emit the `ParseError` for an empty parameter list instead of also including an
// `UnsupportedSyntaxError`.
if !type_params.is_empty() {
Copy link
Member

Choose a reason for hiding this comment

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

Same as above.

Comment on lines 476 to 477
/// [type parameter list]:
/// https://docs.python.org/3/reference/compound_stmts.html#type-parameter-lists
Copy link
Member

Choose a reason for hiding this comment

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

Does this work? Shouldn't the link be after [..]: on the same line?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It appeared to work in Emacs, but I can put it on one line to be safe!

Comment on lines 1798 to 1800
// Only emit the `ParseError` for an empty parameter list instead of also including an
// `UnsupportedSyntaxError`.
if !type_params.is_empty() {
Copy link
Member

Choose a reason for hiding this comment

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

The parser does have some logic to avoid multiple errors at the same location for invalid syntax errors.

fn inner(errors: &mut Vec<ParseError>, error: ParseErrorType, range: TextRange) {
// Avoid flagging multiple errors at the same location
let is_same_location = errors
.last()
.is_some_and(|last| last.location.start() == range.start());
if !is_same_location {
errors.push(ParseError {
error,
location: range,
});
}
}
inner(&mut self.errors, error, ranged.range());

But I agree that we should solve this holisticly because it will otherwise be very error prone to avoid all possible errors at the same location

@ntBre ntBre enabled auto-merge (squash) March 5, 2025 13:16
@ntBre ntBre merged commit 81bcdce into main Mar 5, 2025
20 checks passed
@ntBre ntBre deleted the brent/syn-type-params branch March 5, 2025 13:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
parser Related to the parser preview Related to preview mode features
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants