Skip to content

Commit 9622f9d

Browse files
committed
Auto merge of #56647 - petrochenkov:dcrate2, r=alexcrichton
Rework treatment of `$crate` in procedural macros Important clarification: `$crate` below means "processed `$crate`" or "output `$crate`". In the input of a decl macro `$crate` is just two separate tokens, but in the *output of a decl macro* `$crate` is a single keyword identifier (#55640 (comment)). First of all, this PR removes the `eliminate_crate_var` hack. `$crate::foo` is no longer replaced with `::foo` or `::crate_name::foo` in the input of derive proc macros, it's passed to the macro instead with its precise span and hygiene data, and can be treated as any other path segment keyword (like `crate` or `self`) after that. (Note: `eliminate_crate_var` was never used for non-derive proc macros.) This creates an annoying problem - derive macros still may stringify their input before processing and expect `$crate` survive that stringification and refer to the same crate (the Rust 1.15-1.29 way of doing things). Moreover, the input of proc macro attributes and derives (but not fn-like proc macros) also effectively survives stringification before being passed to the macro (also for legacy implementation reasons). So we kind of resurrect the `eliminate_crate_var` hack in reduced form, but apply it only to AST pretty-printing. If an AST fragment is pretty-printed, the resulting *text* will have `$crate` replaced with `crate` or `::crate_name`. This should be enough to keep all the legacy cases working. Closes #55640 Closes #56622 r? @ghost
2 parents 4755e2f + edab6c7 commit 9622f9d

File tree

19 files changed

+487
-189
lines changed

19 files changed

+487
-189
lines changed

src/libproc_macro/lib.rs

-21
Original file line numberDiff line numberDiff line change
@@ -729,11 +729,6 @@ impl Punct {
729729
/// which can be further configured with the `set_span` method below.
730730
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
731731
pub fn new(ch: char, spacing: Spacing) -> Punct {
732-
const LEGAL_CHARS: &[char] = &['=', '<', '>', '!', '~', '+', '-', '*', '/', '%', '^',
733-
'&', '|', '@', '.', ',', ';', ':', '#', '$', '?', '\''];
734-
if !LEGAL_CHARS.contains(&ch) {
735-
panic!("unsupported character `{:?}`", ch)
736-
}
737732
Punct(bridge::client::Punct::new(ch, spacing))
738733
}
739734

@@ -800,16 +795,6 @@ impl fmt::Debug for Punct {
800795
pub struct Ident(bridge::client::Ident);
801796

802797
impl Ident {
803-
fn is_valid(string: &str) -> bool {
804-
let mut chars = string.chars();
805-
if let Some(start) = chars.next() {
806-
(start == '_' || start.is_xid_start())
807-
&& chars.all(|cont| cont == '_' || cont.is_xid_continue())
808-
} else {
809-
false
810-
}
811-
}
812-
813798
/// Creates a new `Ident` with the given `string` as well as the specified
814799
/// `span`.
815800
/// The `string` argument must be a valid identifier permitted by the
@@ -831,18 +816,12 @@ impl Ident {
831816
/// tokens, requires a `Span` to be specified at construction.
832817
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
833818
pub fn new(string: &str, span: Span) -> Ident {
834-
if !Ident::is_valid(string) {
835-
panic!("`{:?}` is not a valid identifier", string)
836-
}
837819
Ident(bridge::client::Ident::new(string, span.0, false))
838820
}
839821

840822
/// Same as `Ident::new`, but creates a raw identifier (`r#ident`).
841823
#[unstable(feature = "proc_macro_raw_ident", issue = "54723")]
842824
pub fn new_raw(string: &str, span: Span) -> Ident {
843-
if !Ident::is_valid(string) {
844-
panic!("`{:?}` is not a valid identifier", string)
845-
}
846825
Ident(bridge::client::Ident::new(string, span.0, true))
847826
}
848827

src/librustc/hir/print.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -1622,8 +1622,7 @@ impl<'a> State<'a> {
16221622
if i > 0 {
16231623
self.s.word("::")?
16241624
}
1625-
if segment.ident.name != keywords::PathRoot.name() &&
1626-
segment.ident.name != keywords::DollarCrate.name() {
1625+
if segment.ident.name != keywords::PathRoot.name() {
16271626
self.print_ident(segment.ident)?;
16281627
segment.with_generic_args(|generic_args| {
16291628
self.print_generic_args(generic_args, segment.infer_types,
@@ -1636,8 +1635,7 @@ impl<'a> State<'a> {
16361635
}
16371636

16381637
pub fn print_path_segment(&mut self, segment: &hir::PathSegment) -> io::Result<()> {
1639-
if segment.ident.name != keywords::PathRoot.name() &&
1640-
segment.ident.name != keywords::DollarCrate.name() {
1638+
if segment.ident.name != keywords::PathRoot.name() {
16411639
self.print_ident(segment.ident)?;
16421640
segment.with_generic_args(|generic_args| {
16431641
self.print_generic_args(generic_args, segment.infer_types, false)
@@ -1664,8 +1662,7 @@ impl<'a> State<'a> {
16641662
if i > 0 {
16651663
self.s.word("::")?
16661664
}
1667-
if segment.ident.name != keywords::PathRoot.name() &&
1668-
segment.ident.name != keywords::DollarCrate.name() {
1665+
if segment.ident.name != keywords::PathRoot.name() {
16691666
self.print_ident(segment.ident)?;
16701667
segment.with_generic_args(|generic_args| {
16711668
self.print_generic_args(generic_args,

src/librustc_resolve/build_reduced_graph.rs

+11
Original file line numberDiff line numberDiff line change
@@ -1035,4 +1035,15 @@ impl<'a, 'b> Visitor<'a> for BuildReducedGraphVisitor<'a, 'b> {
10351035
}
10361036
visit::walk_attribute(self, attr);
10371037
}
1038+
1039+
fn visit_ident(&mut self, ident: Ident) {
1040+
if ident.name == keywords::DollarCrate.name() {
1041+
let name = match self.resolver.resolve_crate_root(ident).kind {
1042+
ModuleKind::Def(_, name) if name != keywords::Invalid.name() => name,
1043+
_ => keywords::Crate.name(),
1044+
};
1045+
ident.span.ctxt().set_dollar_crate_name(name);
1046+
}
1047+
visit::walk_ident(self, ident);
1048+
}
10381049
}

src/librustc_resolve/lib.rs

-4
Original file line numberDiff line numberDiff line change
@@ -1173,10 +1173,6 @@ impl<'a> ModuleData<'a> {
11731173
}
11741174
}
11751175

1176-
fn is_local(&self) -> bool {
1177-
self.normal_ancestor_id.is_local()
1178-
}
1179-
11801176
fn nearest_item_scope(&'a self) -> Module<'a> {
11811177
if self.is_trait() { self.parent.unwrap() } else { self }
11821178
}

src/librustc_resolve/macros.rs

+1-56
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
use {AmbiguityError, AmbiguityKind, AmbiguityErrorMisc};
1212
use {CrateLint, Resolver, ResolutionError, ScopeSet, Weak};
13-
use {Module, ModuleKind, NameBinding, NameBindingKind, PathResult, Segment, ToNameBinding};
13+
use {Module, NameBinding, NameBindingKind, PathResult, Segment, ToNameBinding};
1414
use {is_known_tool, resolve_error};
1515
use ModuleOrUniformRoot;
1616
use Namespace::*;
@@ -30,8 +30,6 @@ use syntax::ext::expand::{AstFragment, Invocation, InvocationKind};
3030
use syntax::ext::hygiene::{self, Mark};
3131
use syntax::ext::tt::macro_rules;
3232
use syntax::feature_gate::{feature_err, is_builtin_attr_name, GateIssue};
33-
use syntax::fold::{self, Folder};
34-
use syntax::ptr::P;
3533
use syntax::symbol::{Symbol, keywords};
3634
use syntax::util::lev_distance::find_best_match_for_name;
3735
use syntax_pos::{Span, DUMMY_SP};
@@ -138,58 +136,6 @@ impl<'a> base::Resolver for Resolver<'a> {
138136
mark
139137
}
140138

141-
fn eliminate_crate_var(&mut self, item: P<ast::Item>) -> P<ast::Item> {
142-
struct EliminateCrateVar<'b, 'a: 'b>(
143-
&'b mut Resolver<'a>, Span
144-
);
145-
146-
impl<'a, 'b> Folder for EliminateCrateVar<'a, 'b> {
147-
fn fold_path(&mut self, path: ast::Path) -> ast::Path {
148-
match self.fold_qpath(None, path) {
149-
(None, path) => path,
150-
_ => unreachable!(),
151-
}
152-
}
153-
154-
fn fold_qpath(&mut self, mut qself: Option<ast::QSelf>, mut path: ast::Path)
155-
-> (Option<ast::QSelf>, ast::Path) {
156-
qself = qself.map(|ast::QSelf { ty, path_span, position }| {
157-
ast::QSelf {
158-
ty: self.fold_ty(ty),
159-
path_span: self.new_span(path_span),
160-
position,
161-
}
162-
});
163-
164-
if path.segments[0].ident.name == keywords::DollarCrate.name() {
165-
let module = self.0.resolve_crate_root(path.segments[0].ident);
166-
path.segments[0].ident.name = keywords::PathRoot.name();
167-
if !module.is_local() {
168-
let span = path.segments[0].ident.span;
169-
path.segments.insert(1, match module.kind {
170-
ModuleKind::Def(_, name) => ast::PathSegment::from_ident(
171-
ast::Ident::with_empty_ctxt(name).with_span_pos(span)
172-
),
173-
_ => unreachable!(),
174-
});
175-
if let Some(qself) = &mut qself {
176-
qself.position += 1;
177-
}
178-
}
179-
}
180-
(qself, path)
181-
}
182-
183-
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
184-
fold::noop_fold_mac(mac, self)
185-
}
186-
}
187-
188-
let ret = EliminateCrateVar(self, item.span).fold_item(item);
189-
assert!(ret.len() == 1);
190-
ret.into_iter().next().unwrap()
191-
}
192-
193139
fn visit_ast_fragment_with_placeholders(&mut self, mark: Mark, fragment: &AstFragment,
194140
derives: &[Mark]) {
195141
let invocation = self.invocations[&mark];
@@ -259,7 +205,6 @@ impl<'a> base::Resolver for Resolver<'a> {
259205
self.definitions.add_parent_module_of_macro_def(invoc.expansion_data.mark,
260206
normal_module_def_id);
261207
invoc.expansion_data.mark.set_default_transparency(ext.default_transparency());
262-
invoc.expansion_data.mark.set_is_builtin(def_id.krate == CrateNum::BuiltinMacros);
263208
}
264209

265210
Ok(Some(ext))

src/libsyntax/ext/base.rs

-2
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,6 @@ pub type NamedSyntaxExtension = (Name, SyntaxExtension);
732732
pub trait Resolver {
733733
fn next_node_id(&mut self) -> ast::NodeId;
734734
fn get_module_scope(&mut self, id: ast::NodeId) -> Mark;
735-
fn eliminate_crate_var(&mut self, item: P<ast::Item>) -> P<ast::Item>;
736735

737736
fn visit_ast_fragment_with_placeholders(&mut self, mark: Mark, fragment: &AstFragment,
738737
derives: &[Mark]);
@@ -766,7 +765,6 @@ pub struct DummyResolver;
766765
impl Resolver for DummyResolver {
767766
fn next_node_id(&mut self) -> ast::NodeId { ast::DUMMY_NODE_ID }
768767
fn get_module_scope(&mut self, _id: ast::NodeId) -> Mark { Mark::root() }
769-
fn eliminate_crate_var(&mut self, item: P<ast::Item>) -> P<ast::Item> { item }
770768

771769
fn visit_ast_fragment_with_placeholders(&mut self, _invoc: Mark, _fragment: &AstFragment,
772770
_derives: &[Mark]) {}

src/libsyntax/ext/expand.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -203,10 +203,7 @@ fn macro_bang_format(path: &ast::Path) -> ExpnFormat {
203203
if i != 0 {
204204
path_str.push_str("::");
205205
}
206-
207-
if segment.ident.name != keywords::PathRoot.name() &&
208-
segment.ident.name != keywords::DollarCrate.name()
209-
{
206+
if segment.ident.name != keywords::PathRoot.name() {
210207
path_str.push_str(&segment.ident.as_str())
211208
}
212209
}

src/libsyntax/parse/token.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,9 @@ impl Token {
633633
(&Shebang(a), &Shebang(b)) => a == b,
634634

635635
(&Lifetime(a), &Lifetime(b)) => a.name == b.name,
636-
(&Ident(a, b), &Ident(c, d)) => a.name == c.name && b == d,
636+
(&Ident(a, b), &Ident(c, d)) => b == d && (a.name == c.name ||
637+
a.name == keywords::DollarCrate.name() ||
638+
c.name == keywords::DollarCrate.name()),
637639

638640
(&Literal(ref a, b), &Literal(ref c, d)) => {
639641
b == d && a.probably_equal_for_proc_macro(c)

src/libsyntax/print/pprust.rs

+24-22
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ use util::parser::{self, AssocOp, Fixity};
1616
use attr;
1717
use source_map::{self, SourceMap, Spanned};
1818
use syntax_pos::{self, BytePos};
19-
use syntax_pos::hygiene::{Mark, SyntaxContext};
2019
use parse::token::{self, BinOpToken, Token};
2120
use parse::lexer::comments;
2221
use parse::{self, ParseSess};
@@ -724,12 +723,12 @@ pub trait PrintState<'a> {
724723
if i > 0 {
725724
self.writer().word("::")?
726725
}
727-
if segment.ident.name != keywords::PathRoot.name() &&
728-
segment.ident.name != keywords::DollarCrate.name()
729-
{
730-
self.writer().word(segment.ident.as_str().get())?;
731-
} else if segment.ident.name == keywords::DollarCrate.name() {
732-
self.print_dollar_crate(segment.ident.span.ctxt())?;
726+
if segment.ident.name != keywords::PathRoot.name() {
727+
if segment.ident.name == keywords::DollarCrate.name() {
728+
self.print_dollar_crate(segment.ident)?;
729+
} else {
730+
self.writer().word(segment.ident.as_str().get())?;
731+
}
733732
}
734733
}
735734
Ok(())
@@ -843,17 +842,19 @@ pub trait PrintState<'a> {
843842

844843
fn nbsp(&mut self) -> io::Result<()> { self.writer().word(" ") }
845844

846-
fn print_dollar_crate(&mut self, mut ctxt: SyntaxContext) -> io::Result<()> {
847-
if let Some(mark) = ctxt.adjust(Mark::root()) {
848-
// Make a best effort to print something that complies
849-
if mark.is_builtin() {
850-
if let Some(name) = std_inject::injected_crate_name() {
851-
self.writer().word("::")?;
852-
self.writer().word(name)?;
853-
}
854-
}
845+
// AST pretty-printer is used as a fallback for turning AST structures into token streams for
846+
// proc macros. Additionally, proc macros may stringify their input and expect it survive the
847+
// stringification (especially true for proc macro derives written between Rust 1.15 and 1.30).
848+
// So we need to somehow pretty-print `$crate` in paths in a way preserving at least some of
849+
// its hygiene data, most importantly name of the crate it refers to.
850+
// As a result we print `$crate` as `crate` if it refers to the local crate
851+
// and as `::other_crate_name` if it refers to some other crate.
852+
fn print_dollar_crate(&mut self, ident: ast::Ident) -> io::Result<()> {
853+
let name = ident.span.ctxt().dollar_crate_name();
854+
if !ast::Ident::with_empty_ctxt(name).is_path_segment_keyword() {
855+
self.writer().word("::")?;
855856
}
856-
Ok(())
857+
self.writer().word(name.as_str().get())
857858
}
858859
}
859860

@@ -2463,14 +2464,15 @@ impl<'a> State<'a> {
24632464
colons_before_params: bool)
24642465
-> io::Result<()>
24652466
{
2466-
if segment.ident.name != keywords::PathRoot.name() &&
2467-
segment.ident.name != keywords::DollarCrate.name() {
2468-
self.print_ident(segment.ident)?;
2467+
if segment.ident.name != keywords::PathRoot.name() {
2468+
if segment.ident.name == keywords::DollarCrate.name() {
2469+
self.print_dollar_crate(segment.ident)?;
2470+
} else {
2471+
self.print_ident(segment.ident)?;
2472+
}
24692473
if let Some(ref args) = segment.args {
24702474
self.print_generic_args(args, colons_before_params)?;
24712475
}
2472-
} else if segment.ident.name == keywords::DollarCrate.name() {
2473-
self.print_dollar_crate(segment.ident.span.ctxt())?;
24742476
}
24752477
Ok(())
24762478
}

src/libsyntax/tokenstream.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,9 @@ impl TokenStream {
348348
| TokenTree::Token(_, Token::Semi)
349349
// The pretty printer collapses whitespace arbitrarily and can
350350
// introduce whitespace from `NoDelim`.
351-
| TokenTree::Token(_, Token::Whitespace) => false,
351+
| TokenTree::Token(_, Token::Whitespace)
352+
// The pretty printer can turn `$crate` into `::crate_name`
353+
| TokenTree::Token(_, Token::ModSep) => false,
352354
_ => true
353355
}
354356
}

src/libsyntax_ext/deriving/custom.rs

-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ impl MultiItemModifier for ProcMacroDerive {
7474
// Mark attributes as known, and used.
7575
MarkAttrs(&self.attrs).visit_item(&item);
7676

77-
let item = ecx.resolver.eliminate_crate_var(item);
7877
let token = Token::interpolated(token::NtItem(item));
7978
let input = tokenstream::TokenTree::Token(DUMMY_SP, token).into();
8079

0 commit comments

Comments
 (0)