Skip to content

Commit ef8cb40

Browse files
committed
Auto merge of #51519 - ExpHP:issue-51331-b, r=petrochenkov
Fix for $crate var normalization in proc macro for externally defined macros Fixes #51331, a bug that has existed in at least *some* form for a year and a half. The PR includes the addition of a `fold_qpath` method to `syntax::fold::Folder`. Overriding this method is useful for folds that modify paths in a way that invalidates indices (insertion or removal of a component), as it provides the opportunity to update `qself.position` in `<A as B>::C` paths. I added it because the bugfix is messy without it. (unfortunately, grepping around the codebase, I did not see anything else that could use it.)
2 parents 3f3ba6c + d13bfd2 commit ef8cb40

File tree

5 files changed

+147
-32
lines changed

5 files changed

+147
-32
lines changed

src/librustc_resolve/macros.rs

+22-3
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,23 @@ impl<'a> base::Resolver for Resolver<'a> {
138138
struct EliminateCrateVar<'b, 'a: 'b>(&'b mut Resolver<'a>, Span);
139139

140140
impl<'a, 'b> Folder for EliminateCrateVar<'a, 'b> {
141-
fn fold_path(&mut self, mut path: ast::Path) -> ast::Path {
141+
fn fold_path(&mut self, path: ast::Path) -> ast::Path {
142+
match self.fold_qpath(None, path) {
143+
(None, path) => path,
144+
_ => unreachable!(),
145+
}
146+
}
147+
148+
fn fold_qpath(&mut self, mut qself: Option<ast::QSelf>, mut path: ast::Path)
149+
-> (Option<ast::QSelf>, ast::Path) {
150+
qself = qself.map(|ast::QSelf { ty, path_span, position }| {
151+
ast::QSelf {
152+
ty: self.fold_ty(ty),
153+
path_span: self.new_span(path_span),
154+
position,
155+
}
156+
});
157+
142158
let ident = path.segments[0].ident;
143159
if ident.name == keywords::DollarCrate.name() {
144160
path.segments[0].ident.name = keywords::CrateRoot.name();
@@ -150,10 +166,13 @@ impl<'a> base::Resolver for Resolver<'a> {
150166
ast::Ident::with_empty_ctxt(name).with_span_pos(span)
151167
),
152168
_ => unreachable!(),
153-
})
169+
});
170+
if let Some(qself) = &mut qself {
171+
qself.position += 1;
172+
}
154173
}
155174
}
156-
path
175+
(qself, path)
157176
}
158177

