]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
Rollup merge of #89869 - kpreid:from-doc, r=yaahc
[rust.git] / src / tools / clippy / clippy_lints / src / redundant_static_lifetimes.rs
1 use clippy_utils::diagnostics::span_lint_and_then;
2 use clippy_utils::source::snippet;
3 use clippy_utils::{meets_msrv, msrvs};
4 use rustc_ast::ast::{Item, ItemKind, Ty, TyKind};
5 use rustc_errors::Applicability;
6 use rustc_lint::{EarlyContext, EarlyLintPass};
7 use rustc_semver::RustcVersion;
8 use rustc_session::{declare_tool_lint, impl_lint_pass};
9
10 declare_clippy_lint! {
11     /// ### What it does
12     /// Checks for constants and statics with an explicit `'static` lifetime.
13     ///
14     /// ### Why is this bad?
15     /// Adding `'static` to every reference can create very
16     /// complicated types.
17     ///
18     /// ### Example
19     /// ```ignore
20     /// const FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =
21     /// &[...]
22     /// static FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =
23     /// &[...]
24     /// ```
25     /// This code can be rewritten as
26     /// ```ignore
27     ///  const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
28     ///  static FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
29     /// ```
30     #[clippy::version = "1.37.0"]
31     pub REDUNDANT_STATIC_LIFETIMES,
32     style,
33     "Using explicit `'static` lifetime for constants or statics when elision rules would allow omitting them."
34 }
35
36 pub struct RedundantStaticLifetimes {
37     msrv: Option<RustcVersion>,
38 }
39
40 impl RedundantStaticLifetimes {
41     #[must_use]
42     pub fn new(msrv: Option<RustcVersion>) -> Self {
43         Self { msrv }
44     }
45 }
46
47 impl_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]);
48
49 impl RedundantStaticLifetimes {
50     // Recursively visit types
51     fn visit_type(&mut self, ty: &Ty, cx: &EarlyContext<'_>, reason: &str) {
52         match ty.kind {
53             // Be careful of nested structures (arrays and tuples)
54             TyKind::Array(ref ty, _) => {
55                 self.visit_type(&*ty, cx, reason);
56             },
57             TyKind::Tup(ref tup) => {
58                 for tup_ty in tup {
59                     self.visit_type(&*tup_ty, cx, reason);
60                 }
61             },
62             // This is what we are looking for !
63             TyKind::Rptr(ref optional_lifetime, ref borrow_type) => {
64                 // Match the 'static lifetime
65                 if let Some(lifetime) = *optional_lifetime {
66                     match borrow_type.ty.kind {
67                         TyKind::Path(..) | TyKind::Slice(..) | TyKind::Array(..) | TyKind::Tup(..) => {
68                             if lifetime.ident.name == rustc_span::symbol::kw::StaticLifetime {
69                                 let snip = snippet(cx, borrow_type.ty.span, "<type>");
70                                 let sugg = format!("&{}", snip);
71                                 span_lint_and_then(
72                                     cx,
73                                     REDUNDANT_STATIC_LIFETIMES,
74                                     lifetime.ident.span,
75                                     reason,
76                                     |diag| {
77                                         diag.span_suggestion(
78                                             ty.span,
79                                             "consider removing `'static`",
80                                             sugg,
81                                             Applicability::MachineApplicable, //snippet
82                                         );
83                                     },
84                                 );
85                             }
86                         },
87                         _ => {},
88                     }
89                 }
90                 self.visit_type(&*borrow_type.ty, cx, reason);
91             },
92             TyKind::Slice(ref ty) => {
93                 self.visit_type(ty, cx, reason);
94             },
95             _ => {},
96         }
97     }
98 }
99
100 impl EarlyLintPass for RedundantStaticLifetimes {
101     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
102         if !meets_msrv(self.msrv.as_ref(), &msrvs::STATIC_IN_CONST) {
103             return;
104         }
105
106         if !item.span.from_expansion() {
107             if let ItemKind::Const(_, ref var_type, _) = item.kind {
108                 self.visit_type(var_type, cx, "constants have by default a `'static` lifetime");
109                 // Don't check associated consts because `'static` cannot be elided on those (issue
110                 // #2438)
111             }
112
113             if let ItemKind::Static(ref var_type, _, _) = item.kind {
114                 self.visit_type(var_type, cx, "statics have by default a `'static` lifetime");
115             }
116         }
117     }
118
119     extract_msrv_attr!(EarlyContext);
120 }