// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![crate_name = "rustc_resolve"]
-#![crate_type = "dylib"]
-#![crate_type = "rlib"]
#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
html_root_url = "https://doc.rust-lang.org/nightly/")]
}
}
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Namespace {
TypeNS,
ValueNS,
}
}
+struct UsePlacementFinder {
+ target_module: NodeId,
+ span: Option<Span>,
+ found_use: bool,
+}
+
+impl<'tcx> Visitor<'tcx> for UsePlacementFinder {
+ fn visit_mod(
+ &mut self,
+ module: &'tcx ast::Mod,
+ _: Span,
+ _: &[ast::Attribute],
+ node_id: NodeId,
+ ) {
+ if self.span.is_some() {
+ return;
+ }
+ if node_id != self.target_module {
+ visit::walk_mod(self, module);
+ return;
+ }
+ // find a use statement
+ for item in &module.items {
+ match item.node {
+ ItemKind::Use(..) => {
+ // don't suggest placing a use before the prelude
+ // import or other generated ones
+ if item.span == DUMMY_SP {
+ self.span = Some(item.span.with_hi(item.span.lo()));
+ self.found_use = true;
+ return;
+ }
+ },
+ // don't place use before extern crate
+ ItemKind::ExternCrate(_) => {}
+ // but place them before the first other item
+ _ => if self.span.map_or(true, |span| item.span < span ) {
+ self.span = Some(item.span.with_hi(item.span.lo()));
+ },
+ }
+ }
+ assert!(self.span.is_some(), "a file can't have no items and emit suggestions");
+ }
+}
+
impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> {
fn visit_item(&mut self, item: &'tcx Item) {
self.resolve_item(item);
}
}
-pub type ErrorMessage = Option<(Span, String)>;
-
#[derive(Copy, Clone)]
enum TypeParameters<'a, 'b> {
NoTypeParameters,
fn new(kind: RibKind<'a>) -> Rib<'a> {
Rib {
bindings: FxHashMap(),
- kind: kind,
+ kind,
}
}
}
expansion: Mark,
}
-pub type Module<'a> = &'a ModuleData<'a>;
+type Module<'a> = &'a ModuleData<'a>;
impl<'a> ModuleData<'a> {
fn new(parent: Option<Module<'a>>,
expansion: Mark,
span: Span) -> Self {
ModuleData {
- parent: parent,
- kind: kind,
- normal_ancestor_id: normal_ancestor_id,
+ parent,
+ kind,
+ normal_ancestor_id,
resolutions: RefCell::new(FxHashMap()),
legacy_macro_resolutions: RefCell::new(Vec::new()),
macro_resolutions: RefCell::new(Vec::new()),
globs: RefCell::new((Vec::new())),
traits: RefCell::new(None),
populated: Cell::new(normal_ancestor_id.is_local()),
- span: span,
- expansion: expansion,
+ span,
+ expansion,
}
}
}
}
+ fn for_each_child_stable<F: FnMut(Ident, Namespace, &'a NameBinding<'a>)>(&self, mut f: F) {
+ let resolutions = self.resolutions.borrow();
+ let mut resolutions = resolutions.iter().map(|(&(ident, ns), &resolution)| {
+ // Pre-compute keys for sorting
+ (ident.name.as_str(), ns, ident, resolution)
+ })
+ .collect::<Vec<_>>();
+ resolutions.sort_unstable_by_key(|&(str, ns, ..)| (str, ns));
+ for &(_, ns, ident, resolution) in resolutions.iter() {
+ resolution.borrow().binding.map(|binding| f(ident, ns, binding));
+ }
+ }
+
fn def(&self) -> Option<Def> {
match self.kind {
ModuleKind::Def(def, _) => Some(def),
struct PrivacyError<'a>(Span, Name, &'a NameBinding<'a>);
+struct UseError<'a> {
+ err: DiagnosticBuilder<'a>,
+ /// Attach `use` statements for these candidates
+ candidates: Vec<ImportSuggestion>,
+ /// The node id of the module to place the use statements in
+ node_id: NodeId,
+ /// Whether the diagnostic should state that it's "better"
+ better: bool,
+}
+
struct AmbiguityError<'a> {
span: Span,
name: Name,
extern_module_map: FxHashMap<(DefId, bool /* MacrosOnly? */), Module<'a>>,
pub make_glob_map: bool,
- // Maps imports to the names of items actually imported (this actually maps
- // all imports, but only glob imports are actually interesting).
+ /// Maps imports to the names of items actually imported (this actually maps
+ /// all imports, but only glob imports are actually interesting).
pub glob_map: GlobMap,
used_imports: FxHashSet<(NodeId, Namespace)>,
pub maybe_unused_trait_imports: NodeSet,
+ pub maybe_unused_extern_crates: Vec<(NodeId, Span)>,
+ /// privacy errors are delayed until the end in order to deduplicate them
privacy_errors: Vec<PrivacyError<'a>>,
+ /// ambiguity errors are delayed for deduplication
ambiguity_errors: Vec<AmbiguityError<'a>>,
+ /// `use` injections are delayed for better placement and deduplication
+ use_injections: Vec<UseError<'a>>,
+
gated_errors: FxHashSet<Span>,
disallowed_shadowing: Vec<&'a LegacyBinding<'a>>,
macro_defs.insert(Mark::root(), root_def_id);
Resolver {
- session: session,
+ session,
- definitions: definitions,
+ definitions,
// The outermost module has def ID 0; this is not reflected in the
// AST.
- graph_root: graph_root,
+ graph_root,
prelude: None,
has_self: FxHashSet(),
def_map: NodeMap(),
freevars: NodeMap(),
freevars_seen: NodeMap(),
- export_map: NodeMap(),
+ export_map: FxHashMap(),
trait_map: NodeMap(),
- module_map: module_map,
+ module_map,
block_map: NodeMap(),
extern_module_map: FxHashMap(),
used_imports: FxHashSet(),
maybe_unused_trait_imports: NodeSet(),
+ maybe_unused_extern_crates: Vec::new(),
privacy_errors: Vec::new(),
ambiguity_errors: Vec::new(),
+ use_injections: Vec::new(),
gated_errors: FxHashSet(),
disallowed_shadowing: Vec::new(),
- arenas: arenas,
+ arenas,
dummy_binding: arenas.alloc_name_binding(NameBinding {
kind: NameBindingKind::Def(Def::Err),
expansion: Mark::root(),
use_extern_macros:
features.use_extern_macros || features.proc_macro || features.decl_macro,
- crate_loader: crate_loader,
+ crate_loader,
macro_names: FxHashSet(),
global_macros: FxHashMap(),
lexical_macro_resolutions: Vec::new(),
macro_map: FxHashMap(),
macro_exports: Vec::new(),
- invocations: invocations,
- macro_defs: macro_defs,
+ invocations,
+ macro_defs,
local_macro_def_scopes: FxHashMap(),
name_already_seen: FxHashMap(),
whitelisted_legacy_custom_derives: Vec::new(),
ImportResolver { resolver: self }.finalize_imports();
self.current_module = self.graph_root;
self.finalize_current_module_macro_resolutions();
+
visit::walk_crate(self, krate);
check_unused::check_crate(self, krate);
- self.report_errors();
+ self.report_errors(krate);
self.crate_loader.postprocess(krate);
}
NameBindingKind::Import { .. } => false,
NameBindingKind::Ambiguity { b1, b2, legacy } => {
self.ambiguity_errors.push(AmbiguityError {
- span: span, name: ident.name, lexical: false, b1: b1, b2: b2, legacy: legacy,
+ span: span, name: ident.name, lexical: false, b1: b1, b2: b2, legacy,
});
if legacy {
self.record_use(ident, ns, b1, span);
return Some(module.parent.unwrap());
}
- let mut module_expansion = module.expansion.modern(); // for backward compatability
+ let mut module_expansion = module.expansion.modern(); // for backward compatibility
while let Some(parent) = module.parent {
let parent_expansion = parent.expansion.modern();
if module_expansion.is_descendant_of(parent_expansion) &&
fn resolve_self(&mut self, ctxt: &mut SyntaxContext, module: Module<'a>) -> Module<'a> {
let mut module = self.get_module(module.normal_ancestor_id);
- while module.span.ctxt.modern() != *ctxt {
+ while module.span.ctxt().modern() != *ctxt {
let parent = module.parent.unwrap_or_else(|| self.macro_def_scope(ctxt.remove_mark()));
module = self.get_module(parent.normal_ancestor_id);
}
// must not add it if it's in the bindings map
// because that breaks the assumptions later
// passes make about or-patterns.)
- let mut def = Def::Local(self.definitions.local_def_id(pat_id));
+ let mut def = Def::Local(pat_id);
match bindings.get(&ident.node).cloned() {
Some(id) if id == outer_pat_id => {
// `Variant(a, a)`, error
false, pat.span)
.and_then(LexicalScopeBinding::item);
let resolution = binding.map(NameBinding::def).and_then(|def| {
+ let ivmode = BindingMode::ByValue(Mutability::Immutable);
let always_binding = !pat_src.is_refutable() || opt_pat.is_some() ||
- bmode != BindingMode::ByValue(Mutability::Immutable);
+ bmode != ivmode;
match def {
Def::StructCtor(_, CtorKind::Const) |
Def::VariantCtor(_, CtorKind::Const) |
__diagnostic_used!(E0411);
err.code("E0411".into());
err.span_label(span, "`Self` is only available in traits and impls");
- return err;
+ return (err, Vec::new());
}
if is_self_value(path, ns) {
__diagnostic_used!(E0424);
err.code("E0424".into());
err.span_label(span, format!("`self` value is only available in \
methods with `self` parameter"));
- return err;
+ return (err, Vec::new());
}
// Try to lookup the name in more relaxed fashion for better error reporting.
let ident = *path.last().unwrap();
let candidates = this.lookup_import_candidates(ident.node.name, ns, is_expected);
- if !candidates.is_empty() {
- let mut module_span = this.current_module.span;
- module_span.hi = module_span.lo;
- // Report import candidates as help and proceed searching for labels.
- show_candidates(&mut err, module_span, &candidates, def.is_some());
- } else if is_expected(Def::Enum(DefId::local(CRATE_DEF_INDEX))) {
+ if candidates.is_empty() && is_expected(Def::Enum(DefId::local(CRATE_DEF_INDEX))) {
let enum_candidates =
this.lookup_import_candidates(ident.node.name, ns, is_enum_variant);
let mut enum_candidates = enum_candidates.iter()
format!("Self::{}", path_str));
}
}
- return err;
+ return (err, candidates);
}
}
match (def, source) {
(Def::Macro(..), _) => {
err.span_label(span, format!("did you mean `{}!(...)`?", path_str));
- return err;
+ return (err, candidates);
}
(Def::TyAlias(..), PathSource::Trait) => {
err.span_label(span, "type aliases cannot be used for traits");
- return err;
+ return (err, candidates);
}
(Def::Mod(..), PathSource::Expr(Some(parent))) => match parent.node {
ExprKind::Field(_, ident) => {
err.span_label(parent.span, format!("did you mean `{}::{}`?",
path_str, ident.node));
- return err;
+ return (err, candidates);
}
ExprKind::MethodCall(ref segment, ..) => {
err.span_label(parent.span, format!("did you mean `{}::{}(...)`?",
path_str, segment.identifier));
- return err;
+ return (err, candidates);
}
_ => {}
},
}
err.span_label(span, format!("did you mean `{} {{ /* fields */ }}`?",
path_str));
- return err;
+ return (err, candidates);
}
_ => {}
}
err.span_label(base_span, fallback_label);
this.type_ascription_suggestion(&mut err, base_span);
}
- err
+ (err, candidates)
};
let report_errors = |this: &mut Self, def: Option<Def>| {
- report_errors(this, def).emit();
+ let (err, candidates) = report_errors(this, def);
+ let def_id = this.current_module.normal_ancestor_id;
+ let node_id = this.definitions.as_local_node_id(def_id).unwrap();
+ let better = def.is_some();
+ this.use_injections.push(UseError { err, candidates, node_id, better });
err_path_resolution()
};
= self.struct_constructors.get(&def_id).cloned() {
if is_expected(ctor_def) && self.is_accessible(ctor_vis) {
let lint = lint::builtin::LEGACY_CONSTRUCTOR_VISIBILITY;
- self.session.add_lint(lint, id, span,
+ self.session.buffer_lint(lint, id, span,
"private struct constructors are not usable through \
- reexports in outer modules".to_string());
+ reexports in outer modules",
+ );
res = Some(PathResolution::new(ctor_def));
}
}
sp = sp.next_point();
if let Ok(snippet) = cm.span_to_snippet(sp.to(sp.next_point())) {
debug!("snippet {:?}", snippet);
- let line_sp = cm.lookup_char_pos(sp.hi).line;
- let line_base_sp = cm.lookup_char_pos(base_span.lo).line;
+ let line_sp = cm.lookup_char_pos(sp.hi()).line;
+ let line_base_sp = cm.lookup_char_pos(base_span.lo()).line;
debug!("{:?} {:?}", line_sp, line_base_sp);
if snippet == ":" {
err.span_label(base_span,
};
if result.base_def() == unqualified_result {
let lint = lint::builtin::UNUSED_QUALIFICATIONS;
- self.session.add_lint(lint, id, span, "unnecessary qualification".to_string());
+ self.session.buffer_lint(lint, id, span, "unnecessary qualification")
}
}
Def::Upvar(..) => {
span_bug!(span, "unexpected {:?} in bindings", def)
}
- Def::Local(def_id) => {
+ Def::Local(node_id) => {
for rib in ribs {
match rib.kind {
NormalRibKind | ModuleRibKind(..) | MacroDefinition(..) |
}
ClosureRibKind(function_id) => {
let prev_def = def;
- let node_id = self.definitions.as_local_node_id(def_id).unwrap();
let seen = self.freevars_seen
.entry(function_id)
.or_insert_with(|| NodeMap());
if let Some(&index) = seen.get(&node_id) {
- def = Def::Upvar(def_id, index, function_id);
+ def = Def::Upvar(node_id, index, function_id);
continue;
}
let vec = self.freevars
.entry(function_id)
.or_insert_with(|| vec![]);
let depth = vec.len();
- def = Def::Upvar(def_id, depth, function_id);
+ def = Def::Upvar(node_id, depth, function_id);
if record_used {
vec.push(Freevar {
def: prev_def,
- span: span,
+ span,
});
seen.insert(node_id, depth);
}
return def;
}
- // Calls `f` with a `Resolver` whose current lexical scope is `module`'s lexical scope,
- // i.e. the module's items and the prelude (unless the module is `#[no_implicit_prelude]`).
- // FIXME #34673: This needs testing.
- pub fn with_module_lexical_scope<T, F>(&mut self, module: Module<'a>, f: F) -> T
- where F: FnOnce(&mut Resolver<'a>) -> T,
- {
- self.with_empty_ribs(|this| {
- this.ribs[ValueNS].push(Rib::new(ModuleRibKind(module)));
- this.ribs[TypeNS].push(Rib::new(ModuleRibKind(module)));
- f(this)
- })
- }
-
- fn with_empty_ribs<T, F>(&mut self, f: F) -> T
- where F: FnOnce(&mut Resolver<'a>) -> T,
- {
- let ribs = replace(&mut self.ribs, PerNS::<Vec<Rib>>::default());
- let label_ribs = replace(&mut self.label_ribs, Vec::new());
-
- let result = f(self);
- self.ribs = ribs;
- self.label_ribs = label_ribs;
- result
- }
-
fn lookup_assoc_candidate<FilterFn>(&mut self,
ident: Ident,
ns: Namespace,
}
// Fields are generally expected in the same contexts as locals.
- if filter_fn(Def::Local(DefId::local(CRATE_DEF_INDEX))) {
+ if filter_fn(Def::Local(ast::DUMMY_NODE_ID)) {
if let Some(node_id) = self.current_self_type.as_ref().and_then(extract_node_id) {
// Look for a field with the same name in the current self_type.
if let Some(resolution) = self.def_map.get(&node_id) {
for &(trait_name, binding) in traits.as_ref().unwrap().iter() {
let module = binding.module().unwrap();
let mut ident = ident;
- if ident.ctxt.glob_adjust(module.expansion, binding.span.ctxt.modern()).is_none() {
+ if ident.ctxt.glob_adjust(module.expansion, binding.span.ctxt().modern()).is_none() {
continue
}
if self.resolve_ident_in_module_unadjusted(module, ident, ns, false, false, module.span)
in_module_is_extern)) = worklist.pop() {
self.populate_module_if_necessary(in_module);
- in_module.for_each_child(|ident, ns, name_binding| {
-
+ // We have to visit module children in deterministic order to avoid
+ // instabilities in reported imports (#43552).
+ in_module.for_each_child_stable(|ident, ns, name_binding| {
// avoid imports entirely
if name_binding.is_import() && !name_binding.is_extern_crate() { return; }
// avoid non-importable candidates as well
vis.is_accessible_from(module.normal_ancestor_id, self)
}
- fn report_errors(&mut self) {
+ fn report_errors(&mut self, krate: &Crate) {
self.report_shadowing_errors();
+ self.report_with_use_injections(krate);
let mut reported_spans = FxHashSet();
for &AmbiguityError { span, name, b1, b2, lexical, legacy } in &self.ambiguity_errors {
span.push_span_label(b1.span, msg1);
span.push_span_label(b2.span, msg2);
let msg = format!("`{}` is ambiguous", name);
- self.session.add_lint(lint::builtin::LEGACY_IMPORTS, id, span, msg);
+ self.session.buffer_lint(lint::builtin::LEGACY_IMPORTS, id, span, &msg);
} else {
let mut err =
self.session.struct_span_err(span, &format!("`{}` is ambiguous", name));
}
}
+ fn report_with_use_injections(&mut self, krate: &Crate) {
+ for UseError { mut err, candidates, node_id, better } in self.use_injections.drain(..) {
+ let mut finder = UsePlacementFinder {
+ target_module: node_id,
+ span: None,
+ found_use: false,
+ };
+ visit::walk_crate(&mut finder, krate);
+ if !candidates.is_empty() {
+ let span = finder.span.expect("did not find module");
+ show_candidates(&mut err, span, &candidates, better, finder.found_use);
+ }
+ err.emit();
+ }
+ }
+
fn report_shadowing_errors(&mut self) {
for (ident, scope) in replace(&mut self.lexical_macro_resolutions, Vec::new()) {
self.resolve_legacy_scope(scope, ident, true);
new_binding: &NameBinding,
old_binding: &NameBinding) {
// Error on the second of two conflicting names
- if old_binding.span.lo > new_binding.span.lo {
+ if old_binding.span.lo() > new_binding.span.lo() {
return self.report_conflict(parent, ident, ns, old_binding, new_binding);
}
fn warn_legacy_self_import(&self, directive: &'a ImportDirective<'a>) {
let (id, span) = (directive.id, directive.span);
- let msg = "`self` no longer imports values".to_string();
- self.session.add_lint(lint::builtin::LEGACY_IMPORTS, id, span, msg);
+ let msg = "`self` no longer imports values";
+ self.session.buffer_lint(lint::builtin::LEGACY_IMPORTS, id, span, msg);
}
fn check_proc_macro_attrs(&mut self, attrs: &[ast::Attribute]) {
fn show_candidates(err: &mut DiagnosticBuilder,
span: Span,
candidates: &[ImportSuggestion],
- better: bool) {
+ better: bool,
+ found_use: bool) {
// we want consistent results across executions, but candidates are produced
// by iterating through a hash map, so make sure they are ordered:
let msg = format!("possible {}candidate{} into scope", better, msg_diff);
for candidate in &mut path_strings {
- *candidate = format!("use {};\n", candidate);
+ // produce an additional newline to separate the new use statement
+ // from the directly following item.
+ let additional_newline = if found_use {
+ ""
+ } else {
+ "\n"
+ };
+ *candidate = format!("use {};\n{}", candidate, additional_newline);
}
err.span_suggestions(span, &msg, path_strings);