Skip to content

New lint : "const with a static lifetime" #1829

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

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions clippy_lints/src/approx_const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,22 @@ declare_lint! {
}

// Tuples are of the form (constant, name, min_digits)
const KNOWN_CONSTS: &'static [(f64, &'static str, usize)] = &[(f64::E, "E", 4),
(f64::FRAC_1_PI, "FRAC_1_PI", 4),
(f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5),
(f64::FRAC_2_PI, "FRAC_2_PI", 5),
(f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5),
(f64::FRAC_PI_2, "FRAC_PI_2", 5),
(f64::FRAC_PI_3, "FRAC_PI_3", 5),
(f64::FRAC_PI_4, "FRAC_PI_4", 5),
(f64::FRAC_PI_6, "FRAC_PI_6", 5),
(f64::FRAC_PI_8, "FRAC_PI_8", 5),
(f64::LN_10, "LN_10", 5),
(f64::LN_2, "LN_2", 5),
(f64::LOG10_E, "LOG10_E", 5),
(f64::LOG2_E, "LOG2_E", 5),
(f64::PI, "PI", 3),
(f64::SQRT_2, "SQRT_2", 5)];
const KNOWN_CONSTS: &[(f64, &str, usize)] = &[(f64::E, "E", 4),
(f64::FRAC_1_PI, "FRAC_1_PI", 4),
(f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5),
(f64::FRAC_2_PI, "FRAC_2_PI", 5),
(f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5),
(f64::FRAC_PI_2, "FRAC_PI_2", 5),
(f64::FRAC_PI_3, "FRAC_PI_3", 5),
(f64::FRAC_PI_4, "FRAC_PI_4", 5),
(f64::FRAC_PI_6, "FRAC_PI_6", 5),
(f64::FRAC_PI_8, "FRAC_PI_8", 5),
(f64::LN_10, "LN_10", 5),
(f64::LN_2, "LN_2", 5),
(f64::LOG10_E, "LOG10_E", 5),
(f64::LOG2_E, "LOG2_E", 5),
(f64::PI, "PI", 3),
(f64::SQRT_2, "SQRT_2", 5)];

#[derive(Copy,Clone)]
pub struct Pass;
Expand Down
4 changes: 2 additions & 2 deletions clippy_lints/src/block_in_if_condition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ impl<'a, 'tcx: 'a> Visitor<'tcx> for ExVisitor<'a, 'tcx> {
}
}