159178
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {

src/libsyntax/fold.rs

+24-25
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,10 @@ pub trait Folder : Sized {
168168
noop_fold_path(p, self)
169169
}
170170

171+
fn fold_qpath(&mut self, qs: Option<QSelf>, p: Path) -> (Option<QSelf>, Path) {
172+
noop_fold_qpath(qs, p, self)
173+
}
174+
171175
fn fold_path_parameters(&mut self, p: PathParameters) -> PathParameters {
172176
noop_fold_path_parameters(p, self)
173177
}
@@ -370,14 +374,8 @@ pub fn noop_fold_ty<T: Folder>(t: P<Ty>, fld: &mut T) -> P<Ty> {
370374
TyKind::Tup(tys) => TyKind::Tup(tys.move_map(|ty| fld.fold_ty(ty))),
371375
TyKind::Paren(ty) => TyKind::Paren(fld.fold_ty(ty)),
372376
TyKind::Path(qself, path) => {
373-
let qself = qself.map(|QSelf { ty, path_span, position }| {
374-
QSelf {
375-
ty: fld.fold_ty(ty),
376-
path_span: fld.new_span(path_span),
377-
position,
378-
}
379-
});
380-
TyKind::Path(qself, fld.fold_path(path))
377+
let (qself, path) = fld.fold_qpath(qself, path);
378+
TyKind::Path(qself, path)
381379
}
382380
TyKind::Array(ty, length) => {
383381
TyKind::Array(fld.fold_ty(ty), fld.fold_anon_const(length))
@@ -442,6 +440,19 @@ pub fn noop_fold_path<T: Folder>(Path { segments, span }: Path, fld: &mut T) ->
442440
}
443441
}
444442

443+
pub fn noop_fold_qpath<T: Folder>(qself: Option<QSelf>,
444+
path: Path,
445+
fld: &mut T) -> (Option<QSelf>, Path) {
446+
let qself = qself.map(|QSelf { ty, path_span, position }| {
447+
QSelf {
448+
ty: fld.fold_ty(ty),
449+
path_span: fld.new_span(path_span),
450+
position,
451+
}
452+
});
453+
(qself, fld.fold_path(path))
454+
}
455+
445456
pub fn noop_fold_path_parameters<T: Folder>(path_parameters: PathParameters, fld: &mut T)
446457
-> PathParameters
447458
{
@@ -1097,15 +1108,9 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
10971108
PatKind::TupleStruct(folder.fold_path(pth),
10981109
pats.move_map(|x| folder.fold_pat(x)), ddpos)
10991110
}
1100-
PatKind::Path(opt_qself, pth) => {
1101-
let opt_qself = opt_qself.map(|qself| {
1102-
QSelf {
1103-
ty: folder.fold_ty(qself.ty),
1104-
path_span: folder.new_span(qself.path_span),
1105-
position: qself.position,
1106-
}
1107-
});
1108-
PatKind::Path(opt_qself, folder.fold_path(pth))
1111+
PatKind::Path(qself, pth) => {
1112+
let (qself, pth) = folder.fold_qpath(qself, pth);
1113+
PatKind::Path(qself, pth)
11091114
}
11101115
PatKind::Struct(pth, fields, etc) => {
11111116
let pth = folder.fold_path(pth);
@@ -1267,14 +1272,8 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu
12671272
lim)
12681273
}
12691274
ExprKind::Path(qself, path) => {
1270-
let qself = qself.map(|QSelf { ty, path_span, position }| {
1271-
QSelf {
1272-
ty: folder.fold_ty(ty),
1273-
path_span: folder.new_span(path_span),
1274-
position,
1275-
}
1276-
});
1277-
ExprKind::Path(qself, folder.fold_path(path))
1275+
let (qself, path) = folder.fold_qpath(qself, path);
1276+
ExprKind::Path(qself, path)
12781277
}
12791278
ExprKind::Break(opt_label, opt_expr) => {
12801279
ExprKind::Break(opt_label.map(|label| folder.fold_label(label)),

src/test/run-pass-fulldeps/proc-macro/auxiliary/double.rs

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ extern crate proc_macro;
1616

1717
use proc_macro::TokenStream;
1818

19+
// Outputs another copy of the struct. Useful for testing the tokens
20+
// seen by the proc_macro.
1921
#[proc_macro_derive(Double)]
2022
pub fn derive(input: TokenStream) -> TokenStream {
2123
format!("mod foo {{ {} }}", input.to_string()).parse().unwrap()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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+
pub struct ExternFoo;
12+
13+
pub trait ExternTrait {
14+
const CONST: u32;
15+
type Assoc;
16+
}
17+
18+
impl ExternTrait for ExternFoo {
19+
const CONST: u32 = 0;
20+
type Assoc = ExternFoo;
21+
}
22+
23+
#[macro_export]
24+
macro_rules! external { () => {
25+
mod bar {
26+
#[derive(Double)]
27+
struct Bar($crate::ExternFoo);
28+
}
29+
30+
mod qself {
31+
#[derive(Double)]
32+
struct QSelf(<$crate::ExternFoo as $crate::ExternTrait>::Assoc);
33+
}
34+
35+
mod qself_recurse {
36+
#[derive(Double)]
37+
struct QSelfRecurse(<
38+
<$crate::ExternFoo as $crate::ExternTrait>::Assoc
39+
as $crate::ExternTrait>::Assoc
40+
);
41+
}
42+
43+
mod qself_in_const {
44+
#[derive(Double)]
45+
#[repr(u32)]
46+
enum QSelfInConst {
47+
Variant = <$crate::ExternFoo as $crate::ExternTrait>::CONST,
48+
}
49+
}
50+
} }
51+

src/test/run-pass-fulldeps/proc-macro/crate-var.rs

+48-4
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,63 @@
99
// except according to those terms.
1010

1111
// aux-build:double.rs
12+
// aux-build:external-crate-var.rs
1213
// ignore-stage1
1314

1415
#![allow(unused)]
1516

1617
#[macro_use]
1718
extern crate double;
19+
#[macro_use]
20+
extern crate external_crate_var;
1821

1922
struct Foo;
2023

21-
macro_rules! m { () => {
22-
#[derive(Double)]
23-
struct Bar($crate::Foo);
24+
trait Trait {
25+
const CONST: u32;
26+
type Assoc;
27+
}
28+
29+
impl Trait for Foo {
30+
const CONST: u32 = 0;
31+
type Assoc = Foo;
32+
}
33+
34+
macro_rules! local { () => {
35+
// derive_Double outputs secondary copies of each definition
36+
// to test what the proc_macro sees.
37+
mod bar {
38+
#[derive(Double)]
39+
struct Bar($crate::Foo);
40+
}
41+
42+
mod qself {
43+
#[derive(Double)]
44+
struct QSelf(<::Foo as $crate::Trait>::Assoc);
45+
}
46+
47+
mod qself_recurse {
48+
#[derive(Double)]
49+
struct QSelfRecurse(<<$crate::Foo as $crate::Trait>::Assoc as $crate::Trait>::Assoc);
50+
}
51+
52+
mod qself_in_const {
53+
#[derive(Double)]
54+
#[repr(u32)]
55+
enum QSelfInConst {
56+
Variant = <::Foo as $crate::Trait>::CONST,
57+
}
58+
}
2459
} }
25-
m!();
60+
61+
mod local {
62+
local!();
63+
}
64+
65+
// and now repeat the above tests, using a macro defined in another crate
66+
67+
mod external {
68+
external!{}
69+
}
2670

2771
fn main() {}

0 commit comments

Comments
 (0)