-
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
New lint: redundant_test_prefix
#13710
base: master
Are you sure you want to change the base?
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @dswij (or someone else) some time within the next two weeks. 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 (
|
@dswij any ETA on when you'll be able to review (it has been more than 2 weeks -- so, I am not sure whether there needs to be some action on my part to get this rolling). |
@J-ZhengLi @xFrednet sorry to bother you, but maybe you'll have time to review this PR (since you've provided good feedback on its previous incarnation -- and I've incorporated your suggestions). Really eager to receive my first feedback on this one (still learning the clippy's tooling), before diving into any other open tasks/issues. |
Hej, thank you for the ping. Sure, I'll add it to my reviewing list. You should get some feedback this weekend! @J-ZhengLi You're welcome to also help with the review if you want. But also don't feel any pressure :) r? @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.
Very solid start. Thank you for picking up the other PR, it's nice to see it being worked on! :D
Please let me know if you have any questions.
/// Whether to include functions outside of `#[cfg(test)]` in the linting process or not. | ||
/// | ||
/// This option allows running the lint against the integration tests: test functions located | ||
/// there are not inside a node marked with `#[cfg(test)]` annotation (although they are | ||
/// still marked using `#[test]` annotation and thus can have redundant "test_" prefix). |
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.
/// Whether to include functions outside of `#[cfg(test)]` in the linting process or not. | |
/// | |
/// This option allows running the lint against the integration tests: test functions located | |
/// there are not inside a node marked with `#[cfg(test)]` annotation (although they are | |
/// still marked using `#[test]` annotation and thus can have redundant "test_" prefix). | |
/// Indicates if `redundant_test_prefix` should check functions outside of | |
/// items marked with `#[cfg(test)]`. | |
/// | |
/// This option can be used for integration tests which also use the `#[test]` | |
/// attribute without the `#[cfg(test)]`. |
/// What suffix to use to avoid function name collisions when `test_` prefix is removed. | ||
/// | ||
/// If set to `"_works"`, the lint will suggest renaming `test_foo` to `foo_works`. | ||
/// Suffix is added only when there is a collision with an existing function name, | ||
/// otherwise just `test_` prefix is removed (and no suffix added). | ||
#[lints(redundant_test_prefix)] | ||
redundant_test_prefix_custom_suffix: String = String::from("_works"), |
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 think we should create machine applicable suggestions for these cases. Mostly because I don't see a good universal way. The default will look weird for tests that actually try to panic:
#[test]
#[should_panic(expected = "explicit panic")]
fn test_invalid_indexing() {
panic!("explicit panic");
}
The suggested name invalid_indexing_works()
doesn't make much sense. It feels like _test
would be the most universal prefix, but removing the prefix to then have it as a suffix would be weird.
I'm guessing that collisions after the renaming are rare. And if they happen it should be fine to just emit a warning and expect the user to come up with a new name.
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.
Yep, I've had the same feeling that it is not the most elegant approach. So, ignoring in auto-fix (but still warning about it) makes total sense.
/// ``` | ||
#[clippy::version = "1.84.0"] | ||
pub REDUNDANT_TEST_PREFIX, | ||
pedantic, |
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.
Copy of the discussion from the previous PR: https://github.com/rust-lang/rust-clippy/pull/12861/files#r1625940772
This should be decided on during the FCP
@xFrednet
I feel like this shouldn't be inpedantic
but inrestriction
instead. The usage of thetest_
prefix doesn't seem like an antipattern to me. There are good arguments against it, but the number of changes required in existing changes shows that the usage is still quite common.I'll include this decision in the group discussion before we merge this PR :)
@Caylub
There are arguments to be made for several categories:style
,pedantic
,restriction
, etc.The usage of
test_
seems to bepedantic
to me, because it's clearly unnecessary in almost every case, including some of the ones mentioned in the threads in this PR. For example, test names that havetest_
+method_name
, should just besuper::method_name
IMO, following the same like of reasoning for as everywhere else.
@xFrednet
One argument I always consider is the number of lint triggers that happen in current code. Clippy can be a very pedantic, but the number of lint emissions has to be reasonable. If users get 100+ warnings of this lint with the next Clippy release it's way more likely that users get annoyed and simply allow the lint and that would help nobody.This is not meant as a hard no, but just an explanation of my suggestion. I'm not sure where to place it and would leave this up to a group decision.
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.
Have no strong opinion on this one (used pedantic
just as a starting point). Can wait for the group discussion, or can proactively turn it to restriction
, since potentially this can indeed result in 100s of warnings on big projects, and that will annoy ppl.
// Ignore methods and closures. | ||
let FnKind::ItemFn(ref ident, ..) = kind else { | ||
return; | ||
}; |
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.
We should also have a check if the name was written by a macro or user. It should be:
if ident.span.from_expansion() {
return;
}
span_lint_and_sugg( | ||
cx, | ||
REDUNDANT_TEST_PREFIX, | ||
ident.span, | ||
"redundant `test_` prefix in test function name", | ||
help_msg, | ||
non_prefixed, | ||
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.
For long help message I think verbose suggestions look better. Can you try this:
span_lint_and_sugg( | |
cx, | |
REDUNDANT_TEST_PREFIX, | |
ident.span, | |
"redundant `test_` prefix in test function name", | |
help_msg, | |
non_prefixed, | |
Applicability::MachineApplicable, | |
); | |
span_lint_and_then( | |
cx, | |
REDUNDANT_TEST_PREFIX, | |
ident.span, | |
"redundant `test_` prefix in test function name", | |
|diag| { | |
diag.span_suggestion_verbose( | |
ident.span, | |
help_msg, | |
non_prefixed, | |
Applicability::MachineApplicable | |
); | |
} | |
); |
non_prefixed.push_str(&self.custom_sufix); | ||
help_msg = "consider removing the `test_` prefix (suffix avoids name conflict)"; |
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.
As mentioned at the place with the configuration, I think it would be better to just change the Applicability
instead of adding a predefined suffix. We could still suggest renaming it to non_prefixed.push_str("_works")
but have it as Applicability::HasPlaceholders
/// There should be no other function with the same name in the same module/scope. Also, there | ||
/// should not be any function call with the same name within the body of the function, to avoid | ||
/// recursion. | ||
fn name_conflicts<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, fn_name: &str) -> bool { |
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.
Nice check! :D
//@revisions: default outside_cfg_test custom_suffix | ||
//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/redundant_test_prefix/default | ||
//@[outside_cfg_test] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/redundant_test_prefix/outside_cfg_test | ||
//@[custom_suffix] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/redundant_test_prefix/custom_suffix | ||
//@compile-flags: --test |
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.
Awesome that you figured out how to do this all with one test file 💪
} | ||
|
||
if is_test_function(cx.tcx, cx.tcx.local_def_id_to_hir_id(fn_def_id)) && ident.as_str().starts_with("test_") { | ||
let mut non_prefixed = ident.as_str().trim_start_matches("test_").to_string(); |
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.
In theory, it can happen that the new string can no longer be an ident. For example, if it starts with an integer or is empty. IDK, if there is a simple way to check this 🤔
Examples would be test_1
, test_const
, test_
.
It looks like the lexer is responsible for checking this in rustc, but it doesn't have an easily accessible util function. We should be able to reuse the lexer. (rn I just don't know how from the top of my head)
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.
Nice catch! I will look into it, starting from sample case and investigate how we can resolve this.
@@ -0,0 +1,211 @@ | |||
#![allow(dead_code)] | |||
#![warn(clippy::redundant_test_prefix)] |
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 nice comments, this makes the tests easy to read :D
This PR has started as an effort to proceed from the feedback in #12861.
test_foo()
results in a collision (either because functionfoo()
is defined within the current scope, or because thefoo()
call exists within function thus creating an unwanted recursion), lint suggests replacing prefix with suffix, i.e.test_foo()
becomesfoo_works()
. The_works
suffix is configurable.#[cfg(test)]
defined for them, so by default they are not checked. There exists an option to make sure that any functions with#[test]
annotation (including those outside of nodes marked withcfg(test)
) are linted.fixes #8931
changelog: new lint: [
redundant_test_prefix
]