Skip to content

Commit dcaeced

Browse files
committed
resolve: Improve diagnostics for resolution ambiguities
1 parent 3ba2761 commit dcaeced

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+603
-425
lines changed

src/librustc/hir/def.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,6 @@ impl PathResolution {
128128
pub fn unresolved_segments(&self) -> usize {
129129
self.unresolved_segments
130130
}
131-
132-
pub fn kind_name(&self) -> &'static str {
133-
if self.unresolved_segments != 0 {
134-
"associated item"
135-
} else {
136-
self.base_def.kind_name()
137-
}
138-
}
139131
}
140132

141133
/// Different kinds of symbols don't influence each other.
@@ -333,4 +325,12 @@ impl Def {
333325
Def::Err => "unresolved item",
334326
}
335327
}
328+
329+
pub fn article(&self) -> &'static str {
330+
match *self {
331+
Def::AssociatedTy(..) | Def::AssociatedConst(..) | Def::AssociatedExistential(..) |
332+
Def::Enum(..) | Def::Existential(..) | Def::Err => "an",
333+
_ => "a",
334+
}
335+
}
336336
}

src/librustc_mir/hair/pattern/check_match.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,8 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
298298
let label_msg = match pat.node {
299299
PatKind::Path(hir::QPath::Resolved(None, ref path))
300300
if path.segments.len() == 1 && path.segments[0].args.is_none() => {
301-
format!("interpreted as a {} pattern, not new variable", path.def.kind_name())
301+
format!("interpreted as {} {} pattern, not new variable",
302+
path.def.article(), path.def.kind_name())
302303
}
303304
_ => format!("pattern `{}` not covered", pattern_string),
304305
};

src/librustc_resolve/build_reduced_graph.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,23 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
344344
let used = self.process_legacy_macro_imports(item, module, &parent_scope);
345345
let binding =
346346
(module, ty::Visibility::Public, sp, expansion).to_name_binding(self.arenas);
347+
let directive = self.arenas.alloc_import_directive(ImportDirective {
348+
root_id: item.id,
349+
id: item.id,
350+
parent_scope,
351+
imported_module: Cell::new(Some(ModuleOrUniformRoot::Module(module))),
352+
subclass: ImportDirectiveSubclass::ExternCrate {
353+
source: orig_name,
354+
target: ident,
355+
},
356+
root_span: item.span,
357+
span: item.span,
358+
module_path: Vec::new(),
359+
vis: Cell::new(vis),
360+
used: Cell::new(used),
361+
});
362+
self.potentially_unused_imports.push(directive);
363+
let imported_binding = self.import(binding, directive);
347364
if ptr::eq(self.current_module, self.graph_root) {
348365
if let Some(entry) = self.extern_prelude.get(&ident.modern()) {
349366
if expansion != Mark::root() && orig_name.is_some() &&
@@ -358,28 +375,11 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
358375
extern_crate_item: None,
359376
introduced_by_item: true,
360377
});
361-
entry.extern_crate_item = Some(binding);
378+
entry.extern_crate_item = Some(imported_binding);
362379
if orig_name.is_some() {
363380
entry.introduced_by_item = true;
364381
}
365382
}
366-
let directive = self.arenas.alloc_import_directive(ImportDirective {
367-
root_id: item.id,
368-
id: item.id,
369-
parent_scope,
370-
imported_module: Cell::new(Some(ModuleOrUniformRoot::Module(module))),
371-
subclass: ImportDirectiveSubclass::ExternCrate {
372-
source: orig_name,
373-
target: ident,
374-
},
375-
root_span: item.span,
376-
span: item.span,
377-
module_path: Vec::new(),
378-
vis: Cell::new(vis),
379-
used: Cell::new(used),
380-
});
381-
self.potentially_unused_imports.push(directive);
382-
let imported_binding = self.import(binding, directive);
383383
self.define(parent, ident, TypeNS, imported_binding);
384384
}
385385

src/librustc_resolve/lib.rs

