]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/functions/mod.rs
Move too_many_lines to its own module
[rust.git] / clippy_lints / src / functions / mod.rs
1 mod too_many_arguments;
2 mod too_many_lines;
3
4 use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then};
5 use clippy_utils::source::snippet_opt;
6 use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, type_is_unsafe_function};
7 use clippy_utils::{
8     attr_by_name, attrs::is_proc_macro, iter_input_pats, match_def_path, must_use_attr, path_to_local, return_ty,
9     trait_ref_of_method,
10 };
11 use if_chain::if_chain;
12 use rustc_ast::ast::Attribute;
13 use rustc_data_structures::fx::FxHashSet;
14 use rustc_errors::Applicability;
15 use rustc_hir as hir;
16 use rustc_hir::intravisit;
17 use rustc_hir::{def::Res, def_id::DefId, QPath};
18 use rustc_lint::{LateContext, LateLintPass, LintContext};
19 use rustc_middle::hir::map::Map;
20 use rustc_middle::lint::in_external_macro;
21 use rustc_middle::ty::{self, Ty};
22 use rustc_session::{declare_tool_lint, impl_lint_pass};
23 use rustc_span::{sym, Span};
24 use rustc_typeck::hir_ty_to_ty;
25
26 declare_clippy_lint! {
27     /// **What it does:** Checks for functions with too many parameters.
28     ///
29     /// **Why is this bad?** Functions with lots of parameters are considered bad
30     /// style and reduce readability (“what does the 5th parameter mean?”). Consider
31     /// grouping some parameters into a new type.
32     ///
33     /// **Known problems:** None.
34     ///
35     /// **Example:**
36     /// ```rust
37     /// # struct Color;
38     /// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {
39     ///     // ..
40     /// }
41     /// ```
42     pub TOO_MANY_ARGUMENTS,
43     complexity,
44     "functions with too many arguments"
45 }
46
47 declare_clippy_lint! {
48     /// **What it does:** Checks for functions with a large amount of lines.
49     ///
50     /// **Why is this bad?** Functions with a lot of lines are harder to understand
51     /// due to having to look at a larger amount of code to understand what the
52     /// function is doing. Consider splitting the body of the function into
53     /// multiple functions.
54     ///
55     /// **Known problems:** None.
56     ///
57     /// **Example:**
58     /// ```rust
59     /// fn im_too_long() {
60     ///     println!("");
61     ///     // ... 100 more LoC
62     ///     println!("");
63     /// }
64     /// ```
65     pub TOO_MANY_LINES,
66     pedantic,
67     "functions with too many lines"
68 }
69
70 declare_clippy_lint! {
71     /// **What it does:** Checks for public functions that dereference raw pointer
72     /// arguments but are not marked unsafe.
73     ///
74     /// **Why is this bad?** The function should probably be marked `unsafe`, since
75     /// for an arbitrary raw pointer, there is no way of telling for sure if it is
76     /// valid.
77     ///
78     /// **Known problems:**
79     ///
80     /// * It does not check functions recursively so if the pointer is passed to a
81     /// private non-`unsafe` function which does the dereferencing, the lint won't
82     /// trigger.
83     /// * It only checks for arguments whose type are raw pointers, not raw pointers
84     /// got from an argument in some other way (`fn foo(bar: &[*const u8])` or
85     /// `some_argument.get_raw_ptr()`).
86     ///
87     /// **Example:**
88     /// ```rust,ignore
89     /// // Bad
90     /// pub fn foo(x: *const u8) {
91     ///     println!("{}", unsafe { *x });
92     /// }
93     ///
94     /// // Good
95     /// pub unsafe fn foo(x: *const u8) {
96     ///     println!("{}", unsafe { *x });
97     /// }
98     /// ```
99     pub NOT_UNSAFE_PTR_ARG_DEREF,
100     correctness,
101     "public functions dereferencing raw pointer arguments but not marked `unsafe`"
102 }
103
104 declare_clippy_lint! {
105     /// **What it does:** Checks for a [`#[must_use]`] attribute on
106     /// unit-returning functions and methods.
107     ///
108     /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
109     ///
110     /// **Why is this bad?** Unit values are useless. The attribute is likely
111     /// a remnant of a refactoring that removed the return type.
112     ///
113     /// **Known problems:** None.
114     ///
115     /// **Examples:**
116     /// ```rust
117     /// #[must_use]
118     /// fn useless() { }
119     /// ```
120     pub MUST_USE_UNIT,
121     style,
122     "`#[must_use]` attribute on a unit-returning function / method"
123 }
124
125 declare_clippy_lint! {
126     /// **What it does:** Checks for a [`#[must_use]`] attribute without
127     /// further information on functions and methods that return a type already
128     /// marked as `#[must_use]`.
129     ///
130     /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
131     ///
132     /// **Why is this bad?** The attribute isn't needed. Not using the result
133     /// will already be reported. Alternatively, one can add some text to the
134     /// attribute to improve the lint message.
135     ///
136     /// **Known problems:** None.
137     ///
138     /// **Examples:**
139     /// ```rust
140     /// #[must_use]
141     /// fn double_must_use() -> Result<(), ()> {
142     ///     unimplemented!();
143     /// }
144     /// ```
145     pub DOUBLE_MUST_USE,
146     style,
147     "`#[must_use]` attribute on a `#[must_use]`-returning function / method"
148 }
149
150 declare_clippy_lint! {
151     /// **What it does:** Checks for public functions that have no
152     /// [`#[must_use]`] attribute, but return something not already marked
153     /// must-use, have no mutable arg and mutate no statics.
154     ///
155     /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
156     ///
157     /// **Why is this bad?** Not bad at all, this lint just shows places where
158     /// you could add the attribute.
159     ///
160     /// **Known problems:** The lint only checks the arguments for mutable
161     /// types without looking if they are actually changed. On the other hand,
162     /// it also ignores a broad range of potentially interesting side effects,
163     /// because we cannot decide whether the programmer intends the function to
164     /// be called for the side effect or the result. Expect many false
165     /// positives. At least we don't lint if the result type is unit or already
166     /// `#[must_use]`.
167     ///
168     /// **Examples:**
169     /// ```rust
170     /// // this could be annotated with `#[must_use]`.
171     /// fn id<T>(t: T) -> T { t }
172     /// ```
173     pub MUST_USE_CANDIDATE,
174     pedantic,
175     "function or method that could take a `#[must_use]` attribute"
176 }
177
178 declare_clippy_lint! {
179     /// **What it does:** Checks for public functions that return a `Result`
180     /// with an `Err` type of `()`. It suggests using a custom type that
181     /// implements [`std::error::Error`].
182     ///
183     /// **Why is this bad?** Unit does not implement `Error` and carries no
184     /// further information about what went wrong.
185     ///
186     /// **Known problems:** Of course, this lint assumes that `Result` is used
187     /// for a fallible operation (which is after all the intended use). However
188     /// code may opt to (mis)use it as a basic two-variant-enum. In that case,
189     /// the suggestion is misguided, and the code should use a custom enum
190     /// instead.
191     ///
192     /// **Examples:**
193     /// ```rust
194     /// pub fn read_u8() -> Result<u8, ()> { Err(()) }
195     /// ```
196     /// should become
197     /// ```rust,should_panic
198     /// use std::fmt;
199     ///
200     /// #[derive(Debug)]
201     /// pub struct EndOfStream;
202     ///
203     /// impl fmt::Display for EndOfStream {
204     ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205     ///         write!(f, "End of Stream")
206     ///     }
207     /// }
208     ///
209     /// impl std::error::Error for EndOfStream { }
210     ///
211     /// pub fn read_u8() -> Result<u8, EndOfStream> { Err(EndOfStream) }
212     ///# fn main() {
213     ///#     read_u8().unwrap();
214     ///# }
215     /// ```
216     ///
217     /// Note that there are crates that simplify creating the error type, e.g.
218     /// [`thiserror`](https://docs.rs/thiserror).
219     pub RESULT_UNIT_ERR,
220     style,
221     "public function returning `Result` with an `Err` type of `()`"
222 }
223
224 #[derive(Copy, Clone)]
225 pub struct Functions {
226     too_many_arguments_threshold: u64,
227     too_many_lines_threshold: u64,
228 }
229
230 impl Functions {
231     pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64) -> Self {
232         Self {
233             too_many_arguments_threshold,
234             too_many_lines_threshold,
235         }
236     }
237 }
238
239 impl_lint_pass!(Functions => [
240     TOO_MANY_ARGUMENTS,
241     TOO_MANY_LINES,
242     NOT_UNSAFE_PTR_ARG_DEREF,
243     MUST_USE_UNIT,
244     DOUBLE_MUST_USE,
245     MUST_USE_CANDIDATE,
246     RESULT_UNIT_ERR,
247 ]);
248
249 impl<'tcx> LateLintPass<'tcx> for Functions {
250     fn check_fn(
251         &mut self,
252         cx: &LateContext<'tcx>,
253         kind: intravisit::FnKind<'tcx>,
254         decl: &'tcx hir::FnDecl<'_>,
255         body: &'tcx hir::Body<'_>,
256         span: Span,
257         hir_id: hir::HirId,
258     ) {
259         too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold);
260         too_many_lines::check(cx, span, body, self.too_many_lines_threshold);
261
262         let unsafety = match kind {
263             intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }, _) => unsafety,
264             intravisit::FnKind::Method(_, sig, _) => sig.header.unsafety,
265             intravisit::FnKind::Closure => return,
266         };
267
268         Self::check_raw_ptr(cx, unsafety, decl, body, hir_id);
269     }
270
271     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
272         let attrs = cx.tcx.hir().attrs(item.hir_id());
273         let attr = must_use_attr(attrs);
274         if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind {
275             let is_public = cx.access_levels.is_exported(item.hir_id());
276             let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
277             if is_public {
278                 check_result_unit_err(cx, &sig.decl, item.span, fn_header_span);
279             }
280             if let Some(attr) = attr {
281                 check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr);
282                 return;
283             }
284             if is_public && !is_proc_macro(cx.sess(), attrs) && attr_by_name(attrs, "no_mangle").is_none() {
285                 check_must_use_candidate(
286                     cx,
287                     &sig.decl,
288                     cx.tcx.hir().body(*body_id),
289                     item.span,
290                     item.hir_id(),
291                     item.span.with_hi(sig.decl.output.span().hi()),
292                     "this function could have a `#[must_use]` attribute",
293                 );
294             }
295         }
296     }
297
298     fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
299         if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind {
300             let is_public = cx.access_levels.is_exported(item.hir_id());
301             let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
302             if is_public && trait_ref_of_method(cx, item.hir_id()).is_none() {
303                 check_result_unit_err(cx, &sig.decl, item.span, fn_header_span);
304             }
305             let attrs = cx.tcx.hir().attrs(item.hir_id());
306             let attr = must_use_attr(attrs);
307             if let Some(attr) = attr {
308                 check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr);
309             } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.hir_id()).is_none()
310             {
311                 check_must_use_candidate(
312                     cx,
313                     &sig.decl,
314                     cx.tcx.hir().body(*body_id),
315                     item.span,
316                     item.hir_id(),
317                     item.span.with_hi(sig.decl.output.span().hi()),
318                     "this method could have a `#[must_use]` attribute",
319                 );
320             }
321         }
322     }
323
324     fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
325         too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold);
326
327         if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind {
328             let is_public = cx.access_levels.is_exported(item.hir_id());
329             let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
330             if is_public {
331                 check_result_unit_err(cx, &sig.decl, item.span, fn_header_span);
332             }
333
334             let attrs = cx.tcx.hir().attrs(item.hir_id());
335             let attr = must_use_attr(attrs);
336             if let Some(attr) = attr {
337                 check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr);
338             }
339             if let hir::TraitFn::Provided(eid) = *eid {
340                 let body = cx.tcx.hir().body(eid);
341                 Self::check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id());
342
343                 if attr.is_none() && is_public && !is_proc_macro(cx.sess(), attrs) {
344                     check_must_use_candidate(
345                         cx,
346                         &sig.decl,
347                         body,
348                         item.span,
349                         item.hir_id(),
350                         item.span.with_hi(sig.decl.output.span().hi()),
351                         "this method could have a `#[must_use]` attribute",
352                     );
353                 }
354             }
355         }
356     }
357 }
358
359 impl<'tcx> Functions {
360     fn check_raw_ptr(
361         cx: &LateContext<'tcx>,
362         unsafety: hir::Unsafety,
363         decl: &'tcx hir::FnDecl<'_>,
364         body: &'tcx hir::Body<'_>,
365         hir_id: hir::HirId,
366     ) {
367         let expr = &body.value;
368         if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(hir_id) {
369             let raw_ptrs = iter_input_pats(decl, body)
370                 .zip(decl.inputs.iter())
371                 .filter_map(|(arg, ty)| raw_ptr_arg(arg, ty))
372                 .collect::<FxHashSet<_>>();
373
374             if !raw_ptrs.is_empty() {
375                 let typeck_results = cx.tcx.typeck_body(body.id());
376                 let mut v = DerefVisitor {
377                     cx,
378                     ptrs: raw_ptrs,
379                     typeck_results,
380                 };
381
382                 intravisit::walk_expr(&mut v, expr);
383             }
384         }
385     }
386 }
387
388 fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span: Span, fn_header_span: Span) {
389     if_chain! {
390         if !in_external_macro(cx.sess(), item_span);
391         if let hir::FnRetTy::Return(ref ty) = decl.output;
392         let ty = hir_ty_to_ty(cx.tcx, ty);
393         if is_type_diagnostic_item(cx, ty, sym::result_type);
394         if let ty::Adt(_, substs) = ty.kind();
395         let err_ty = substs.type_at(1);
396         if err_ty.is_unit();
397         then {
398             span_lint_and_help(
399                 cx,
400                 RESULT_UNIT_ERR,
401                 fn_header_span,
402                 "this returns a `Result<_, ()>",
403                 None,
404                 "use a custom Error type instead",
405             );
406         }
407     }
408 }
409
410 fn check_needless_must_use(
411     cx: &LateContext<'_>,
412     decl: &hir::FnDecl<'_>,
413     item_id: hir::HirId,
414     item_span: Span,
415     fn_header_span: Span,
416     attr: &Attribute,
417 ) {
418     if in_external_macro(cx.sess(), item_span) {
419         return;
420     }
421     if returns_unit(decl) {
422         span_lint_and_then(
423             cx,
424             MUST_USE_UNIT,
425             fn_header_span,
426             "this unit-returning function has a `#[must_use]` attribute",
427             |diag| {
428                 diag.span_suggestion(
429                     attr.span,
430                     "remove the attribute",
431                     "".into(),
432                     Applicability::MachineApplicable,
433                 );
434             },
435         );
436     } else if !attr.is_value_str() && is_must_use_ty(cx, return_ty(cx, item_id)) {
437         span_lint_and_help(
438             cx,
439             DOUBLE_MUST_USE,
440             fn_header_span,
441             "this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`",
442             None,
443             "either add some descriptive text or remove the attribute",
444         );
445     }
446 }
447
448 fn check_must_use_candidate<'tcx>(
449     cx: &LateContext<'tcx>,
450     decl: &'tcx hir::FnDecl<'_>,
451     body: &'tcx hir::Body<'_>,
452     item_span: Span,
453     item_id: hir::HirId,
454     fn_span: Span,
455     msg: &str,
456 ) {
457     if has_mutable_arg(cx, body)
458         || mutates_static(cx, body)
459         || in_external_macro(cx.sess(), item_span)
460         || returns_unit(decl)
461         || !cx.access_levels.is_exported(item_id)
462         || is_must_use_ty(cx, return_ty(cx, item_id))
463     {
464         return;
465     }
466     span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| {
467         if let Some(snippet) = snippet_opt(cx, fn_span) {
468             diag.span_suggestion(
469                 fn_span,
470                 "add the attribute",
471                 format!("#[must_use] {}", snippet),
472                 Applicability::MachineApplicable,
473             );
474         }
475     });
476 }
477
478 fn returns_unit(decl: &hir::FnDecl<'_>) -> bool {
479     match decl.output {
480         hir::FnRetTy::DefaultReturn(_) => true,
481         hir::FnRetTy::Return(ref ty) => match ty.kind {
482             hir::TyKind::Tup(ref tys) => tys.is_empty(),
483             hir::TyKind::Never => true,
484             _ => false,
485         },
486     }
487 }
488
489 fn has_mutable_arg(cx: &LateContext<'_>, body: &hir::Body<'_>) -> bool {
490     let mut tys = FxHashSet::default();
491     body.params.iter().any(|param| is_mutable_pat(cx, &param.pat, &mut tys))
492 }
493
494 fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut FxHashSet<DefId>) -> bool {
495     if let hir::PatKind::Wild = pat.kind {
496         return false; // ignore `_` patterns
497     }
498     if cx.tcx.has_typeck_results(pat.hir_id.owner.to_def_id()) {
499         is_mutable_ty(cx, &cx.tcx.typeck(pat.hir_id.owner).pat_ty(pat), pat.span, tys)
500     } else {
501         false
502     }
503 }
504
505 static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]];
506
507 fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut FxHashSet<DefId>) -> bool {
508     match *ty.kind() {
509         // primitive types are never mutable
510         ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false,
511         ty::Adt(ref adt, ref substs) => {
512             tys.insert(adt.did) && !ty.is_freeze(cx.tcx.at(span), cx.param_env)
513                 || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did, path))
514                     && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys))
515         },
516         ty::Tuple(ref substs) => substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)),
517         ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys),
518         ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => {
519             mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys)
520         },
521         // calling something constitutes a side effect, so return true on all callables
522         // also never calls need not be used, so return true for them, too
523         _ => true,
524     }
525 }
526
527 fn raw_ptr_arg(arg: &hir::Param<'_>, ty: &hir::Ty<'_>) -> Option<hir::HirId> {
528     if let (&hir::PatKind::Binding(_, id, _, _), &hir::TyKind::Ptr(_)) = (&arg.pat.kind, &ty.kind) {
529         Some(id)
530     } else {
531         None
532     }
533 }
534
535 struct DerefVisitor<'a, 'tcx> {
536     cx: &'a LateContext<'tcx>,
537     ptrs: FxHashSet<hir::HirId>,
538     typeck_results: &'a ty::TypeckResults<'tcx>,
539 }
540
541 impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> {
542     type Map = Map<'tcx>;
543
544     fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
545         match expr.kind {
546             hir::ExprKind::Call(ref f, args) => {
547                 let ty = self.typeck_results.expr_ty(f);
548
549                 if type_is_unsafe_function(self.cx, ty) {
550                     for arg in args {
551                         self.check_arg(arg);
552                     }
553                 }
554             },
555             hir::ExprKind::MethodCall(_, _, args, _) => {
556                 let def_id = self.typeck_results.type_dependent_def_id(expr.hir_id).unwrap();
557                 let base_type = self.cx.tcx.type_of(def_id);
558
559                 if type_is_unsafe_function(self.cx, base_type) {
560                     for arg in args {
561                         self.check_arg(arg);
562                     }
563                 }
564             },
565             hir::ExprKind::Unary(hir::UnOp::Deref, ref ptr) => self.check_arg(ptr),
566             _ => (),
567         }
568
569         intravisit::walk_expr(self, expr);
570     }
571
572     fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
573         intravisit::NestedVisitorMap::None
574     }
575 }
576
577 impl<'a, 'tcx> DerefVisitor<'a, 'tcx> {
578     fn check_arg(&self, ptr: &hir::Expr<'_>) {
579         if let Some(id) = path_to_local(ptr) {
580             if self.ptrs.contains(&id) {
581                 span_lint(
582                     self.cx,
583                     NOT_UNSAFE_PTR_ARG_DEREF,
584                     ptr.span,
585                     "this public function dereferences a raw pointer but is not marked `unsafe`",
586                 );
587             }
588         }
589     }
590 }
591
592 struct StaticMutVisitor<'a, 'tcx> {
593     cx: &'a LateContext<'tcx>,
594     mutates_static: bool,
595 }
596
597 impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> {
598     type Map = Map<'tcx>;
599
600     fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
601         use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall};
602
603         if self.mutates_static {
604             return;
605         }
606         match expr.kind {
607             Call(_, args) | MethodCall(_, _, args, _) => {
608                 let mut tys = FxHashSet::default();
609                 for arg in args {
610                     if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
611                         && is_mutable_ty(
612                             self.cx,
613                             self.cx.tcx.typeck(arg.hir_id.owner).expr_ty(arg),
614                             arg.span,
615                             &mut tys,
616                         )
617                         && is_mutated_static(arg)
618                     {
619                         self.mutates_static = true;
620                         return;
621                     }
622                     tys.clear();
623                 }
624             },
625             Assign(ref target, ..) | AssignOp(_, ref target, _) | AddrOf(_, hir::Mutability::Mut, ref target) => {
626                 self.mutates_static |= is_mutated_static(target)
627             },
628             _ => {},
629         }
630     }
631
632     fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
633         intravisit::NestedVisitorMap::None
634     }
635 }
636
637 fn is_mutated_static(e: &hir::Expr<'_>) -> bool {
638     use hir::ExprKind::{Field, Index, Path};
639
640     match e.kind {
641         Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)),
642         Path(_) => true,
643         Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(inner),
644         _ => false,
645     }
646 }
647
648 fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool {
649     let mut v = StaticMutVisitor {
650         cx,
651         mutates_static: false,
652     };
653     intravisit::walk_expr(&mut v, &body.value);
654     v.mutates_static
655 }