Skip to content

Commit f0ea1c6

Browse files
committed
resolve: Improve diagnostics for resolution ambiguities
1 parent 9d7d9ad commit f0ea1c6

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

+604
-426
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
@@ -347,6 +347,23 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
347347
let used = self.process_legacy_macro_imports(item, module, &parent_scope);
348348
let binding =
349349
(module, ty::Visibility::Public, sp, expansion).to_name_binding(self.arenas);
350+
let directive = self.arenas.alloc_import_directive(ImportDirective {
351+
root_id: item.id,
352+
id: item.id,
353+
parent_scope,
354+
imported_module: Cell::new(Some(ModuleOrUniformRoot::Module(module))),
355+
subclass: ImportDirectiveSubclass::ExternCrate {
356+
source: orig_name,
357+
target: ident,
358+
},
359+
root_span: item.span,
360+
span: item.span,
361+
module_path: Vec::new(),
362+
vis: Cell::new(vis),
363+
used: Cell::new(used),
364+
});
365+
self.potentially_unused_imports.push(directive);
366+
let imported_binding = self.import(binding, directive);
350367
if ptr::eq(self.current_module, self.graph_root) {
351368
if let Some(entry) = self.extern_prelude.get(&ident.modern()) {
352369
if expansion != Mark::root() && orig_name.is_some() &&
@@ -361,28 +378,11 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
361378
extern_crate_item: None,
362379
introduced_by_item: true,
363380
});
364-
entry.extern_crate_item = Some(binding);
381+
entry.extern_crate_item = Some(imported_binding);
365382
if orig_name.is_some() {
366383
entry.introduced_by_item = true;
367384
}
368385
}
369-
let directive = self.arenas.alloc_import_directive(ImportDirective {
370-
root_id: item.id,
371-
id: item.id,
372-
parent_scope,
373-
imported_module: Cell::new(Some(ModuleOrUniformRoot::Module(module))),
374-
subclass: ImportDirectiveSubclass::ExternCrate {
375-
source: orig_name,
376-
target: ident,
377-
},
378-
root_span: item.span,
379-
span: item.span,
380-
module_path: Vec::new(),
381-
vis: Cell::new(vis),
382-
used: Cell::new(used),
383-
});
384-
self.potentially_unused_imports.push(directive);
385-
let imported_binding = self.import(binding, directive);
386386
self.define(parent, ident, TypeNS, imported_binding);
387387
}
388388

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
}
@@ -1195,6 +1194,7 @@ enum NameBindingKind<'a> {
11951194
used: Cell<bool>,
11961195
},
11971196
Ambiguity {
1197+
kind: AmbiguityKind,
11981198
b1: &'a NameBinding<'a>,
11991199
b2: &'a NameBinding<'a>,
12001200
}
@@ -1212,10 +1212,61 @@ struct UseError<'a> {
12121212
better: bool,
12131213
}
12141214

1215+
#[derive(Clone, Copy, PartialEq, Debug)]
1216+
enum AmbiguityKind {
1217+
Import,
1218+
BuiltinAttr,
1219+
DeriveHelper,
1220+
LegacyHelperVsPrelude,
1221+
LegacyVsModern,
1222+
GlobVsOuter,
1223+
GlobVsGlob,
1224+
GlobVsExpanded,
1225+
MoreExpandedVsOuter,
1226+
}
1227+
1228+
impl AmbiguityKind {
1229+
fn descr(self) -> &'static str {
1230+
match self {
1231+
AmbiguityKind::Import =>
1232+
"name vs any other name during import resolution",
1233+
AmbiguityKind::BuiltinAttr =>
1234+
"built-in attribute vs any other name",
1235+
AmbiguityKind::DeriveHelper =>
1236+
"derive helper attribute vs any other name",
1237+
AmbiguityKind::LegacyHelperVsPrelude =>
1238+
"legacy plugin helper attribute vs name from prelude",
1239+
AmbiguityKind::LegacyVsModern =>
1240+
"`macro_rules` vs non-`macro_rules` from other module",
1241+
AmbiguityKind::GlobVsOuter =>
1242+
"glob import vs any other name from outer scope during import/macro resolution",
1243+
AmbiguityKind::GlobVsGlob =>
1244+
"glob import vs glob import in the same module",
1245+
AmbiguityKind::GlobVsExpanded =>
1246+
"glob import vs macro-expanded name in the same \
1247+
module during import/macro resolution",
1248+
AmbiguityKind::MoreExpandedVsOuter =>
1249+
"macro-expanded name vs less macro-expanded name \
1250+
from outer scope during import/macro resolution",
1251+
}
1252+
}
1253+
}
1254+
1255+
/// Miscellaneous bits of metadata for better ambiguity error reporting.
1256+
#[derive(Clone, Copy, PartialEq)]
1257+
enum AmbiguityErrorMisc {
1258+
SuggestSelf,
1259+
FromPrelude,
1260+
None,
1261+
}
1262+
12151263
struct AmbiguityError<'a> {
1264+
kind: AmbiguityKind,
12161265
ident: Ident,
12171266
b1: &'a NameBinding<'a>,
12181267
b2: &'a NameBinding<'a>,
1268+
misc1: AmbiguityErrorMisc,
1269+
misc2: AmbiguityErrorMisc,
12191270
}
12201271

