]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/ptr.rs
4ecde8f49581ea60a6e46d6fbcf99ddb68de41de
[rust.git] / clippy_lints / src / ptr.rs
1 //! Checks for usage of  `&Vec[_]` and `&String`.
2
3 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
4 use clippy_utils::source::snippet_opt;
5 use clippy_utils::ty::expr_sig;
6 use clippy_utils::visitors::contains_unsafe_block;
7 use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, paths};
8 use if_chain::if_chain;
9 use rustc_errors::Applicability;
10 use rustc_hir::def_id::DefId;
11 use rustc_hir::hir_id::HirIdMap;
12 use rustc_hir::intravisit::{walk_expr, Visitor};
13 use rustc_hir::{
14     self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg,
15     ImplItemKind, ItemKind, Lifetime, LifetimeName, Mutability, Node, Param, ParamName, PatKind, QPath, TraitFn,
16     TraitItem, TraitItemKind, TyKind, Unsafety,
17 };
18 use rustc_lint::{LateContext, LateLintPass};
19 use rustc_middle::hir::nested_filter;
20 use rustc_middle::ty::{self, AssocItems, AssocKind, Ty};
21 use rustc_session::{declare_lint_pass, declare_tool_lint};
22 use rustc_span::source_map::Span;
23 use rustc_span::symbol::Symbol;
24 use rustc_span::{sym, MultiSpan};
25 use std::fmt;
26 use std::iter;
27
28 declare_clippy_lint! {
29     /// ### What it does
30     /// This lint checks for function arguments of type `&String`, `&Vec`,
31     /// `&PathBuf`, and `Cow<_>`. It will also suggest you replace `.clone()` calls
32     /// with the appropriate `.to_owned()`/`to_string()` calls.
33     ///
34     /// ### Why is this bad?
35     /// Requiring the argument to be of the specific size
36     /// makes the function less useful for no benefit; slices in the form of `&[T]`
37     /// or `&str` usually suffice and can be obtained from other types, too.
38     ///
39     /// ### Known problems
40     /// There may be `fn(&Vec)`-typed references pointing to your function.
41     /// If you have them, you will get a compiler error after applying this lint's
42     /// suggestions. You then have the choice to undo your changes or change the
43     /// type of the reference.
44     ///
45     /// Note that if the function is part of your public interface, there may be
46     /// other crates referencing it, of which you may not be aware. Carefully
47     /// deprecate the function before applying the lint suggestions in this case.
48     ///
49     /// ### Example
50     /// ```ignore
51     /// // Bad
52     /// fn foo(&Vec<u32>) { .. }
53     ///
54     /// // Good
55     /// fn foo(&[u32]) { .. }
56     /// ```
57     #[clippy::version = "pre 1.29.0"]
58     pub PTR_ARG,
59     style,
60     "fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively"
61 }
62
63 declare_clippy_lint! {
64     /// ### What it does
65     /// This lint checks for equality comparisons with `ptr::null`
66     ///
67     /// ### Why is this bad?
68     /// It's easier and more readable to use the inherent
69     /// `.is_null()`
70     /// method instead
71     ///
72     /// ### Example
73     /// ```ignore
74     /// // Bad
75     /// if x == ptr::null {
76     ///     ..
77     /// }
78     ///
79     /// // Good
80     /// if x.is_null() {
81     ///     ..
82     /// }
83     /// ```
84     #[clippy::version = "pre 1.29.0"]
85     pub CMP_NULL,
86     style,
87     "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead"
88 }
89
90 declare_clippy_lint! {
91     /// ### What it does
92     /// This lint checks for functions that take immutable
93     /// references and return mutable ones.
94     ///
95     /// ### Why is this bad?
96     /// This is trivially unsound, as one can create two
97     /// mutable references from the same (immutable!) source.
98     /// This [error](https://github.com/rust-lang/rust/issues/39465)
99     /// actually lead to an interim Rust release 1.15.1.
100     ///
101     /// ### Known problems
102     /// To be on the conservative side, if there's at least one
103     /// mutable reference with the output lifetime, this lint will not trigger.
104     /// In practice, this case is unlikely anyway.
105     ///
106     /// ### Example
107     /// ```ignore
108     /// fn foo(&Foo) -> &mut Bar { .. }
109     /// ```
110     #[clippy::version = "pre 1.29.0"]
111     pub MUT_FROM_REF,
112     correctness,
113     "fns that create mutable refs from immutable ref args"
114 }
115
116 declare_clippy_lint! {
117     /// ### What it does
118     /// This lint checks for invalid usages of `ptr::null`.
119     ///
120     /// ### Why is this bad?
121     /// This causes undefined behavior.
122     ///
123     /// ### Example
124     /// ```ignore
125     /// // Bad. Undefined behavior
126     /// unsafe { std::slice::from_raw_parts(ptr::null(), 0); }
127     /// ```
128     ///
129     /// ```ignore
130     /// // Good
131     /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); }
132     /// ```
133     #[clippy::version = "1.53.0"]
134     pub INVALID_NULL_PTR_USAGE,
135     correctness,
136     "invalid usage of a null pointer, suggesting `NonNull::dangling()` instead"
137 }
138
139 declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE]);
140
141 impl<'tcx> LateLintPass<'tcx> for Ptr {
142     fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
143         if let TraitItemKind::Fn(sig, trait_method) = &item.kind {
144             if matches!(trait_method, TraitFn::Provided(_)) {
145                 // Handled by check body.
146                 return;
147             }
148
149             check_mut_from_ref(cx, sig, None);
150             for arg in check_fn_args(
151                 cx,
152                 cx.tcx.fn_sig(item.def_id).skip_binder().inputs(),
153                 sig.decl.inputs,
154                 &[],
155             )
156             .filter(|arg| arg.mutability() == Mutability::Not)
157             {
158                 span_lint_and_sugg(
159                     cx,
160                     PTR_ARG,
161                     arg.span,
162                     &arg.build_msg(),
163                     "change this to",
164                     format!("{}{}", arg.ref_prefix, arg.deref_ty.display(cx)),
165                     Applicability::Unspecified,
166                 );
167             }
168         }
169     }
170
171     fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
172         let hir = cx.tcx.hir();
173         let mut parents = hir.parent_iter(body.value.hir_id);
174         let (item_id, sig, is_trait_item) = match parents.next() {
175             Some((_, Node::Item(i))) => {
176                 if let ItemKind::Fn(sig, ..) = &i.kind {
177                     (i.def_id, sig, false)
178                 } else {
179                     return;
180                 }
181             },
182             Some((_, Node::ImplItem(i))) => {
183                 if !matches!(parents.next(),
184                     Some((_, Node::Item(i))) if matches!(&i.kind, ItemKind::Impl(i) if i.of_trait.is_none())
185                 ) {
186                     return;
187                 }
188                 if let ImplItemKind::Fn(sig, _) = &i.kind {
189                     (i.def_id, sig, false)
190                 } else {
191                     return;
192                 }
193             },
194             Some((_, Node::TraitItem(i))) => {
195                 if let TraitItemKind::Fn(sig, _) = &i.kind {
196                     (i.def_id, sig, true)
197                 } else {
198                     return;
199                 }
200             },
201             _ => return,
202         };
203
204         check_mut_from_ref(cx, sig, Some(body));
205         let decl = sig.decl;
206         let sig = cx.tcx.fn_sig(item_id).skip_binder();
207         let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, body.params)
208             .filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not)
209             .collect();
210         let results = check_ptr_arg_usage(cx, body, &lint_args);
211
212         for (result, args) in results.iter().zip(lint_args.iter()).filter(|(r, _)| !r.skip) {
213             span_lint_and_then(cx, PTR_ARG, args.span, &args.build_msg(), |diag| {
214                 diag.multipart_suggestion(
215                     "change this to",
216                     iter::once((args.span, format!("{}{}", args.ref_prefix, args.deref_ty.display(cx))))
217                         .chain(result.replacements.iter().map(|r| {
218                             (
219                                 r.expr_span,
220                                 format!("{}{}", snippet_opt(cx, r.self_span).unwrap(), r.replacement),
221                             )
222                         }))
223                         .collect(),
224                     Applicability::Unspecified,
225                 );
226             });
227         }
228     }
229
230     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
231         if let ExprKind::Binary(ref op, l, r) = expr.kind {
232             if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(cx, l) || is_null_path(cx, r)) {
233                 span_lint(
234                     cx,
235                     CMP_NULL,
236                     expr.span,
237                     "comparing with null is better expressed by the `.is_null()` method",
238                 );
239             }
240         } else {
241             check_invalid_ptr_usage(cx, expr);
242         }
243     }
244 }
245
246 fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
247     // (fn_path, arg_indices) - `arg_indices` are the `arg` positions where null would cause U.B.
248     const INVALID_NULL_PTR_USAGE_TABLE: [(&[&str], &[usize]); 16] = [
249         (&paths::SLICE_FROM_RAW_PARTS, &[0]),
250         (&paths::SLICE_FROM_RAW_PARTS_MUT, &[0]),
251         (&paths::PTR_COPY, &[0, 1]),
252         (&paths::PTR_COPY_NONOVERLAPPING, &[0, 1]),
253         (&paths::PTR_READ, &[0]),
254         (&paths::PTR_READ_UNALIGNED, &[0]),
255         (&paths::PTR_READ_VOLATILE, &[0]),
256         (&paths::PTR_REPLACE, &[0]),
257         (&paths::PTR_SLICE_FROM_RAW_PARTS, &[0]),
258         (&paths::PTR_SLICE_FROM_RAW_PARTS_MUT, &[0]),
259         (&paths::PTR_SWAP, &[0, 1]),
260         (&paths::PTR_SWAP_NONOVERLAPPING, &[0, 1]),
261         (&paths::PTR_WRITE, &[0]),
262         (&paths::PTR_WRITE_UNALIGNED, &[0]),
263         (&paths::PTR_WRITE_VOLATILE, &[0]),
264         (&paths::PTR_WRITE_BYTES, &[0]),
265     ];
266
267     if_chain! {
268         if let ExprKind::Call(fun, args) = expr.kind;
269         if let ExprKind::Path(ref qpath) = fun.kind;
270         if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
271         let fun_def_path = cx.get_def_path(fun_def_id).into_iter().map(Symbol::to_ident_string).collect::<Vec<_>>();
272         if let Some(&(_, arg_indices)) = INVALID_NULL_PTR_USAGE_TABLE
273             .iter()
274             .find(|&&(fn_path, _)| fn_path == fun_def_path);
275         then {
276             for &arg_idx in arg_indices {
277                 if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) {
278                     span_lint_and_sugg(
279                         cx,
280                         INVALID_NULL_PTR_USAGE,
281                         arg.span,
282                         "pointer must be non-null",
283                         "change this to",
284                         "core::ptr::NonNull::dangling().as_ptr()".to_string(),
285                         Applicability::MachineApplicable,
286                     );
287                 }
288             }
289         }
290     }
291 }
292
293 #[derive(Default)]
294 struct PtrArgResult {
295     skip: bool,
296     replacements: Vec<PtrArgReplacement>,
297 }
298
299 struct PtrArgReplacement {
300     expr_span: Span,
301     self_span: Span,
302     replacement: &'static str,
303 }
304
305 struct PtrArg<'tcx> {
306     idx: usize,
307     span: Span,
308     ty_did: DefId,
309     ty_name: Symbol,
310     method_renames: &'static [(&'static str, &'static str)],
311     ref_prefix: RefPrefix,
312     deref_ty: DerefTy<'tcx>,
313     deref_assoc_items: Option<(DefId, &'tcx AssocItems<'tcx>)>,
314 }
315 impl PtrArg<'_> {
316     fn build_msg(&self) -> String {
317         format!(
318             "writing `&{}{}` instead of `&{}{}` involves a new object where a slice will do",
319             self.ref_prefix.mutability.prefix_str(),
320             self.ty_name,
321             self.ref_prefix.mutability.prefix_str(),
322             self.deref_ty.argless_str(),
323         )
324     }
325
326     fn mutability(&self) -> Mutability {
327         self.ref_prefix.mutability
328     }
329 }
330
331 struct RefPrefix {
332     lt: LifetimeName,
333     mutability: Mutability,
334 }
335 impl fmt::Display for RefPrefix {
336     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
337         use fmt::Write;
338         f.write_char('&')?;
339         match self.lt {
340             LifetimeName::Param(ParamName::Plain(name)) => {
341                 name.fmt(f)?;
342                 f.write_char(' ')?;
343             },
344             LifetimeName::Underscore => f.write_str("'_ ")?,
345             LifetimeName::Static => f.write_str("'static ")?,
346             _ => (),
347         }
348         f.write_str(self.mutability.prefix_str())
349     }
350 }
351
352 struct DerefTyDisplay<'a, 'tcx>(&'a LateContext<'tcx>, &'a DerefTy<'tcx>);
353 impl fmt::Display for DerefTyDisplay<'_, '_> {
354     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
355         use std::fmt::Write;
356         match self.1 {
357             DerefTy::Str => f.write_str("str"),
358             DerefTy::Path => f.write_str("Path"),
359             DerefTy::Slice(hir_ty, ty) => {
360                 f.write_char('[')?;
361                 match hir_ty.and_then(|s| snippet_opt(self.0, s)) {
362                     Some(s) => f.write_str(&s)?,
363                     None => ty.fmt(f)?,
364                 }
365                 f.write_char(']')
366             },
367         }
368     }
369 }
370
371 enum DerefTy<'tcx> {
372     Str,
373     Path,
374     Slice(Option<Span>, Ty<'tcx>),
375 }
376 impl<'tcx> DerefTy<'tcx> {
377     fn argless_str(&self) -> &'static str {
378         match *self {
379             Self::Str => "str",
380             Self::Path => "Path",
381             Self::Slice(..) => "[_]",
382         }
383     }
384
385     fn display<'a>(&'a self, cx: &'a LateContext<'tcx>) -> DerefTyDisplay<'a, 'tcx> {
386         DerefTyDisplay(cx, self)
387     }
388 }
389
390 fn check_fn_args<'cx, 'tcx: 'cx>(
391     cx: &'cx LateContext<'tcx>,
392     tys: &'tcx [Ty<'_>],
393     hir_tys: &'tcx [hir::Ty<'_>],
394     params: &'tcx [Param<'_>],
395 ) -> impl Iterator<Item = PtrArg<'tcx>> + 'cx {
396     tys.iter()
397         .zip(hir_tys.iter())
398         .enumerate()
399         .filter_map(|(i, (ty, hir_ty))| {
400             if_chain! {
401                 if let ty::Ref(_, ty, mutability) = *ty.kind();
402                 if let ty::Adt(adt, substs) = *ty.kind();
403
404                 if let TyKind::Rptr(lt, ref ty) = hir_ty.kind;
405                 if let TyKind::Path(QPath::Resolved(None, path)) = ty.ty.kind;
406
407                 // Check that the name as typed matches the actual name of the type.
408                 // e.g. `fn foo(_: &Foo)` shouldn't trigger the lint when `Foo` is an alias for `Vec`
409                 if let [.., name] = path.segments;
410                 if cx.tcx.item_name(adt.did()) == name.ident.name;
411
412                 if !is_lint_allowed(cx, PTR_ARG, hir_ty.hir_id);
413                 if params.get(i).map_or(true, |p| !is_lint_allowed(cx, PTR_ARG, p.hir_id));
414
415                 then {
416                     let (method_renames, deref_ty, deref_impl_id) = match cx.tcx.get_diagnostic_name(adt.did()) {
417                         Some(sym::Vec) => (
418                             [("clone", ".to_owned()")].as_slice(),
419                             DerefTy::Slice(
420                                 name.args
421                                     .and_then(|args| args.args.first())
422                                     .and_then(|arg| if let GenericArg::Type(ty) = arg {
423                                         Some(ty.span)
424                                     } else {
425                                         None
426                                     }),
427                                 substs.type_at(0),
428                             ),
429                             cx.tcx.lang_items().slice_impl()
430                         ),
431                         Some(sym::String) => (
432                             [("clone", ".to_owned()"), ("as_str", "")].as_slice(),
433                             DerefTy::Str,
434                             cx.tcx.lang_items().str_impl()
435                         ),
436                         Some(sym::PathBuf) => (
437                             [("clone", ".to_path_buf()"), ("as_path", "")].as_slice(),
438                             DerefTy::Path,
439                             None,
440                         ),
441                         Some(sym::Cow) if mutability == Mutability::Not => {
442                             let ty_name = name.args
443                                 .and_then(|args| {
444                                     args.args.iter().find_map(|a| match a {
445                                         GenericArg::Type(x) => Some(x),
446                                         _ => None,
447                                     })
448                                 })
449                                 .and_then(|arg| snippet_opt(cx, arg.span))
450                                 .unwrap_or_else(|| substs.type_at(1).to_string());
451                             span_lint_and_sugg(
452                                 cx,
453                                 PTR_ARG,
454                                 hir_ty.span,
455                                 "using a reference to `Cow` is not recommended",
456                                 "change this to",
457                                 format!("&{}{}", mutability.prefix_str(), ty_name),
458                                 Applicability::Unspecified,
459                             );
460                             return None;
461                         },
462                         _ => return None,
463                     };
464                     return Some(PtrArg {
465                         idx: i,
466                         span: hir_ty.span,
467                         ty_did: adt.did(),
468                         ty_name: name.ident.name,
469                         method_renames,
470                         ref_prefix: RefPrefix {
471                             lt: lt.name,
472                             mutability,
473                         },
474                         deref_ty,
475                         deref_assoc_items: deref_impl_id.map(|id| (id, cx.tcx.associated_items(id))),
476                     });
477                 }
478             }
479             None
480         })
481 }
482
483 fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&'tcx Body<'_>>) {
484     if let FnRetTy::Return(ty) = sig.decl.output
485         && let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty)
486     {
487         let args: Option<Vec<_>> = sig
488             .decl
489             .inputs
490             .iter()
491             .filter_map(get_rptr_lm)
492             .filter(|&(lt, _, _)| lt.name == out.name)
493             .map(|(_, mutability, span)| (mutability == Mutability::Not).then(|| span))
494             .collect();
495         if let Some(args) = args
496             && !args.is_empty()
497             && body.map_or(true, |body| {
498                 sig.header.unsafety == Unsafety::Unsafe || contains_unsafe_block(cx, &body.value)
499             })
500         {
501             span_lint_and_then(
502                 cx,
503                 MUT_FROM_REF,
504                 ty.span,
505                 "mutable borrow from immutable input(s)",
506                 |diag| {
507                     let ms = MultiSpan::from_spans(args);
508                     diag.span_note(ms, "immutable borrow here");
509                 },
510             );
511         }
512     }
513 }
514
515 #[allow(clippy::too_many_lines)]
516 fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: &[PtrArg<'tcx>]) -> Vec<PtrArgResult> {
517     struct V<'cx, 'tcx> {
518         cx: &'cx LateContext<'tcx>,
519         /// Map from a local id to which argument it came from (index into `Self::args` and
520         /// `Self::results`)
521         bindings: HirIdMap<usize>,
522         /// The arguments being checked.
523         args: &'cx [PtrArg<'tcx>],
524         /// The results for each argument (len should match args.len)
525         results: Vec<PtrArgResult>,
526         /// The number of arguments which can't be linted. Used to return early.
527         skip_count: usize,
528     }
529     impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
530         type NestedFilter = nested_filter::OnlyBodies;
531         fn nested_visit_map(&mut self) -> Self::Map {
532             self.cx.tcx.hir()
533         }
534
535         fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
536
537         fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
538             if self.skip_count == self.args.len() {
539                 return;
540             }
541
542             // Check if this is local we care about
543             let args_idx = match path_to_local(e).and_then(|id| self.bindings.get(&id)) {
544                 Some(&i) => i,
545                 None => return walk_expr(self, e),
546             };
547             let args = &self.args[args_idx];
548             let result = &mut self.results[args_idx];
549
550             // Helper function to handle early returns.
551             let mut set_skip_flag = || {
552                 if !result.skip {
553                     self.skip_count += 1;
554                 }
555                 result.skip = true;
556             };
557
558             match get_expr_use_or_unification_node(self.cx.tcx, e) {
559                 Some((Node::Stmt(_), _)) => (),
560                 Some((Node::Local(l), _)) => {
561                     // Only trace simple bindings. e.g `let x = y;`
562                     if let PatKind::Binding(BindingAnnotation::Unannotated, id, _, None) = l.pat.kind {
563                         self.bindings.insert(id, args_idx);
564                     } else {
565                         set_skip_flag();
566                     }
567                 },
568                 Some((Node::Expr(e), child_id)) => match e.kind {
569                     ExprKind::Call(f, expr_args) => {
570                         let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0);
571                         if expr_sig(self.cx, f)
572                             .map(|sig| sig.input(i).skip_binder().peel_refs())
573                             .map_or(true, |ty| match *ty.kind() {
574                                 ty::Param(_) => true,
575                                 ty::Adt(def, _) => def.did() == args.ty_did,
576                                 _ => false,
577                             })
578                         {
579                             // Passed to a function taking the non-dereferenced type.
580                             set_skip_flag();
581                         }
582                     },
583                     ExprKind::MethodCall(name, expr_args @ [self_arg, ..], _) => {
584                         let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0);
585                         if i == 0 {
586                             // Check if the method can be renamed.
587                             let name = name.ident.as_str();
588                             if let Some((_, replacement)) = args.method_renames.iter().find(|&&(x, _)| x == name) {
589                                 result.replacements.push(PtrArgReplacement {
590                                     expr_span: e.span,
591                                     self_span: self_arg.span,
592                                     replacement,
593                                 });
594                                 return;
595                             }
596                         }
597
598                         let id = if let Some(x) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) {
599                             x
600                         } else {
601                             set_skip_flag();
602                             return;
603                         };
604
605                         match *self.cx.tcx.fn_sig(id).skip_binder().inputs()[i].peel_refs().kind() {
606                             ty::Param(_) => {
607                                 set_skip_flag();
608                             },
609                             // If the types match check for methods which exist on both types. e.g. `Vec::len` and
610                             // `slice::len`
611                             ty::Adt(def, _)
612                                 if def.did() == args.ty_did
613                                     && (i != 0
614                                         || self.cx.tcx.trait_of_item(id).is_some()
615                                         || !args.deref_assoc_items.map_or(false, |(id, items)| {
616                                             items
617                                                 .find_by_name_and_kind(self.cx.tcx, name.ident, AssocKind::Fn, id)
618                                                 .is_some()
619                                         })) =>
620                             {
621                                 set_skip_flag();
622                             },
623                             _ => (),
624                         }
625                     },
626                     // Indexing is fine for currently supported types.
627                     ExprKind::Index(e, _) if e.hir_id == child_id => (),
628                     _ => set_skip_flag(),
629                 },
630                 _ => set_skip_flag(),
631             }
632         }
633     }
634
635     let mut skip_count = 0;
636     let mut results = args.iter().map(|_| PtrArgResult::default()).collect::<Vec<_>>();
637     let mut v = V {
638         cx,
639         bindings: args
640             .iter()
641             .enumerate()
642             .filter_map(|(i, arg)| {
643                 let param = &body.params[arg.idx];
644                 match param.pat.kind {
645                     PatKind::Binding(BindingAnnotation::Unannotated, id, _, None)
646                         if !is_lint_allowed(cx, PTR_ARG, param.hir_id) =>
647                     {
648                         Some((id, i))
649                     },
650                     _ => {
651                         skip_count += 1;
652                         results[i].skip = true;
653                         None
654                     },
655                 }
656             })
657             .collect(),
658         args,
659         results,
660         skip_count,
661     };
662     v.visit_expr(&body.value);
663     v.results
664 }
665
666 fn get_rptr_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> {
667     if let TyKind::Rptr(ref lt, ref m) = ty.kind {
668         Some((lt, m.mutbl, ty.span))
669     } else {
670         None
671     }
672 }
673
674 fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
675     if let ExprKind::Call(pathexp, []) = expr.kind {
676         path_def_id(cx, pathexp).map_or(false, |id| {
677             matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ptr_null | sym::ptr_null_mut))
678         })
679     } else {
680         false
681     }
682 }