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

Fix literal_string_with_formatting_args lint emitted when it should not #13953

Merged

Conversation

GuillaumeGomez
Copy link
Member

@GuillaumeGomez GuillaumeGomez commented Jan 6, 2025

Fixes #13885.
Fixes #14007.

Problem was that I forgot to check whether or not the span was a "real" one. Because if not, then it starts pointing to pretty much only wrong content, hence the problems we saw with clippy linting on clippy.toml.

changelog: Fix literal_string_with_formatting_args lint emitted when it should not

r? @samueltardieu

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Jan 6, 2025
Copy link
Contributor

@samueltardieu samueltardieu left a comment

Choose a reason for hiding this comment

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

LGTM, with the addition of a non-formatting true positive macro test case.

You should reroll the dice as I don't have the privilege to approve this patch.

r?

);
let value = 0;
assert!(format!("{value}").is_ascii());
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you also add another true positive test such as

Suggested change
}
assert!("{value}".is_ascii());
}

so that we can check that the lint properly triggers in non-formatting-macro arguments?

@rustbot
Copy link
Collaborator

rustbot commented Jan 7, 2025

Error: Parsing assign command in comment failed: ...'' | error: specify user to assign to at >| ''...

Please file an issue on GitHub at triagebot if there's a problem with this bot, or reach out on #t-infra on Zulip.

1 similar comment
@rustbot
Copy link
Collaborator

rustbot commented Jan 7, 2025

Error: Parsing assign command in comment failed: ...'' | error: specify user to assign to at >| ''...

Please file an issue on GitHub at triagebot if there's a problem with this bot, or reach out on #t-infra on Zulip.

@samueltardieu
Copy link
Contributor

r? clippy

@rustbot rustbot assigned xFrednet and unassigned samueltardieu Jan 7, 2025
@profetia
Copy link
Contributor

profetia commented Jan 7, 2025

Can you check if this case works? I think just checking dummy is not enough, that is why I switched to checking the enclosing exprs in #13898

@samueltardieu
Copy link
Contributor

Can you check if this case works? I think just checking dummy is not enough, that is why I switched to checking the enclosing exprs in #13898

It still triggers for me indeed:

warning: this looks like a formatting argument but it is not part of a formatting macro
 --> {value}.rs:7:6
  |
7 |     dbg!("something");
  |      ^^^^^^^
  |
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#literal_string_with_formatting_args
  = note: `#[warn(clippy::literal_string_with_formatting_args)]` on by default

@GuillaumeGomez GuillaumeGomez force-pushed the fix-literal_string_with_formatting_args branch from 9515896 to a7fb37c Compare January 7, 2025 14:55
@GuillaumeGomez
Copy link
Member Author

GuillaumeGomez commented Jan 7, 2025

Indeed. Sadly, @profetia, your fix doesn't lint with:

assert!("{y}".is_ascii());

So I have a not so great middle-ground solution but I think it'll do the trick for the time being: I simply check if the string is part of the snippet in the user code. If not, then I skip the lint. What do you think?

@y21
Copy link
Member

y21 commented Jan 10, 2025

Nominating for beta backport because the lint is on beta now and the false positive leads to pretty confusing warnings.

@rustbot label +beta-nominated

@rustbot rustbot added the beta-nominated Nominated for backporting to the compiler in the beta channel. label Jan 10, 2025
@GuillaumeGomez
Copy link
Member Author

Changelog CI also passed. :)

@Icxolu Icxolu mentioned this pull request Jan 11, 2025
@GuillaumeGomez
Copy link
Member Author

r? @y21

@rustbot rustbot assigned y21 and unassigned xFrednet Jan 14, 2025

