]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/ptr.rs
bump version to 1.79.0+dfsg1-1~bpo12+pve2
[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;
ed00b5ec 7use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local};
fe692bf9 8use hir::LifetimeName;
04454e1e 9use rustc_errors::{Applicability, MultiSpan};
5099ac24 10use rustc_hir::def_id::DefId;
e8be2606 11use rustc_hir::hir_id::{HirId, HirIdMap};
5099ac24 12use rustc_hir::intravisit::{walk_expr, Visitor};
f20569fa 13use rustc_hir::{
e8be2606 14 self as hir, AnonConst, BinOpKind, BindingMode, 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;
fe692bf9 22use rustc_middle::ty::{self, Binder, ClauseKind, ExistentialPredicate, List, PredicateKind, Ty};
4b012472 23use rustc_session::declare_lint_pass;
cdc7bbd5 24use rustc_span::symbol::Symbol;
4b012472 25use rustc_span::{sym, Span};
add651ee 26use rustc_target::spec::abi::Abi;
2b03887a
FG
27use rustc_trait_selection::infer::InferCtxtExt as _;
28use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
add651ee 29use std::{fmt, iter};
f20569fa 30
4b012472
FG
31use crate::vec::is_allowed_vec_method;
32
f20569fa 33declare_clippy_lint! {
94222f64 34 /// ### What it does
5099ac24
FG
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.
f20569fa 38 ///
94222f64
XL
39 /// ### Why is this bad?
40 /// Requiring the argument to be of the specific size
f20569fa
XL
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 ///
94222f64 44 /// ### Known problems
5099ac24 45 /// There may be `fn(&Vec)`-typed references pointing to your function.
f20569fa
XL
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 ///
94222f64 54 /// ### Example
f20569fa 55 /// ```ignore
f20569fa 56 /// fn foo(&Vec<u32>) { .. }
923072b8 57 /// ```
f20569fa 58 ///
923072b8
FG
59 /// Use instead:
60 /// ```ignore
f20569fa
XL
61 /// fn foo(&[u32]) { .. }
62 /// ```
a2a8927a 63 #[clippy::version = "pre 1.29.0"]
f20569fa
XL
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! {
94222f64
XL
70 /// ### What it does
71 /// This lint checks for equality comparisons with `ptr::null`
f20569fa 72 ///
94222f64
XL
73 /// ### Why is this bad?
74 /// It's easier and more readable to use the inherent
f20569fa
XL
75 /// `.is_null()`
76 /// method instead
77 ///
94222f64 78 /// ### Example
923072b8
FG
79 /// ```rust,ignore
80 /// use std::ptr;
81 ///
f20569fa 82 /// if x == ptr::null {
923072b8 83 /// // ..
f20569fa 84 /// }
923072b8 85 /// ```
f20569fa 86 ///
923072b8
FG
87 /// Use instead:
88 /// ```rust,ignore
f20569fa 89 /// if x.is_null() {
923072b8 90 /// // ..
f20569fa
XL
91 /// }
92 /// ```
a2a8927a 93 #[clippy::version = "pre 1.29.0"]
f20569fa
XL
94 pub CMP_NULL,
95 style,
cdc7bbd5 96 "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead"
f20569fa
XL
97}
98
99declare_clippy_lint! {
94222f64 100 /// ### What it does
04454e1e
FG
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.
f20569fa 107 ///
94222f64 108 /// ### Why is this bad?
04454e1e
FG
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.
f20569fa 115 ///
94222f64 116 /// ### Known problems
04454e1e
FG
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.
f20569fa 121 ///
94222f64 122 /// ### Example
f20569fa
XL
123 /// ```ignore
124 /// fn foo(&Foo) -> &mut Bar { .. }
125 /// ```
a2a8927a 126 #[clippy::version = "pre 1.29.0"]
f20569fa
XL
127 pub MUT_FROM_REF,
128 correctness,
129 "fns that create mutable refs from immutable ref args"
130}
131
cdc7bbd5 132declare_clippy_lint! {
94222f64
XL
133 /// ### What it does
134 /// This lint checks for invalid usages of `ptr::null`.
cdc7bbd5 135 ///
94222f64
XL
136 /// ### Why is this bad?
137 /// This causes undefined behavior.
cdc7bbd5 138 ///
94222f64 139 /// ### Example
cdc7bbd5 140 /// ```ignore
923072b8 141 /// // Undefined behavior
cdc7bbd5
XL
142 /// unsafe { std::slice::from_raw_parts(ptr::null(), 0); }
143 /// ```
144 ///
923072b8 145 /// Use instead:
3c0e092e 146 /// ```ignore
cdc7bbd5
XL
147 /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); }
148 /// ```
a2a8927a 149 #[clippy::version = "1.53.0"]
cdc7bbd5
XL
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]);
f20569fa
XL
156
157impl<'tcx> LateLintPass<'tcx> for Ptr {
5099ac24
FG
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 }
f20569fa 164
04454e1e 165 check_mut_from_ref(cx, sig, None);
add651ee
FG
166
167 if !matches!(sig.header.abi, Abi::Rust) {
168 // Ignore `extern` functions with non-Rust calling conventions
169 return;
170 }
171
5099ac24
FG
172 for arg in check_fn_args(
173 cx,
781aab86 174 cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder(),
5099ac24
FG
175 sig.decl.inputs,
176 &[],
177 )
178 .filter(|arg| arg.mutability() == Mutability::Not)
179 {
e8be2606 180 span_lint_hir_and_then(cx, PTR_ARG, arg.emission_id, arg.span, arg.build_msg(), |diag| {
064997fb
FG
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 });
f20569fa 188 }
f20569fa
XL
189 }
190 }
191
5099ac24
FG
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);
04454e1e 195 let (item_id, sig, is_trait_item) = match parents.next() {
5099ac24
FG
196 Some((_, Node::Item(i))) => {
197 if let ItemKind::Fn(sig, ..) = &i.kind {
2b03887a 198 (i.owner_id, sig, false)
5099ac24
FG
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 {
2b03887a 210 (i.owner_id, sig, false)
5099ac24
FG
211 } else {
212 return;
213 }
214 },
215 Some((_, Node::TraitItem(i))) => {
216 if let TraitItemKind::Fn(sig, _) = &i.kind {
2b03887a 217 (i.owner_id, sig, true)
5099ac24
FG
218 } else {
219 return;
220 }
221 },
222 _ => return,
223 };
224
04454e1e 225 check_mut_from_ref(cx, sig, Some(body));
add651ee
FG
226
227 if !matches!(sig.header.abi, Abi::Rust) {
228 // Ignore `extern` functions with non-Rust calling conventions
229 return;
230 }
231
04454e1e 232 let decl = sig.decl;
add651ee 233 let sig = cx.tcx.fn_sig(item_id).instantiate_identity().skip_binder();
781aab86 234 let lint_args: Vec<_> = check_fn_args(cx, sig, decl.inputs, body.params)
5099ac24
FG
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) {
e8be2606 240 span_lint_hir_and_then(cx, PTR_ARG, args.emission_id, args.span, args.build_msg(), |diag| {
5099ac24
FG
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 });
f20569fa
XL
254 }
255 }
256
257 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
cdc7bbd5
XL
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)) {
f20569fa
XL
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 }
cdc7bbd5
XL
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<'_>) {
ed00b5ec
FG
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 };
cdc7bbd5 296
ed00b5ec
FG
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 );
cdc7bbd5 308 }
f20569fa
XL
309 }
310 }
311}
312
5099ac24
FG
313#[derive(Default)]
314struct PtrArgResult {
315 skip: bool,
316 replacements: Vec<PtrArgReplacement>,
317}
f20569fa 318
5099ac24
FG
319struct PtrArgReplacement {
320 expr_span: Span,
321 self_span: Span,
322 replacement: &'static str,
323}
3c0e092e 324
5099ac24
FG
325struct PtrArg<'tcx> {
326 idx: usize,
e8be2606 327 emission_id: HirId,
5099ac24
FG
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>,
5099ac24
FG
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 {
487cf647 352 lt: Lifetime,
5099ac24
FG
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('&')?;
487cf647
FG
359 if !self.lt.is_anonymous() {
360 self.lt.ident.fmt(f)?;
361 f.write_char(' ')?;
5099ac24
FG
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)?,
f20569fa 379 }
5099ac24 380 f.write_char(']')
3c0e092e 381 },
5099ac24
FG
382 }
383 }
384}
385
386enum DerefTy<'tcx> {
387 Str,
388 Path,
389 Slice(Option<Span>, Ty<'tcx>),
390}
391impl<'tcx> DerefTy<'tcx> {
2b03887a
FG
392 fn ty(&self, cx: &LateContext<'tcx>) -> Ty<'tcx> {
393 match *self {
394 Self::Str => cx.tcx.types.str_,
add651ee
FG
395 Self::Path => Ty::new_adt(
396 cx.tcx,
2b03887a
FG
397 cx.tcx.adt_def(cx.tcx.get_diagnostic_item(sym::Path).unwrap()),
398 List::empty(),
399 ),
add651ee 400 Self::Slice(_, ty) => Ty::new_slice(cx.tcx, ty),
2b03887a
FG
401 }
402 }
403
5099ac24
FG
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>,
781aab86 419 fn_sig: ty::FnSig<'tcx>,
923072b8
FG
420 hir_tys: &'tcx [hir::Ty<'tcx>],
421 params: &'tcx [Param<'tcx>],
5099ac24 422) -> impl Iterator<Item = PtrArg<'tcx>> + 'cx {
781aab86
FG
423 fn_sig
424 .inputs()
425 .iter()
5099ac24
FG
426 .zip(hir_tys.iter())
427 .enumerate()
fe692bf9
FG
428 .filter_map(move |(i, (ty, hir_ty))| {
429 if let ty::Ref(_, ty, mutability) = *ty.kind()
add651ee 430 && let ty::Adt(adt, args) = *ty.kind()
fe692bf9
FG
431 && let TyKind::Ref(lt, ref ty) = hir_ty.kind
432 && let TyKind::Path(QPath::Resolved(None, path)) = ty.ty.kind
5099ac24
FG
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`
fe692bf9
FG
435 && let [.., name] = path.segments
436 && cx.tcx.item_name(adt.did()) == name.ident.name
437 {
ed00b5ec
FG
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 {
fe692bf9 447 None
064997fb 448 }
ed00b5ec
FG
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() {
4b012472
FG
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(),
ed00b5ec
FG
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 }
fe692bf9 492
ed00b5ec 493 let ty_name = snippet_opt(cx, ty.span()).unwrap_or_else(|| args.type_at(1).to_string());
fe692bf9 494
ed00b5ec
FG
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 });
5099ac24
FG
525 }
526 None
527 })
528}
f20569fa 529
04454e1e
FG
530fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&'tcx Body<'_>>) {
531 if let FnRetTy::Return(ty) = sig.decl.output
9c376795 532 && let Some((out, Mutability::Mut, _)) = get_ref_lm(ty)
04454e1e 533 {
9ffffee4 534 let out_region = cx.tcx.named_bound_var(out.hir_id);
04454e1e
FG
535 let args: Option<Vec<_>> = sig
536 .decl
537 .inputs
538 .iter()
9c376795 539 .filter_map(get_ref_lm)
9ffffee4 540 .filter(|&(lt, _, _)| cx.tcx.named_bound_var(lt.hir_id) == out_region)
064997fb 541 .map(|(_, mutability, span)| (mutability == Mutability::Not).then_some(span))
04454e1e
FG
542 .collect();
543 if let Some(args) = args
544 && !args.is_empty()
545 && body.map_or(true, |body| {
f2b60f7d 546 sig.header.unsafety == Unsafety::Unsafe || contains_unsafe_block(cx, body.value)
04454e1e
FG
547 })
548 {
f20569fa
XL
549 span_lint_and_then(
550 cx,
551 MUT_FROM_REF,
552 ty.span,
553 "mutable borrow from immutable input(s)",
554 |diag| {
04454e1e 555 let ms = MultiSpan::from_spans(args);
f20569fa
XL
556 diag.span_note(ms, "immutable borrow here");
557 },
558 );
559 }
560 }
561}
562
923072b8 563#[expect(clippy::too_many_lines)]
5099ac24
FG
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
2b03887a
FG
591 let Some(&args_idx) = path_to_local(e).and_then(|id| self.bindings.get(&id)) else {
592 return walk_expr(self, e);
5099ac24
FG
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 = || {
5e7ed085 599 if !result.skip {
5099ac24
FG
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(_), _)) => (),
e8be2606 607 Some((Node::LetStmt(l), _)) => {
5099ac24 608 // Only trace simple bindings. e.g `let x = y;`
e8be2606 609 if let PatKind::Binding(BindingMode::NONE, id, _, None) = l.pat.kind {
5099ac24
FG
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);
064997fb
FG
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() {
2b03887a 620 ty::Dynamic(preds, _, _) => !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds),
5099ac24 621 ty::Param(_) => true,
5e7ed085 622 ty::Adt(def, _) => def.did() == args.ty_did,
5099ac24 623 _ => false,
064997fb
FG
624 }
625 }) {
5099ac24
FG
626 // Passed to a function taking the non-dereferenced type.
627 set_skip_flag();
628 }
629 },
f2b60f7d 630 ExprKind::MethodCall(name, self_arg, expr_args, _) => {
e8be2606 631 let i = iter::once(self_arg)
f2b60f7d
FG
632 .chain(expr_args.iter())
633 .position(|arg| arg.hir_id == child_id)
634 .unwrap_or(0);
5099ac24
FG
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
2b03887a 648 let Some(id) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) else {
5099ac24
FG
649 set_skip_flag();
650 return;
651 };
652
add651ee 653 match *self.cx.tcx.fn_sig(id).instantiate_identity().skip_binder().inputs()[i]
9ffffee4
FG
654 .peel_refs()
655 .kind()
656 {
2b03887a
FG
657 ty::Dynamic(preds, _, _) if !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds) => {
658 set_skip_flag();
659 },
5099ac24
FG
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`
4b012472 665 ty::Adt(def, _) if def.did() == args.ty_did && !is_allowed_vec_method(self.cx, e) => {
5099ac24
FG
666 set_skip_flag();
667 },
668 _ => (),
669 }
670 },
671 // Indexing is fine for currently supported types.
add651ee 672 ExprKind::Index(e, _, _) if e.hir_id == child_id => (),
5099ac24
FG
673 _ => set_skip_flag(),
674 },
675 _ => set_skip_flag(),
676 }
f20569fa
XL
677 }
678 }
5099ac24
FG
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 {
e8be2606 690 PatKind::Binding(BindingMode::NONE, id, _, None)
5099ac24
FG
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 };
f2b60f7d 707 v.visit_expr(body.value);
5099ac24 708 v.results
f20569fa
XL
709}
710
2b03887a
FG
711fn matches_preds<'tcx>(
712 cx: &LateContext<'tcx>,
713 ty: Ty<'tcx>,
487cf647 714 preds: &'tcx [ty::PolyExistentialPredicate<'tcx>],
2b03887a
FG
715) -> bool {
716 let infcx = cx.tcx.infer_ctxt().build();
4b012472
FG
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 })
2b03887a
FG
736}
737
9c376795
FG
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 {
f20569fa
XL
740 Some((lt, m.mutbl, ty.span))
741 } else {
742 None
743 }
744}
745
cdc7bbd5
XL
746fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
747 if let ExprKind::Call(pathexp, []) = expr.kind {
5099ac24
FG
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))
cdc7bbd5
XL
750 })
751 } else {
752 false
f20569fa 753 }
f20569fa 754}