-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Conversation
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
|
@@ -449,6 +449,7 @@ pub enum UnsupportedSyntaxErrorKind { | |||
Match, | |||
Walrus, | |||
ExceptStar, | |||
TypeParams, |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
I think this is because the |
// 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]: ... |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
I think that's fair. I can just hoist the check to where |
// Only emit the `ParseError` for an empty parameter list instead of also including an | ||
// `UnsupportedSyntaxError`. | ||
if !type_params.is_empty() { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
ruff/crates/ruff_python_parser/src/parser/mod.rs
Lines 424 to 438 in 37fbe58
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
There was a problem hiding this comment.
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 UnsupportedSyntaxError
s to that check or a similar one later.
// Only emit the `ParseError` for an empty parameter list instead of also including an | ||
// `UnsupportedSyntaxError`. | ||
if !type_params.is_empty() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above.
/// [type parameter list]: | ||
/// https://docs.python.org/3/reference/compound_stmts.html#type-parameter-lists |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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!
// Only emit the `ParseError` for an empty parameter list instead of also including an | ||
// `UnsupportedSyntaxError`. | ||
if !type_params.is_empty() { |
There was a problem hiding this comment.
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.
ruff/crates/ruff_python_parser/src/parser/mod.rs
Lines 424 to 438 in 37fbe58
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
Summary
Another simple one, just detect type parameter lists in
type
statements, functions, and classes. pyright only reports thetype
statement in the alias case, whereas we'll currently report both diagnostics, in combination with #16478.Test Plan
Inline tests.