-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Implement a recursive check for RUF060 #17976
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
|
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!
| Expr::Set(ast::ExprSet { elts, .. }) => match elts.as_slice() { | ||
| [] => true, | ||
| [element] => is_empty(element, semantic), | ||
| _ => false, | ||
| }, |
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.
Actually, is this part correct? I tested locally and
frozenset() in {frozenset()}is getting a diagnostic now, even though Python returns True:
>>> frozenset() in {frozenset()}
TrueI think we may need to limit the recursive case to calls like down below.
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 very good find. Indeed, I did not consider the difference between how the two ways of constructing a set differ
>>> set([frozenset()]) == {frozenset()} # note the [...]
True
Will fix
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.
Did not expect this either, but...
>>> len({""})
1
>>> len(set(""))
0
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 that is a funny one, calling set on a string will create a set of the characters:
>>> set("123")
{'1', '2', '3'}so the empty string gives the empty set.
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.
Classic str is an Iterable[str] ^-^
|
Thanks @ntBre - I reduced the PR but added some more test cases from comments above. (changes in new commit) |
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!
…eepish * origin/main: [ty] Induct into instances and subclasses when finding and applying generics (#18052) [ty] Allow classes to inherit from `type[Any]` or `type[Unknown]` (#18060) [ty] Allow a class to inherit from an intersection if the intersection contains a dynamic type and the intersection is not disjoint from `type` (#18055) [ty] Narrowing for `hasattr()` (#18053) Update reference documentation for `--python-version` (#18056) [`flake8-bugbear`] Ignore `B028` if `skip_file_prefixes` is present (#18047) [`airflow`] Apply try-catch guard to all AIR3 rules (`AIR3`) (#17887) [`pylint`] add fix safety section (`PLW3301`) (#17878) Update `--python` to accept paths to executables in virtual environments (#17954) [`pylint`] add fix safety section (`PLE4703`) (#17824) [`ruff`] Implement a recursive check for `RUF060` (#17976) [`flake8-use-pathlib`] `PTH*` suppress diagnostic for all `os.*` functions that have the `dir_fd` parameter (#17968) [`refurb`] Mark autofix as safe only for number literals in `FURB116` (#17692) [`flake8-simplify`] Fix `SIM905` autofix for `rsplit` creating a reversed list literal (#18045) Avoid initializing progress bars early (#18049)
…eep-dish * origin/main: [ty] Infer parameter specializations of generic aliases (#18021) [ty] Understand homogeneous tuple annotations (#17998) [ty] Induct into instances and subclasses when finding and applying generics (#18052) [ty] Allow classes to inherit from `type[Any]` or `type[Unknown]` (#18060) [ty] Allow a class to inherit from an intersection if the intersection contains a dynamic type and the intersection is not disjoint from `type` (#18055) [ty] Narrowing for `hasattr()` (#18053) Update reference documentation for `--python-version` (#18056) [`flake8-bugbear`] Ignore `B028` if `skip_file_prefixes` is present (#18047) [`airflow`] Apply try-catch guard to all AIR3 rules (`AIR3`) (#17887) [`pylint`] add fix safety section (`PLW3301`) (#17878) Update `--python` to accept paths to executables in virtual environments (#17954) [`pylint`] add fix safety section (`PLE4703`) (#17824) [`ruff`] Implement a recursive check for `RUF060` (#17976) [`flake8-use-pathlib`] `PTH*` suppress diagnostic for all `os.*` functions that have the `dir_fd` parameter (#17968) [`refurb`] Mark autofix as safe only for number literals in `FURB116` (#17692) [`flake8-simplify`] Fix `SIM905` autofix for `rsplit` creating a reversed list literal (#18045) Avoid initializing progress bars early (#18049)
<!-- Thank you for contributing to Ruff! To help us out with reviewing, please consider the following: - Does this pull request include a summary of the change? (See below.) - Does this pull request include a descriptive title? - Does this pull request include references to any relevant issues? --> ## Summary <!-- What's the purpose of the change? What does it do, and why? --> The existing implementation of RUF060 (InEmptyCollection) is not recursive, meaning that although set([]) results in an empty collection, the existing code fails it because set is taking an argument. The updated implementation allows set and frozenset to take empty collection as positional argument (which results in empty set/frozenset). ## Test Plan Added test cases for recursive cases + updated snapshot (see RUF060.py). --------- Co-authored-by: Marcus Näslund <marcus.naslund@kognity.com>
Summary
The existing implementation of RUF060 (InEmptyCollection) is not recursive, meaning that although set([]) results in an empty collection, the existing code fails it because set is taking an argument.
The updated implementation allows set and frozenset to take empty collection as positional argument (which results in empty set/frozenset).
Test Plan
Added test cases for recursive cases + updated snapshot (see RUF060.py).