Skip to content

resolve: Partially unify early and late scope-relative identifier resolution #83103

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1977,7 +1977,7 @@ declare_lint! {
Warn,
"detects proc macro derives using inaccessible names from parent modules",
@future_incompatible = FutureIncompatibleInfo {
reference: "issue #50504 <https://github.com/rust-lang/rust/issues/50504>",
reference: "issue #83583 <https://github.com/rust-lang/rust/issues/83583>",
edition: None,
};
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_resolve/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ impl<'a> Resolver<'a> {
/// Lookup typo candidate in scope for a macro or import.
fn early_lookup_typo_candidate(
&mut self,
scope_set: ScopeSet,
scope_set: ScopeSet<'a>,
parent_scope: &ParentScope<'a>,
ident: Ident,
filter_fn: &impl Fn(Res) -> bool,
Expand Down Expand Up @@ -662,7 +662,7 @@ impl<'a> Resolver<'a> {
let root_module = this.resolve_crate_root(root_ident);
this.add_module_candidates(root_module, &mut suggestions, filter_fn);
}
Scope::Module(module) => {
Scope::Module(module, _) => {
this.add_module_candidates(module, &mut suggestions, filter_fn);
}
Scope::RegisteredAttrs => {
Expand Down
194 changes: 63 additions & 131 deletions compiler/rustc_resolve/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ use Determinacy::*;
use rustc_arena::{DroplessArena, TypedArena};
use rustc_ast::node_id::NodeMap;
use rustc_ast::ptr::P;
use rustc_ast::unwrap_or;
use rustc_ast::visit::{self, Visitor};
use rustc_ast::{self as ast, NodeId};
use rustc_ast::{Crate, CRATE_NODE_ID};
Expand All @@ -42,7 +41,7 @@ use rustc_hir::def::Namespace::*;
use rustc_hir::def::{self, CtorOf, DefKind, NonMacroAttrKind, PartialRes};
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, CRATE_DEF_INDEX};
use rustc_hir::definitions::{DefKey, DefPathData, Definitions};
use rustc_hir::{PrimTy, TraitCandidate};
use rustc_hir::TraitCandidate;
use rustc_index::vec::IndexVec;
use rustc_metadata::creader::{CStore, CrateLoader};
use rustc_middle::hir::exports::ExportMap;
Expand Down Expand Up @@ -108,7 +107,9 @@ enum Scope<'a> {
DeriveHelpersCompat,
MacroRules(MacroRulesScopeRef<'a>),
CrateRoot,
Module(Module<'a>),
// The node ID is for reporting the `PROC_MACRO_DERIVE_RESOLUTION_FALLBACK`
// lint if it should be reported.
Module(Module<'a>, Option<NodeId>),
RegisteredAttrs,
MacroUsePrelude,
BuiltinAttrs,
Expand All @@ -122,13 +123,17 @@ enum Scope<'a> {
/// with different restrictions when looking up the resolution.
/// This enum is currently used only for early resolution (imports and macros),
/// but not for late resolution yet.
enum ScopeSet {
#[derive(Clone, Copy)]
enum ScopeSet<'a> {
/// All scopes with the given namespace.
All(Namespace, /*is_import*/ bool),
/// Crate root, then extern prelude (used for mixed 2015-2018 mode in macros).
AbsolutePath(Namespace),
/// All scopes with macro namespace and the given macro kind restriction.
Macro(MacroKind),
/// All scopes with the given namespace, used for partially performing late resolution.
/// The node id enables lints and is used for reporting them.
Late(Namespace, Module<'a>, Option<NodeId>),
}

/// Everything you need to know about a name's location to resolve it.
Expand Down Expand Up @@ -1466,7 +1471,7 @@ impl<'a> Resolver<'a> {

self.visit_scopes(ScopeSet::All(TypeNS, false), parent_scope, ctxt, |this, scope, _, _| {
match scope {
Scope::Module(module) => {
Scope::Module(module, _) => {
this.traits_in_module(module, assoc_item, &mut found_traits);
}
Scope::StdLibPrelude => {
Expand Down Expand Up @@ -1630,7 +1635,7 @@ impl<'a> Resolver<'a> {
/// If the callback returns `Some` result, we stop visiting scopes and return it.
fn visit_scopes<T>(
&mut self,
scope_set: ScopeSet,
scope_set: ScopeSet<'a>,
parent_scope: &ParentScope<'a>,
ctxt: SyntaxContext,
mut visitor: impl FnMut(
Expand Down Expand Up @@ -1686,12 +1691,17 @@ impl<'a> Resolver<'a> {
ScopeSet::All(ns, _) => (ns, None, false),
ScopeSet::AbsolutePath(ns) => (ns, None, true),
ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind), false),
ScopeSet::Late(ns, ..) => (ns, None, false),
};
let module = match scope_set {
// Start with the specified module.
ScopeSet::Late(_, module, _) => module,
// Jump out of trait or enum modules, they do not act as scopes.
_ => parent_scope.module.nearest_item_scope(),
};
// Jump out of trait or enum modules, they do not act as scopes.
let module = parent_scope.module.nearest_item_scope();
let mut scope = match ns {
_ if is_absolute_path => Scope::CrateRoot,
TypeNS | ValueNS => Scope::Module(module),
TypeNS | ValueNS => Scope::Module(module, None),
MacroNS => Scope::DeriveHelpers(parent_scope.expansion),
};
let mut ctxt = ctxt.normalize_to_macros_2_0();
Expand Down Expand Up @@ -1756,7 +1766,7 @@ impl<'a> Resolver<'a> {
MacroRulesScope::Invocation(invoc_id) => {
Scope::MacroRules(self.invocation_parent_scopes[&invoc_id].macro_rules)
}
MacroRulesScope::Empty => Scope::Module(module),
MacroRulesScope::Empty => Scope::Module(module, None),
},
Scope::CrateRoot => match ns {
TypeNS => {
Expand All @@ -1765,10 +1775,16 @@ impl<'a> Resolver<'a> {
}
ValueNS | MacroNS => break,
},
Scope::Module(module) => {
Scope::Module(module, prev_lint_id) => {
use_prelude = !module.no_implicit_prelude;
match self.hygienic_lexical_parent(module, &mut ctxt) {
Some(parent_module) => Scope::Module(parent_module),
let derive_fallback_lint_id = match scope_set {
ScopeSet::Late(.., lint_id) => lint_id,
_ => None,
};
match self.hygienic_lexical_parent(module, &mut ctxt, derive_fallback_lint_id) {
Some((parent_module, lint_id)) => {
Scope::Module(parent_module, lint_id.or(prev_lint_id))
}
None => {
ctxt.adjust(ExpnId::root());
match ns {
Expand Down Expand Up @@ -1824,6 +1840,7 @@ impl<'a> Resolver<'a> {
ribs: &[Rib<'a>],
) -> Option<LexicalScopeBinding<'a>> {
assert!(ns == TypeNS || ns == ValueNS);
let orig_ident = ident;
if ident.name == kw::Empty {
return Some(LexicalScopeBinding::Res(Res::Err));
}
Expand Down Expand Up @@ -1873,135 +1890,49 @@ impl<'a> Resolver<'a> {
_ => continue,
};

let item = self.resolve_ident_in_module_unadjusted(
ModuleOrUniformRoot::Module(module),
ident,
ns,
parent_scope,
record_used,
path_span,
);
if let Ok(binding) = item {
// The ident resolves to an item.
return Some(LexicalScopeBinding::Item(binding));
}

match module.kind {
ModuleKind::Block(..) => {} // We can see through blocks
_ => break,
}
}

ident = normalized_ident;
let mut poisoned = None;
loop {
let mut span_data = ident.span.data();
let opt_module = if let Some(node_id) = record_used_id {
self.hygienic_lexical_parent_with_compatibility_fallback(
module,
&mut span_data.ctxt,
node_id,
&mut poisoned,
)
} else {
self.hygienic_lexical_parent(module, &mut span_data.ctxt)
};
ident.span = span_data.span();
module = unwrap_or!(opt_module, break);
let adjusted_parent_scope = &ParentScope { module, ..*parent_scope };
let result = self.resolve_ident_in_module_unadjusted(
let item = self.resolve_ident_in_module_unadjusted(
ModuleOrUniformRoot::Module(module),
ident,
ns,
adjusted_parent_scope,
parent_scope,
record_used,
path_span,
);

match result {
Ok(binding) => {
if let Some(node_id) = poisoned {
self.lint_buffer.buffer_lint_with_diagnostic(
lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
node_id,
ident.span,
&format!("cannot find {} `{}` in this scope", ns.descr(), ident),
BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(ident.span),
);
}
return Some(LexicalScopeBinding::Item(binding));
}
Err(Determined) => continue,
Err(Undetermined) => {
span_bug!(ident.span, "undetermined resolution during main resolution pass")
}
}
}

if !module.no_implicit_prelude {
ident.span.adjust(ExpnId::root());
if ns == TypeNS {
if let Some(binding) = self.extern_prelude_get(ident, !record_used) {
return Some(LexicalScopeBinding::Item(binding));
}
if let Some(ident) = self.registered_tools.get(&ident) {
let binding =
(Res::ToolMod, ty::Visibility::Public, ident.span, ExpnId::root())
.to_name_binding(self.arenas);
return Some(LexicalScopeBinding::Item(binding));
}
}
if let Some(prelude) = self.prelude {
if let Ok(binding) = self.resolve_ident_in_module_unadjusted(
ModuleOrUniformRoot::Module(prelude),
ident,
ns,
parent_scope,
false,
path_span,
) {
return Some(LexicalScopeBinding::Item(binding));
}
}
}

if ns == TypeNS {
if let Some(prim_ty) = PrimTy::from_name(ident.name) {
let binding =
(Res::PrimTy(prim_ty), ty::Visibility::Public, DUMMY_SP, ExpnId::root())
.to_name_binding(self.arenas);
if let Ok(binding) = item {
// The ident resolves to an item.
return Some(LexicalScopeBinding::Item(binding));
}
}

None
self.early_resolve_ident_in_lexical_scope(
orig_ident,
ScopeSet::Late(ns, module, record_used_id),
parent_scope,
record_used,
record_used,
path_span,
)
.ok()
.map(LexicalScopeBinding::Item)
}

fn hygienic_lexical_parent(
&mut self,
module: Module<'a>,
ctxt: &mut SyntaxContext,
) -> Option<Module<'a>> {
derive_fallback_lint_id: Option<NodeId>,
) -> Option<(Module<'a>, Option<NodeId>)> {
if !module.expansion.outer_expn_is_descendant_of(*ctxt) {
return Some(self.macro_def_scope(ctxt.remove_mark()));
return Some((self.macro_def_scope(ctxt.remove_mark()), None));
}

if let ModuleKind::Block(..) = module.kind {
return Some(module.parent.unwrap().nearest_item_scope());
}

None
}

fn hygienic_lexical_parent_with_compatibility_fallback(
&mut self,
module: Module<'a>,
ctxt: &mut SyntaxContext,
node_id: NodeId,
poisoned: &mut Option<NodeId>,
) -> Option<Module<'a>> {
if let module @ Some(..) = self.hygienic_lexical_parent(module, ctxt) {
return module;
return Some((module.parent.unwrap().nearest_item_scope(), None));
}

// We need to support the next case under a deprecation warning
Expand All @@ -2015,20 +1946,21 @@ impl<'a> Resolver<'a> {
// ---- end
// ```
// So we have to fall back to the module's parent during lexical resolution in this case.
if let Some(parent) = module.parent {
// Inner module is inside the macro, parent module is outside of the macro.
if module.expansion != parent.expansion
&& module.expansion.is_descendant_of(parent.expansion)
{
// The macro is a proc macro derive
if let Some(def_id) = module.expansion.expn_data().macro_def_id {
let ext = self.get_macro_by_def_id(def_id);
if ext.builtin_name.is_none()
&& ext.macro_kind() == MacroKind::Derive
&& parent.expansion.outer_expn_is_descendant_of(*ctxt)
{
*poisoned = Some(node_id);
return module.parent;
if derive_fallback_lint_id.is_some() {
if let Some(parent) = module.parent {
// Inner module is inside the macro, parent module is outside of the macro.
if module.expansion != parent.expansion
&& module.expansion.is_descendant_of(parent.expansion)
{
// The macro is a proc macro derive
if let Some(def_id) = module.expansion.expn_data().macro_def_id {
let ext = self.get_macro_by_def_id(def_id);
if ext.builtin_name.is_none()
&& ext.macro_kind() == MacroKind::Derive
&& parent.expansion.outer_expn_is_descendant_of(*ctxt)
{
return Some((parent, derive_fallback_lint_id));
}
}
}
}
Expand Down
Loading