]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/ptr.rs
Merge commit 'd7b5cbf065b88830ca519adcb73fad4c0d24b1c7' into clippyup
[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, 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_and_sugg(
170                     cx,
171                     PTR_ARG,
172                     arg.span,
173                     &arg.build_msg(),
174                     "change this to",
175                     format!("{}{}", arg.ref_prefix, arg.deref_ty.display(cx)),
176                     Applicability::Unspecified,
177                 );
178             }
179         }
180     }
181
182     fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
183         let hir = cx.tcx.hir();
184         let mut parents = hir.parent_iter(body.value.hir_id);
185         let (item_id, sig, is_trait_item) = match parents.next() {
186             Some((_, Node::Item(i))) => {
187                 if let ItemKind::Fn(sig, ..) = &i.kind {
188                     (i.def_id, sig, false)
189                 } else {
190                     return;
191                 }
192             },
193             Some((_, Node::ImplItem(i))) => {
194                 if !matches!(parents.next(),
195                     Some((_, Node::Item(i))) if matches!(&i.kind, ItemKind::Impl(i) if i.of_trait.is_none())
196                 ) {
197                     return;
198                 }
199                 if let ImplItemKind::Fn(sig, _) = &i.kind {
200                     (i.def_id, sig, false)
201                 } else {
202                     return;
203                 }
204             },
205             Some((_, Node::TraitItem(i))) => {
206                 if let TraitItemKind::Fn(sig, _) = &i.kind {
207                     (i.def_id, sig, true)
208                 } else {
209                     return;
210                 }
211             },
212             _ => return,
213         };
214
215         check_mut_from_ref(cx, sig, Some(body));
216         let decl = sig.decl;
217         let sig = cx.tcx.fn_sig(item_id).skip_binder();
218         let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, body.params)
219             .filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not)
220             .collect();
221         let results = check_ptr_arg_usage(cx, body, &lint_args);
222
223         for (result, args) in results.iter().zip(lint_args.iter()).filter(|(r, _)| !r.skip) {
224             span_lint_and_then(cx, PTR_ARG, args.span, &args.build_msg(), |diag| {
225                 diag.multipart_suggestion(
226                     "change this to",
227                     iter::once((args.span, format!("{}{}", args.ref_prefix, args.deref_ty.display(cx))))
228                         .chain(result.replacements.iter().map(|r| {
229                             (
230                                 r.expr_span,
231                                 format!("{}{}", snippet_opt(cx, r.self_span).unwrap(), r.replacement),
232                             )
233                         }))
234                         .collect(),
235                     Applicability::Unspecified,
236                 );
237             });
238         }
239     }
240
241     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
242         if let ExprKind::Binary(ref op, l, r) = expr.kind {
243             if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(cx, l) || is_null_path(cx, r)) {
244                 span_lint(
245                     cx,
246                     CMP_NULL,
247                     expr.span,
248                     "comparing with null is better expressed by the `.is_null()` method",
249                 );
250             }
251         } else {
252             check_invalid_ptr_usage(cx, expr);
253         }
254     }
255 }
256
257 fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
258     // (fn_path, arg_indices) - `arg_indices` are the `arg` positions where null would cause U.B.
259     const INVALID_NULL_PTR_USAGE_TABLE: [(&[&str], &[usize]); 16] = [
260         (&paths::SLICE_FROM_RAW_PARTS, &[0]),
261         (&paths::SLICE_FROM_RAW_PARTS_MUT, &[0]),
262         (&paths::PTR_COPY, &[0, 1]),
263         (&paths::PTR_COPY_NONOVERLAPPING, &[0, 1]),
264         (&paths::PTR_READ, &[0]),
265         (&paths::PTR_READ_UNALIGNED, &[0]),
266         (&paths::PTR_READ_VOLATILE, &[0]),
267         (&paths::PTR_REPLACE, &[0]),
268         (&paths::PTR_SLICE_FROM_RAW_PARTS, &[0]),
269         (&paths::PTR_SLICE_FROM_RAW_PARTS_MUT, &[0]),
270         (&paths::PTR_SWAP, &[0, 1]),
271         (&paths::PTR_SWAP_NONOVERLAPPING, &[0, 1]),
272         (&paths::PTR_WRITE, &[0]),
273         (&paths::PTR_WRITE_UNALIGNED, &[0]),
274         (&paths::PTR_WRITE_VOLATILE, &[0]),
275         (&paths::PTR_WRITE_BYTES, &[0]),
276     ];
277
278     if_chain! {
279         if let ExprKind::Call(fun, args) = expr.kind;
280         if let ExprKind::Path(ref qpath) = fun.kind;
281         if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
282         let fun_def_path = cx.get_def_path(fun_def_id).into_iter().map(Symbol::to_ident_string).collect::<Vec<_>>();
283         if let Some(&(_, arg_indices)) = INVALID_NULL_PTR_USAGE_TABLE
284             .iter()
285             .find(|&&(fn_path, _)| fn_path == fun_def_path);
286         then {
287             for &arg_idx in arg_indices {
288                 if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) {
289                     span_lint_and_sugg(
290                         cx,
291                         INVALID_NULL_PTR_USAGE,
292                         arg.span,
293                         "pointer must be non-null",
294                         "change this to",
295                         "core::ptr::NonNull::dangling().as_ptr()".to_string(),
296                         Applicability::MachineApplicable,
297                     );
298                 }
299             }
300         }
301     }
302 }
303
304 #[derive(Default)]
305 struct PtrArgResult {
306     skip: bool,
307     replacements: Vec<PtrArgReplacement>,
308 }
309
310 struct PtrArgReplacement {
311     expr_span: Span,
312     self_span: Span,
313     replacement: &'static str,
314 }
315
316 struct PtrArg<'tcx> {
317     idx: usize,
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::Underscore => 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                 if !is_lint_allowed(cx, PTR_ARG, hir_ty.hir_id);
423                 if params.get(i).map_or(true, |p| !is_lint_allowed(cx, PTR_ARG, p.hir_id));
424
425                 then {
426                     let (method_renames, deref_ty) = match cx.tcx.get_diagnostic_name(adt.did()) {
427                         Some(sym::Vec) => (
428                             [("clone", ".to_owned()")].as_slice(),
429                             DerefTy::Slice(
430                                 name.args
431                                     .and_then(|args| args.args.first())
432                                     .and_then(|arg| if let GenericArg::Type(ty) = arg {
433                                         Some(ty.span)
434                                     } else {
435                                         None
436                                     }),
437                                 substs.type_at(0),
438                             ),
439                         ),
440                         Some(sym::String) => (
441                             [("clone", ".to_owned()"), ("as_str", "")].as_slice(),
442                             DerefTy::Str,
443                         ),
444                         Some(sym::PathBuf) => (
445                             [("clone", ".to_path_buf()"), ("as_path", "")].as_slice(),
446                             DerefTy::Path,
447                         ),
448                         Some(sym::Cow) if mutability == Mutability::Not => {
449                             let ty_name = name.args
450                                 .and_then(|args| {
451                                     args.args.iter().find_map(|a| match a {
452                                         GenericArg::Type(x) => Some(x),
453                                         _ => None,
454                                     })
455                                 })
456                                 .and_then(|arg| snippet_opt(cx, arg.span))
457                                 .unwrap_or_else(|| substs.type_at(1).to_string());
458                             span_lint_and_sugg(
459                                 cx,
460                                 PTR_ARG,
461                                 hir_ty.span,
462                                 "using a reference to `Cow` is not recommended",
463                                 "change this to",
464                                 format!("&{}{}", mutability.prefix_str(), ty_name),
465                                 Applicability::Unspecified,
466                             );
467                             return None;
468                         },
469                         _ => return None,
470                     };
471                     return Some(PtrArg {
472                         idx: i,
473                         span: hir_ty.span,
474                         ty_did: adt.did(),
475                         ty_name: name.ident.name,
476                         method_renames,
477                         ref_prefix: RefPrefix {
478                             lt: lt.name,
479                             mutability,
480                         },
481                         deref_ty,
482                     });
483                 }
484             }
485             None
486         })
487 }
488
489 fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&'tcx Body<'_>>) {
490     if let FnRetTy::Return(ty) = sig.decl.output
491         && let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty)
492     {
493         let args: Option<Vec<_>> = sig
494             .decl
495             .inputs
496             .iter()
497             .filter_map(get_rptr_lm)
498             .filter(|&(lt, _, _)| lt.name == out.name)
499             .map(|(_, mutability, span)| (mutability == Mutability::Not).then(|| span))
500             .collect();
501         if let Some(args) = args
502             && !args.is_empty()
503             && body.map_or(true, |body| {
504                 sig.header.unsafety == Unsafety::Unsafe || contains_unsafe_block(cx, &body.value)
505             })
506         {
507             span_lint_and_then(
508                 cx,
509                 MUT_FROM_REF,
510                 ty.span,
511                 "mutable borrow from immutable input(s)",
512                 |diag| {
513                     let ms = MultiSpan::from_spans(args);
514                     diag.span_note(ms, "immutable borrow here");
515                 },
516             );
517         }
518     }
519 }
520
521 #[expect(clippy::too_many_lines)]
522 fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: &[PtrArg<'tcx>]) -> Vec<PtrArgResult> {
523     struct V<'cx, 'tcx> {
524         cx: &'cx LateContext<'tcx>,
525         /// Map from a local id to which argument it came from (index into `Self::args` and
526         /// `Self::results`)
527         bindings: HirIdMap<usize>,
528         /// The arguments being checked.
529         args: &'cx [PtrArg<'tcx>],
530         /// The results for each argument (len should match args.len)
531         results: Vec<PtrArgResult>,
532         /// The number of arguments which can't be linted. Used to return early.
533         skip_count: usize,
534     }
535     impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
536         type NestedFilter = nested_filter::OnlyBodies;
537         fn nested_visit_map(&mut self) -> Self::Map {
538             self.cx.tcx.hir()
539         }
540
541         fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
542
543         fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
544             if self.skip_count == self.args.len() {
545                 return;
546             }
547
548             // Check if this is local we care about
549             let args_idx = match path_to_local(e).and_then(|id| self.bindings.get(&id)) {
550                 Some(&i) => i,
551                 None => return walk_expr(self, e),
552             };
553             let args = &self.args[args_idx];
554             let result = &mut self.results[args_idx];
555
556             // Helper function to handle early returns.
557             let mut set_skip_flag = || {
558                 if !result.skip {
559                     self.skip_count += 1;
560                 }
561                 result.skip = true;
562             };
563
564             match get_expr_use_or_unification_node(self.cx.tcx, e) {
565                 Some((Node::Stmt(_), _)) => (),
566                 Some((Node::Local(l), _)) => {
567                     // Only trace simple bindings. e.g `let x = y;`
568                     if let PatKind::Binding(BindingAnnotation::Unannotated, id, _, None) = l.pat.kind {
569                         self.bindings.insert(id, args_idx);
570                     } else {
571                         set_skip_flag();
572                     }
573                 },
574                 Some((Node::Expr(e), child_id)) => match e.kind {
575                     ExprKind::Call(f, expr_args) => {
576                         let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0);
577                         if expr_sig(self.cx, f)
578                             .map(|sig| sig.input(i).skip_binder().peel_refs())
579                             .map_or(true, |ty| match *ty.kind() {
580                                 ty::Param(_) => true,
581                                 ty::Adt(def, _) => def.did() == args.ty_did,
582                                 _ => false,
583                             })
584                         {
585                             // Passed to a function taking the non-dereferenced type.
586                             set_skip_flag();
587                         }
588                     },
589                     ExprKind::MethodCall(name, expr_args @ [self_arg, ..], _) => {
590                         let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0);
591                         if i == 0 {
592                             // Check if the method can be renamed.
593                             let name = name.ident.as_str();
594                             if let Some((_, replacement)) = args.method_renames.iter().find(|&&(x, _)| x == name) {
595                                 result.replacements.push(PtrArgReplacement {
596                                     expr_span: e.span,
597                                     self_span: self_arg.span,
598                                     replacement,
599                                 });
600                                 return;
601                             }
602                         }
603
604                         let id = if let Some(x) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) {
605                             x
606                         } else {
607                             set_skip_flag();
608                             return;
609                         };
610
611                         match *self.cx.tcx.fn_sig(id).skip_binder().inputs()[i].peel_refs().kind() {
612                             ty::Param(_) => {
613                                 set_skip_flag();
614                             },
615                             // If the types match check for methods which exist on both types. e.g. `Vec::len` and
616                             // `slice::len`
617                             ty::Adt(def, _) if def.did() == args.ty_did => {
618                                 set_skip_flag();
619                             },
620                             _ => (),
621                         }
622                     },
623                     // Indexing is fine for currently supported types.
624                     ExprKind::Index(e, _) if e.hir_id == child_id => (),
625                     _ => set_skip_flag(),
626                 },
627                 _ => set_skip_flag(),
628             }
629         }
630     }
631
632     let mut skip_count = 0;
633     let mut results = args.iter().map(|_| PtrArgResult::default()).collect::<Vec<_>>();
634     let mut v = V {
635         cx,
636         bindings: args
637             .iter()
638             .enumerate()
639             .filter_map(|(i, arg)| {
640                 let param = &body.params[arg.idx];
641                 match param.pat.kind {
642                     PatKind::Binding(BindingAnnotation::Unannotated, id, _, None)
643                         if !is_lint_allowed(cx, PTR_ARG, param.hir_id) =>
644                     {
645                         Some((id, i))
646                     },
647                     _ => {
648                         skip_count += 1;
649                         results[i].skip = true;
650                         None
651                     },
652                 }
653             })
654             .collect(),
655         args,
656         results,
657         skip_count,
658     };
659     v.visit_expr(&body.value);
660     v.results
661 }
662
663 fn get_rptr_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> {
664     if let TyKind::Rptr(ref lt, ref m) = ty.kind {
665         Some((lt, m.mutbl, ty.span))
666     } else {
667         None
668     }
669 }
670
671 fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
672     if let ExprKind::Call(pathexp, []) = expr.kind {
673         path_def_id(cx, pathexp).map_or(false, |id| {
674             matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ptr_null | sym::ptr_null_mut))
675         })
676     } else {
677         false
678     }
679 }