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