Skip to content

Commit

Permalink
Auto merge of #83103 - petrochenkov:unilex, r=Aaron1011
Browse files Browse the repository at this point in the history
resolve: Partially unify early and late scope-relative identifier resolution

Reuse `early_resolve_ident_in_lexical_scope` instead of a chunk of code in `resolve_ident_in_lexical_scope` doing the same job.

`early_resolve_ident_in_lexical_scope`/`visit_scopes` had to be slightly extended to be able to 1) start from a specific module instead of the current parent scope and 2) report one deprecation lint.
`early_resolve_ident_in_lexical_scope` still doesn't support walking through "ribs", that part is left in `resolve_ident_in_lexical_scope` (moreover, I'm pretty sure it's buggy, but that's a separate issue, cc #52389 at least).
  • Loading branch information
bors committed Mar 27, 2021
2 parents 9b0edb7 + ee0357a commit 8cd7d86
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 143 deletions.
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 @@ -1935,7 +1935,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 @@ -26,7 +26,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 @@ -43,7 +42,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 @@ -109,7 +108,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 @@ -123,13 +124,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 @@ -1467,7 +1472,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 @@ -1631,7 +1636,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 @@ -1687,12 +1692,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 @@ -1757,7 +1767,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 @@ -1766,10 +1776,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 @@ -1825,6 +1841,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 @@ -1874,135 +1891,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 @@ -2016,20 +1947,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

0 comments on commit 8cd7d86

Please sign in to comment.