diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f34d24cd47..03c8be124c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1088,6 +1088,7 @@ All notable changes to this project will be documented in this file. [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization +[`static_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#static_static_lifetime [`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string [`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add [`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2bec5cb2d01..0b1c7685969 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -256,6 +256,7 @@ pub mod returns; pub mod serde_api; pub mod shadow; pub mod slow_vector_initialization; +pub mod static_static_lifetime; pub mod strings; pub mod suspicious_trait_impl; pub mod swap; @@ -554,6 +555,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { reg.register_late_lint_pass(box identity_conversion::IdentityConversion::default()); reg.register_late_lint_pass(box types::ImplicitHasher); reg.register_early_lint_pass(box const_static_lifetime::StaticConst); + reg.register_early_lint_pass(box static_static_lifetime::StaticStatic); reg.register_late_lint_pass(box fallible_impl_from::FallibleImplFrom); reg.register_late_lint_pass(box replace_consts::ReplaceConsts); reg.register_late_lint_pass(box types::UnitArg); @@ -844,6 +846,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { returns::UNUSED_UNIT, serde_api::SERDE_API_MISUSE, slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, + static_static_lifetime::STATIC_STATIC_LIFETIME, strings::STRING_LIT_AS_BYTES, suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, @@ -962,6 +965,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { returns::LET_AND_RETURN, returns::NEEDLESS_RETURN, returns::UNUSED_UNIT, + static_static_lifetime::STATIC_STATIC_LIFETIME, strings::STRING_LIT_AS_BYTES, types::FN_TO_NUMERIC_CAST, types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION, diff --git a/clippy_lints/src/static_static_lifetime.rs b/clippy_lints/src/static_static_lifetime.rs new file mode 100644 index 00000000000..338d9e76602 --- /dev/null +++ b/clippy_lints/src/static_static_lifetime.rs @@ -0,0 +1,93 @@ +use crate::utils::{in_macro_or_desugar, snippet, span_lint_and_then}; +use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass}; +use rustc::{declare_lint_pass, declare_tool_lint}; +use rustc_errors::Applicability; +use syntax::ast::*; + +declare_clippy_lint! { + /// **What it does:** Checks for statics with an explicit `'static` lifetime. + /// + /// **Why is this bad?** Adding `'static` to every reference can create very + /// complicated types. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// static FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] = + /// &[...] + /// ``` + /// This code can be rewritten as + /// ```ignore + /// static FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...] + /// ``` + pub STATIC_STATIC_LIFETIME, + style, + "Using explicit `'static` lifetime for statics when elision rules would allow omitting them." +} + +declare_lint_pass!(StaticStatic => [STATIC_STATIC_LIFETIME]); + +impl StaticStatic { + // Recursively visit types + fn visit_type(&mut self, ty: &Ty, cx: &EarlyContext<'_>) { + match ty.node { + // Be careful of nested structures (arrays and tuples) + TyKind::Array(ref ty, _) => { + self.visit_type(&*ty, cx); + }, + TyKind::Tup(ref tup) => { + for tup_ty in tup { + self.visit_type(&*tup_ty, cx); + } + }, + // This is what we are looking for ! + TyKind::Rptr(ref optional_lifetime, ref borrow_type) => { + // Match the 'static lifetime + if let Some(lifetime) = *optional_lifetime { + match borrow_type.ty.node { + TyKind::Path(..) | TyKind::Slice(..) | TyKind::Array(..) | TyKind::Tup(..) => { + if lifetime.ident.name == syntax::symbol::kw::StaticLifetime { + let snip = snippet(cx, borrow_type.ty.span, ""); + let sugg = format!("&{}", snip); + span_lint_and_then( + cx, + STATIC_STATIC_LIFETIME, + lifetime.ident.span, + "Statics have by default a `'static` lifetime", + |db| { + db.span_suggestion( + ty.span, + "consider removing `'static`", + sugg, + Applicability::MachineApplicable, //snippet + ); + }, + ); + } + }, + _ => {}, + } + } + self.visit_type(&*borrow_type.ty, cx); + }, + TyKind::Slice(ref ty) => { + self.visit_type(ty, cx); + }, + _ => {}, + } + } +} + +impl EarlyLintPass for StaticStatic { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if !in_macro_or_desugar(item.span) { + // Match only statics... + if let ItemKind::Static(ref var_type, _, _) = item.node { + self.visit_type(var_type, cx); + } + } + } + + // Don't check associated consts because `'static` cannot be elided on those (issue #2438) +} diff --git a/tests/ui/static_static_lifetime.rs b/tests/ui/static_static_lifetime.rs new file mode 100644 index 00000000000..04fecbfaeb2 --- /dev/null +++ b/tests/ui/static_static_lifetime.rs @@ -0,0 +1,49 @@ +#[derive(Debug)] +struct Foo {} + +static VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static. + +static VAR_TWO: &str = "Test static #2"; // This line should not raise a warning. + +static VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static + +static VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static + +static VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static + +static VAR_SIX: &'static u8 = &5; + +static VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; + +static VAR_HEIGHT: &'static Foo = &Foo {}; + +static VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static. + +static VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. + +static VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. + +fn main() { + let false_positive: &'static str = "test"; + println!("{}", VAR_ONE); + println!("{}", VAR_TWO); + println!("{:?}", VAR_THREE); + println!("{:?}", VAR_FOUR); + println!("{:?}", VAR_FIVE); + println!("{:?}", VAR_SIX); + println!("{:?}", VAR_SEVEN); + println!("{:?}", VAR_HEIGHT); + println!("{}", false_positive); +} + +// trait Bar { +// static TRAIT_VAR: &'static str; +// } + +// impl Foo { +// static IMPL_VAR: &'static str = "var"; +// } + +// impl Bar for Foo { +// static TRAIT_VAR: &'static str = "foo"; +// } diff --git a/tests/ui/static_static_lifetime.stderr b/tests/ui/static_static_lifetime.stderr new file mode 100644 index 00000000000..c4111b1f13c --- /dev/null +++ b/tests/ui/static_static_lifetime.stderr @@ -0,0 +1,82 @@ +error: Statics have by default a `'static` lifetime + --> $DIR/static_static_lifetime.rs:4:18 + | +LL | static VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static. + | -^^^^^^^---- help: consider removing `'static`: `&str` + | + = note: `-D clippy::static-static-lifetime` implied by `-D warnings` + +error: Statics have by default a `'static` lifetime + --> $DIR/static_static_lifetime.rs:8:22 + | +LL | static VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Statics have by default a `'static` lifetime + --> $DIR/static_static_lifetime.rs:10:33 + | +LL | static VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Statics have by default a `'static` lifetime + --> $DIR/static_static_lifetime.rs:10:48 + | +LL | static VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Statics have by default a `'static` lifetime + --> $DIR/static_static_lifetime.rs:12:19 + | +LL | static VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static + | -^^^^^^^------------------ help: consider removing `'static`: `&[&[&'static str]]` + +error: Statics have by default a `'static` lifetime + --> $DIR/static_static_lifetime.rs:12:31 + | +LL | static VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Statics have by default a `'static` lifetime + --> $DIR/static_static_lifetime.rs:14:18 + | +LL | static VAR_SIX: &'static u8 = &5; + | -^^^^^^^--- help: consider removing `'static`: `&u8` + +error: Statics have by default a `'static` lifetime + --> $DIR/static_static_lifetime.rs:16:30 + | +LL | static VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; + | -^^^^^^^--------------- help: consider removing `'static`: `&[&'static str]` + +error: Statics have by default a `'static` lifetime + --> $DIR/static_static_lifetime.rs:16:40 + | +LL | static VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Statics have by default a `'static` lifetime + --> $DIR/static_static_lifetime.rs:18:21 + | +LL | static VAR_HEIGHT: &'static Foo = &Foo {}; + | -^^^^^^^---- help: consider removing `'static`: `&Foo` + +error: Statics have by default a `'static` lifetime + --> $DIR/static_static_lifetime.rs:20:20 + | +LL | static VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static. + | -^^^^^^^----- help: consider removing `'static`: `&[u8]` + +error: Statics have by default a `'static` lifetime + --> $DIR/static_static_lifetime.rs:22:20 + | +LL | static VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. + | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` + +error: Statics have by default a `'static` lifetime + --> $DIR/static_static_lifetime.rs:24:20 + | +LL | static VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. + | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` + +error: aborting due to 13 previous errors +