Skip to content

Commit 7254072

Browse files
authored
needless_pass_by_value: reference the innermost Option content (rust-lang#14392)
If types such as `Option<Option<String>>` are not used by value, then `Option<Option<&String>>` will be suggested, instead of `Option<&Option<String>>`. changelog: [`needless_pass_by_value`]: suggest using a reference on the innermost `Option` content fix rust-lang#14375
2 parents 0730678 + 417d4e6 commit 7254072

File tree

5 files changed

+210
-37
lines changed

5 files changed

+210
-37
lines changed

clippy_lints/src/needless_pass_by_value.rs

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
2-
use clippy_utils::is_self;
32
use clippy_utils::ptr::get_spans;
43
use clippy_utils::source::{SpanRangeExt, snippet};
54
use clippy_utils::ty::{
65
implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item,
76
};
7+
use clippy_utils::{is_self, peel_hir_ty_options};
88
use rustc_abi::ExternAbi;
99
use rustc_errors::{Applicability, Diag};
1010
use rustc_hir::intravisit::FnKind;
@@ -279,18 +279,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
279279
}
280280
}
281281

282-
let suggestion = if is_type_diagnostic_item(cx, ty, sym::Option)
283-
&& let snip = snippet(cx, input.span, "_")
284-
&& let Some((first, rest)) = snip.split_once('<')
285-
{
286-
format!("{first}<&{rest}")
287-
} else {
288-
format!("&{}", snippet(cx, input.span, "_"))
289-
};
290-
diag.span_suggestion(
291-
input.span,
282+
diag.span_suggestion_verbose(
283+
peel_hir_ty_options(cx, input).span.shrink_to_lo(),
292284
"consider taking a reference instead",
293-
suggestion,
285+
'&',
294286
Applicability::MaybeIncorrect,
295287
);
296288
};

clippy_utils/src/lib.rs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,10 @@ use rustc_hir::hir_id::{HirIdMap, HirIdSet};
107107
use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
108108
use rustc_hir::{
109109
self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext,
110-
Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind,
111-
ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat,
112-
PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind,
113-
TraitItemRef, TraitRef, TyKind, UnOp, def,
110+
Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl, ImplItem,
111+
ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode,
112+
Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitFn, TraitItem,
113+
TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, def,
114114
};
115115
use rustc_lexer::{TokenKind, tokenize};
116116
use rustc_lint::{LateContext, Level, Lint, LintContext};
@@ -435,7 +435,7 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tc
435435
.map_or(&[][..], |a| a.args)
436436
.iter()
437437
.filter_map(|a| match a {
438-
hir::GenericArg::Type(ty) => Some(ty.as_unambig_ty()),
438+
GenericArg::Type(ty) => Some(ty.as_unambig_ty()),
439439
_ => None,
440440
})
441441
}
@@ -3709,3 +3709,21 @@ pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
37093709
true
37103710
}
37113711
}
3712+
3713+
/// Peel `Option<…>` from `hir_ty` as long as the HIR name is `Option` and it corresponds to the
3714+
/// `core::Option<_>` type.
3715+
pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
3716+
let Some(option_def_id) = cx.tcx.get_diagnostic_item(sym::Option) else {
3717+
return hir_ty;
3718+
};
3719+
while let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind
3720+
&& let Some(segment) = path.segments.last()
3721+
&& segment.ident.name == sym::Option
3722+
&& let Res::Def(DefKind::Enum, def_id) = segment.res
3723+
&& def_id == option_def_id
3724+
&& let [GenericArg::Type(arg_ty)] = segment.args().args
3725+
{
3726+
hir_ty = arg_ty.as_unambig_ty();
3727+
}
3728+
hir_ty
3729+
}

tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: this argument is passed by value, but not consumed in the function body
22
--> tests/ui/crashes/needless_pass_by_value-w-late-bound.rs:7:12
33
|
44
LL | fn test(x: Foo<'_>) {}
5-
| ^^^^^^^ help: consider taking a reference instead: `&Foo<'_>`
5+
| ^^^^^^^
66
|
77
help: or consider marking this type as `Copy`
88
--> tests/ui/crashes/needless_pass_by_value-w-late-bound.rs:5:1
@@ -11,6 +11,10 @@ LL | struct Foo<'a>(&'a [(); 100]);
1111
| ^^^^^^^^^^^^^^
1212
= note: `-D clippy::needless-pass-by-value` implied by `-D warnings`
1313
= help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_value)]`
14+
help: consider taking a reference instead
15+
|
16+
LL | fn test(x: &Foo<'_>) {}
17+
| +
1418

1519
error: aborting due to 1 previous error
1620

tests/ui/needless_pass_by_value.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,35 @@ fn option_inner_ref(x: Option<String>) {
196196
assert!(x.is_some());
197197
}
198198

199+
mod non_standard {
200+
#[derive(Debug)]
201+
pub struct Option<T>(T);
202+
}
203+
204+
fn non_standard_option(x: non_standard::Option<String>) {
205+
//~^ needless_pass_by_value
206+
dbg!(&x);
207+
}
208+
209+
fn option_by_name(x: Option<std::option::Option<core::option::Option<non_standard::Option<String>>>>) {
210+
//~^ needless_pass_by_value
211+
dbg!(&x);
212+
}
213+
214+
type OptStr = Option<String>;
215+
216+
fn non_option(x: OptStr) {
217+
//~^ needless_pass_by_value
218+
dbg!(&x);
219+
}
220+
221+
type Opt<T> = Option<T>;
222+
223+
fn non_option_either(x: Opt<String>) {
224+
//~^ needless_pass_by_value
225+
dbg!(&x);
226+
}
227+
199228
fn main() {
200229
// This should not cause an ICE either
201230
// https://github.com/rust-lang/rust-clippy/issues/3144

0 commit comments

Comments
 (0)