]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/functions.rs
Merge remote-tracking branch 'upstream/master' into rustup
[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, path_to_local, 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, QPath};
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                     if let Some(i) = line.find("*/") {
409                         line = &line[i + 2..];
410                         in_comment = false;
411                         continue;
412                     }
413                 } else {
414                     let multi_idx = line.find("/*").unwrap_or_else(|| line.len());
415                     let single_idx = line.find("//").unwrap_or_else(|| line.len());
416                     code_in_line |= multi_idx > 0 && single_idx > 0;
417                     // Implies multi_idx is below line.len()
418                     if multi_idx < single_idx {
419                         line = &line[multi_idx + 2..];
420                         in_comment = true;
421                         continue;
422                     }
423                 }
424                 break;
425             }
426             if code_in_line {
427                 line_count += 1;
428             }
429         }
430
431         if line_count > self.max_lines {
432             span_lint(
433                 cx,
434                 TOO_MANY_LINES,
435                 span,
436                 &format!("this function has too many lines ({}/{})", line_count, self.max_lines),
437             )
438         }
439     }
440
441     fn check_raw_ptr(
442         cx: &LateContext<'tcx>,
443         unsafety: hir::Unsafety,
444         decl: &'tcx hir::FnDecl<'_>,
445         body: &'tcx hir::Body<'_>,
446         hir_id: hir::HirId,
447     ) {
448         let expr = &body.value;
449         if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(hir_id) {
450             let raw_ptrs = iter_input_pats(decl, body)
451                 .zip(decl.inputs.iter())
452                 .filter_map(|(arg, ty)| raw_ptr_arg(arg, ty))
453                 .collect::<FxHashSet<_>>();
454
455             if !raw_ptrs.is_empty() {
456                 let typeck_results = cx.tcx.typeck_body(body.id());
457                 let mut v = DerefVisitor {
458                     cx,
459                     ptrs: raw_ptrs,
460                     typeck_results,
461                 };
462
463                 intravisit::walk_expr(&mut v, expr);
464             }
465         }
466     }
467 }
468
469 fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span: Span, fn_header_span: Span) {
470     if_chain! {
471         if !in_external_macro(cx.sess(), item_span);
472         if let hir::FnRetTy::Return(ref ty) = decl.output;
473         if let hir::TyKind::Path(ref qpath) = ty.kind;
474         if is_type_diagnostic_item(cx, hir_ty_to_ty(cx.tcx, ty), sym::result_type);
475         if let Some(ref args) = last_path_segment(qpath).args;
476         if let [_, hir::GenericArg::Type(ref err_ty)] = args.args;
477         if let hir::TyKind::Tup(t) = err_ty.kind;
478         if t.is_empty();
479         then {
480             span_lint_and_help(
481                 cx,
482                 RESULT_UNIT_ERR,
483                 fn_header_span,
484                 "this returns a `Result<_, ()>",
485                 None,
486                 "use a custom Error type instead",
487             );
488         }
489     }
490 }
491
492 fn check_needless_must_use(
493     cx: &LateContext<'_>,
494     decl: &hir::FnDecl<'_>,
495     item_id: hir::HirId,
496     item_span: Span,
497     fn_header_span: Span,
498     attr: &Attribute,
499 ) {
500     if in_external_macro(cx.sess(), item_span) {
501         return;
502     }
503     if returns_unit(decl) {
504         span_lint_and_then(
505             cx,
506             MUST_USE_UNIT,
507             fn_header_span,
508             "this unit-returning function has a `#[must_use]` attribute",
509             |diag| {
510                 diag.span_suggestion(
511                     attr.span,
512                     "remove the attribute",
513                     "".into(),
514                     Applicability::MachineApplicable,
515                 );
516             },
517         );
518     } else if !attr.is_value_str() && is_must_use_ty(cx, return_ty(cx, item_id)) {
519         span_lint_and_help(
520             cx,
521             DOUBLE_MUST_USE,
522             fn_header_span,
523             "this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`",
524             None,
525             "either add some descriptive text or remove the attribute",
526         );
527     }
528 }
529
530 fn check_must_use_candidate<'tcx>(
531     cx: &LateContext<'tcx>,
532     decl: &'tcx hir::FnDecl<'_>,
533     body: &'tcx hir::Body<'_>,
534     item_span: Span,
535     item_id: hir::HirId,
536     fn_span: Span,
537     msg: &str,
538 ) {
539     if has_mutable_arg(cx, body)
540         || mutates_static(cx, body)
541         || in_external_macro(cx.sess(), item_span)
542         || returns_unit(decl)
543         || !cx.access_levels.is_exported(item_id)
544         || is_must_use_ty(cx, return_ty(cx, item_id))
545     {
546         return;
547     }
548     span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| {
549         if let Some(snippet) = snippet_opt(cx, fn_span) {
550             diag.span_suggestion(
551                 fn_span,
552                 "add the attribute",
553                 format!("#[must_use] {}", snippet),
554                 Applicability::MachineApplicable,
555             );
556         }
557     });
558 }
559
560 fn returns_unit(decl: &hir::FnDecl<'_>) -> bool {
561     match decl.output {
562         hir::FnRetTy::DefaultReturn(_) => true,
563         hir::FnRetTy::Return(ref ty) => match ty.kind {
564             hir::TyKind::Tup(ref tys) => tys.is_empty(),
565             hir::TyKind::Never => true,
566             _ => false,
567         },
568     }
569 }
570
571 fn has_mutable_arg(cx: &LateContext<'_>, body: &hir::Body<'_>) -> bool {
572     let mut tys = FxHashSet::default();
573     body.params.iter().any(|param| is_mutable_pat(cx, &param.pat, &mut tys))
574 }
575
576 fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut FxHashSet<DefId>) -> bool {
577     if let hir::PatKind::Wild = pat.kind {
578         return false; // ignore `_` patterns
579     }
580     if cx.tcx.has_typeck_results(pat.hir_id.owner.to_def_id()) {
581         is_mutable_ty(cx, &cx.tcx.typeck(pat.hir_id.owner).pat_ty(pat), pat.span, tys)
582     } else {
583         false
584     }
585 }
586
587 static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]];
588
589 fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut FxHashSet<DefId>) -> bool {
590     match *ty.kind() {
591         // primitive types are never mutable
592         ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false,
593         ty::Adt(ref adt, ref substs) => {
594             tys.insert(adt.did) && !ty.is_freeze(cx.tcx.at(span), cx.param_env)
595                 || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did, path))
596                     && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys))
597         },
598         ty::Tuple(ref substs) => substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)),
599         ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys),
600         ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => {
601             mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys)
602         },
603         // calling something constitutes a side effect, so return true on all callables
604         // also never calls need not be used, so return true for them, too
605         _ => true,
606     }
607 }
608
609 fn raw_ptr_arg(arg: &hir::Param<'_>, ty: &hir::Ty<'_>) -> Option<hir::HirId> {
610     if let (&hir::PatKind::Binding(_, id, _, _), &hir::TyKind::Ptr(_)) = (&arg.pat.kind, &ty.kind) {
611         Some(id)
612     } else {
613         None
614     }
615 }
616
617 struct DerefVisitor<'a, 'tcx> {
618     cx: &'a LateContext<'tcx>,
619     ptrs: FxHashSet<hir::HirId>,
620     typeck_results: &'a ty::TypeckResults<'tcx>,
621 }
622
623 impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> {
624     type Map = Map<'tcx>;
625
626     fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
627         match expr.kind {
628             hir::ExprKind::Call(ref f, args) => {
629                 let ty = self.typeck_results.expr_ty(f);
630
631                 if type_is_unsafe_function(self.cx, ty) {
632                     for arg in args {
633                         self.check_arg(arg);
634                     }
635                 }
636             },
637             hir::ExprKind::MethodCall(_, _, args, _) => {
638                 let def_id = self.typeck_results.type_dependent_def_id(expr.hir_id).unwrap();
639                 let base_type = self.cx.tcx.type_of(def_id);
640
641                 if type_is_unsafe_function(self.cx, base_type) {
642                     for arg in args {
643                         self.check_arg(arg);
644                     }
645                 }
646             },
647             hir::ExprKind::Unary(hir::UnOp::Deref, ref ptr) => self.check_arg(ptr),
648             _ => (),
649         }
650
651         intravisit::walk_expr(self, expr);
652     }
653
654     fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
655         intravisit::NestedVisitorMap::None
656     }
657 }
658
659 impl<'a, 'tcx> DerefVisitor<'a, 'tcx> {
660     fn check_arg(&self, ptr: &hir::Expr<'_>) {
661         if let Some(id) = path_to_local(ptr) {
662             if self.ptrs.contains(&id) {
663                 span_lint(
664                     self.cx,
665                     NOT_UNSAFE_PTR_ARG_DEREF,
666                     ptr.span,
667                     "this public function dereferences a raw pointer but is not marked `unsafe`",
668                 );
669             }
670         }
671     }
672 }
673
674 struct StaticMutVisitor<'a, 'tcx> {
675     cx: &'a LateContext<'tcx>,
676     mutates_static: bool,
677 }
678
679 impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> {
680     type Map = Map<'tcx>;
681
682     fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
683         use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall};
684
685         if self.mutates_static {
686             return;
687         }
688         match expr.kind {
689             Call(_, args) | MethodCall(_, _, args, _) => {
690                 let mut tys = FxHashSet::default();
691                 for arg in args {
692                     if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
693                         && is_mutable_ty(
694                             self.cx,
695                             self.cx.tcx.typeck(arg.hir_id.owner).expr_ty(arg),
696                             arg.span,
697                             &mut tys,
698                         )
699                         && is_mutated_static(arg)
700                     {
701                         self.mutates_static = true;
702                         return;
703                     }
704                     tys.clear();
705                 }
706             },
707             Assign(ref target, ..) | AssignOp(_, ref target, _) | AddrOf(_, hir::Mutability::Mut, ref target) => {
708                 self.mutates_static |= is_mutated_static(target)
709             },
710             _ => {},
711         }
712     }
713
714     fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
715         intravisit::NestedVisitorMap::None
716     }
717 }
718
719 fn is_mutated_static(e: &hir::Expr<'_>) -> bool {
720     use hir::ExprKind::{Field, Index, Path};
721
722     match e.kind {
723         Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)),
724         Path(_) => true,
725         Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(inner),
726         _ => false,
727     }
728 }
729
730 fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool {
731     let mut v = StaticMutVisitor {
732         cx,
733         mutates_static: false,
734     };
735     intravisit::walk_expr(&mut v, &body.value);
736     v.mutates_static
737 }