Lines changed: 144 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -391,14 +391,13 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver,
391391
err
392392
}
393393
ResolutionError::BindingShadowsSomethingUnacceptable(what_binding, name, binding) => {
394-
let shadows_what = PathResolution::new(binding.def()).kind_name();
395-
let mut err = struct_span_err!(resolver.session,
396-
span,
397-
E0530,
398-
"{}s cannot shadow {}s", what_binding, shadows_what);
399-
err.span_label(span, format!("cannot be named the same as a {}", shadows_what));
394+
let (shadows_what, article) = (binding.descr(), binding.article());
395+
let mut err = struct_span_err!(resolver.session, span, E0530, "{}s cannot shadow {}s",
396+
what_binding, shadows_what);
397+
err.span_label(span, format!("cannot be named the same as {} {}",
398+
article, shadows_what));
400399
let participle = if binding.is_import() { "imported" } else { "defined" };
401-
let msg = format!("a {} `{}` is {} here", shadows_what, name, participle);
400+
let msg = format!("{} {} `{}` is {} here", article, shadows_what, name, participle);
402401
err.span_label(binding.span, msg);
403402
err
404403
}
@@ -1158,6 +1157,7 @@ enum NameBindingKind<'a> {
11581157
used: Cell<bool>,
11591158
},
11601159
Ambiguity {
1160+
kind: AmbiguityKind,
11611161
b1: &'a NameBinding<'a>,
11621162
b2: &'a NameBinding<'a>,
11631163
}
@@ -1175,10 +1175,61 @@ struct UseError<'a> {
11751175
better: bool,
11761176
}
11771177

1178+
#[derive(Clone, Copy, PartialEq, Debug)]
1179+
enum AmbiguityKind {
1180+
Import,
1181+
BuiltinAttr,
1182+
DeriveHelper,
1183+
LegacyHelperVsPrelude,
1184+
LegacyVsModern,
1185+
GlobVsOuter,
1186+
GlobVsGlob,
1187+
GlobVsExpanded,
1188+
MoreExpandedVsOuter,
1189+
}
1190+
1191+
impl AmbiguityKind {
1192+
fn descr(self) -> &'static str {
1193+
match self {
1194+
AmbiguityKind::Import =>
1195+
"name vs any other name during import resolution",
1196+
AmbiguityKind::BuiltinAttr =>
1197+
"built-in attribute vs any other name",
1198+
AmbiguityKind::DeriveHelper =>
1199+
"derive helper attribute vs any other name",
1200+
AmbiguityKind::LegacyHelperVsPrelude =>
1201+
"legacy plugin helper attribute vs name from prelude",
1202+
AmbiguityKind::LegacyVsModern =>
1203+
"`macro_rules` vs non-`macro_rules` from other module",
1204+
AmbiguityKind::GlobVsOuter =>
1205+
"glob import vs any other name from outer scope during import/macro resolution",
1206+
AmbiguityKind::GlobVsGlob =>
1207+
"glob import vs glob import in the same module",
1208+
AmbiguityKind::GlobVsExpanded =>
1209+
"glob import vs macro-expanded name in the same \
1210+
module during import/macro resolution",
1211+
AmbiguityKind::MoreExpandedVsOuter =>
1212+
"macro-expanded name vs less macro-expanded name \
1213+
from outer scope during import/macro resolution",
1214+
}
1215+
}
1216+
}
1217+
1218+
/// Miscellaneous bits of metadata for better ambiguity error reporting.
1219+
#[derive(Clone, Copy, PartialEq)]
1220+
enum AmbiguityErrorMisc {
1221+
SuggestSelf,
1222+
FromPrelude,
1223+
None,
1224+
}
1225+
11781226
struct AmbiguityError<'a> {
1227+
kind: AmbiguityKind,
11791228
ident: Ident,
11801229
b1: &'a NameBinding<'a>,
11811230
b2: &'a NameBinding<'a>,
1231+
misc1: AmbiguityErrorMisc,
1232+
misc2: AmbiguityErrorMisc,
11821233
}
11831234

11841235
impl<'a> NameBinding<'a> {
@@ -1231,6 +1282,9 @@ impl<'a> NameBinding<'a> {
12311282
subclass: ImportDirectiveSubclass::ExternCrate { .. }, ..
12321283
}, ..
12331284
} => true,
1285+
NameBindingKind::Module(
1286+
&ModuleData { kind: ModuleKind::Def(Def::Mod(def_id), _), .. }
1287+
) => def_id.index == CRATE_DEF_INDEX,
12341288
_ => false,
12351289
}
12361290
}
@@ -1276,6 +1330,10 @@ impl<'a> NameBinding<'a> {
12761330
if self.is_extern_crate() { "extern crate" } else { self.def().kind_name() }
12771331
}
12781332

