Skip to content

Commit 0ecdc00

Browse files
committed
Implementation of the const_static_lifetime lint.
1 parent f47e564 commit 0ecdc00

File tree

4 files changed

+125
-19
lines changed

4 files changed

+125
-19
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use syntax::ast::{Item, ItemKind, TyKind, Ty};
2+
use rustc::lint::{LintPass, EarlyLintPass, LintArray, EarlyContext};
3+
use utils::{span_help_and_lint, in_macro};
4+
5+
/// **What it does:** Checks for constants with an explicit `'static` lifetime.
6+
///
7+
/// **Why is this bad?** Adding `'static` to every reference can create very complicated types.
8+
///
9+
/// **Known problems:** None.
10+
///
11+
/// **Example:**
12+
/// ```rust
13+
/// const FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] = &[..]
14+
/// ```
15+
/// This code can be rewritten as
16+
/// ```rust
17+
/// const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
18+
/// ```
19+
20+
declare_lint! {
21+
pub CONST_STATIC_LIFETIME,
22+
Warn,
23+
"Using explicit `'static` lifetime for constants when elision rules would allow omitting them."
24+
}
25+
26+
pub struct StaticConst;
27+
28+
impl LintPass for StaticConst {
29+
fn get_lints(&self) -> LintArray {
30+
lint_array!(CONST_STATIC_LIFETIME)
31+
}
32+
}
33+
34+
impl StaticConst {
35+
// Recursively visit types
36+
fn visit_type(&mut self, ty: &Ty, cx: &EarlyContext) {
37+
match ty.node {
38+
// Be carefull of nested structures (arrays and tuples)
39+
TyKind::Array(ref ty, _) => {
40+
println!("array");
41+
self.visit_type(&*ty, cx);
42+
},
43+
TyKind::Tup(ref tup) => {
44+
for tup_ty in tup {
45+
self.visit_type(&*tup_ty, cx);
46+
}
47+
},
48+
// This is what we are looking for !
49+
TyKind::Rptr(ref optional_lifetime, ref borrow_type) => {
50+
// Match the 'static lifetime
51+
if let Some(lifetime) = *optional_lifetime {
52+
if let TyKind::Path(_, _) = borrow_type.ty.node {
53+
// Verify that the path is a str
54+
if lifetime.ident.name == "'static" {
55+
span_help_and_lint(cx,
56+
CONST_STATIC_LIFETIME,
57+
lifetime.span,
58+
"Constants have by default a `'static` lifetime",
59+
"consider removing `'static`");
60+
}
61+
}
62+
}
63+
self.visit_type(&*borrow_type.ty, cx);
64+
},
65+
TyKind::Slice(ref ty) => {
66+
self.visit_type(&ty, cx);
67+
},
68+
_ => {},
69+
}
70+
}
71+
}
72+
73+
impl EarlyLintPass for StaticConst {
74+
fn check_item(&mut self, cx: &EarlyContext, item: &Item) {
75+
if !in_macro(item.span) {
76+
// Match only constants...
77+
if let ItemKind::Const(ref var_type, _) = item.node {
78+
self.visit_type(var_type, cx);
79+
}
80+
}
81+
}
82+
}

clippy_lints/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ pub mod block_in_if_condition;
7676
pub mod booleans;
7777
pub mod bytecount;
7878
pub mod collapsible_if;
79+
pub mod const_static_lifetime;
7980
pub mod copies;
8081
pub mod cyclomatic_complexity;
8182
pub mod derive;
@@ -337,6 +338,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
337338
reg.register_late_lint_pass(box invalid_ref::InvalidRef);
338339
reg.register_late_lint_pass(box identity_conversion::IdentityConversion::default());
339340
reg.register_late_lint_pass(box types::ImplicitHasher);
341+
reg.register_early_lint_pass(box const_static_lifetime::StaticConst);
340342

341343
reg.register_lint_group("clippy_restrictions", vec![
342344
arithmetic::FLOAT_ARITHMETIC,
@@ -347,6 +349,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
347349

348350
reg.register_lint_group("clippy_pedantic", vec![
349351
booleans::NONMINIMAL_BOOL,
352+
const_static_lifetime::CONST_STATIC_LIFETIME,
350353
empty_enum::EMPTY_ENUM,
351354
enum_glob_use::ENUM_GLOB_USE,
352355
enum_variants::PUB_ENUM_VARIANT_NAMES,

tests/ui/const_static_lifetime.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#![feature(plugin)]
2+
3+
#[derive(Debug)]
4+
struct Foo {}
5+
6+
const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static.
7+
8+
const VAR_TWO: &str = "Test constant #2"; // This line should not raise a warning.
9+
10+
const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
11+
12+
const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
13+
14+
const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
15+
16+
const VAR_SIX: &'static u8 = &5;
17+
18+
const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
19+
20+
const VAR_HEIGHT: &'static Foo = &Foo {};
21+
22+
fn main() {
23+
let false_positive: &'static str = "test";
24+
println!("{}", VAR_ONE);
25+
println!("{}", VAR_TWO);
26+
println!("{:?}", VAR_THREE);
27+
println!("{:?}", VAR_FOUR);
28+
println!("{:?}", VAR_FIVE);
29+
println!("{:?}", VAR_SIX);
30+
println!("{:?}", VAR_SEVEN);
31+
println!("{:?}", VAR_HEIGHT);
32+
println!("{}", false_positive);
33+
}

tests/ui/regex.rs

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ extern crate regex;
99
use regex::{Regex, RegexSet, RegexBuilder};
1010
use regex::bytes::{Regex as BRegex, RegexSet as BRegexSet, RegexBuilder as BRegexBuilder};
1111

12-
const OPENING_PAREN : &'static str = "(";
13-
const NOT_A_REAL_REGEX : &'static str = "foobar";
12+
const OPENING_PAREN: &str = "(";
13+
const NOT_A_REAL_REGEX: &str = "foobar";
1414

1515
fn syntax_error() {
1616
let pipe_in_wrong_position = Regex::new("|");
@@ -27,23 +27,11 @@ fn syntax_error() {
2727
let closing_paren = ")";
2828
let not_linted = Regex::new(closing_paren);
2929

30-
let set = RegexSet::new(&[
31-
r"[a-z]+@[a-z]+\.(com|org|net)",
32-
r"[a-z]+\.(com|org|net)",
33-
]);
34-
let bset = BRegexSet::new(&[
35-
r"[a-z]+@[a-z]+\.(com|org|net)",
36-
r"[a-z]+\.(com|org|net)",
37-
]);
38-
39-
let set_error = RegexSet::new(&[
40-
OPENING_PAREN,
41-
r"[a-z]+\.(com|org|net)",
42-
]);
43-
let bset_error = BRegexSet::new(&[
44-
OPENING_PAREN,
45-
r"[a-z]+\.(com|org|net)",
46-
]);
30+
let set = RegexSet::new(&[r"[a-z]+@[a-z]+\.(com|org|net)", r"[a-z]+\.(com|org|net)"]);
31+
let bset = BRegexSet::new(&[r"[a-z]+@[a-z]+\.(com|org|net)", r"[a-z]+\.(com|org|net)"]);
32+
33+
let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]);
34+
let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]);
4735
}
4836

4937
fn trivial_regex() {

0 commit comments

Comments
 (0)