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