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