-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Added new lint "path_join_correction" #10663
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
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @xFrednet (or someone else) soon. Please see the contribution instructions for more information. Namely, in order to ensure the minimum review times lag, PR authors and assigned reviewers should ensure that the review label (
|
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.
Welcome to Clippy 👋. This taught me something new about the behavior of Path::join()
. I think this is an excellent lint, which should be warn-by-default. You already have a good start and added the lint at the right location. I've left comments, how to improve the implementation.
If you require any further assistance, you're very welcome to reach out :)
Also, small side note: The commits are not linked to your user account, probably because they use a different email. For as that is totally fine, and we can merge it with that, but I wanted to make you aware in case that is unintentional :)
clippy_lints/src/methods/mod.rs
Outdated
@@ -3655,6 +3678,7 @@ impl Methods { | |||
if let Some(("collect", _, _, span, _)) = method_call(recv) { | |||
unnecessary_join::check(cx, expr, recv, join_arg, span); | |||
} | |||
else {path_join_correction::check(cx, expr, join_arg, span);} |
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.
Clippy uses spaces for indention, sadly rustfmt, doesn't work with if-let chains which are used in this function. Could you ensure that everything uses spaces here? :)
else {path_join_correction::check(cx, expr, join_arg, span);} | |
else {path_join_correction::check(cx, expr, join_arg, span);} |
clippy_lints/src/methods/mod.rs
Outdated
/// ### What it does | ||
/// Checks for initial '/' in an argument to .join on a path. | ||
/// ### Why is this bad? | ||
/// .join() calls on paths already attach the '/'. | ||
/// ### Example | ||
/// ```rust | ||
/// // example code where clippy issues a warning | ||
/// let path = std::path::Path::new("/bin"); | ||
/// path.join("/sh"); | ||
/// ``` | ||
/// Use instead: | ||
/// ```rust | ||
/// // path.join("sh"); | ||
/// ``` |
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.
Some smaller formatting adjustments :)
/// Checks for initial `'/'` in an argument to `.join()` on a `Path`.
///
/// ### Why is this bad?
/// `.join()` comments starting with a separator, can replace the entire path.
/// If this is intentional, prefer creating a new `Path` instead.
///
/// See [`Path::join()`](https://doc.rust-lang.org/std/path/struct.Path.html#method.join)
///
/// ### Example
/// ```rust
/// let path = std::path::Path::new("/bin");
/// let res = path.join("/sh");
/// assert_eq!(res, PathBuf::from("/sh"));
/// ```
///
/// Use instead:
/// ```rust
/// let path = std::path::Path::new("/bin");
///
/// // If this was unintentional, remove the leading separator
/// let extend = path.join("sh");
/// assert_eq!(extend, PathBuf::from("/bin/sh"));
///
/// // If this was intentional, create a new path instead
/// let new = Path::new("/sh")
/// assert_eq!(new PathBuf::from("/sh"));
/// ```
Edit: I've just checked the documentation for Path::join
and it turns out that .join("/xyz")
actually behaves differently than I would first expect, as it actually replaces the path. I'd say that we should actually make this lint warn by default, in the suspicious
category. The disadvantage is, that we have to construct two suggestions, one for the case that they intended this behavior and one if they didn't. I'll explain more below.
|
||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, join_arg: &'tcx Expr<'tcx>, span: Span) { | ||
let applicability = Applicability::MachineApplicable; | ||
if_chain!( |
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 sadly another case, where rustfmt doesn't work correctly. Could you try fixing the indention here? :)
if_chain!( | ||
if let ExprKind::Lit(spanned) = &join_arg.kind; | ||
if let LitKind::Str(symbol, _) = spanned.node; | ||
if symbol.as_str().starts_with('/'); |
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.
Here we could check for both separators '/'
and '\\'
clippy_lints/src/methods/mod.rs
Outdated
/// // path.join("sh"); | ||
/// ``` | ||
#[clippy::version = "1.70.0"] | ||
pub PATH_JOIN_CORRECTION, |
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 suggest a different name, as #![warn(path_join_correction)]
doesn't really explain what the lint does. Maybe extending_path_with_separator
would be better, even if it's longer :). That name is also generic enough to include PathBuf.push()
. We don't need to include that in this iteration, but that would be a nice addition for a follow-up.
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 personally think the original lint name (join_absolute_path) is the perfect fit for this lint. It lints when you use the join
method with an absolute path. It's quite literal and illustrative.
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, join_arg: &'tcx Expr<'tcx>, span: Span) { | ||
let applicability = Applicability::MachineApplicable; | ||
if_chain!( | ||
if let ExprKind::Lit(spanned) = &join_arg.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.
Here, it should be checked, if the join()
call is actually done on a Path
. We have some documentation how this can be done: Checking for a specific type
span.with_hi(expr.span.hi()), | ||
r#"argument in join called on path contains a starting '/'"#, | ||
"try removing first '/'", | ||
"join(\"your/path/here\")".to_owned(), |
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.
It's perfect that you tried to include a suggestion. These greatly improve lint messages and usability. The string given in the suggestion should ideally be something the user can copy and directly past into their code, which is not yet the case for this one.
In Clippy/rustc suggestions are usually created by requesting code snippets and concatenating/modifying them in a compatible way.
For this lint, the user can have two different intentions, which makes it a bit harder. I'd suggest using span_lint_and_then
to create two suggestions. Something like this (Everything inside <>
is still a todo):
span_lint_and_then(
cx,
<LINT>,
<span>,
"`Path::join()` starts with a path separator",
|diag| {
diag.note("expanding the path with a leading separator, will replace the path");
// The applicability has to be unspecified, as the intention of the user is not clear
diag.span_suggestion_verbose(
<str-lit-span>,
"if the path should be extended, try removing the separator",
<str-lit-without-separator>,
Applicability::Unspecified,
);
diag.span_suggestion_verbose(
<expr-span>,
"if the path should be replaced, try creating a new path instead",
format!("Path::new(<str-lit-snippet>)"),
Applicability::Unspecified,
);
}
);
To get specific snippets, you can take a look at snippet_with_applicability
tests/ui/path_join_correction.rs
Outdated
// should be linted | ||
let path = std::path::Path::new("/bin"); | ||
path.join("/sh"); | ||
println!("{}", path.display()); |
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.
Could you add some more tests, with PathBuf
, windows path separators \\
and raw strings like r#"/xyz"#
to see if the suggestion is correct? 🙃
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.
Hi! Sorry I have not looked at my computer for a couple of days and the app I uses on my phone to interact with github did not notify me of any of these replies. I've read them all now and will start on implementing each of your requests one at a time.
I was thinking at the time of submitting this about implementing some of these suggestions but I was admittedly pretty tired that day.
EDIT: Also, thank you for letting me know about the email address clashing. I use magit in emacs and forgot to set a valid email address. 🤣
No problem at all, my studies are currently quite time-consuming. Having some time between reviews helps me as well. Just let me know when it's ready for the next review or when you need some assistance!
No problem! I also mention it, in case, the email/name is supposed to be private. I once commited something under my real name, before I had it linked to my GH and luckily caught it before it landed on master :) |
☔ The latest upstream changes (presumably #10769) made this pull request unmergeable. Please resolve the merge conflicts. |
Oh, I totally missed the PR update. Sorry, I'll give you a review soon. @blyxyas Do you maybe want to give this review a go if you have the time? :) |
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.
Very good for a first contribution! Thanks for joining the project! ❤️
/// Checks for initial `'/'` in an argument to `.join()` on a `Path`. | ||
/// | ||
/// ### Why is this bad? | ||
/// `.join()` comments starting with a separator, can replace the entire path. |
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.
/// `.join()` comments starting with a separator, can replace the entire path. | |
/// `.join()` comments starting with a separator (`/` or `\\`) can replace the entire path. |
@@ -3194,6 +3195,41 @@ declare_clippy_lint! { | |||
"calling `drain` in order to `clear` a container" | |||
} | |||
|
|||
declare_clippy_lint! { | |||
/// ### What it does | |||
/// Checks for initial `'/'` in an argument to `.join()` on a `Path`. |
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.
/// Checks for initial `'/'` in an argument to `.join()` on a `Path`. | |
/// Checks for initial `/` or `\\` in an argument to `.join()` on a `Path`. |
clippy_lints/src/methods/mod.rs
Outdated
/// // path.join("sh"); | ||
/// ``` | ||
#[clippy::version = "1.70.0"] | ||
pub PATH_JOIN_CORRECTION, |
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 personally think the original lint name (join_absolute_path) is the perfect fit for this lint. It lints when you use the join
method with an absolute path. It's quite literal and illustrative.
let ty = cx.typeck_results().expr_ty(expr); | ||
if_chain!( | ||
if is_type_diagnostic_item(cx, ty, Path); | ||
let 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.
No need to bind the applicability to a variable, don't worry :)
(Personal taste here, it's still fine)
tests/ui/path_join_correction.rs
Outdated
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.
Could you also commit the .stderr
version and .fixed
of this files? You can get those by using cargo dev bless
after a cargo test <blah blah>
.
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.
Headers now use //@
@@ -0,0 +1,26 @@ | |||
// run-rustfix |
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.
// run-rustfix | |
//@run-rustfix |
It seems like blessing isn't working... Could you describe your workflow? |
Not sure. Eventually I noticed that .fixed and .stderr files weren't being created. I use emacs and with this I just grep'd the old name and replaced all instances of the old name with a new name using sed. I wonder if I should just refork clippy and copy all the work I've done to the new fork. |
Just a |
I did that and those files just don't get created. Also, this still fails. Unsure what possibly happened, but it's been a while since I last updated this and for all I can remember most of what I did was just keep doing cargo commands until the thing worked. I probably bugged it out at some point. |
I'll make a PR against your fork in a few hours |
Just before my PR, could you create a new branch named "path_join_correct" and change this PR to point to that new branch (copy-pasting these changes)? Making a PR from the master branch will have some problems. |
Do I have to make a new PR to change the source branch? Github doesn't seem to just let me change ofeeg:master to ofeeg:path_join_correct. EDIT: I made the branch and pasted the supposed changes. |
No, you dont need make a new PR. I'm on mobile currently, but I'm pretty sure it's possible as I've done it for a few PRs |
All the sources I'm reading are telling me I have to make a new PR and close this one in order to merge from a different branch. If you know how to do it let me know; otherwise I will make a new PR and reference this one. I'll remember to not try to merge from my forks master in the future to prevent this. |
As far as I know, there is no way to change the base branch of a PR. You can only force push another branch to the source branch of a PR, that would override the changes on the branch. But this PR is linked to |
I have a preference for creating a new branch. Having clean git history is kinda very important when working in Rust (my first Pr had 110 commits, 75 comments of me fighting with Git, deleting the whole fork and remaking everything 😢) From personal experience, it isn't something fun :( Yeah, I'm not currently with a pc at range, but although I'm pretty sure it's possible, creating a new PR is also fine. Make sure to use |
Thank you for making Clippy better!
We're collecting our changelog from pull request descriptions.
If your PR only includes internal changes, you can just write
changelog: none
. Otherwise, please write a short commentexplaining your change.
It's also helpful for us that the lint name is put within backticks (
` `
),and then encapsulated by square brackets (
[]
), for example:If your PR fixes an issue, you can add
fixes #issue_number
into thisPR description. This way the issue will be automatically closed when
your PR is merged.
If you added a new lint, here's a checklist for things that will be
checked during review or continuous integration.
.stderr
file)cargo test
passes locallycargo dev update_lints
cargo dev fmt
Note that you can skip the above if you are just opening a WIP PR in
order to get feedback.
Delete this line and everything above before opening your PR.
Please write a short comment explaining your change (or "none" for internal only changes)
changelog: Added lint resolving issue #10655