-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[syntax-errors] Name is parameter and global #20426
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
Conversation
|
| /// | ||
| /// [#111123]: https://github.com/python/cpython/issues/111123 | ||
| LoadBeforeGlobalDeclaration { name: String, start: TextSize }, | ||
| LoadBeforeGlobalDeclaration { |
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'd prefer not to reformat unrelated sections of code, as I mentioned on your other PR
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.
sure , actually I am not changing these , these are automatically added by pre-commit jobs
0a88141 to
3e5464b
Compare
Diagnostic diff on typing conformance testsNo changes detected when running ty on typing conformance tests ✅ |
|
ntBre
left a comment
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.
Thank you, this is looking good. We just need to be a bit more careful with our scope handling and revert the unnecessary Violation changes.
crates/ruff_linter/resources/test/fixtures/syntax_errors/global_parameter.py
Outdated
Show resolved
Hide resolved
| } | ||
| ) | ||
| } | ||
| fn is_binded_parameter(&self, name: &str) -> bool { |
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.
nit: I think a name like is_parameter or is_bound_parameter would be a bit better.
More importantly, and related to my comment on the test case above, I don't think this is quite right. We need to limit our parameter check to the nearest-enclosing function scope. In other words, we need to search through self.semantic.current_scopes more like we do in some of the other context methods:
ruff/crates/ruff_linter/src/checkers/ast/mod.rs
Lines 737 to 749 in bedfc6f
| fn in_async_context(&self) -> bool { | |
| for scope in self.semantic.current_scopes() { | |
| match scope.kind { | |
| ScopeKind::Class(_) | ScopeKind::Lambda(_) => return false, | |
| ScopeKind::Function(ast::StmtFunctionDef { is_async, .. }) => return *is_async, | |
| ScopeKind::Generator { .. } | |
| | ScopeKind::Module | |
| | ScopeKind::Type | |
| | ScopeKind::DunderClassCell => {} | |
| } | |
| } | |
| false | |
| } |
until we find the first function scope, and then only check for shadowed bindings within that scope. We can also return early if we find a class scope, for example, because this is also valid, like the nested function case I showed above:
>>> def h(a):
... class C:
... global a
...
>>>I think the checks on the binding below look roughly correct, but we need to be more careful about identifying the scope holding the relevant binding.
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.
done
ntBre
left a comment
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.
Thank you, this is looking good for the tests currently implemented. I think we should a few more test cases, for example:
def f(a):
a = 1
global a # errorThis tests the shadowing code. I'm actually not sure I was correct to recommend checking shadowed_bindings. Rereading the docstring, it sounds like this only catches shadowing from different scopes. We might actually need to check for rebinding_scopes. In any case, a test like the one above, and possibly one with multiple rebindings, would be a good idea:
def f(a):
a = 1
a = 2
global a # errorI see that is_bound_parameter also has special handling for classes, so it seems worth including a test case with an intervening class scope, probably more than one case showing some of the interleavings like function-class-function, class-function-class, etc, or at least the interesting ones. I think something like this would be interesting at a minimum:
def f(a):
class Inner:
global aI also had a couple of minor nits about spaces between functions.
|
So the shadowed_bindings in We can utilize this to trace back whether a global declaration is originally an argument or not — and I’ve done that accordingly. I also think we don’t really need to interact with rebindings_scope here. I manually checked with an example, and it was coming out to be empty in the following case: Also, while trying to solve this issue, I read a lot about bindings, shadowed bindings, etc., and I’m genuinely thankful for the opportunity to work on this. The tests are working as expected now Thank you : ) |
|
is the |
| invalid-syntax: name `a` is parameter and global | ||
| --> <filename>:30:16 | ||
| | | ||
| 28 | def f(a): | ||
| 29 | class Inner: | ||
| 30 | global a | ||
| | ^ |
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.
This is what the fuzzer seeds that are failing look like. This is not supposed to be an error:
>>> def f(a):
... class Inner:
... global a
...
>>> f
<function f at 0x7fbec085be20>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.
done , have fixed it & fixed the merge conflict as well (rebased)
Thank you : )
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.
Currently we are doing an early return for class scope , please let me know if there are any more edge cases you would like me to test , would be happy to add them.
Thank you
568cb96 to
9ab9411
Compare
ntBre
left a comment
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.
Thanks, this is looking good. I had one idea to simplify is_bound_parameter and a suggestion for a follow-up.
| ScopeKind::Class(_) => { | ||
| return false; | ||
| } | ||
| ScopeKind::Function(_) | ScopeKind::Lambda(_) => { |
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, I overlooked this initially, but the ScopeKinds here actually contain these nodes' definitions. We should be able to extract the parameters from that and avoid this binding stuff entirely. I always get a little nervous when I see a loop, so I looked a bit harder at this today.
fn is_bound_parameter(&self, name: &str) -> bool {
for scope in self.semantic.current_scopes() {
match scope.kind {
ScopeKind::Class(_) => return false,
ScopeKind::Function(ast::StmtFunctionDef { parameters, .. })
| ScopeKind::Lambda(ast::ExprLambda {
parameters: Some(parameters),
..
}) => return parameters.includes(name),
ScopeKind::Lambda(_)
| ScopeKind::Generator { .. }
| ScopeKind::Module
| ScopeKind::Type
| ScopeKind::DunderClassCell => {}
}
}
false
}I believe it's actually impossible for a lambda to contain a global since it can only contain a single expression and not a statement, so we could probably eliminate the lambda branch here too.
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.
done
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.
Now that I’m looking at this and the previous iterations, I feel like if we’re starting to make the code more complex, something’s probably off with the approach. From now on, I’ll be more careful and focus on keeping things simpler.
Thank you : )
Signed-off-by: 11happy <soni5happy@gmail.com>
Signed-off-by: 11happy <soni5happy@gmail.com>
Signed-off-by: 11happy <soni5happy@gmail.com>
Signed-off-by: 11happy <soni5happy@gmail.com>
Signed-off-by: 11happy <soni5happy@gmail.com>
Signed-off-by: 11happy <soni5happy@gmail.com>
Signed-off-by: 11happy <soni5happy@gmail.com>
Signed-off-by: 11happy <soni5happy@gmail.com>
Signed-off-by: 11happy <soni5happy@gmail.com>
Signed-off-by: 11happy <soni5happy@gmail.com>
Signed-off-by: 11happy <soni5happy@gmail.com>
81a26be to
327c8af
Compare
ntBre
left a comment
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.
Thank you!
* main: (65 commits) [ty] Some more simplifications when rendering constraint sets (#21009) [ty] Make `attributes.md` mdtests faster (#21030) [ty] Set `INSTA_FORCE_PASS` and `INSTA_OUTPUT` environment variables from mdtest.py (#21029) [ty] Fall back to `Divergent` for deeply nested specializations (#20988) [`ruff`] Autogenerate TypeParam nodes (#21028) [ty] Add assertions to ensure that we never call `KnownClass::Tuple.to_instance()` or similar (#21027) [`ruff`] Auto generate ast Pattern nodes (#21024) [`flake8-simplify`] Skip `SIM911` when unknown arguments are present (#20697) Render a diagnostic for syntax errors introduced in formatter tests (#21021) [ty] Support goto-definition on vendored typeshed stubs (#21020) [ty] Implement go-to for binary and unary operators (#21001) [ty] Avoid ever-growing default types (#20991) [syntax-errors] Name is parameter and global (#20426) [ty] Disable panicking mdtest (#21016) [ty] Fix completions at end of file (#20993) [ty] Fix out-of-order semantic token for function with regular argument after kwargs (#21013) [ty] Fix auto import for files with `from __future__` import (#20987) [`fastapi`] Handle ellipsis defaults in FAST002 autofix (`FAST002`) (#20810) [`ruff`] Skip autofix for keyword and `__debug__` path params (#20960) [`flake8-bugbear`] Skip `B905` and `B912` if <2 iterables and no starred arguments (#20998) ...
Summary
This PR implements a new semantic syntax error where name is parameter & global.
Test Plan
I have written inline test as directed in #17412