12211272
impl<'a> NameBinding<'a> {
@@ -1268,6 +1319,9 @@ impl<'a> NameBinding<'a> {
12681319
subclass: ImportDirectiveSubclass::ExternCrate { .. }, ..
12691320
}, ..
12701321
} => true,
1322+
NameBindingKind::Module(
1323+
&ModuleData { kind: ModuleKind::Def(Def::Mod(def_id), _), .. }
1324+
) => def_id.index == CRATE_DEF_INDEX,
12711325
_ => false,
12721326
}
12731327
}
@@ -1313,6 +1367,10 @@ impl<'a> NameBinding<'a> {
13131367
if self.is_extern_crate() { "extern crate" } else { self.def().kind_name() }
13141368
}
13151369

1370+
fn article(&self) -> &'static str {
1371+
if self.is_extern_crate() { "an" } else { self.def().article() }
1372+
}
1373+
13161374
// Suppose that we resolved macro invocation with `invoc_parent_expansion` to binding `binding`
13171375
// at some expansion round `max(invoc, binding)` when they both emerged from macros.
13181376
// Then this function returns `true` if `self` may emerge from a macro *after* that
@@ -1885,8 +1943,12 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
18851943
self.record_use(ident, ns, binding)
18861944
}
18871945
NameBindingKind::Import { .. } => false,
1888-
NameBindingKind::Ambiguity { b1, b2 } => {
1889-
self.ambiguity_errors.push(AmbiguityError { ident, b1, b2 });
1946+
NameBindingKind::Ambiguity { kind, b1, b2 } => {
1947+
self.ambiguity_errors.push(AmbiguityError {
1948+
kind, ident, b1, b2,
1949+
misc1: AmbiguityErrorMisc::None,
1950+
misc2: AmbiguityErrorMisc::None,
1951+
});
18901952
true
18911953
}
18921954
_ => false
@@ -2024,7 +2086,7 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
20242086
}
20252087
if ns == TypeNS && is_known_tool(ident.name) {
20262088
let binding = (Def::ToolMod, ty::Visibility::Public,
2027-
ident.span, Mark::root()).to_name_binding(self.arenas);
2089+
DUMMY_SP, Mark::root()).to_name_binding(self.arenas);
20282090
return Some(LexicalScopeBinding::Item(binding));
20292091
}
20302092
if let Some(prelude) = self.prelude {
@@ -4631,37 +4693,79 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
46314693
}
46324694
}
46334695

