Skip to content

Commit fc74e35

Browse files
committed
Remove fallback to parent modules from lexical resolution
1 parent 94ef9f5 commit fc74e35

File tree

16 files changed

+317
-102
lines changed

16 files changed

+317
-102
lines changed

src/librustc/hir/def.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,16 @@ pub enum Namespace {
129129
MacroNS,
130130
}
131131

132+
impl Namespace {
133+
pub fn descr(self) -> &'static str {
134+
match self {
135+
TypeNS => "type",
136+
ValueNS => "value",
137+
MacroNS => "macro",
138+
}
139+
}
140+
}
141+
132142
/// Just a helper ‒ separate structure for each namespace.
133143
#[derive(Copy, Clone, Default, Debug)]
134144
pub struct PerNS<T> {

src/librustc/hir/map/definitions.rs

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ impl Decodable for DefPathTable {
153153
/// The definition table containing node definitions.
154154
/// It holds the DefPathTable for local DefIds/DefPaths and it also stores a
155155
/// mapping from NodeIds to local DefIds.
156+
#[derive(Clone)]
156157
pub struct Definitions {
157158
table: DefPathTable,
158159
node_to_def_index: NodeMap<DefIndex>,
@@ -161,34 +162,12 @@ pub struct Definitions {
161162
/// If `Mark` is an ID of some macro expansion,
162163
/// then `DefId` is the normal module (`mod`) in which the expanded macro was defined.
163164
parent_modules_of_macro_defs: FxHashMap<Mark, DefId>,
164-
/// Item with a given `DefIndex` was defined during opaque macro expansion with ID `Mark`.
165-
/// It can actually be defined during transparent macro expansions inside that opaque expansion,
166-
/// but transparent expansions are ignored here.
167-
opaque_expansions_that_defined: FxHashMap<DefIndex, Mark>,
165+
/// Item with a given `DefIndex` was defined during macro expansion with ID `Mark`.
166+
expansions_that_defined: FxHashMap<DefIndex, Mark>,
168167
next_disambiguator: FxHashMap<(DefIndex, DefPathData), u32>,
169168
def_index_to_span: FxHashMap<DefIndex, Span>,
170169
}
171170

172-
// Unfortunately we have to provide a manual impl of Clone because of the
173-
// fixed-sized array field.
174-
impl Clone for Definitions {
175-
fn clone(&self) -> Self {
176-
Definitions {
177-
table: self.table.clone(),
178-
node_to_def_index: self.node_to_def_index.clone(),
179-
def_index_to_node: [
180-
self.def_index_to_node[0].clone(),
181-
self.def_index_to_node[1].clone(),
182-
],
183-
node_to_hir_id: self.node_to_hir_id.clone(),
184-
parent_modules_of_macro_defs: self.parent_modules_of_macro_defs.clone(),
185-
opaque_expansions_that_defined: self.opaque_expansions_that_defined.clone(),
186-
next_disambiguator: self.next_disambiguator.clone(),
187-
def_index_to_span: self.def_index_to_span.clone(),
188-
}
189-
}
190-
}
191-
192171
/// A unique identifier that we can use to lookup a definition
193172
/// precisely. It combines the index of the definition's parent (if
194173
/// any) with a `DisambiguatedDefPathData`.
@@ -409,7 +388,7 @@ impl Definitions {
409388
def_index_to_node: [vec![], vec![]],
410389
node_to_hir_id: IndexVec::new(),
411390
parent_modules_of_macro_defs: FxHashMap(),
412-
opaque_expansions_that_defined: FxHashMap(),
391+
expansions_that_defined: FxHashMap(),
413392
next_disambiguator: FxHashMap(),
414393
def_index_to_span: FxHashMap(),
415394
}
@@ -584,9 +563,8 @@ impl Definitions {
584563
self.node_to_def_index.insert(node_id, index);
585564
}
586565

587-
let expansion = expansion.modern();
588566
if expansion != Mark::root() {
589-
self.opaque_expansions_that_defined.insert(index, expansion);
567+
self.expansions_that_defined.insert(index, expansion);
590568
}
591569

592570
// The span is added if it isn't dummy
@@ -606,8 +584,8 @@ impl Definitions {
606584
self.node_to_hir_id = mapping;
607585
}
608586

609-
pub fn opaque_expansion_that_defined(&self, index: DefIndex) -> Mark {
610-
self.opaque_expansions_that_defined.get(&index).cloned().unwrap_or(Mark::root())
587+
pub fn expansion_that_defined(&self, index: DefIndex) -> Mark {
588+
self.expansions_that_defined.get(&index).cloned().unwrap_or(Mark::root())
611589
}
612590

613591
pub fn parent_module_of_macro_def(&self, mark: Mark) -> DefId {

src/librustc/lint/builtin.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,12 @@ declare_lint! {
316316
"checks the object safety of where clauses"
317317
}
318318

319+
declare_lint! {
320+
pub PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
321+
Warn,
322+
"detects proc macro derives using inaccessible names from parent modules"
323+
}
324+
319325
/// Does nothing as a lint pass, but registers some `Lint`s
320326
/// which are used by other parts of the compiler.
321327
#[derive(Copy, Clone)]
@@ -372,6 +378,7 @@ impl LintPass for HardwiredLints {
372378
DUPLICATE_MACRO_EXPORTS,
373379
INTRA_DOC_LINK_RESOLUTION_FAILURE,
374380
WHERE_CLAUSES_OBJECT_SAFETY,
381+
PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
375382
)
376383
}
377384
}
@@ -384,6 +391,7 @@ pub enum BuiltinLintDiagnostics {
384391
BareTraitObject(Span, /* is_global */ bool),
385392
AbsPathWithModule(Span),
386393
DuplicatedMacroExports(ast::Ident, Span, Span),
394+
ProcMacroDeriveResolutionFallback(Span),
387395
}
388396

389397
impl BuiltinLintDiagnostics {
@@ -420,6 +428,10 @@ impl BuiltinLintDiagnostics {
420428
db.span_label(later_span, format!("`{}` already exported", ident));
421429
db.span_note(earlier_span, "previous macro export is now shadowed");
422430
}
431+
BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(span) => {
432+
db.span_label(span, "names from parent modules are not \
433+
accessible without an explicit import");
434+
}
423435
}
424436
}
425437
}

src/librustc/ty/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2724,7 +2724,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
27242724
pub fn adjust_ident(self, mut ident: Ident, scope: DefId, block: NodeId) -> (Ident, DefId) {
27252725
ident = ident.modern();
27262726
let target_expansion = match scope.krate {
2727-
LOCAL_CRATE => self.hir.definitions().opaque_expansion_that_defined(scope.index),
2727+
LOCAL_CRATE => self.hir.definitions().expansion_that_defined(scope.index),
27282728
_ => Mark::root(),
27292729
};
27302730
let scope = match ident.span.adjust(target_expansion) {

src/librustc_lint/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,11 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
293293
reference: "issue #50589 <https://github.com/rust-lang/rust/issues/50589>",
294294
edition: None,
295295
},
296+
FutureIncompatibleInfo {
297+
id: LintId::of(PROC_MACRO_DERIVE_RESOLUTION_FALLBACK),
298+
reference: "issue #50504 <https://github.com/rust-lang/rust/issues/50504>",
299+
edition: None,
300+
},
296301
]);
297302

298303
// Register renamed and removed lints

src/librustc_resolve/lib.rs

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ use syntax::util::lev_distance::find_best_match_for_name;
5555

5656
use syntax::visit::{self, FnKind, Visitor};
5757
use syntax::attr;
58-
use syntax::ast::{Arm, IsAsync, BindingMode, Block, Crate, Expr, ExprKind};
58+
use syntax::ast::{CRATE_NODE_ID, Arm, IsAsync, BindingMode, Block, Crate, Expr, ExprKind};
5959
use syntax::ast::{FnDecl, ForeignItem, ForeignItemKind, GenericParamKind, Generics};
6060
use syntax::ast::{Item, ItemKind, ImplItem, ImplItemKind};
6161
use syntax::ast::{Label, Local, Mutability, Pat, PatKind, Path};
@@ -1891,7 +1891,12 @@ impl<'a> Resolver<'a> {
18911891

18921892
ident.span = ident.span.modern();
18931893
loop {
1894-
module = unwrap_or!(self.hygienic_lexical_parent(module, &mut ident.span), break);
1894+
let (opt_module, poisoned) = if record_used {
1895+
self.hygienic_lexical_parent_with_compatibility_fallback(module, &mut ident.span)
1896+
} else {
1897+
(self.hygienic_lexical_parent(module, &mut ident.span), false)
1898+
};
1899+
module = unwrap_or!(opt_module, break);
18951900
let orig_current_module = self.current_module;
18961901
self.current_module = module; // Lexical resolutions can never be a privacy error.
18971902
let result = self.resolve_ident_in_module_unadjusted(
@@ -1900,7 +1905,19 @@ impl<'a> Resolver<'a> {
19001905
self.current_module = orig_current_module;
19011906

19021907
match result {
1903-
Ok(binding) => return Some(LexicalScopeBinding::Item(binding)),
1908+
Ok(binding) => {
1909+
if poisoned {
1910+
self.session.buffer_lint_with_diagnostic(
1911+
lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
1912+
CRATE_NODE_ID, ident.span,
1913+
&format!("cannot find {} `{}` in this scope", ns.descr(), ident),
1914+
lint::builtin::BuiltinLintDiagnostics::
1915+
ProcMacroDeriveResolutionFallback(ident.span),
1916+
);
1917+
}
1918+
return Some(LexicalScopeBinding::Item(binding))
1919+
}
1920+
_ if poisoned => break,
19041921
Err(Undetermined) => return None,
19051922
Err(Determined) => {}
19061923
}
@@ -1935,7 +1952,7 @@ impl<'a> Resolver<'a> {
19351952
None
19361953
}
19371954

1938-
fn hygienic_lexical_parent(&mut self, mut module: Module<'a>, span: &mut Span)
1955+
fn hygienic_lexical_parent(&mut self, module: Module<'a>, span: &mut Span)
19391956
-> Option<Module<'a>> {
19401957
if !module.expansion.is_descendant_of(span.ctxt().outer()) {
19411958
return Some(self.macro_def_scope(span.remove_mark()));
@@ -1945,22 +1962,41 @@ impl<'a> Resolver<'a> {
19451962
return Some(module.parent.unwrap());
19461963
}
19471964

1948-
let mut module_expansion = module.expansion.modern(); // for backward compatibility
1949-
while let Some(parent) = module.parent {
1950-
let parent_expansion = parent.expansion.modern();
1951-
if module_expansion.is_descendant_of(parent_expansion) &&
1952-
parent_expansion != module_expansion {
1953-
return if parent_expansion.is_descendant_of(span.ctxt().outer()) {
1954-
Some(parent)
1955-
} else {
1956-
None
1957-
};
1965+
None
1966+
}
1967+
1968+
fn hygienic_lexical_parent_with_compatibility_fallback(
1969+
&mut self, module: Module<'a>, span: &mut Span) -> (Option<Module<'a>>, /* poisoned */ bool
1970+
) {
1971+
if let module @ Some(..) = self.hygienic_lexical_parent(module, span) {
1972+
return (module, false);
1973+
}
1974+
1975+
// We need to support the next case under a deprecation warning
1976+
// ```
1977+
// struct MyStruct;
1978+
// ---- begin: this comes from a proc macro derive
1979+
// mod implementation_details {
1980+
// // Note that `MyStruct` is not in scope here.
1981+
// impl SomeTrait for MyStruct { ... }
1982+
// }
1983+
// ---- end
1984+
// ```
1985+
// So we have to fall back to the module's parent during lexical resolution in this case.
1986+
if let Some(parent) = module.parent {
1987+
// Inner module is inside the macro, parent module is outside of the macro.
1988+
if module.expansion != parent.expansion &&
1989+
module.expansion.is_descendant_of(parent.expansion) {
1990+
// The macro is a proc macro derive
1991+
if module.expansion.looks_like_proc_macro_derive() {
1992+
if parent.expansion.is_descendant_of(span.ctxt().outer()) {
1993+
return (module.parent, true);
1994+
}
1995+
}
19581996
}
1959-
module = parent;
1960-
module_expansion = parent_expansion;
19611997
}
19621998

1963-
None
1999+
(None, false)
19642000
}
19652001

19662002
fn resolve_ident_in_module(&mut self,
@@ -4037,8 +4073,9 @@ impl<'a> Resolver<'a> {
40374073
let mut search_module = self.current_module;
40384074
loop {
40394075
self.get_traits_in_module_containing_item(ident, ns, search_module, &mut found_traits);
4040-
search_module =
4041-
unwrap_or!(self.hygienic_lexical_parent(search_module, &mut ident.span), break);
4076+
search_module = unwrap_or!(
4077+
self.hygienic_lexical_parent(search_module, &mut ident.span), break
4078+
);
40424079
}
40434080

40444081
if let Some(prelude) = self.prelude {
@@ -4395,12 +4432,6 @@ impl<'a> Resolver<'a> {
43954432
(TypeNS, _) => "type",
43964433
};
43974434

4398-
let namespace = match ns {
4399-
ValueNS => "value",
4400-
MacroNS => "macro",
4401-
TypeNS => "type",
4402-
};
4403-
44044435
let msg = format!("the name `{}` is defined multiple times", name);
44054436

44064437
let mut err = match (old_binding.is_extern_crate(), new_binding.is_extern_crate()) {
@@ -4418,7 +4449,7 @@ impl<'a> Resolver<'a> {
44184449

44194450
err.note(&format!("`{}` must be defined only once in the {} namespace of this {}",
44204451
name,
4421-
namespace,
4452+
ns.descr(),
44224453
container));
44234454

44244455
err.span_label(span, format!("`{}` re{} here", name, new_participle));

src/libsyntax_pos/hygiene.rs

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -72,26 +72,16 @@ pub enum Transparency {
7272
}
7373

7474
impl Mark {
75-
fn fresh_with_data(mark_data: MarkData, data: &mut HygieneData) -> Self {
76-
data.marks.push(mark_data);
77-
Mark(data.marks.len() as u32 - 1)
78-
}
79-
8075
pub fn fresh(parent: Mark) -> Self {
8176
HygieneData::with(|data| {
82-
Mark::fresh_with_data(MarkData {
77+
data.marks.push(MarkData {
8378
parent,
8479
// By default expansions behave like `macro_rules`.
8580
default_transparency: Transparency::SemiTransparent,
8681
is_builtin: false,
8782
expn_info: None,
88-
}, data)
89-
})
90-
}
91-
92-
pub fn fresh_cloned(clone_from: Mark) -> Self {
93-
HygieneData::with(|data| {
94-
Mark::fresh_with_data(data.marks[clone_from.0 as usize].clone(), data)
83+
});
84+
Mark(data.marks.len() as u32 - 1)
9585
})
9686
}
9787

@@ -128,17 +118,6 @@ impl Mark {
128118
})
129119
}
130120

131-
// FIXME: This operation doesn't really make sense when single macro expansion
132-
// can produce tokens with different transparencies. Figure out how to avoid it.
133-
pub fn modern(mut self) -> Mark {
134-
HygieneData::with(|data| {
135-
while data.marks[self.0 as usize].default_transparency != Transparency::Opaque {
136-
self = data.marks[self.0 as usize].parent;
137-
}
138-
self
139-
})
140-
}
141-
142121
#[inline]
143122
pub fn set_default_transparency(self, transparency: Transparency) {
144123
assert_ne!(self, Mark::root());
@@ -194,6 +173,24 @@ impl Mark {
194173
b
195174
})
196175
}
176+
177+
// Used for enabling some compatibility fallback in resolve.
178+
#[inline]
179+
pub fn looks_like_proc_macro_derive(self) -> bool {
180+
HygieneData::with(|data| {
181+
let mark_data = &data.marks[self.0 as usize];
182+
if mark_data.default_transparency == Transparency::Opaque {
183+
if let Some(expn_info) = &mark_data.expn_info {
184+
if let ExpnFormat::MacroAttribute(name) = expn_info.format {
185+
if name.as_str().starts_with("derive(") {
186+
return true;
187+
}
188+
}
189+
}
190+
}
191+
false
192+
})
193+
}
197194
}
198195

199196
#[derive(Debug)]
@@ -285,6 +282,7 @@ impl SyntaxContext {
285282
})
286283
}
287284

285+
/// Extend a syntax context with a given mark and default transparency for that mark.
288286
pub fn apply_mark(self, mark: Mark) -> SyntaxContext {
289287
assert_ne!(mark, Mark::root());
290288
self.apply_mark_with_transparency(

0 commit comments

Comments
 (0)