-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Add new lint paths_from_format
#8833
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @camsteffen (or someone else) soon. Please see the contribution instructions for more information. |
[
path_from_format]
`path_from_format
]
path_from_format
]path_from_format
☔ The latest upstream changes (presumably #8882) made this pull request unmergeable. Please resolve the merge conflicts. |
☔ The latest upstream changes (presumably #8866) made this pull request unmergeable. Please resolve the merge conflicts. |
Hey @merelymyself, welcome to the project! r? @dswij (I'd appreciate it if you could leave a normal comment so that I can also assign you to this PR) 🙃 |
☔ The latest upstream changes (presumably #8918) made this pull request unmergeable. Please resolve the merge conflicts. |
☔ The latest upstream changes (presumably #8916) made this pull request unmergeable. Please resolve the merge conflicts. |
Could you do a rebase, not merge commit? We follow no merge-commit policy. |
@giraffate , does this work? |
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 for the PR, and welcome!
The basic idea for the lint looks good to me 👍 .
I left some suggestions and questions.
Hi, @dswij. Made some changes, could you take another look? Thanks! |
☔ The latest upstream changes (presumably #8831) made this pull request unmergeable. Please resolve the merge conflicts. |
Thanks for the patience and advice, @dswij. Could you take another look? If there's a situation where a path like 'foo{}/bar' shows up, instead of offering a suggestion, a note is offered instead now. |
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 for all the changes!
I think we can just not emit the lint for foo{}/bar
. I'd imagine someone uses it like this, which is legit
let prefix = "some-prefixes";
let _ Path::new(format!("./{prefix}.csv"));
Note that the suggestion is also wrong right now when the variable in join()
has a path separator:
let x = "/bar";
let y = Path::new("/foo").join(x); // y = "bar"
clippy_lints/src/path_from_format.rs
Outdated
PATH_FROM_FORMAT, | ||
expr.span, | ||
"`format!(..)` used to form `PathBuf`", | ||
"consider using `Path::new()` and `.join()` to make it OS-agnostic and improve code readability", |
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.
Thinking about this, for long paths, .join()
might actually be annoying. Maybe we can only suggest when the path is pretty short. Need some opinion on this, WDYT? @merelymyself @xFrednet
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 don't really mind either way, but I feel that having consistency is slightly more important. Plus, I don't really see long paths that often in Rust projects (or is that just confirmation bias?)
☔ The latest upstream changes (presumably #8964) made this pull request unmergeable. Please resolve the merge conflicts. |
☔ The latest upstream changes (presumably #9658) made this pull request unmergeable. Please resolve the merge conflicts. |
Hi, @dswij ! Sorry to disturb, but do you mind taking a look soon? |
☔ The latest upstream changes (presumably #9670) made this pull request unmergeable. Please resolve the merge conflicts. |
Hey @dswij, this is a ping from triage, could you review this PR? If you don't have the time, feel free to pass it to me. @merelymyself Would you mind rebasing the branch on master? IDK, what changes have been made to |
@xFrednet Thanks for taking time to look at this. I'll pass this to you if you don't mind :) r? @xFrednet @merelymyself Sorry I missed the pings, I've been distracted with other stuff and somehow didn't notice the ping :\ |
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 basic structure and idea is already good. Though, while reading though the documentation, I found examples, that suggested a different way. I'll add a comment to the original issue and ask the author :)
@rustbot author
/// `.join()` introduces additional allocations that are not present when `PathBuf::push` is | ||
/// used instead. |
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.
Looking at this, I think it would be better to suggest PathBuf
directly. Even if that might require additional modifications by the user. PathBuf
is intended for modifications as this one AFAIK :)
Though, in that case we should have the applicability be less than machine applicable
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.
Sorry, to clarify, you mean you want to do some or all of the following?
- Replace
.join()
with.push()
- Replace the
Path::new
withPathBuf::from
- Instead of suggesting a replacement, just flag it out to the user
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.
Sorry, if my comment wasn't clear. I mean a combination of the first two. So suggest PathBuf::from()
and then add segments using push()
. The thing is, that this probably change the type of the buffer and require further adjustments by the user. Therefore, we should display the suggestion, but not make it machine applicable.
Does this make sense, or should I try to describe it better? :)
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'm looking at the docs for PathBuf
now, and I just realised that there might be a recommended solution for situations like this 😵💫.
let var = "path";
pathbuf = ["/", var, "filename.txt"].iter().collect::<PathBuf>();
Functionally equivalent to
let var = "path";
pathbuf = PathBuf::from(format!("/{var}/filename.txt"));
Will you be ok with this? Given it should still be machine applicable but I don't think it will have the same problems as before.
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.
You have to be careful when using the array as all the values have to be the same type.
☔ The latest upstream changes (presumably #10206) made this pull request unmergeable. Please resolve the merge conflicts. |
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.
Hey, thank you for your patience, the review sadly took a bit longer. You're welcome to ask if you have any questions :)
PATHS_FROM_FORMAT, | ||
expr.span, | ||
"`format!(..)` used to form `PathBuf`", | ||
"consider using `Path::new()` and `.join()` to make it OS-agnostic and improve code readability", |
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 would remove the second part, as some users might not consider this more readable. The OS-agnostic part is also the main point, IMO.
if_chain! { | ||
if let ExprKind::Call(_, [arg, ..]) = expr.kind; | ||
if let ty = cx.typeck_results().expr_ty(expr); | ||
if is_type_diagnostic_item(cx, ty, sym::PathBuf); |
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 we maybe also check for calls to Path::new
?
impl<'tcx> LateLintPass<'tcx> for PathsFromFormat { | ||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { | ||
if_chain! { | ||
if let ExprKind::Call(_, [arg, ..]) = expr.kind; |
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.
Right now, this lint only checks for function calls, where the output is a PathBuf
and the first argument is a format macro. The lint logic should also check the first value of ExprKind::Call
to ensure that it's a path to a function called new
or other related function names.
The logic should also contain a check if the code originated from a macro. I believe the check should be as simple as !expr.span.from_expansion()
.iter() | ||
.map(rustc_span::Symbol::as_str) | ||
.collect(); | ||
let mut applicability = Applicability::MachineApplicable; |
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.
Since the applicability is explicitly set with the lint emission, you can remove this value and all usages of it.
For Sugg::hir_with_applicability
you can just use Sugg::hir
} | ||
else { |
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.
Formatting nit, as well as the other else cases in the if_chain!
} | |
else { | |
} else { |
let _ = write!(sugg, ".join(&{arg})"); | ||
} | ||
else { | ||
sugg = format!("{sugg}.join(&{arg})"); |
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.
Is there a reason, why you use two different methods to append text to an existing string?
|
||
// In essence, this checks if the format!() used is made for concatenation of the filenames / folder | ||
// names itself. This returns true when it is something like | ||
// `PathBuf::from(format!("/x/folder{}/textfile.txt", folder_number))` |
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.
Based on the comment in the related issue, I would actually think that such a paht can still be problematic. I'd suggest either, checking if the path starts with //?/
which makes it a verbatim path under windows or (IMO the better approach) add a config value, to allow this. I have the feeling the lint can be very noisy without, but ignoring these cases can also be wrong.
☔ The latest upstream changes (presumably #10028) made this pull request unmergeable. Please resolve the merge conflicts. |
Hey @merelymyself this is a ping from triage. Are you still working on this PR? If you have any questions, you're always welcome to reach out :) |
Hi, so sorry. Thanks for all the help so far, but I don't think I'll have much time in the near future to work on this with any level of consistency - again, apologies for any inconvenience caused. I'll close the PR. |
It was no inconvenience at all, I hope you still had some fun! I'll mark this PR as |
Please write a short comment explaining your change (or "none" for internal only changes)
As per #8812
changelog: Add new lint
[`path_from_format`]