const BRACED_EXPR_MESSAGE: &'static str = "omit braces around single expression condition";
const COMPLEX_BLOCK_MESSAGE: &'static str = "in an 'if' condition, avoid complex blocks or closures with blocks; \
const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression condition";
const COMPLEX_BLOCK_MESSAGE: &str = "in an 'if' condition, avoid complex blocks or closures with blocks; \
instead, move the block or closure higher and bind it with a 'let'";

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition {
Expand Down
63 changes: 63 additions & 0 deletions clippy_lints/src/const_static_lifetime.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use syntax::ast::{Item, ItemKind, TyKind, ExprKind};
use rustc::lint::{LintPass, EarlyLintPass, LintArray, EarlyContext};
use utils::span_help_and_lint;

/// **What it does:** Checks for constants with an explicit `'static` lifetime.
///
/// **Why is this bad?** Adding `'static` to every reference can create very complicated types.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// const FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] = &[..]
/// ```
/// This code can be rewritten as
/// ```rust
/// const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
/// ```

declare_lint! {
pub CONST_STATIC_LIFETIME,
Warn,
Copy link
Member

Choose a reason for hiding this comment

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

I don't know if this should be Warn by default, at least not now. This warns against a syntax that was the only possible syntax for a long time, and lots of projects are using it now. Plus there is little value in updating old code except for "it's possible that way now".

Maybe we should ask for the lint level to be a dynamic parameter to rustc's devs. That way we could have a minimal_rustc_version parameter in clippy.toml, and only have the lint warn if the minimal version of rustc you want to support is higher that whatever version of rustc added that shortcut.

Copy link
Contributor

Choose a reason for hiding this comment

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

Well.. there was no need to update from try macro invocations to the question mark operator, other than readability. But yes, we should delay making it warn by default for the next few releases. I think 3 releases compatibility is what nursery crates require.

"Using explicit `'static` lifetime for constants when elision rules would allow omitting them."
}

pub struct StaticConst;

impl LintPass for StaticConst {
fn get_lints(&self) -> LintArray {
lint_array!(CONST_STATIC_LIFETIME)
}
}

impl EarlyLintPass for StaticConst {
fn check_item(&mut self, cx: &EarlyContext, item: &Item) {
// Match only constants...
if let ItemKind::Const(ref var_type, ref expr) = item.node {
// ... which are actually variables, not declaration such as #![feature(clippy)] ...
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't get this check... I don't think attributes create any constants...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If I added it, it is because I was having warning on things such as https://github.com/Manishearth/rust-clippy/blob/master/tests/cc_seme.rs:l1 and l2. I was running cargo test from the root folder if that helps
I don't know if this was a side effect from my code, but when I inspected the AST there was nodes which where constants without being Lit.

Copy link
Contributor

Choose a reason for hiding this comment

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

Very confusing... I have no clue how those are related. Can you show the exact compiler output you get without this check?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Of course, here is the result : https://pastebin.com/tzWKUcnV
Can you point me where some piece of code that implement a visitor ? I know about serde's visitor for serializing/deserializing structs, but I am not familiar with this concept...

Copy link
Contributor

Choose a reason for hiding this comment

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

O_o that is really weird. Try dumping the expression itself with debug formatting with println so we see what that thing is.

https://github.com/Manishearth/rust-clippy/blob/7535afc0fa9cec38f0fdd3a9deafb44fb3e5fcf1/clippy_lints/src/mut_mut.rs#L36 is an example invocation of a visitor. Look below that for the implementation.

Copy link
Contributor

Choose a reason for hiding this comment

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

Nope, this is rustc inserted. But I'm not sure why... I'll look around. You can try to call the in_macro function, maybe it returns true on the item span?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since this problem is fixed, I'll let you dive into it 😉
I prefer to spend time trying to implement a visitor.
I am not sure where to start : your example uses stuff from the hir module, but this is an early lint pass, so I'm not sure what to do. I think I will implement a recursive visitor manually.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh... sorry about that, should have paid more attention. https://github.com/Manishearth/rust-clippy/blob/3b3e47f4518d83ab625d34e93f0855b713ebd1c3/clippy_lints/src/non_expressive_names.rs#L81 is a (convoluted) example of an AST visitor

Copy link
Contributor

Choose a reason for hiding this comment

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

I tested it. You just need to add

        if in_macro(item.span) {
            return;
        }

to the beginning of the check_item method

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will take care of finishing the implementation after Wednesday, I'm a little bit busy before... Thanks for the tip !

// if let ExprKind::Lit(_) = expr.node {
// ... which are reference ...
if let TyKind::Rptr(ref optionnal_lifetime, _) = var_type.node {
// ... and have an explicit 'static lifetime.
if let Some(lifetime) = *optionnal_lifetime {

println!("item : \n {:?} \n item.node : \n {:?}", item, item.node);
println!("var_type : \n {:?} \n var_type.node : \n {:?}", var_type, var_type.node);
println!("expr : \n {:?} \n expr.node : \n {:?}", expr, expr.node);
println!("lifetime : {:?}", lifetime);


if lifetime.ident.name == "'static" {
span_help_and_lint(cx,
CONST_STATIC_LIFETIME,
lifetime.span,
"Constants have by default a `'static` lifetime",
"consider removing the `'static`");
}
}
// }
}
}
}
}
2 changes: 1 addition & 1 deletion clippy_lints/src/doc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ impl EarlyLintPass for Doc {
#[allow(cast_possible_truncation)]
pub fn strip_doc_comment_decoration((comment, span): (String, Span)) -> Vec<(String, Span)> {
// one-line comments lose their prefix
const ONELINERS: &'static [&'static str] = &["///!", "///", "//!", "//"];
const ONELINERS: &[&str] = &["///!", "///", "//!", "//"];
for prefix in ONELINERS {
if comment.starts_with(*prefix) {
return vec![(comment[prefix.len()..].to_owned(),
Expand Down
3 changes: 3 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ pub mod blacklisted_name;
pub mod block_in_if_condition;
pub mod booleans;
pub mod collapsible_if;
pub mod const_static_lifetime;
pub mod copies;
pub mod cyclomatic_complexity;
pub mod derive;
Expand Down Expand Up @@ -308,6 +309,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
reg.register_late_lint_pass(box large_enum_variant::LargeEnumVariant::new(conf.enum_variant_size_threshold));
reg.register_late_lint_pass(box should_assert_eq::ShouldAssertEq);
reg.register_late_lint_pass(box needless_pass_by_value::NeedlessPassByValue);
reg.register_early_lint_pass(box const_static_lifetime::StaticConst);

reg.register_lint_group("clippy_restrictions", vec![
arithmetic::FLOAT_ARITHMETIC,
Expand Down Expand Up @@ -520,6 +522,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
unused_label::UNUSED_LABEL,
vec::USELESS_VEC,
zero_div_zero::ZERO_DIVIDED_BY_ZERO,
const_static_lifetime::CONST_STATIC_LIFETIME
]);
}

Expand Down
6 changes: 3 additions & 3 deletions clippy_lints/src/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1277,7 +1277,7 @@ enum Convention {
}

#[cfg_attr(rustfmt, rustfmt_skip)]
const CONVENTIONS: [(Convention, &'static [SelfKind]); 6] = [
const CONVENTIONS: [(Convention, &[SelfKind]); 6] = [
(Convention::Eq("new"), &[SelfKind::No]),
(Convention::StartsWith("as_"), &[SelfKind::Ref, SelfKind::RefMut]),
(Convention::StartsWith("from_"), &[SelfKind::No]),
Expand All @@ -1287,7 +1287,7 @@ const CONVENTIONS: [(Convention, &'static [SelfKind]); 6] = [
];

#[cfg_attr(rustfmt, rustfmt_skip)]
const TRAIT_METHODS: [(&'static str, usize, SelfKind, OutType, &'static str); 30] = [
const TRAIT_METHODS: [(&str, usize, SelfKind, OutType, &str); 30] = [
("add", 2, SelfKind::Value, OutType::Any, "std::ops::Add"),
("as_mut", 1, SelfKind::RefMut, OutType::Ref, "std::convert::AsMut"),
("as_ref", 1, SelfKind::Ref, OutType::Ref, "std::convert::AsRef"),
Expand Down Expand Up @@ -1321,7 +1321,7 @@ const TRAIT_METHODS: [(&'static str, usize, SelfKind, OutType, &'static str); 30
];

#[cfg_attr(rustfmt, rustfmt_skip)]
const PATTERN_METHODS: [(&'static str, usize); 17] = [
const PATTERN_METHODS: [(&str, usize); 17] = [
("contains", 1),
("starts_with", 1),
("ends_with", 1),
Expand Down
8 changes: 4 additions & 4 deletions clippy_lints/src/needless_continue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,15 +252,15 @@ struct LintData<'a> {
block_stmts: &'a [ast::Stmt],
}

const MSG_REDUNDANT_ELSE_BLOCK: &'static str = "This else block is redundant.\n";
const MSG_REDUNDANT_ELSE_BLOCK: &str = "This else block is redundant.\n";

const MSG_ELSE_BLOCK_NOT_NEEDED: &'static str = "There is no need for an explicit `else` block for this `if` \
const MSG_ELSE_BLOCK_NOT_NEEDED: &str = "There is no need for an explicit `else` block for this `if` \
expression\n";

const DROP_ELSE_BLOCK_AND_MERGE_MSG: &'static str = "Consider dropping the else clause and merging the code that \
const DROP_ELSE_BLOCK_AND_MERGE_MSG: &str = "Consider dropping the else clause and merging the code that \
follows (in the loop) with the if block, like so:\n";

const DROP_ELSE_BLOCK_MSG: &'static str = "Consider dropping the else clause, and moving out the code in the else \
const DROP_ELSE_BLOCK_MSG: &str = "Consider dropping the else clause, and moving out the code in the else \
block, like so:\n";


Expand Down
2 changes: 1 addition & 1 deletion clippy_lints/src/non_expressive_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ struct SimilarNamesLocalVisitor<'a, 'tcx: 'a> {
// this list contains lists of names that are allowed to be similar
// the assumption is that no name is ever contained in multiple lists.
#[cfg_attr(rustfmt, rustfmt_skip)]
const WHITELIST: &'static [&'static [&'static str]] = &[
const WHITELIST: &[&[&str]] = &[
Copy link
Member

Choose a reason for hiding this comment

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

Wow that's better 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

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

C'mon, It's my first time implementing a lint, even touching something relating to compiler stuff, please be tolerant. Even if it is disabled by default I would be happy 😄

Copy link
Member

Choose a reason for hiding this comment

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

This was a positive comment… 😕

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh I'm sorry, I thought it was sarcastic 😞

&["parsed", "parser"],
&["lhs", "rhs"],
&["tx", "rx"],
Expand Down
13 changes: 13 additions & 0 deletions clippy_tests/examples/const_static_lifetime.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#![feature(plugin)]
#![plugin(clippy)]

const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add a test for a &[&'static str]?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Of course !
I'll also add tests for tuples, I forgot about them.


const VAR_TWO: &str = "Test constant #2"; // This line should not raise a warning.

fn main() {

println!("{}", VAR_ONE);
println!("{}", VAR_TWO);

}
14 changes: 14 additions & 0 deletions clippy_tests/examples/const_static_lifetime.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error: Constants have by default a `'static` lifetime
--> const_static_lifetime.rs:4:17
|
4 | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static.
| ^^^^^^^
|
= note: `-D const-static-lifetime` implied by `-D warnings`
= help: consider removing the `'static`
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 try adding a suggestion for rustfix 😄


error: aborting due to previous error(s)

error: Could not compile `clippy_tests`.

To learn more, run the command again with --verbose.