]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/ptr.rs
New upstream version 1.74.1+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};
fe692bf9 8use hir::LifetimeName;
f20569fa 9use if_chain::if_chain;
04454e1e 10use rustc_errors::{Applicability, MultiSpan};
5099ac24
FG
11use rustc_hir::def_id::DefId;
12use rustc_hir::hir_id::HirIdMap;
13use rustc_hir::intravisit::{walk_expr, Visitor};
f20569fa 14use rustc_hir::{
04454e1e 15 self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg,
487cf647
FG
16 ImplItemKind, ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind,
17 TyKind, Unsafety,
f20569fa 18};
2b03887a
FG
19use rustc_infer::infer::TyCtxtInferExt;
20use rustc_infer::traits::{Obligation, ObligationCause};
f20569fa 21use rustc_lint::{LateContext, LateLintPass};
5099ac24 22use rustc_middle::hir::nested_filter;
fe692bf9 23use rustc_middle::ty::{self, Binder, ClauseKind, ExistentialPredicate, List, PredicateKind, Ty};
f20569fa
XL
24use rustc_session::{declare_lint_pass, declare_tool_lint};
25use rustc_span::source_map::Span;
04454e1e 26use rustc_span::sym;
cdc7bbd5 27use rustc_span::symbol::Symbol;
add651ee 28use rustc_target::spec::abi::Abi;
2b03887a
FG
29use rustc_trait_selection::infer::InferCtxtExt as _;
30use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
add651ee 31use std::{fmt, iter};
f20569fa
XL
32
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 {
064997fb
FG
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 });
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) {
064997fb 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<'_>) {
274 // (fn_path, arg_indices) - `arg_indices` are the `arg` positions where null would cause U.B.
781aab86 275 const INVALID_NULL_PTR_USAGE_TABLE: [(&[&str], &[usize]); 13] = [
cdc7bbd5
XL
276 (&paths::SLICE_FROM_RAW_PARTS, &[0]),
277 (&paths::SLICE_FROM_RAW_PARTS_MUT, &[0]),
278 (&paths::PTR_COPY, &[0, 1]),
279 (&paths::PTR_COPY_NONOVERLAPPING, &[0, 1]),
280 (&paths::PTR_READ, &[0]),
281 (&paths::PTR_READ_UNALIGNED, &[0]),
282 (&paths::PTR_READ_VOLATILE, &[0]),
283 (&paths::PTR_REPLACE, &[0]),
284 (&paths::PTR_SLICE_FROM_RAW_PARTS, &[0]),
285 (&paths::PTR_SLICE_FROM_RAW_PARTS_MUT, &[0]),
286 (&paths::PTR_SWAP, &[0, 1]),
287 (&paths::PTR_SWAP_NONOVERLAPPING, &[0, 1]),
cdc7bbd5
XL
288 (&paths::PTR_WRITE_BYTES, &[0]),
289 ];
781aab86
FG
290 let invalid_null_ptr_usage_table_diag_items: [(Option<DefId>, &[usize]); 3] = [
291 (cx.tcx.get_diagnostic_item(sym::ptr_write), &[0]),
292 (cx.tcx.get_diagnostic_item(sym::ptr_write_unaligned), &[0]),
293 (cx.tcx.get_diagnostic_item(sym::ptr_write_volatile), &[0]),
294 ];
cdc7bbd5
XL
295
296 if_chain! {
17df50a5 297 if let ExprKind::Call(fun, args) = expr.kind;
cdc7bbd5
XL
298 if let ExprKind::Path(ref qpath) = fun.kind;
299 if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
300 let fun_def_path = cx.get_def_path(fun_def_id).into_iter().map(Symbol::to_ident_string).collect::<Vec<_>>();
781aab86 301 if let Some(arg_indices) = INVALID_NULL_PTR_USAGE_TABLE
cdc7bbd5 302 .iter()
781aab86
FG
303 .find_map(|&(fn_path, indices)| if fn_path == fun_def_path { Some(indices) } else { None })
304 .or_else(|| {
305 invalid_null_ptr_usage_table_diag_items
306 .iter()
307 .find_map(|&(def_id, indices)| {
308 if def_id == Some(fun_def_id) {
309 Some(indices)
310 } else {
311 None
312 }
313 })
314 });
cdc7bbd5
XL
315 then {
316 for &arg_idx in arg_indices {
317 if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) {
318 span_lint_and_sugg(
319 cx,
320 INVALID_NULL_PTR_USAGE,
321 arg.span,
322 "pointer must be non-null",
323 "change this to",
324 "core::ptr::NonNull::dangling().as_ptr()".to_string(),
325 Applicability::MachineApplicable,
326 );
327 }
328 }
f20569fa
XL
329 }
330 }
331}
332
5099ac24
FG
333#[derive(Default)]
334struct PtrArgResult {
335 skip: bool,
336 replacements: Vec<PtrArgReplacement>,
337}
f20569fa 338
5099ac24
FG
339struct PtrArgReplacement {
340 expr_span: Span,
341 self_span: Span,
342 replacement: &'static str,
343}
3c0e092e 344
5099ac24
FG
345struct PtrArg<'tcx> {
346 idx: usize,
064997fb 347 emission_id: hir::HirId,
5099ac24
FG
348 span: Span,
349 ty_did: DefId,
350 ty_name: Symbol,
351 method_renames: &'static [(&'static str, &'static str)],
352 ref_prefix: RefPrefix,
353 deref_ty: DerefTy<'tcx>,
5099ac24
FG
354}
355impl PtrArg<'_> {
356 fn build_msg(&self) -> String {
357 format!(
358 "writing `&{}{}` instead of `&{}{}` involves a new object where a slice will do",
359 self.ref_prefix.mutability.prefix_str(),
360 self.ty_name,
361 self.ref_prefix.mutability.prefix_str(),
362 self.deref_ty.argless_str(),
363 )
364 }
365
366 fn mutability(&self) -> Mutability {
367 self.ref_prefix.mutability
368 }
369}
370
371struct RefPrefix {
487cf647 372 lt: Lifetime,
5099ac24
FG
373 mutability: Mutability,
374}
375impl fmt::Display for RefPrefix {
376 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
377 use fmt::Write;
378 f.write_char('&')?;
487cf647
FG
379 if !self.lt.is_anonymous() {
380 self.lt.ident.fmt(f)?;
381 f.write_char(' ')?;
5099ac24
FG
382 }
383 f.write_str(self.mutability.prefix_str())
384 }
385}
386
387struct DerefTyDisplay<'a, 'tcx>(&'a LateContext<'tcx>, &'a DerefTy<'tcx>);
388impl fmt::Display for DerefTyDisplay<'_, '_> {
389 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390 use std::fmt::Write;
391 match self.1 {
392 DerefTy::Str => f.write_str("str"),
393 DerefTy::Path => f.write_str("Path"),
394 DerefTy::Slice(hir_ty, ty) => {
395 f.write_char('[')?;
396 match hir_ty.and_then(|s| snippet_opt(self.0, s)) {
397 Some(s) => f.write_str(&s)?,
398 None => ty.fmt(f)?,
f20569fa 399 }
5099ac24 400 f.write_char(']')
3c0e092e 401 },
5099ac24
FG
402 }
403 }
404}
405
406enum DerefTy<'tcx> {
407 Str,
408 Path,
409 Slice(Option<Span>, Ty<'tcx>),
410}
411impl<'tcx> DerefTy<'tcx> {
2b03887a
FG
412 fn ty(&self, cx: &LateContext<'tcx>) -> Ty<'tcx> {
413 match *self {
414 Self::Str => cx.tcx.types.str_,
add651ee
FG
415 Self::Path => Ty::new_adt(
416 cx.tcx,
2b03887a
FG
417 cx.tcx.adt_def(cx.tcx.get_diagnostic_item(sym::Path).unwrap()),
418 List::empty(),
419 ),
add651ee 420 Self::Slice(_, ty) => Ty::new_slice(cx.tcx, ty),
2b03887a
FG
421 }
422 }
423
5099ac24
FG
424 fn argless_str(&self) -> &'static str {
425 match *self {
426 Self::Str => "str",
427 Self::Path => "Path",
428 Self::Slice(..) => "[_]",
429 }
430 }
431
432 fn display<'a>(&'a self, cx: &'a LateContext<'tcx>) -> DerefTyDisplay<'a, 'tcx> {
433 DerefTyDisplay(cx, self)
434 }
435}
436
fe692bf9 437#[expect(clippy::too_many_lines)]
5099ac24
FG
438fn check_fn_args<'cx, 'tcx: 'cx>(
439 cx: &'cx LateContext<'tcx>,
781aab86 440 fn_sig: ty::FnSig<'tcx>,
923072b8
FG
441 hir_tys: &'tcx [hir::Ty<'tcx>],
442 params: &'tcx [Param<'tcx>],
5099ac24 443) -> impl Iterator<Item = PtrArg<'tcx>> + 'cx {
781aab86
FG
444 fn_sig
445 .inputs()
446 .iter()
5099ac24
FG
447 .zip(hir_tys.iter())
448 .enumerate()
fe692bf9
FG
449 .filter_map(move |(i, (ty, hir_ty))| {
450 if let ty::Ref(_, ty, mutability) = *ty.kind()
add651ee 451 && let ty::Adt(adt, args) = *ty.kind()
fe692bf9
FG
452 && let TyKind::Ref(lt, ref ty) = hir_ty.kind
453 && let TyKind::Path(QPath::Resolved(None, path)) = ty.ty.kind
5099ac24
FG
454 // Check that the name as typed matches the actual name of the type.
455 // e.g. `fn foo(_: &Foo)` shouldn't trigger the lint when `Foo` is an alias for `Vec`
fe692bf9
FG
456 && let [.., name] = path.segments
457 && cx.tcx.item_name(adt.did()) == name.ident.name
458 {
064997fb 459 let emission_id = params.get(i).map_or(hir_ty.hir_id, |param| param.hir_id);
5e7ed085 460 let (method_renames, deref_ty) = match cx.tcx.get_diagnostic_name(adt.did()) {
5099ac24
FG
461 Some(sym::Vec) => (
462 [("clone", ".to_owned()")].as_slice(),
463 DerefTy::Slice(
464 name.args
465 .and_then(|args| args.args.first())
466 .and_then(|arg| if let GenericArg::Type(ty) = arg {
467 Some(ty.span)
468 } else {
469 None
470 }),
add651ee 471 args.type_at(0),
5099ac24 472 ),
5099ac24 473 ),
487cf647 474 _ if Some(adt.did()) == cx.tcx.lang_items().string() => (
5099ac24
FG
475 [("clone", ".to_owned()"), ("as_str", "")].as_slice(),
476 DerefTy::Str,
5099ac24
FG
477 ),
478 Some(sym::PathBuf) => (
479 [("clone", ".to_path_buf()"), ("as_path", "")].as_slice(),
480 DerefTy::Path,
5099ac24 481 ),
5e7ed085 482 Some(sym::Cow) if mutability == Mutability::Not => {
fe692bf9 483 if let Some((lifetime, ty)) = name.args
5099ac24 484 .and_then(|args| {
fe692bf9
FG
485 if let [GenericArg::Lifetime(lifetime), ty] = args.args {
486 return Some((lifetime, ty));
487 }
488 None
5099ac24 489 })
fe692bf9
FG
490 {
491 if !lifetime.is_anonymous()
781aab86 492 && fn_sig.output()
fe692bf9
FG
493 .walk()
494 .filter_map(|arg| {
495 arg.as_region().and_then(|lifetime| {
496 match lifetime.kind() {
497 ty::ReEarlyBound(r) => Some(r.def_id),
498 ty::ReLateBound(_, r) => r.kind.get_id(),
499 ty::ReFree(r) => r.bound_region.get_id(),
500 ty::ReStatic
501 | ty::ReVar(_)
502 | ty::RePlaceholder(_)
503 | ty::ReErased
504 | ty::ReError(_) => None,
505 }
506 })
507 })
508 .any(|def_id| {
509 matches!(
510 lifetime.res,
511 LifetimeName::Param(param_def_id) if def_id
512 .as_local()
513 .is_some_and(|def_id| def_id == param_def_id),
514 )
515 })
516 {
517 // `&Cow<'a, T>` when the return type uses 'a is okay
518 return None;
064997fb 519 }
fe692bf9
FG
520
521 let ty_name =
add651ee 522 snippet_opt(cx, ty.span()).unwrap_or_else(|| args.type_at(1).to_string());
fe692bf9
FG
523
524 span_lint_hir_and_then(
525 cx,
526 PTR_ARG,
527 emission_id,
528 hir_ty.span,
529 "using a reference to `Cow` is not recommended",
530 |diag| {
531 diag.span_suggestion(
532 hir_ty.span,
533 "change this to",
534 format!("&{}{ty_name}", mutability.prefix_str()),
535 Applicability::Unspecified,
536 );
537 }
538 );
539 }
5099ac24 540 return None;
f20569fa 541 },
5099ac24
FG
542 _ => return None,
543 };
544 return Some(PtrArg {
545 idx: i,
064997fb 546 emission_id,
5099ac24 547 span: hir_ty.span,
5e7ed085 548 ty_did: adt.did(),
5099ac24
FG
549 ty_name: name.ident.name,
550 method_renames,
551 ref_prefix: RefPrefix {
487cf647 552 lt: *lt,
5099ac24
FG
553 mutability,
554 },
555 deref_ty,
f20569fa 556 });
5099ac24
FG
557 }
558 None
559 })
560}
f20569fa 561
04454e1e
FG
562fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&'tcx Body<'_>>) {
563 if let FnRetTy::Return(ty) = sig.decl.output
9c376795 564 && let Some((out, Mutability::Mut, _)) = get_ref_lm(ty)
04454e1e 565 {
9ffffee4 566 let out_region = cx.tcx.named_bound_var(out.hir_id);
04454e1e
FG
567 let args: Option<Vec<_>> = sig
568 .decl
569 .inputs
570 .iter()
9c376795 571 .filter_map(get_ref_lm)
9ffffee4 572 .filter(|&(lt, _, _)| cx.tcx.named_bound_var(lt.hir_id) == out_region)
064997fb 573 .map(|(_, mutability, span)| (mutability == Mutability::Not).then_some(span))
04454e1e
FG
574 .collect();
575 if let Some(args) = args
576 && !args.is_empty()
577 && body.map_or(true, |body| {
f2b60f7d 578 sig.header.unsafety == Unsafety::Unsafe || contains_unsafe_block(cx, body.value)
04454e1e
FG
579 })
580 {
f20569fa
XL
581 span_lint_and_then(
582 cx,
583 MUT_FROM_REF,
584 ty.span,
585 "mutable borrow from immutable input(s)",
586 |diag| {
04454e1e 587 let ms = MultiSpan::from_spans(args);
f20569fa
XL
588 diag.span_note(ms, "immutable borrow here");
589 },
590 );
591 }
592 }
593}
594
923072b8 595#[expect(clippy::too_many_lines)]
5099ac24
FG
596fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: &[PtrArg<'tcx>]) -> Vec<PtrArgResult> {
597 struct V<'cx, 'tcx> {
598 cx: &'cx LateContext<'tcx>,
599 /// Map from a local id to which argument it came from (index into `Self::args` and
600 /// `Self::results`)
601 bindings: HirIdMap<usize>,
602 /// The arguments being checked.
603 args: &'cx [PtrArg<'tcx>],
604 /// The results for each argument (len should match args.len)
605 results: Vec<PtrArgResult>,
606 /// The number of arguments which can't be linted. Used to return early.
607 skip_count: usize,
608 }
609 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
610 type NestedFilter = nested_filter::OnlyBodies;
611 fn nested_visit_map(&mut self) -> Self::Map {
612 self.cx.tcx.hir()
613 }
614
615 fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
616
617 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
618 if self.skip_count == self.args.len() {
619 return;
620 }
621
622 // Check if this is local we care about
2b03887a
FG
623 let Some(&args_idx) = path_to_local(e).and_then(|id| self.bindings.get(&id)) else {
624 return walk_expr(self, e);
5099ac24
FG
625 };
626 let args = &self.args[args_idx];
627 let result = &mut self.results[args_idx];
628
629 // Helper function to handle early returns.
630 let mut set_skip_flag = || {
5e7ed085 631 if !result.skip {
5099ac24
FG
632 self.skip_count += 1;
633 }
634 result.skip = true;
635 };
636
637 match get_expr_use_or_unification_node(self.cx.tcx, e) {
638 Some((Node::Stmt(_), _)) => (),
639 Some((Node::Local(l), _)) => {
640 // Only trace simple bindings. e.g `let x = y;`
f2b60f7d 641 if let PatKind::Binding(BindingAnnotation::NONE, id, _, None) = l.pat.kind {
5099ac24
FG
642 self.bindings.insert(id, args_idx);
643 } else {
644 set_skip_flag();
645 }
646 },
647 Some((Node::Expr(e), child_id)) => match e.kind {
648 ExprKind::Call(f, expr_args) => {
649 let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0);
064997fb
FG
650 if expr_sig(self.cx, f).and_then(|sig| sig.input(i)).map_or(true, |ty| {
651 match *ty.skip_binder().peel_refs().kind() {
2b03887a 652 ty::Dynamic(preds, _, _) => !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds),
5099ac24 653 ty::Param(_) => true,
5e7ed085 654 ty::Adt(def, _) => def.did() == args.ty_did,
5099ac24 655 _ => false,
064997fb
FG
656 }
657 }) {
5099ac24
FG
658 // Passed to a function taking the non-dereferenced type.
659 set_skip_flag();
660 }
661 },
f2b60f7d
FG
662 ExprKind::MethodCall(name, self_arg, expr_args, _) => {
663 let i = std::iter::once(self_arg)
664 .chain(expr_args.iter())
665 .position(|arg| arg.hir_id == child_id)
666 .unwrap_or(0);
5099ac24
FG
667 if i == 0 {
668 // Check if the method can be renamed.
669 let name = name.ident.as_str();
670 if let Some((_, replacement)) = args.method_renames.iter().find(|&&(x, _)| x == name) {
671 result.replacements.push(PtrArgReplacement {
672 expr_span: e.span,
673 self_span: self_arg.span,
674 replacement,
675 });
676 return;
677 }
678 }
679
2b03887a 680 let Some(id) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) else {
5099ac24
FG
681 set_skip_flag();
682 return;
683 };
684
add651ee 685 match *self.cx.tcx.fn_sig(id).instantiate_identity().skip_binder().inputs()[i]
9ffffee4
FG
686 .peel_refs()
687 .kind()
688 {
2b03887a
FG
689 ty::Dynamic(preds, _, _) if !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds) => {
690 set_skip_flag();
691 },
5099ac24
FG
692 ty::Param(_) => {
693 set_skip_flag();
694 },
695 // If the types match check for methods which exist on both types. e.g. `Vec::len` and
696 // `slice::len`
04454e1e 697 ty::Adt(def, _) if def.did() == args.ty_did => {
5099ac24
FG
698 set_skip_flag();
699 },
700 _ => (),
701 }
702 },
703 // Indexing is fine for currently supported types.
add651ee 704 ExprKind::Index(e, _, _) if e.hir_id == child_id => (),
5099ac24
FG
705 _ => set_skip_flag(),
706 },
707 _ => set_skip_flag(),
708 }
f20569fa
XL
709 }
710 }
5099ac24
FG
711
712 let mut skip_count = 0;
713 let mut results = args.iter().map(|_| PtrArgResult::default()).collect::<Vec<_>>();
714 let mut v = V {
715 cx,
716 bindings: args
717 .iter()
718 .enumerate()
719 .filter_map(|(i, arg)| {
720 let param = &body.params[arg.idx];
721 match param.pat.kind {
f2b60f7d 722 PatKind::Binding(BindingAnnotation::NONE, id, _, None)
5099ac24
FG
723 if !is_lint_allowed(cx, PTR_ARG, param.hir_id) =>
724 {
725 Some((id, i))
726 },
727 _ => {
728 skip_count += 1;
729 results[i].skip = true;
730 None
731 },
732 }
733 })
734 .collect(),
735 args,
736 results,
737 skip_count,
738 };
f2b60f7d 739 v.visit_expr(body.value);
5099ac24 740 v.results
f20569fa
XL
741}
742
2b03887a
FG
743fn matches_preds<'tcx>(
744 cx: &LateContext<'tcx>,
745 ty: Ty<'tcx>,
487cf647 746 preds: &'tcx [ty::PolyExistentialPredicate<'tcx>],
2b03887a
FG
747) -> bool {
748 let infcx = cx.tcx.infer_ctxt().build();
749 preds.iter().all(|&p| match cx.tcx.erase_late_bound_regions(p) {
750 ExistentialPredicate::Trait(p) => infcx
add651ee 751 .type_implements_trait(p.def_id, [ty.into()].into_iter().chain(p.args.iter()), cx.param_env)
2b03887a
FG
752 .must_apply_modulo_regions(),
753 ExistentialPredicate::Projection(p) => infcx.predicate_must_hold_modulo_regions(&Obligation::new(
487cf647 754 cx.tcx,
2b03887a
FG
755 ObligationCause::dummy(),
756 cx.param_env,
487cf647 757 cx.tcx
fe692bf9 758 .mk_predicate(Binder::dummy(PredicateKind::Clause(ClauseKind::Projection(
487cf647
FG
759 p.with_self_ty(cx.tcx, ty),
760 )))),
2b03887a
FG
761 )),
762 ExistentialPredicate::AutoTrait(p) => infcx
487cf647 763 .type_implements_trait(p, [ty], cx.param_env)
2b03887a
FG
764 .must_apply_modulo_regions(),
765 })
766}
767
9c376795
FG
768fn get_ref_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> {
769 if let TyKind::Ref(lt, ref m) = ty.kind {
f20569fa
XL
770 Some((lt, m.mutbl, ty.span))
771 } else {
772 None
773 }
774}
775
cdc7bbd5
XL
776fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
777 if let ExprKind::Call(pathexp, []) = expr.kind {
5099ac24
FG
778 path_def_id(cx, pathexp).map_or(false, |id| {
779 matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ptr_null | sym::ptr_null_mut))
cdc7bbd5
XL
780 })
781 } else {
782 false
f20569fa 783 }
f20569fa 784}