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