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