1333+
fn article(&self) -> &'static str {
1334+
if self.is_extern_crate() { "an" } else { self.def().article() }
1335+
}
1336+
12791337
// Suppose that we resolved macro invocation with `invoc_parent_expansion` to binding `binding`
12801338
// at some expansion round `max(invoc, binding)` when they both emerged from macros.
12811339
// Then this function returns `true` if `self` may emerge from a macro *after* that
@@ -1826,8 +1884,12 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
18261884
self.record_use(ident, ns, binding)
18271885
}
18281886
NameBindingKind::Import { .. } => false,
1829-
NameBindingKind::Ambiguity { b1, b2 } => {
1830-
self.ambiguity_errors.push(AmbiguityError { ident, b1, b2 });
1887+
NameBindingKind::Ambiguity { kind, b1, b2 } => {
1888+
self.ambiguity_errors.push(AmbiguityError {
1889+
kind, ident, b1, b2,
1890+
misc1: AmbiguityErrorMisc::None,
1891+
misc2: AmbiguityErrorMisc::None,
1892+
});
18311893
true
18321894
}
18331895
_ => false
@@ -1965,7 +2027,7 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
19652027
}
19662028
if ns == TypeNS && is_known_tool(ident.name) {
19672029
let binding = (Def::ToolMod, ty::Visibility::Public,
1968-
ident.span, Mark::root()).to_name_binding(self.arenas);
2030+
DUMMY_SP, Mark::root()).to_name_binding(self.arenas);
19692031
return Some(LexicalScopeBinding::Item(binding));
19702032
}
19712033
if let Some(prelude) = self.prelude {
@@ -4565,37 +4627,79 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
45654627
}
45664628
}
45674629

