Skip to content

Commit b3eec8b

Browse files
committed
Split traits::error_reporting to keep files smaller
1 parent 97124d5 commit b3eec8b

File tree

5 files changed

+3478
-3446
lines changed

5 files changed

+3478
-3446
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
2+
use crate::infer::InferCtxt;
3+
use crate::traits::{Obligation, ObligationCause, ObligationCtxt};
4+
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed};
5+
use rustc_hir as hir;
6+
use rustc_hir::Node;
7+
use rustc_middle::ty::{self, Ty};
8+
use rustc_span::{Span, DUMMY_SP};
9+
10+
use super::ArgKind;
11+
12+
pub use rustc_infer::traits::error_reporting::*;
13+
14+
pub trait InferCtxtExt<'tcx> {
15+
/// Given some node representing a fn-like thing in the HIR map,
16+
/// returns a span and `ArgKind` information that describes the
17+
/// arguments it expects. This can be supplied to
18+
/// `report_arg_count_mismatch`.
19+
fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Option<Span>, Vec<ArgKind>)>;
20+
21+
/// Reports an error when the number of arguments needed by a
22+
/// trait match doesn't match the number that the expression
23+
/// provides.
24+
fn report_arg_count_mismatch(
25+
&self,
26+
span: Span,
27+
found_span: Option<Span>,
28+
expected_args: Vec<ArgKind>,
29+
found_args: Vec<ArgKind>,
30+
is_closure: bool,
31+
closure_pipe_span: Option<Span>,
32+
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
33+
34+
/// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce`
35+
/// in that order, and returns the generic type corresponding to the
36+
/// argument of that trait (corresponding to the closure arguments).
37+
fn type_implements_fn_trait(
38+
&self,
39+
param_env: ty::ParamEnv<'tcx>,
40+
ty: ty::Binder<'tcx, Ty<'tcx>>,
41+
polarity: ty::ImplPolarity,
42+
) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()>;
43+
}
44+
45+
impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
46+
/// Given some node representing a fn-like thing in the HIR map,
47+
/// returns a span and `ArgKind` information that describes the
48+
/// arguments it expects. This can be supplied to
49+
/// `report_arg_count_mismatch`.
50+
fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Option<Span>, Vec<ArgKind>)> {
51+
let sm = self.tcx.sess.source_map();
52+
let hir = self.tcx.hir();
53+
Some(match node {
54+
Node::Expr(&hir::Expr {
55+
kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, fn_arg_span, .. }),
56+
..
57+
}) => (
58+
fn_decl_span,
59+
fn_arg_span,
60+
hir.body(body)
61+
.params
62+
.iter()
63+
.map(|arg| {
64+
if let hir::Pat { kind: hir::PatKind::Tuple(ref args, _), span, .. } =
65+
*arg.pat
66+
{
67+
Some(ArgKind::Tuple(
68+
Some(span),
69+
args.iter()
70+
.map(|pat| {
71+
sm.span_to_snippet(pat.span)
72+
.ok()
73+
.map(|snippet| (snippet, "_".to_owned()))
74+
})
75+
.collect::<Option<Vec<_>>>()?,
76+
))
77+
} else {
78+
let name = sm.span_to_snippet(arg.pat.span).ok()?;
79+
Some(ArgKind::Arg(name, "_".to_owned()))
80+
}
81+
})
82+
.collect::<Option<Vec<ArgKind>>>()?,
83+
),
84+
Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref sig, ..), .. })
85+
| Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref sig, _), .. })
86+
| Node::TraitItem(&hir::TraitItem {
87+
kind: hir::TraitItemKind::Fn(ref sig, _), ..
88+
}) => (
89+
sig.span,
90+
None,
91+
sig.decl
92+
.inputs
93+
.iter()
94+
.map(|arg| match arg.kind {
95+
hir::TyKind::Tup(ref tys) => ArgKind::Tuple(
96+
Some(arg.span),
97+
vec![("_".to_owned(), "_".to_owned()); tys.len()],
98+
),
99+
_ => ArgKind::empty(),
100+
})
101+
.collect::<Vec<ArgKind>>(),
102+
),
103+
Node::Ctor(ref variant_data) => {
104+
let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id));
105+
(span, None, vec![ArgKind::empty(); variant_data.fields().len()])
106+
}
107+
_ => panic!("non-FnLike node found: {node:?}"),
108+
})
109+
}
110+
111+
/// Reports an error when the number of arguments needed by a
112+
/// trait match doesn't match the number that the expression
113+
/// provides.
114+
fn report_arg_count_mismatch(
115+
&self,
116+
span: Span,
117+
found_span: Option<Span>,
118+
expected_args: Vec<ArgKind>,
119+
found_args: Vec<ArgKind>,
120+
is_closure: bool,
121+
closure_arg_span: Option<Span>,
122+
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
123+
let kind = if is_closure { "closure" } else { "function" };
124+
125+
let args_str = |arguments: &[ArgKind], other: &[ArgKind]| {
126+
let arg_length = arguments.len();
127+
let distinct = matches!(other, &[ArgKind::Tuple(..)]);
128+
match (arg_length, arguments.get(0)) {
129+
(1, Some(ArgKind::Tuple(_, fields))) => {
130+
format!("a single {}-tuple as argument", fields.len())
131+
}
132+
_ => format!(
133+
"{} {}argument{}",
134+
arg_length,
135+
if distinct && arg_length > 1 { "distinct " } else { "" },
136+
pluralize!(arg_length)
137+
),
138+
}
139+
};
140+
141+
let expected_str = args_str(&expected_args, &found_args);
142+
let found_str = args_str(&found_args, &expected_args);
143+
144+
let mut err = struct_span_err!(
145+
self.tcx.sess,
146+
span,
147+
E0593,
148+
"{} is expected to take {}, but it takes {}",
149+
kind,
150+
expected_str,
151+
found_str,
152+
);
153+
154+
err.span_label(span, format!("expected {kind} that takes {expected_str}"));
155+
156+
if let Some(found_span) = found_span {
157+
err.span_label(found_span, format!("takes {found_str}"));
158+
159+
// Suggest to take and ignore the arguments with expected_args_length `_`s if
160+
// found arguments is empty (assume the user just wants to ignore args in this case).
161+
// For example, if `expected_args_length` is 2, suggest `|_, _|`.
162+
if found_args.is_empty() && is_closure {
163+
let underscores = vec!["_"; expected_args.len()].join(", ");
164+
err.span_suggestion_verbose(
165+
closure_arg_span.unwrap_or(found_span),
166+
format!(
167+
"consider changing the closure to take and ignore the expected argument{}",
168+
pluralize!(expected_args.len())
169+
),
170+
format!("|{underscores}|"),
171+
Applicability::MachineApplicable,
172+
);
173+
}
174+
175+
if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] {
176+
if fields.len() == expected_args.len() {
177+
let sugg = fields
178+
.iter()
179+
.map(|(name, _)| name.to_owned())
180+
.collect::<Vec<String>>()
181+
.join(", ");
182+
err.span_suggestion_verbose(
183+
found_span,
184+
"change the closure to take multiple arguments instead of a single tuple",
185+
format!("|{sugg}|"),
186+
Applicability::MachineApplicable,
187+
);
188+
}
189+
}
190+
if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..]
191+
&& fields.len() == found_args.len()
192+
&& is_closure
193+
{
194+
let sugg = format!(
195+
"|({}){}|",
196+
found_args
197+
.iter()
198+
.map(|arg| match arg {
199+
ArgKind::Arg(name, _) => name.to_owned(),
200+
_ => "_".to_owned(),
201+
})
202+
.collect::<Vec<String>>()
203+
.join(", "),
204+
// add type annotations if available
205+
if found_args.iter().any(|arg| match arg {
206+
ArgKind::Arg(_, ty) => ty != "_",
207+
_ => false,
208+
}) {
209+
format!(
210+
": ({})",
211+
fields
212+
.iter()
213+
.map(|(_, ty)| ty.to_owned())
214+
.collect::<Vec<String>>()
215+
.join(", ")
216+
)
217+
} else {
218+
String::new()
219+
},
220+
);
221+
err.span_suggestion_verbose(
222+
found_span,
223+
"change the closure to accept a tuple instead of individual arguments",
224+
sugg,
225+
Applicability::MachineApplicable,
226+
);
227+
}
228+
}
229+
230+
err
231+
}
232+
233+
fn type_implements_fn_trait(
234+
&self,
235+
param_env: ty::ParamEnv<'tcx>,
236+
ty: ty::Binder<'tcx, Ty<'tcx>>,
237+
polarity: ty::ImplPolarity,
238+
) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> {
239+
self.commit_if_ok(|_| {
240+
for trait_def_id in [
241+
self.tcx.lang_items().fn_trait(),
242+
self.tcx.lang_items().fn_mut_trait(),
243+
self.tcx.lang_items().fn_once_trait(),
244+
] {
245+
let Some(trait_def_id) = trait_def_id else { continue };
246+
// Make a fresh inference variable so we can determine what the substitutions
247+
// of the trait are.
248+
let var = self.next_ty_var(TypeVariableOrigin {
249+
span: DUMMY_SP,
250+
kind: TypeVariableOriginKind::MiscVariable,
251+
});
252+
// FIXME(effects)
253+
let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty.skip_binder(), var]);
254+
let obligation = Obligation::new(
255+
self.tcx,
256+
ObligationCause::dummy(),
257+
param_env,
258+
ty.rebind(ty::TraitPredicate { trait_ref, polarity }),
259+
);
260+
let ocx = ObligationCtxt::new(self);
261+
ocx.register_obligation(obligation);
262+
if ocx.select_all_or_error().is_empty() {
263+
return Ok((
264+
self.tcx
265+
.fn_trait_kind_from_def_id(trait_def_id)
266+
.expect("expected to map DefId to ClosureKind"),
267+
ty.rebind(self.resolve_vars_if_possible(var)),
268+
));
269+
}
270+
}
271+
272+
Err(())
273+
})
274+
}
275+
}

0 commit comments

Comments
 (0)