4634-
fn report_ambiguity_error(&self, ident: Ident, b1: &NameBinding, b2: &NameBinding) {
4635-
let participle = |is_import: bool| if is_import { "imported" } else { "defined" };
4636-
let msg1 =
4637-
format!("`{}` could refer to the name {} here", ident, participle(b1.is_import()));
4638-
let msg2 =
4639-
format!("`{}` could also refer to the name {} here", ident, participle(b2.is_import()));
4640-
let note = if b1.expansion != Mark::root() {
4641-
Some(if let Def::Macro(..) = b1.def() {
4642-
format!("macro-expanded {} do not shadow",
4643-
if b1.is_import() { "macro imports" } else { "macros" })
4644-
} else {
4645-
format!("macro-expanded {} do not shadow when used in a macro invocation path",
4646-
if b1.is_import() { "imports" } else { "items" })
4647-
})
4648-
} else if b1.is_glob_import() {
4649-
Some(format!("consider adding an explicit import of `{}` to disambiguate", ident))
4696+
fn report_ambiguity_error(&self, ambiguity_error: &AmbiguityError) {
4697+
let AmbiguityError { kind, ident, b1, b2, misc1, misc2 } = *ambiguity_error;
4698+
let (b1, b2, misc1, misc2, swapped) = if b2.span.is_dummy() && !b1.span.is_dummy() {
4699+
// We have to print the span-less alternative first, otherwise formatting looks bad.
4700+
(b2, b1, misc2, misc1, true)
46504701
} else {
4651-
None
4702+
(b1, b2, misc1, misc2, false)
46524703
};
46534704

4654-
let mut err = struct_span_err!(self.session, ident.span, E0659, "`{}` is ambiguous", ident);
4705+
let mut err = struct_span_err!(self.session, ident.span, E0659,
4706+
"`{ident}` is ambiguous ({why})",
4707+
ident = ident, why = kind.descr());
46554708
err.span_label(ident.span, "ambiguous name");
4656-
err.span_note(b1.span, &msg1);
4657-
match b2.def() {
4658-
Def::Macro(..) if b2.span.is_dummy() =>
4659-
err.note(&format!("`{}` is also a builtin macro", ident)),
4660-
_ => err.span_note(b2.span, &msg2),
4709+
4710+
let mut could_refer_to = |b: &NameBinding, misc: AmbiguityErrorMisc, also: &str| {
4711+
let what = if b.span.is_dummy() {
4712+
let add_built_in = match b.def() {
4713+
// These already contain the "built-in" prefix or look bad with it.
4714+
Def::NonMacroAttr(..) | Def::PrimTy(..) | Def::ToolMod => false,
4715+
_ => true,
4716+
};
4717+
let (built_in, from) = if misc == AmbiguityErrorMisc::FromPrelude {
4718+
("", " from prelude")
4719+
} else if b.is_extern_crate() && !b.is_import() &&
4720+
self.session.opts.externs.get(&ident.as_str()).is_some() {
4721+
("", " passed with `--extern`")
4722+
} else if add_built_in {
4723+
(" built-in", "")
4724+
} else {
4725+
("", "")
4726+
};
4727+
4728+
let article = if built_in.is_empty() { b.article() } else { "a" };
4729+
format!("{a}{built_in} {thing}{from}",
4730+
a = article, thing = b.descr(), built_in = built_in, from = from)
4731+
} else {
4732+
let participle = if b.is_import() { "imported" } else { "defined" };
4733+
format!("the {thing} {introduced} here",
4734+
thing = b.descr(), introduced = participle)
4735+
};
4736+
let note_msg = format!("`{ident}` could{also} refer to {what}",
4737+
ident = ident, also = also, what = what);
4738+
4739+
let mut help_msgs = Vec::new();
4740+
if b.is_glob_import() && (kind == AmbiguityKind::GlobVsGlob ||
4741+
kind == AmbiguityKind::GlobVsExpanded ||
4742+
kind == AmbiguityKind::GlobVsOuter &&
4743+
swapped != also.is_empty()) {
4744+
help_msgs.push(format!("consider adding an explicit import of \
4745+
`{ident}` to disambiguate", ident = ident))
4746+
}
4747+
if b.is_extern_crate() && self.session.rust_2018() {
4748+
help_msgs.push(format!("use `::{ident}` to refer to the {thing} unambiguously",
4749+
ident = ident, thing = b.descr()))
4750+
}
4751+
if misc == AmbiguityErrorMisc::SuggestSelf {
4752+
help_msgs.push(format!("use `self::{ident}` to refer to the {thing} unambiguously",
4753+
ident = ident, thing = b.descr()))
4754+
}
4755+
4756+
if b.span.is_dummy() {
4757+
err.note(&note_msg);
4758+
} else {
4759+
err.span_note(b.span, &note_msg);
4760+
}
4761+
for (i, help_msg) in help_msgs.iter().enumerate() {
4762+
let or = if i == 0 { "" } else { "or " };
4763+
err.help(&format!("{}{}", or, help_msg));
4764+
}
46614765
};
4662-
if let Some(note) = note {
4663-
err.note(&note);
4664-
}
4766+
4767+
could_refer_to(b1, misc1, "");
4768+
could_refer_to(b2, misc2, " also");
46654769
err.emit();
46664770
}
46674771

@@ -4680,9 +4784,9 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
46804784
);
46814785
}
46824786

4683-
for &AmbiguityError { ident, b1, b2 } in &self.ambiguity_errors {
4684-
if reported_spans.insert(ident.span) {
4685-
self.report_ambiguity_error(ident, b1, b2);
4787+
for ambiguity_error in &self.ambiguity_errors {
4788+
if reported_spans.insert(ambiguity_error.ident.span) {
4789+
self.report_ambiguity_error(ambiguity_error);
46864790
}
46874791
}
46884792

@@ -4860,7 +4964,7 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
48604964
};
48614965
let crate_root = self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX });
48624966
self.populate_module_if_necessary(&crate_root);
4863-
Some((crate_root, ty::Visibility::Public, ident.span, Mark::root())
4967+
Some((crate_root, ty::Visibility::Public, DUMMY_SP, Mark::root())
48644968
.to_name_binding(self.arenas))
48654969
}
48664970
})

0 commit comments

Comments
 (0)