fn another_bad() {
let literal_string_with_formatting_args = 0;
dbg!("something");
Copy link
Member

Choose a reason for hiding this comment

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

I'm surprised that the expr.span.from_expansion() check doesn't already prevent this. Looking at -Zunpretty=hir-tree output it does seem like the span of the string with the file name is properly marked as being from the non-root context

Lit(
    Spanned {
        node: Str(
            "[/app/example.rs:2:5] \"something\" = ",
            Cooked,
        ),
        span: /rustc/eb54a50837ad4bcc9842924f27e7287ca66e294c/library/std/src/macros.rs:365:35: 365:58 (#4),
    },
),

Odd

Copy link
Member

Choose a reason for hiding this comment

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

Ah, I see what's happening. Clippy sets -Zflatten_format_args=no, which changes the default behavior of how format args are lowered and only that one has this non-macro span. Adding the flag makes it repro on godbolt and shows a root context span for the file name string.

Lit(
    Spanned {
        node: Str(
            "/app/example.rs",
            Cooked,
        ),
        span: /app/example.rs:2:5: 2:22 (#0),
    },
)

Maybe add to the comment that clippy sets this flag and only with that flag does it have a non-macro span which gets around the from_expansion check, in case anyone wants to look into this in the future and doesn't have to go down this rabbit hole?

Copy link
Member Author

Choose a reason for hiding this comment

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

I looked into it but couldn't figure out why it didn't work. Thanks a lot for the detailed explanations! Adding it as a comment.

Comment on lines 99 to 107
let Some(snippet) = snippet_opt(cx, expr.span) else {
return;
};
let fmt_str = symbol.as_str();
// If the literal has been generated by the macro, the snippet should not contain it,
// allowing us to skip it.
if !snippet.contains(fmt_str) {
return;
}
Copy link
Member

Choose a reason for hiding this comment

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

This will add false negatives for escaped strings (eg "{foo} \n bar") and similar, but as a temporary solution to fix FPs it's probably fine

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 be worth trying to use is_from_proc_macro here instead, because that also solves the same issue of a node not matching up with its snippet. It seems like it should also fix the FP while not running into this FN since it doesn't actually check the string contents.

(If we do go with the current approach, a small nit: it might be a good use for the newer SpanRange API with if !expr.span.check_source_text(|snippet| snippet.contains(fmt_str)) { return; }. Slightly simpler and avoids allocating a temporary string)

Copy link
Member Author

Choose a reason for hiding this comment

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

Not sure why is_from_proc_macro didn't work last time I tried but this time it behaves as expected, so switching back to it.

@GuillaumeGomez
Copy link
Member Author

@rustbot ready

@samueltardieu
Copy link
Contributor

As it is currently, this does not fix #14007.

@GuillaumeGomez
Copy link
Member Author

Fixed #14007 as well.

@Alexendoo Alexendoo removed the beta-nominated Nominated for backporting to the compiler in the beta channel. label Jan 19, 2025
@Alexendoo
Copy link
Member

Removing the beta nomination in favour of #14014 since backporting would no longer be urgent

Comment on lines +131 to +135
let mut start = pos.start;
while start < fmt_str.len() && !fmt_str.is_char_boundary(start) {
start += 1;
}
let start = fmt_str[..start].rfind('{').unwrap_or(start);
Copy link
Member

Choose a reason for hiding this comment

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

Is this the same as fmt_str[..fmt_str.ceil_char_boundary(pos.start)].rfind('{')?

Copy link
Member

Choose a reason for hiding this comment

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

For what it's worth, it looks like the format parser offsets all spans by 1 (presumably because it's relative to the " quote), so that's what causes the index to be in the middle of a character sometimes. Just doing pos.start - 1 instead of pos.start on the old deleted line (to undo the parser's offset) should fix this too as far as I can tell, but this over-conservative approach should be fine too I guess

Copy link
Member Author

Choose a reason for hiding this comment

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

I'd prefer to keep it over-conservative if you don't mind. 😉

@y21
Copy link
Member

y21 commented Jan 23, 2025

I've been reading through the surrounding code to get a better understanding of this lint and (somewhat tangentially related to the ICE) I'm wondering why we continue parsing after encountering parser errors? It seems to be adding a fair amount of complexity (remapping the spans, reconstructing parsers) while at the same time I'm not sure if it really makes sense to lint strings that failed to parse in the first place. It feels unlikely that the user meant to write a format string if it can't parse as one anyway? Maybe there is a good reason for this though and I don't know about it!

@GuillaumeGomez
Copy link
Member Author

We need to remap the spans at every { we encounter, whether or not it's valid. For example if you have "{} bla {}", we still need to remap the spans after the first {}.

As for why continuing after an error, it's mainly because you can do typos and if this is the case, still expect formatting afterwards (like in panic) and not realizing that:

  1. There is no formatting.
  2. There is a typo.

Does my reasoning makes sense to you?

@GuillaumeGomez
Copy link
Member Author

Is there anything else to be done here?

@y21 y21 added this pull request to the merge queue Feb 13, 2025
Merged via the queue into rust-lang:master with commit b83762c Feb 13, 2025
11 of 13 checks passed
@GuillaumeGomez GuillaumeGomez deleted the fix-literal_string_with_formatting_args branch February 13, 2025 16:27
@larseggert
Copy link

Should this be fixed with 1.85.0? I still see this:

cargo clippy --all-targets --no-default-features -- -D warnings
...
error: this looks like a formatting argument but it is not part of a formatting macro
 --> /Users/lars/Documents/Code/neqo/.clippy.toml:2:68
  |
2 |     { path = "std::slice::from_raw_parts", reason = "see null_safe_slice" }
  |                                                                    ^^^^^
  |
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#literal_string_with_formatting_args
  = note: `-D clippy::literal-string-with-formatting-args` implied by `-D warnings`
  = help: to override `-D warnings` add `#[allow(clippy::literal_string_with_formatting_args)]`

error: could not compile `neqo-transport` (lib test) due to 1 previous error

with https://github.com/mozilla/neqo. (Example: https://github.com/mozilla/neqo/actions/runs/13468547315/job/37638898254?pr=2444#step:6:512)

@y21
Copy link
Member

y21 commented Feb 22, 2025

This fix will be in 1.87.0. But there was another PR that downgraded this lint's category to nursery (#14014) which was backported and should be on stable, so you shouldn't really see this unless you explicitly enabled the lint.

Do you have something like #![warn(clippy::literal_string_with_formatting_args)] or #![warn(clippy::nursery)] in your crate?

@larseggert
Copy link

Yes, we have enabled nursery. Thanks for the pointer, will work around it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties
Projects
None yet
Development

Successfully merging this pull request may close these issues.

A Compiler bug literal_string_with_formatting_args false positive on test
8 participants