Skip to content

Commit 6cb98e3

Browse files
authored
Rollup merge of #75711 - CohenArthur:split-up-astconv, r=oli-obk
Split `astconv.rs` into its own submodule Fixes #67418 This changed induced a few changes across the Type checker, but only there. Mostly, it was just renaming `Self::` into something else to call specific methods from a subtrait instead of having a 2500+ lines one. I split up the `astconv.rs` file into its own module. This way, directives such as ```rust use crate::astconv::AstConv; ``` are still valid, and doing ```rust use crate::astconv::{AstConv, AstConvGeneric}; ``` is possible (instead of having two modules, one named `astconv_generic.rs` for example and `astconv.rs`) I'm not entirely sure that the name `AstConvGeneric` is a good one. However, only methods related to lifetimes or generics have been moved over to this module. Sorry about the large diff. I'd be very happy to make any correction you deem necessary. r? @oli-obk
2 parents 8af33ad + e6642e4 commit 6cb98e3

File tree

6 files changed

+1096
-1059
lines changed

6 files changed

+1096
-1059
lines changed

src/librustc_typeck/astconv/errors.rs

+388
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,388 @@
1+
use crate::astconv::AstConv;
2+
use rustc_ast::util::lev_distance::find_best_match_for_name;
3+
use rustc_data_structures::fx::FxHashMap;
4+
use rustc_errors::{pluralize, struct_span_err, Applicability};
5+
use rustc_hir as hir;
6+
use rustc_hir::def_id::DefId;
7+
use rustc_middle::ty;
8+
use rustc_session::parse::feature_err;
9+
use rustc_span::symbol::{sym, Ident};
10+
use rustc_span::{Span, DUMMY_SP};
11+
12+
use std::collections::BTreeSet;
13+
14+
impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
15+
/// On missing type parameters, emit an E0393 error and provide a structured suggestion using
16+
/// the type parameter's name as a placeholder.
17+
pub(crate) fn complain_about_missing_type_params(
18+
&self,
19+
missing_type_params: Vec<String>,
20+
def_id: DefId,
21+
span: Span,
22+
empty_generic_args: bool,
23+
) {
24+
if missing_type_params.is_empty() {
25+
return;
26+
}
27+
let display =
28+
missing_type_params.iter().map(|n| format!("`{}`", n)).collect::<Vec<_>>().join(", ");
29+
let mut err = struct_span_err!(
30+
self.tcx().sess,
31+
span,
32+
E0393,
33+
"the type parameter{} {} must be explicitly specified",
34+
pluralize!(missing_type_params.len()),
35+
display,
36+
);
37+
err.span_label(
38+
self.tcx().def_span(def_id),
39+
&format!(
40+
"type parameter{} {} must be specified for this",
41+
pluralize!(missing_type_params.len()),
42+
display,
43+
),
44+
);
45+
let mut suggested = false;
46+
if let (Ok(snippet), true) = (
47+
self.tcx().sess.source_map().span_to_snippet(span),
48+
// Don't suggest setting the type params if there are some already: the order is
49+
// tricky to get right and the user will already know what the syntax is.
50+
empty_generic_args,
51+
) {
52+
if snippet.ends_with('>') {
53+
// The user wrote `Trait<'a, T>` or similar. To provide an accurate suggestion
54+
// we would have to preserve the right order. For now, as clearly the user is
55+
// aware of the syntax, we do nothing.
56+
} else {
57+
// The user wrote `Iterator`, so we don't have a type we can suggest, but at
58+
// least we can clue them to the correct syntax `Iterator<Type>`.
59+
err.span_suggestion(
60+
span,
61+
&format!(
62+
"set the type parameter{plural} to the desired type{plural}",
63+
plural = pluralize!(missing_type_params.len()),
64+
),
65+
format!("{}<{}>", snippet, missing_type_params.join(", ")),
66+
Applicability::HasPlaceholders,
67+
);
68+
suggested = true;
69+
}
70+
}
71+
if !suggested {
72+
err.span_label(
73+
span,
74+
format!(
75+
"missing reference{} to {}",
76+
pluralize!(missing_type_params.len()),
77+
display,
78+
),
79+
);
80+
}
81+
err.note(
82+
"because of the default `Self` reference, type parameters must be \
83+
specified on object types",
84+
);
85+
err.emit();
86+
}
87+
88+
/// When the code is using the `Fn` traits directly, instead of the `Fn(A) -> B` syntax, emit
89+
/// an error and attempt to build a reasonable structured suggestion.
90+
pub(crate) fn complain_about_internal_fn_trait(
91+
&self,
92+
span: Span,
93+
trait_def_id: DefId,
94+
trait_segment: &'a hir::PathSegment<'a>,
95+
) {
96+
let trait_def = self.tcx().trait_def(trait_def_id);
97+
98+
if !self.tcx().features().unboxed_closures
99+
&& trait_segment.generic_args().parenthesized != trait_def.paren_sugar
100+
{
101+
let sess = &self.tcx().sess.parse_sess;
102+
// For now, require that parenthetical notation be used only with `Fn()` etc.
103+
let (msg, sugg) = if trait_def.paren_sugar {
104+
(
105+
"the precise format of `Fn`-family traits' type parameters is subject to \
106+
change",
107+
Some(format!(
108+
"{}{} -> {}",
109+
trait_segment.ident,
110+
trait_segment
111+
.args
112+
.as_ref()
113+
.and_then(|args| args.args.get(0))
114+
.and_then(|arg| match arg {
115+
hir::GenericArg::Type(ty) => match ty.kind {
116+
hir::TyKind::Tup(t) => t
117+
.iter()
118+
.map(|e| sess.source_map().span_to_snippet(e.span))
119+
.collect::<Result<Vec<_>, _>>()
120+
.map(|a| a.join(", ")),
121+
_ => sess.source_map().span_to_snippet(ty.span),
122+
}
123+
.map(|s| format!("({})", s))
124+
.ok(),
125+
_ => None,
126+
})
127+
.unwrap_or_else(|| "()".to_string()),
128+
trait_segment
129+
.generic_args()
130+
.bindings
131+
.iter()
132+
.find_map(|b| match (b.ident.name == sym::Output, &b.kind) {
133+
(true, hir::TypeBindingKind::Equality { ty }) => {
134+
sess.source_map().span_to_snippet(ty.span).ok()
135+
}
136+
_ => None,
137+
})
138+
.unwrap_or_else(|| "()".to_string()),
139+
)),
140+
)
141+
} else {
142+
("parenthetical notation is only stable when used with `Fn`-family traits", None)
143+
};
144+
let mut err = feature_err(sess, sym::unboxed_closures, span, msg);
145+
if let Some(sugg) = sugg {
146+
let msg = "use parenthetical notation instead";
147+
err.span_suggestion(span, msg, sugg, Applicability::MaybeIncorrect);
148+
}
149+
err.emit();
150+
}
151+
}
152+
153+
pub(crate) fn complain_about_assoc_type_not_found<I>(
154+
&self,
155+
all_candidates: impl Fn() -> I,
156+
ty_param_name: &str,
157+
assoc_name: Ident,
158+
span: Span,
159+
) where
160+
I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
161+
{
162+
// The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a
163+
// valid span, so we point at the whole path segment instead.
164+
let span = if assoc_name.span != DUMMY_SP { assoc_name.span } else { span };
165+
let mut err = struct_span_err!(
166+
self.tcx().sess,
167+
span,
168+
E0220,
169+
"associated type `{}` not found for `{}`",
170+
assoc_name,
171+
ty_param_name
172+
);
173+
174+
let all_candidate_names: Vec<_> = all_candidates()
175+
.map(|r| self.tcx().associated_items(r.def_id()).in_definition_order())
176+
.flatten()
177+
.filter_map(
178+
|item| if item.kind == ty::AssocKind::Type { Some(item.ident.name) } else { None },
179+
)
180+
.collect();
181+
182+
if let (Some(suggested_name), true) = (
183+
find_best_match_for_name(all_candidate_names.iter(), assoc_name.name, None),
184+
assoc_name.span != DUMMY_SP,
185+
) {
186+
err.span_suggestion(
187+
assoc_name.span,
188+
"there is an associated type with a similar name",
189+
suggested_name.to_string(),
190+
Applicability::MaybeIncorrect,
191+
);
192+
} else {
193+
err.span_label(span, format!("associated type `{}` not found", assoc_name));
194+
}
195+
196+
err.emit();
197+
}
198+
199+
/// When there are any missing associated types, emit an E0191 error and attempt to supply a
200+
/// reasonable suggestion on how to write it. For the case of multiple associated types in the
201+
/// same trait bound have the same name (as they come from different super-traits), we instead
202+
/// emit a generic note suggesting using a `where` clause to constraint instead.
203+
pub(crate) fn complain_about_missing_associated_types(
204+
&self,
205+
associated_types: FxHashMap<Span, BTreeSet<DefId>>,
206+
potential_assoc_types: Vec<Span>,
207+
trait_bounds: &[hir::PolyTraitRef<'_>],
208+
) {
209+
if associated_types.values().all(|v| v.is_empty()) {
210+
return;
211+
}
212+
let tcx = self.tcx();
213+
// FIXME: Marked `mut` so that we can replace the spans further below with a more
214+
// appropriate one, but this should be handled earlier in the span assignment.
215+
let mut associated_types: FxHashMap<Span, Vec<_>> = associated_types
216+
.into_iter()
217+
.map(|(span, def_ids)| {
218+
(span, def_ids.into_iter().map(|did| tcx.associated_item(did)).collect())
219+
})
220+
.collect();
221+
let mut names = vec![];
222+
223+
// Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and
224+
// `issue-22560.rs`.
225+
let mut trait_bound_spans: Vec<Span> = vec![];
226+
for (span, items) in &associated_types {
227+
if !items.is_empty() {
228+
trait_bound_spans.push(*span);
229+
}
230+
for assoc_item in items {
231+
let trait_def_id = assoc_item.container.id();
232+
names.push(format!(
233+
"`{}` (from trait `{}`)",
234+
assoc_item.ident,
235+
tcx.def_path_str(trait_def_id),
236+
));
237+
}
238+
}
239+
if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) {
240+
match &bound.trait_ref.path.segments[..] {
241+
// FIXME: `trait_ref.path.span` can point to a full path with multiple
242+
// segments, even though `trait_ref.path.segments` is of length `1`. Work
243+
// around that bug here, even though it should be fixed elsewhere.
244+
// This would otherwise cause an invalid suggestion. For an example, look at
245+
// `src/test/ui/issues/issue-28344.rs` where instead of the following:
246+
//
247+
// error[E0191]: the value of the associated type `Output`
248+
// (from trait `std::ops::BitXor`) must be specified
249+
// --> $DIR/issue-28344.rs:4:17
250+
// |
251+
// LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
252+
// | ^^^^^^ help: specify the associated type:
253+
// | `BitXor<Output = Type>`
254+
//
255+
// we would output:
256+
//
257+
// error[E0191]: the value of the associated type `Output`
258+
// (from trait `std::ops::BitXor`) must be specified
259+
// --> $DIR/issue-28344.rs:4:17
260+
// |
261+
// LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
262+
// | ^^^^^^^^^^^^^ help: specify the associated type:
263+
// | `BitXor::bitor<Output = Type>`
264+
[segment] if segment.args.is_none() => {
265+
trait_bound_spans = vec![segment.ident.span];
266+
associated_types = associated_types
267+
.into_iter()
268+
.map(|(_, items)| (segment.ident.span, items))
269+
.collect();
270+
}
271+
_ => {}
272+
}
273+
}
274+
names.sort();
275+
trait_bound_spans.sort();
276+
let mut err = struct_span_err!(
277+
tcx.sess,
278+
trait_bound_spans,
279+
E0191,
280+
"the value of the associated type{} {} must be specified",
281+
pluralize!(names.len()),
282+
names.join(", "),
283+
);
284+
let mut suggestions = vec![];
285+
let mut types_count = 0;
286+
let mut where_constraints = vec![];
287+
for (span, assoc_items) in &associated_types {
288+
let mut names: FxHashMap<_, usize> = FxHashMap::default();
289+
for item in assoc_items {
290+
types_count += 1;
291+
*names.entry(item.ident.name).or_insert(0) += 1;
292+
}
293+
let mut dupes = false;
294+
for item in assoc_items {
295+
let prefix = if names[&item.ident.name] > 1 {
296+
let trait_def_id = item.container.id();
297+
dupes = true;
298+
format!("{}::", tcx.def_path_str(trait_def_id))
299+
} else {
300+
String::new()
301+
};
302+
if let Some(sp) = tcx.hir().span_if_local(item.def_id) {
303+
err.span_label(sp, format!("`{}{}` defined here", prefix, item.ident));
304+
}
305+
}
306+
if potential_assoc_types.len() == assoc_items.len() {
307+
// Only suggest when the amount of missing associated types equals the number of
308+
// extra type arguments present, as that gives us a relatively high confidence
309+
// that the user forgot to give the associtated type's name. The canonical
310+
// example would be trying to use `Iterator<isize>` instead of
311+
// `Iterator<Item = isize>`.
312+
for (potential, item) in potential_assoc_types.iter().zip(assoc_items.iter()) {
313+
if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*potential) {
314+
suggestions.push((*potential, format!("{} = {}", item.ident, snippet)));
315+
}
316+
}
317+
} else if let (Ok(snippet), false) =
318+
(tcx.sess.source_map().span_to_snippet(*span), dupes)
319+
{
320+
let types: Vec<_> =
321+
assoc_items.iter().map(|item| format!("{} = Type", item.ident)).collect();
322+
let code = if snippet.ends_with('>') {
323+
// The user wrote `Trait<'a>` or similar and we don't have a type we can
324+
// suggest, but at least we can clue them to the correct syntax
325+
// `Trait<'a, Item = Type>` while accounting for the `<'a>` in the
326+
// suggestion.
327+
format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", "))
328+
} else {
329+
// The user wrote `Iterator`, so we don't have a type we can suggest, but at
330+
// least we can clue them to the correct syntax `Iterator<Item = Type>`.
331+
format!("{}<{}>", snippet, types.join(", "))
332+
};
333+
suggestions.push((*span, code));
334+
} else if dupes {
335+
where_constraints.push(*span);
336+
}
337+
}
338+
let where_msg = "consider introducing a new type parameter, adding `where` constraints \
339+
using the fully-qualified path to the associated types";
340+
if !where_constraints.is_empty() && suggestions.is_empty() {
341+
// If there are duplicates associated type names and a single trait bound do not
342+
// use structured suggestion, it means that there are multiple super-traits with
343+
// the same associated type name.
344+
err.help(where_msg);
345+
}
346+
if suggestions.len() != 1 {
347+
// We don't need this label if there's an inline suggestion, show otherwise.
348+
for (span, assoc_items) in &associated_types {
349+
let mut names: FxHashMap<_, usize> = FxHashMap::default();
350+
for item in assoc_items {
351+
types_count += 1;
352+
*names.entry(item.ident.name).or_insert(0) += 1;
353+
}
354+
let mut label = vec![];
355+
for item in assoc_items {
356+
let postfix = if names[&item.ident.name] > 1 {
357+
let trait_def_id = item.container.id();
358+
format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id))
359+
} else {
360+
String::new()
361+
};
362+
label.push(format!("`{}`{}", item.ident, postfix));
363+
}
364+
if !label.is_empty() {
365+
err.span_label(
366+
*span,
367+
format!(
368+
"associated type{} {} must be specified",
369+
pluralize!(label.len()),
370+
label.join(", "),
371+
),
372+
);
373+
}
374+
}
375+
}
376+
if !suggestions.is_empty() {
377+
err.multipart_suggestion(
378+
&format!("specify the associated type{}", pluralize!(types_count)),
379+
suggestions,
380+
Applicability::HasPlaceholders,
381+
);
382+
if !where_constraints.is_empty() {
383+
err.span_help(where_constraints, where_msg);
384+
}
385+
}
386+
err.emit();
387+
}
388+
}

0 commit comments

Comments
 (0)