4568-
fn report_ambiguity_error(&self, ident: Ident, b1: &NameBinding, b2: &NameBinding) {
4569-
let participle = |is_import: bool| if is_import { "imported" } else { "defined" };
4570-
let msg1 =
4571-
format!("`{}` could refer to the name {} here", ident, participle(b1.is_import()));
4572-
let msg2 =
4573-
format!("`{}` could also refer to the name {} here", ident, participle(b2.is_import()));
4574-
let note = if b1.expansion != Mark::root() {
4575-
Some(if let Def::Macro(..) = b1.def() {
4576-
format!("macro-expanded {} do not shadow",
4577-
if b1.is_import() { "macro imports" } else { "macros" })
4578-
} else {
4579-
format!("macro-expanded {} do not shadow when used in a macro invocation path",
4580-
if b1.is_import() { "imports" } else { "items" })
4581-
})
4582-
} else if b1.is_glob_import() {
4583-
Some(format!("consider adding an explicit import of `{}` to disambiguate", ident))
4630+
fn report_ambiguity_error(&self, ambiguity_error: &AmbiguityError) {
4631+
let AmbiguityError { kind, ident, b1, b2, misc1, misc2 } = *ambiguity_error;
4632+
let (b1, b2, misc1, misc2, swapped) = if b2.span.is_dummy() && !b1.span.is_dummy() {
4633+
// We have to print the span-less alternative first, otherwise formatting looks bad.
4634+
(b2, b1, misc2, misc1, true)
45844635
} else {
4585-
None
4636+
(b1, b2, misc1, misc2, false)
45864637
};
45874638

4588-
let mut err = struct_span_err!(self.session, ident.span, E0659, "`{}` is ambiguous", ident);
4639+
let mut err = struct_span_err!(self.session, ident.span, E0659,
4640+
"`{ident}` is ambiguous ({why})",
4641+
ident = ident, why = kind.descr());
45894642
err.span_label(ident.span, "ambiguous name");
4590-
err.span_note(b1.span, &msg1);
4591-
match b2.def() {
4592-
Def::Macro(..) if b2.span.is_dummy() =>
4593-
err.note(&format!("`{}` is also a builtin macro", ident)),
4594-
_ => err.span_note(b2.span, &msg2),
4643+
4644+
let mut could_refer_to = |b: &NameBinding, misc: AmbiguityErrorMisc, also: &str| {
4645+
let what = if b.span.is_dummy() {
4646+
let add_built_in = match b.def() {
4647+
// These already contain the "built-in" prefix or look bad with it.
4648+
Def::NonMacroAttr(..) | Def::PrimTy(..) | Def::ToolMod => false,
4649+
_ => true,
4650+
};
4651+
let (built_in, from) = if misc == AmbiguityErrorMisc::FromPrelude {
4652+
("", " from prelude")
4653+
} else if b.is_extern_crate() && !b.is_import() &&
4654+
self.session.opts.externs.get(&ident.as_str()).is_some() {
4655+
("", " passed with `--extern`")
4656+
} else if add_built_in {
4657+
(" built-in", "")
4658+
} else {
4659+
("", "")
4660+
};
4661+
4662+
let article = if built_in.is_empty() { b.article() } else { "a" };
4663+
format!("{a}{built_in} {thing}{from}",
4664+
a = article, thing = b.descr(), built_in = built_in, from = from)
4665+
} else {
4666+
let participle = if b.is_import() { "imported" } else { "defined" };
4667+
format!("the {thing} {introduced} here",
4668+
thing = b.descr(), introduced = participle)
4669+
};
4670+
let note_msg = format!("`{ident}` could{also} refer to {what}",
4671+
ident = ident, also = also, what = what);
4672+
4673+
let mut help_msgs = Vec::new();
4674+
if b.is_glob_import() && (kind == AmbiguityKind::GlobVsGlob ||
4675+
kind == AmbiguityKind::GlobVsExpanded ||
4676+
kind == AmbiguityKind::GlobVsOuter &&
4677+
swapped != also.is_empty()) {
4678+
help_msgs.push(format!("consider adding an explicit import of \
4679+
`{ident}` to disambiguate", ident = ident))
4680+
}
4681+
if b.is_extern_crate() && self.session.rust_2018() {
4682+
help_msgs.push(format!("use `::{ident}` to refer to the {thing} unambiguously",
4683+
ident = ident, thing = b.descr()))
4684+
}
4685+
if misc == AmbiguityErrorMisc::SuggestSelf {
4686+
help_msgs.push(format!("use `self::{ident}` to refer to the {thing} unambiguously",
4687+
ident = ident, thing = b.descr()))
4688+
}
4689+
4690+
if b.span.is_dummy() {
4691+
err.note(&note_msg);
4692+
} else {
4693+
err.span_note(b.span, &note_msg);
4694+
}
4695+
for (i, help_msg) in help_msgs.iter().enumerate() {
4696+
let or = if i == 0 { "" } else { "or " };
4697+
err.help(&format!("{}{}", or, help_msg));
4698+
}
45954699
};
4596-
if let Some(note) = note {
4597-
err.note(&note);
4598-
}
4700+
4701+
could_refer_to(b1, misc1, "");
4702+
could_refer_to(b2, misc2, " also");
45994703
err.emit();
46004704
}
46014705

@@ -4614,9 +4718,9 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
46144718
);
46154719
}
46164720

4617-
for &AmbiguityError { ident, b1, b2 } in &self.ambiguity_errors {
4618-
if reported_spans.insert(ident.span) {
4619-
self.report_ambiguity_error(ident, b1, b2);
4721+
for ambiguity_error in &self.ambiguity_errors {
4722+
if reported_spans.insert(ambiguity_error.ident.span) {
4723+
self.report_ambiguity_error(ambiguity_error);
46204724
}
46214725
}
46224726

@@ -4794,7 +4898,7 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
47944898
};
47954899
let crate_root = self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX });
47964900
self.populate_module_if_necessary(&crate_root);
4797-
Some((crate_root, ty::Visibility::Public, ident.span, Mark::root())
4901+
Some((crate_root, ty::Visibility::Public, DUMMY_SP, Mark::root())
47984902
.to_name_binding(self.arenas))
47994903
}
48004904
})

0 commit comments

Comments
 (0)