Skip to content

Commit b90880d

Browse files
Optimize unit_return_expecting_ord (#14905)
This lint was previously written very clumsily, not short-circuiting and doing a lot of unnecessary work. Now it makes sure to do the cheaper functions earlier and in general, is just smarter. (I specifically focused on minimizing binder instantiation Sadly, I'm not finding any relevant result in a benchmark. Still with the LLVM coverage instruments, the expensive bits are called lots of less times (The binder instantiation that I care about is reduced from 95k to 10k throughout our test suite). changelog:[`unit_return_expecting_ord`]: Optimize the lint
2 parents 3a1159e + 7e590de commit b90880d

File tree

1 file changed

+79
-47
lines changed

1 file changed

+79
-47
lines changed

clippy_lints/src/unit_return_expecting_ord.rs

Lines changed: 79 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_lint::{LateContext, LateLintPass};
55
use rustc_middle::ty;
66
use rustc_middle::ty::{ClauseKind, GenericPredicates, ProjectionPredicate, TraitPredicate};
77
use rustc_session::declare_lint_pass;
8-
use rustc_span::{BytePos, Span, sym};
8+
use rustc_span::{BytePos, Span, Symbol, sym};
99

1010
declare_clippy_lint! {
1111
/// ### What it does
@@ -36,21 +36,26 @@ declare_clippy_lint! {
3636

3737
declare_lint_pass!(UnitReturnExpectingOrd => [UNIT_RETURN_EXPECTING_ORD]);
3838

39-
fn get_trait_predicates_for_trait_id<'tcx>(
39+
// For each
40+
fn get_trait_predicates_for_trait_ids<'tcx>(
4041
cx: &LateContext<'tcx>,
4142
generics: GenericPredicates<'tcx>,
42-
trait_id: Option<DefId>,
43-
) -> Vec<TraitPredicate<'tcx>> {
44-
let mut preds = Vec::new();
43+
trait_ids: &[Option<DefId>], // At least 2 ids
44+
) -> [Vec<TraitPredicate<'tcx>>; 3] {
45+
debug_assert!(trait_ids.len() >= 2);
46+
let mut preds = [Vec::new(), Vec::new(), Vec::new()];
4547
for (pred, _) in generics.predicates {
46-
if let ClauseKind::Trait(poly_trait_pred) = pred.kind().skip_binder()
47-
&& let trait_pred = cx
48+
if let ClauseKind::Trait(poly_trait_pred) = pred.kind().skip_binder() {
49+
let trait_pred = cx
4850
.tcx
49-
.instantiate_bound_regions_with_erased(pred.kind().rebind(poly_trait_pred))
50-
&& let Some(trait_def_id) = trait_id
51-
&& trait_def_id == trait_pred.trait_ref.def_id
52-
{
53-
preds.push(trait_pred);
51+
.instantiate_bound_regions_with_erased(pred.kind().rebind(poly_trait_pred));
52+
for (i, tid) in trait_ids.iter().enumerate() {
53+
if let Some(tid) = tid
54+
&& *tid == trait_pred.trait_ref.def_id
55+
{
56+
preds[i].push(trait_pred);
57+
}
58+
}
5459
}
5560
}
5661
preds
@@ -74,15 +79,24 @@ fn get_projection_pred<'tcx>(
7479
})
7580
}
7681

77-
fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Vec<(usize, String)> {
82+
fn get_args_to_check<'tcx>(
83+
cx: &LateContext<'tcx>,
84+
expr: &'tcx Expr<'tcx>,
85+
args_len: usize,
86+
fn_mut_trait: DefId,
87+
ord_trait: Option<DefId>,
88+
partial_ord_trait: Option<DefId>,
89+
) -> Vec<(usize, Symbol)> {
7890
let mut args_to_check = Vec::new();
7991
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
8092
let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
8193
let generics = cx.tcx.predicates_of(def_id);
82-
let fn_mut_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().fn_mut_trait());
83-
let ord_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.get_diagnostic_item(sym::Ord));
84-
let partial_ord_preds =
85-
get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().partial_ord_trait());
94+
let [fn_mut_preds, ord_preds, partial_ord_preds] =
95+
get_trait_predicates_for_trait_ids(cx, generics, &[Some(fn_mut_trait), ord_trait, partial_ord_trait]);
96+
if fn_mut_preds.is_empty() {
97+
return vec![];
98+
}
99+
86100
// Trying to call instantiate_bound_regions_with_erased on fn_sig.inputs() gives the following error
87101
// The trait `rustc::ty::TypeFoldable<'_>` is not implemented for
88102
// `&[rustc_middle::ty::Ty<'_>]`
@@ -102,12 +116,18 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve
102116
.iter()
103117
.any(|ord| Some(ord.self_ty()) == return_ty_pred.term.as_type())
104118
{
105-
args_to_check.push((i, "Ord".to_string()));
119+
args_to_check.push((i, sym::Ord));
120+
if args_to_check.len() == args_len - 1 {
121+
break;
122+
}
106123
} else if partial_ord_preds
107124
.iter()
108125
.any(|pord| pord.self_ty() == return_ty_pred.term.expect_type())
109126
{
110-
args_to_check.push((i, "PartialOrd".to_string()));
127+
args_to_check.push((i, sym::PartialOrd));
128+
if args_to_check.len() == args_len - 1 {
129+
break;
130+
}
111131
}
112132
}
113133
}
@@ -142,38 +162,50 @@ fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Spa
142162

