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

[ruff] Implement unnecessary-nested-literal (RUF039) #14323

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

sbrugman
Copy link
Contributor

@sbrugman sbrugman commented Nov 13, 2024

Summary

Implementing unnecessary-nested-literal.

This rule could help simplify other rules' fixes by handling the flattening of Literals here.

See also https://github.com/astral-sh/ruff/pull/14270/files#r1837810594 (unions in a follow-up PR)

Test Plan

cargo test

The ecosystem results are correct.

Some of the nesting emits multiple violations.
I've got a fix for this, but that depends on #14280.
We can go on with merging this PR after review regardless (the violations are not wrong).

@sbrugman sbrugman force-pushed the unnecessary-nested-literal branch 2 times, most recently from 15e08d3 to ae4a677 Compare November 13, 2024 16:26
Copy link
Contributor

github-actions bot commented Nov 13, 2024

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

ℹ️ ecosystem check detected linter changes. (+2 -0 violations, +0 -0 fixes in 1 projects; 53 projects unchanged)

DisnakeDev/disnake (+2 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --ignore RUF9 --output-format concise --preview

+ tests/test_utils.py:730:18: RUF039 [*] Unnecessary nested `Literal`
+ tests/test_utils.py:758:10: RUF039 [*] Unnecessary nested `Literal`

Changes by rule (1 rules affected)

code total + violation - violation + fix - fix
RUF039 2 2 0 0 0

@MichaReiser MichaReiser added rule Implementing or modifying a lint rule preview Related to preview mode features labels Nov 14, 2024
Copy link
Member

@MichaReiser MichaReiser left a comment

Choose a reason for hiding this comment

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

@AlexWaygood would you mind doing a quick glance at the rule definition? I already reviewed the code


let mut check_for_duplicate_members = |expr: &'a Expr, parent: &'a Expr| {
// If the parent is not equal to the `literal_expr` then we know we are traversing recursively.
if parent != literal_expr {
Copy link
Member

Choose a reason for hiding this comment

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

You could consider using AnyNodeRef::ptr_eq here because all you want to test is if the expressions are the same (Expr::eq does a deep comparison by default)

Comment on lines +94 to +96
if !is_nested {
return;
}
Copy link
Member

Choose a reason for hiding this comment

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

Can you expand on why we have to check inside the rule whether it is a nested literal, considering that the rule is only called when semantic.in_nested_literal is true?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We only consider the top-level union, so when semantic.in_nested_literal is false. I've considered to run this run only when semantic.in_nested_literal is true, but then the top-level union is unavailable for the autofix.

Copy link
Member

Choose a reason for hiding this comment

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

Oh, we actually only run the rule when not in a nested literal...


/// RUF039
pub(crate) fn unnecessary_nested_literal<'a>(checker: &mut Checker, literal_expr: &'a Expr) {
let mut nodes: Vec<&Expr> = Vec::new();
Copy link
Member

Choose a reason for hiding this comment

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

It might also be worth to use a SmallVec here with a size of 1 to avoid allocating if this is a not-nested literal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

nodes will be populated with all entries in Literal, so even for not-nested literals it can exceed 1. Shall I do a first pass to check if the Literal is nested, followed by another to collect the nodes? This will reduce the allocation on the common path.

Copy link
Member

@AlexWaygood AlexWaygood left a comment

Choose a reason for hiding this comment

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

Similar to #14319 (review), I feel like I'm not sure how much this antipattern really comes up in practice. But, I can see the value if this is a pattern that could be introduced by the fix for other rules we implement!

/// ## What it does
/// Checks for unnecessary nested `Literal`.
///
/// ## Why is this bad?
Copy link
Member

Choose a reason for hiding this comment

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

The direct answer to the question of "Why is this bad?" is "it's less readable than the alternative". I think something to that effect should be the first sentence of this section, as it makes it clear that this rule is about readability and style rather than correctness.

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.

3 participants