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