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