Skip to content

Commit 18b640a

Browse files
Suggest calling when operator types mismatch
1 parent 2f78dd1 commit 18b640a

File tree

8 files changed

+199
-164
lines changed

8 files changed

+199
-164
lines changed

compiler/rustc_errors/src/lib.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,9 +1248,13 @@ impl HandlerInner {
12481248
}
12491249

12501250
fn treat_err_as_bug(&self) -> bool {
1251-
self.flags
1252-
.treat_err_as_bug
1253-
.map_or(false, |c| self.err_count() + self.lint_err_count >= c.get())
1251+
self.flags.treat_err_as_bug.map_or(false, |c| {
1252+
self.err_count()
1253+
+ self.lint_err_count
1254+
+ self.delayed_span_bugs.len()
1255+
+ self.delayed_good_path_bugs.len()
1256+
>= c.get()
1257+
})
12541258
}
12551259

12561260
fn print_error_count(&mut self, registry: &Registry) {
@@ -1406,7 +1410,14 @@ impl HandlerInner {
14061410
// This is technically `self.treat_err_as_bug()` but `delay_span_bug` is called before
14071411
// incrementing `err_count` by one, so we need to +1 the comparing.
14081412
// FIXME: Would be nice to increment err_count in a more coherent way.
1409-
if self.flags.treat_err_as_bug.map_or(false, |c| self.err_count() + 1 >= c.get()) {
1413+
if self.flags.treat_err_as_bug.map_or(false, |c| {
1414+
self.err_count()
1415+
+ self.lint_err_count
1416+
+ self.delayed_span_bugs.len()
1417+
+ self.delayed_good_path_bugs.len()
1418+
+ 1
1419+
>= c.get()
1420+
}) {
14101421
// FIXME: don't abort here if report_delayed_bugs is off
14111422
self.span_bug(sp, msg);
14121423
}

compiler/rustc_lint_defs/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ macro_rules! pluralize {
4141
/// All suggestions are marked with an `Applicability`. Tools use the applicability of a suggestion
4242
/// to determine whether it should be automatically applied or if the user should be consulted
4343
/// before applying the suggestion.
44-
#[derive(Copy, Clone, Debug, PartialEq, Hash, Encodable, Decodable, Serialize, Deserialize)]
44+
#[derive(Copy, Clone, Debug, Hash, Encodable, Decodable, Serialize, Deserialize)]
45+
#[derive(PartialEq, Eq, PartialOrd, Ord)]
4546
pub enum Applicability {
4647
/// The suggestion is definitely what the user intended, or maintains the exact meaning of the code.
4748
/// This suggestion should be automatically applied.

compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs

Lines changed: 126 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
7676
found: Ty<'tcx>,
7777
can_satisfy: impl FnOnce(Ty<'tcx>) -> bool,
7878
) -> bool {
79-
enum DefIdOrName {
80-
DefId(DefId),
81-
Name(&'static str),
79+
let Some((def_id_or_name, output, num_inputs)) = self.extract_callable_info(expr, found)
80+
else { return false; };
81+
if can_satisfy(output) {
82+
let (sugg_call, mut applicability) = match num_inputs {
83+
0 => ("".to_string(), Applicability::MachineApplicable),
84+
1..=4 => (
85+
(0..num_inputs).map(|_| "_").collect::<Vec<_>>().join(", "),
86+
Applicability::MachineApplicable,
87+
),
88+
_ => ("...".to_string(), Applicability::HasPlaceholders),
89+
};
90+
91+
let msg = match def_id_or_name {
92+
DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
93+
DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct".to_string(),
94+
DefKind::Ctor(CtorOf::Variant, _) => {
95+
"instantiate this tuple variant".to_string()
96+
}
97+
kind => format!("call this {}", kind.descr(def_id)),
98+
},
99+
DefIdOrName::Name(name) => format!("call this {name}"),
100+
};
101+
102+
let sugg = match expr.kind {
103+
hir::ExprKind::Call(..)
104+
| hir::ExprKind::Path(..)
105+
| hir::ExprKind::Index(..)
106+
| hir::ExprKind::Lit(..) => {
107+
vec![(expr.span.shrink_to_hi(), format!("({sugg_call})"))]
108+
}
109+
hir::ExprKind::Closure { .. } => {
110+
// Might be `{ expr } || { bool }`
111+
applicability = Applicability::MaybeIncorrect;
112+
vec![
113+
(expr.span.shrink_to_lo(), "(".to_string()),
114+
(expr.span.shrink_to_hi(), format!(")({sugg_call})")),
115+
]
116+
}
117+
_ => {
118+
vec![
119+
(expr.span.shrink_to_lo(), "(".to_string()),
120+
(expr.span.shrink_to_hi(), format!(")({sugg_call})")),
121+
]
122+
}
123+
};
124+
125+
err.multipart_suggestion_verbose(
126+
format!("use parentheses to {msg}"),
127+
sugg,
128+
applicability,
129+
);
130+
131+
return true;
82132
}
133+
false
134+
}
135+
136+
fn extract_callable_info(
137+
&self,
138+
expr: &Expr<'_>,
139+
found: Ty<'tcx>,
140+
) -> Option<(DefIdOrName, Ty<'tcx>, usize)> {
83141
// Autoderef is useful here because sometimes we box callables, etc.
84142
let Some((def_id_or_name, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| {
85143
match *found.kind() {
@@ -148,67 +206,83 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
148206
}
149207
_ => None,
150208
}
151-
}) else { return false; };
209+
}) else { return None; };
152210

153211
let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output);
212+
154213
// We don't want to register any extra obligations, which should be
155214
// implied by wf, but also because that would possibly result in
156215
// erroneous errors later on.
157216
let infer::InferOk { value: output, obligations: _ } =
158217
self.normalize_associated_types_in_as_infer_ok(expr.span, output);
159-
if !output.is_ty_var() && can_satisfy(output) {
160-
let (sugg_call, mut applicability) = match inputs {
161-
0 => ("".to_string(), Applicability::MachineApplicable),
162-
1..=4 => (
163-
(0..inputs).map(|_| "_").collect::<Vec<_>>().join(", "),
164-
Applicability::MachineApplicable,
165-
),
166-
_ => ("...".to_string(), Applicability::HasPlaceholders),
167-
};
168218

169-
let msg = match def_id_or_name {
170-
DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
171-
DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct".to_string(),
172-
DefKind::Ctor(CtorOf::Variant, _) => {
173-
"instantiate this tuple variant".to_string()
174-
}
175-
kind => format!("call this {}", kind.descr(def_id)),
176-
},
177-
DefIdOrName::Name(name) => format!("call this {name}"),
178-
};
219+
if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }
220+
}
179221

180-
let sugg = match expr.kind {
181-
hir::ExprKind::Call(..)
182-
| hir::ExprKind::Path(..)
183-
| hir::ExprKind::Index(..)
184-
| hir::ExprKind::Lit(..) => {
185-
vec![(expr.span.shrink_to_hi(), format!("({sugg_call})"))]
186-
}
187-
hir::ExprKind::Closure { .. } => {
188-
// Might be `{ expr } || { bool }`
189-
applicability = Applicability::MaybeIncorrect;
190-
vec![
191-
(expr.span.shrink_to_lo(), "(".to_string()),
192-
(expr.span.shrink_to_hi(), format!(")({sugg_call})")),
193-
]
194-
}
195-
_ => {
196-
vec![
197-
(expr.span.shrink_to_lo(), "(".to_string()),
198-
(expr.span.shrink_to_hi(), format!(")({sugg_call})")),
199-
]
222+
pub fn suggest_two_fn_call(
223+
&self,
224+
err: &mut Diagnostic,
225+
lhs_expr: &'tcx hir::Expr<'tcx>,
226+
lhs_ty: Ty<'tcx>,
227+
rhs_expr: &'tcx hir::Expr<'tcx>,
228+
rhs_ty: Ty<'tcx>,
229+
can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool,
230+
) -> bool {
231+
let Some((_, lhs_output_ty, num_lhs_inputs)) = self.extract_callable_info(lhs_expr, lhs_ty)
232+
else { return false; };
233+
let Some((_, rhs_output_ty, num_rhs_inputs)) = self.extract_callable_info(rhs_expr, rhs_ty)
234+
else { return false; };
235+
236+
if can_satisfy(lhs_output_ty, rhs_output_ty) {
237+
let mut sugg = vec![];
238+
let mut applicability = Applicability::MachineApplicable;
239+
240+
for (expr, num_inputs) in [(lhs_expr, num_lhs_inputs), (rhs_expr, num_rhs_inputs)] {
241+
let (sugg_call, this_applicability) = match num_inputs {
242+
0 => ("".to_string(), Applicability::MachineApplicable),
243+
1..=4 => (
244+
(0..num_inputs).map(|_| "_").collect::<Vec<_>>().join(", "),
245+
Applicability::MachineApplicable,
246+
),
247+
_ => ("...".to_string(), Applicability::HasPlaceholders),
248+
};
249+
250+
applicability = applicability.max(this_applicability);
251+
252+
match expr.kind {
253+
hir::ExprKind::Call(..)
254+
| hir::ExprKind::Path(..)
255+
| hir::ExprKind::Index(..)
256+
| hir::ExprKind::Lit(..) => {
257+
sugg.extend([(expr.span.shrink_to_hi(), format!("({sugg_call})"))]);
258+
}
259+
hir::ExprKind::Closure { .. } => {
260+
// Might be `{ expr } || { bool }`
261+
applicability = Applicability::MaybeIncorrect;
262+
sugg.extend([
263+
(expr.span.shrink_to_lo(), "(".to_string()),
264+
(expr.span.shrink_to_hi(), format!(")({sugg_call})")),
265+
]);
266+
}
267+
_ => {
268+
sugg.extend([
269+
(expr.span.shrink_to_lo(), "(".to_string()),
270+
(expr.span.shrink_to_hi(), format!(")({sugg_call})")),
271+
]);
272+
}
200273
}
201-
};
274+
}
202275

203276
err.multipart_suggestion_verbose(
204-
format!("use parentheses to {msg}"),
277+
format!("use parentheses to call these"),
205278
sugg,
206279
applicability,
207280
);
208281

209-
return true;
282+
true
283+
} else {
284+
false
210285
}
211-
false
212286
}
213287

214288
pub fn suggest_deref_ref_or_into(
@@ -959,3 +1033,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
9591033
}
9601034
}
9611035
}
1036+
1037+
enum DefIdOrName {
1038+
DefId(DefId),
1039+
Name(&'static str),
1040+
}

0 commit comments

Comments
 (0)