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