143163
impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd {
144164
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
145-
if let ExprKind::MethodCall(_, receiver, args, _) = expr.kind {
146-
let arg_indices = get_args_to_check(cx, expr);
165+
if let ExprKind::MethodCall(_, receiver, args, _) = expr.kind
166+
&& args.iter().any(|arg| {
167+
matches!(
168+
arg.peel_blocks().peel_borrows().peel_drop_temps().kind,
169+
ExprKind::Path(_) | ExprKind::Closure(_)
170+
)
171+
})
172+
&& let Some(fn_mut_trait) = cx.tcx.lang_items().fn_mut_trait()
173+
{
174+
let ord_trait = cx.tcx.get_diagnostic_item(sym::Ord);
175+
let partial_ord_trait = cx.tcx.lang_items().partial_ord_trait();
176+
if (ord_trait, partial_ord_trait) == (None, None) {
177+
return;
178+
}
179+
147180
let args = std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>();
181+
let arg_indices = get_args_to_check(cx, expr, args.len(), fn_mut_trait, ord_trait, partial_ord_trait);
148182
for (i, trait_name) in arg_indices {
149-
if i < args.len() {
150-
match check_arg(cx, args[i]) {
151-
Some((span, None)) => {
152-
span_lint(
153-
cx,
154-
UNIT_RETURN_EXPECTING_ORD,
155-
span,
156-
format!(
157-
"this closure returns \
183+
match check_arg(cx, args[i]) {
184+
Some((span, None)) => {
185+
span_lint(
186+
cx,
187+
UNIT_RETURN_EXPECTING_ORD,
188+
span,
189+
format!(
190+
"this closure returns \
158191
the unit type which also implements {trait_name}"
159-
),
160-
);
161-
},
162-
Some((span, Some(last_semi))) => {
163-
span_lint_and_help(
164-
cx,
165-
UNIT_RETURN_EXPECTING_ORD,
166-
span,
167-
format!(
168-
"this closure returns \
192+
),
193+
);
194+
},
195+
Some((span, Some(last_semi))) => {
196+
span_lint_and_help(
197+
cx,
198+
UNIT_RETURN_EXPECTING_ORD,
199+
span,
200+
format!(
201+
"this closure returns \
169202
the unit type which also implements {trait_name}"
170-
),
171-
Some(last_semi),
172-
"probably caused by this trailing semicolon",
173-
);
174-
},
175-
None => {},
176-
}
203+
),
204+
Some(last_semi),
205+
"probably caused by this trailing semicolon",
206+
);
207+
},
208+
None => {},
177209
}
178210
}
179211
}

0 commit comments

Comments
 (0)