Skip to content

Commit 09fc1af

Browse files
authored
Auto merge of #37506 - jseyfried:improve_shadowing_checks, r=nrc
macros: improve shadowing checks This PR improves macro-expanded shadowing checks to work with out-of-(pre)order expansion. Out-of-order expansion became possible in #37084, so this technically a [breaking-change] for nightly. The regression test from this PR is an example of code that would break. r? @nrc
2 parents 8e2b57d + 1e6c275 commit 09fc1af

File tree

4 files changed

+66
-16
lines changed

4 files changed

+66
-16
lines changed

src/librustc_resolve/lib.rs

+12-6
Original file line numberDiff line numberDiff line change
@@ -1067,7 +1067,7 @@ pub struct Resolver<'a> {
10671067

10681068
privacy_errors: Vec<PrivacyError<'a>>,
10691069
ambiguity_errors: Vec<AmbiguityError<'a>>,
1070-
disallowed_shadowing: Vec<(Name, Span, LegacyScope<'a>)>,
1070+
disallowed_shadowing: Vec<&'a LegacyBinding<'a>>,
10711071

10721072
arenas: &'a ResolverArenas<'a>,
10731073
dummy_binding: &'a NameBinding<'a>,
@@ -1077,6 +1077,7 @@ pub struct Resolver<'a> {
10771077
crate_loader: &'a mut CrateLoader,
10781078
macro_names: FnvHashSet<Name>,
10791079
builtin_macros: FnvHashMap<Name, Rc<SyntaxExtension>>,
1080+
lexical_macro_resolutions: Vec<(Name, LegacyScope<'a>)>,
10801081

10811082
// Maps the `Mark` of an expansion to its containing module or block.
10821083
invocations: FnvHashMap<Mark, &'a InvocationData<'a>>,
@@ -1267,6 +1268,7 @@ impl<'a> Resolver<'a> {
12671268
crate_loader: crate_loader,
12681269
macro_names: FnvHashSet(),
12691270
builtin_macros: FnvHashMap(),
1271+
lexical_macro_resolutions: Vec::new(),
12701272
invocations: invocations,
12711273
}
12721274
}
@@ -3363,12 +3365,16 @@ impl<'a> Resolver<'a> {
33633365
}
33643366

33653367
fn report_shadowing_errors(&mut self) {
3368+
for (name, scope) in replace(&mut self.lexical_macro_resolutions, Vec::new()) {
3369+
self.resolve_macro_name(scope, name);
3370+
}
3371+
33663372
let mut reported_errors = FnvHashSet();
3367-
for (name, span, scope) in replace(&mut self.disallowed_shadowing, Vec::new()) {
3368-
if self.resolve_macro_name(scope, name, false).is_some() &&
3369-
reported_errors.insert((name, span)) {
3370-
let msg = format!("`{}` is already in scope", name);
3371-
self.session.struct_span_err(span, &msg)
3373+
for binding in replace(&mut self.disallowed_shadowing, Vec::new()) {
3374+
if self.resolve_macro_name(binding.parent, binding.name).is_some() &&
3375+
reported_errors.insert((binding.name, binding.span)) {
3376+
let msg = format!("`{}` is already in scope", binding.name);
3377+
self.session.struct_span_err(binding.span, &msg)
33723378
.note("macro-expanded `macro_rules!`s may not shadow \
33733379
existing macros (see RFC 1560)")
33743380
.emit();

src/librustc_resolve/macros.rs

+17-10
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,10 @@ impl<'a> LegacyScope<'a> {
7373
}
7474

7575
pub struct LegacyBinding<'a> {
76-
parent: LegacyScope<'a>,
77-
name: ast::Name,
76+
pub parent: LegacyScope<'a>,
77+
pub name: ast::Name,
7878
ext: Rc<SyntaxExtension>,
79-
span: Span,
79+
pub span: Span,
8080
}
8181

8282
impl<'a> base::Resolver for Resolver<'a> {
@@ -171,7 +171,7 @@ impl<'a> base::Resolver for Resolver<'a> {
171171
if let LegacyScope::Expansion(parent) = invocation.legacy_scope.get() {
172172
invocation.legacy_scope.set(LegacyScope::simplify_expansion(parent));
173173
}
174-
self.resolve_macro_name(invocation.legacy_scope.get(), name, true).ok_or_else(|| {
174+
self.resolve_macro_name(invocation.legacy_scope.get(), name).ok_or_else(|| {
175175
if force {
176176
let msg = format!("macro undefined: '{}!'", name);
177177
let mut err = self.session.struct_span_err(path.span, &msg);
@@ -186,17 +186,18 @@ impl<'a> base::Resolver for Resolver<'a> {
186186
}
187187

188188
impl<'a> Resolver<'a> {
189-
pub fn resolve_macro_name(&mut self,
190-
mut scope: LegacyScope<'a>,
191-
name: ast::Name,
192-
record_used: bool)
189+
pub fn resolve_macro_name(&mut self, mut scope: LegacyScope<'a>, name: ast::Name)
193190
-> Option<Rc<SyntaxExtension>> {
191+
let mut possible_time_travel = None;
194192
let mut relative_depth: u32 = 0;
195193
loop {
196194
scope = match scope {
197195
LegacyScope::Empty => break,
198196
LegacyScope::Expansion(invocation) => {
199197
if let LegacyScope::Empty = invocation.expansion.get() {
198+
if possible_time_travel.is_none() {
199+
possible_time_travel = Some(scope);
200+
}
200201
invocation.legacy_scope.get()
201202
} else {
202203
relative_depth += 1;
@@ -209,8 +210,11 @@ impl<'a> Resolver<'a> {
209210
}
210211
LegacyScope::Binding(binding) => {
211212
if binding.name == name {
212-
if record_used && relative_depth > 0 {
213-
self.disallowed_shadowing.push((name, binding.span, binding.parent));
213+
if let Some(scope) = possible_time_travel {
214+
// Check for disallowed shadowing later
215+
self.lexical_macro_resolutions.push((name, scope));
216+
} else if relative_depth > 0 {
217+
self.disallowed_shadowing.push(binding);
214218
}
215219
return Some(binding.ext.clone());
216220
}
@@ -219,6 +223,9 @@ impl<'a> Resolver<'a> {
219223
};
220224
}
221225

226+
if let Some(scope) = possible_time_travel {
227+
self.lexical_macro_resolutions.push((name, scope));
228+
}
222229
self.builtin_macros.get(&name).cloned()
223230
}
224231

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#[macro_export]
12+
macro_rules! define_macro {
13+
($i:ident) => {
14+
macro_rules! $i { () => {} }
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// aux-build:define_macro.rs
12+
// error-pattern: `bar` is already in scope
13+
14+
macro_rules! bar { () => {} }
15+
define_macro!(bar);
16+
bar!();
17+
18+
macro_rules! m { () => { #[macro_use] extern crate define_macro; } }
19+
m!();
20+
21+
fn main() {}

0 commit comments

Comments
 (0)