Skip to content

Commit 2e6e668

Browse files
committed
Optimize unit_return_expecting_ord
This lint was previously written very clumsily, not shortcircuiting and doing a lot of unnecessary work. Now it makes sure to do the cheaper functions earlier and in general, just be smarter. (I specifically focused on minimizing binder instantiation)
1 parent 32a3744 commit 2e6e668

File tree

1 file changed

+78
-46
lines changed

1 file changed

+78
-46
lines changed

clippy_lints/src/unit_return_expecting_ord.rs

Lines changed: 78 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -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, String)> {
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, String::from("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, String::from("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)