diff options
| author | bors <bors@rust-lang.org> | 2018-12-20 12:42:54 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2018-12-20 12:42:54 +0000 |
| commit | 9622f9dc4745eb59fd229477f453ae83e8044db9 (patch) | |
| tree | 4d230e44e0f31c6c44a2728e4a1e89d513bfb441 /src/libsyntax | |
| parent | 4755e2f3b635ad7391ba88b18672b05cbae5ba4c (diff) | |
| parent | edab6c74923cacb3058fb7942577ad27e7cd32ff (diff) | |
| download | rust-9622f9dc4745eb59fd229477f453ae83e8044db9.tar.gz rust-9622f9dc4745eb59fd229477f453ae83e8044db9.zip | |
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 (https://github.com/rust-lang/rust/issues/55640#issuecomment-435692791). 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 https://github.com/rust-lang/rust/issues/55640 Closes https://github.com/rust-lang/rust/issues/56622 r? @ghost
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ext/base.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/ext/expand.rs | 5 | ||||
| -rw-r--r-- | src/libsyntax/parse/token.rs | 4 | ||||
| -rw-r--r-- | src/libsyntax/print/pprust.rs | 46 | ||||
| -rw-r--r-- | src/libsyntax/tokenstream.rs | 4 |
5 files changed, 31 insertions, 30 deletions
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 310cf27689b..b807a65f6ae 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -732,7 +732,6 @@ pub type NamedSyntaxExtension = (Name, SyntaxExtension); pub trait Resolver { fn next_node_id(&mut self) -> ast::NodeId; fn get_module_scope(&mut self, id: ast::NodeId) -> Mark; - fn eliminate_crate_var(&mut self, item: P<ast::Item>) -> P<ast::Item>; fn visit_ast_fragment_with_placeholders(&mut self, mark: Mark, fragment: &AstFragment, derives: &[Mark]); @@ -766,7 +765,6 @@ pub struct DummyResolver; impl Resolver for DummyResolver { fn next_node_id(&mut self) -> ast::NodeId { ast::DUMMY_NODE_ID } fn get_module_scope(&mut self, _id: ast::NodeId) -> Mark { Mark::root() } - fn eliminate_crate_var(&mut self, item: P<ast::Item>) -> P<ast::Item> { item } fn visit_ast_fragment_with_placeholders(&mut self, _invoc: Mark, _fragment: &AstFragment, _derives: &[Mark]) {} diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 03c7aa96824..57ccc3e9817 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -203,10 +203,7 @@ fn macro_bang_format(path: &ast::Path) -> ExpnFormat { if i != 0 { path_str.push_str("::"); } - - if segment.ident.name != keywords::PathRoot.name() && - segment.ident.name != keywords::DollarCrate.name() - { + if segment.ident.name != keywords::PathRoot.name() { path_str.push_str(&segment.ident.as_str()) } } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index ed746657459..badcc4ed876 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -633,7 +633,9 @@ impl Token { (&Shebang(a), &Shebang(b)) => a == b, (&Lifetime(a), &Lifetime(b)) => a.name == b.name, - (&Ident(a, b), &Ident(c, d)) => a.name == c.name && b == d, + (&Ident(a, b), &Ident(c, d)) => b == d && (a.name == c.name || + a.name == keywords::DollarCrate.name() || + c.name == keywords::DollarCrate.name()), (&Literal(ref a, b), &Literal(ref c, d)) => { b == d && a.probably_equal_for_proc_macro(c) diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 41165c7e36d..5e7707f4e5c 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -16,7 +16,6 @@ use util::parser::{self, AssocOp, Fixity}; use attr; use source_map::{self, SourceMap, Spanned}; use syntax_pos::{self, BytePos}; -use syntax_pos::hygiene::{Mark, SyntaxContext}; use parse::token::{self, BinOpToken, Token}; use parse::lexer::comments; use parse::{self, ParseSess}; @@ -724,12 +723,12 @@ pub trait PrintState<'a> { if i > 0 { self.writer().word("::")? } - if segment.ident.name != keywords::PathRoot.name() && - segment.ident.name != keywords::DollarCrate.name() - { - self.writer().word(segment.ident.as_str().get())?; - } else if segment.ident.name == keywords::DollarCrate.name() { - self.print_dollar_crate(segment.ident.span.ctxt())?; + if segment.ident.name != keywords::PathRoot.name() { + if segment.ident.name == keywords::DollarCrate.name() { + self.print_dollar_crate(segment.ident)?; + } else { + self.writer().word(segment.ident.as_str().get())?; + } } } Ok(()) @@ -843,17 +842,19 @@ pub trait PrintState<'a> { fn nbsp(&mut self) -> io::Result<()> { self.writer().word(" ") } - fn print_dollar_crate(&mut self, mut ctxt: SyntaxContext) -> io::Result<()> { - if let Some(mark) = ctxt.adjust(Mark::root()) { - // Make a best effort to print something that complies - if mark.is_builtin() { - if let Some(name) = std_inject::injected_crate_name() { - self.writer().word("::")?; - self.writer().word(name)?; - } - } + // AST pretty-printer is used as a fallback for turning AST structures into token streams for + // proc macros. Additionally, proc macros may stringify their input and expect it survive the + // stringification (especially true for proc macro derives written between Rust 1.15 and 1.30). + // So we need to somehow pretty-print `$crate` in paths in a way preserving at least some of + // its hygiene data, most importantly name of the crate it refers to. + // As a result we print `$crate` as `crate` if it refers to the local crate + // and as `::other_crate_name` if it refers to some other crate. + fn print_dollar_crate(&mut self, ident: ast::Ident) -> io::Result<()> { + let name = ident.span.ctxt().dollar_crate_name(); + if !ast::Ident::with_empty_ctxt(name).is_path_segment_keyword() { + self.writer().word("::")?; } - Ok(()) + self.writer().word(name.as_str().get()) } } @@ -2463,14 +2464,15 @@ impl<'a> State<'a> { colons_before_params: bool) -> io::Result<()> { - if segment.ident.name != keywords::PathRoot.name() && - segment.ident.name != keywords::DollarCrate.name() { - self.print_ident(segment.ident)?; + if segment.ident.name != keywords::PathRoot.name() { + if segment.ident.name == keywords::DollarCrate.name() { + self.print_dollar_crate(segment.ident)?; + } else { + self.print_ident(segment.ident)?; + } if let Some(ref args) = segment.args { self.print_generic_args(args, colons_before_params)?; } - } else if segment.ident.name == keywords::DollarCrate.name() { - self.print_dollar_crate(segment.ident.span.ctxt())?; } Ok(()) } diff --git a/src/libsyntax/tokenstream.rs b/src/libsyntax/tokenstream.rs index c11ef33f931..013ecd3d343 100644 --- a/src/libsyntax/tokenstream.rs +++ b/src/libsyntax/tokenstream.rs @@ -348,7 +348,9 @@ impl TokenStream { | TokenTree::Token(_, Token::Semi) // The pretty printer collapses whitespace arbitrarily and can // introduce whitespace from `NoDelim`. - | TokenTree::Token(_, Token::Whitespace) => false, + | TokenTree::Token(_, Token::Whitespace) + // The pretty printer can turn `$crate` into `::crate_name` + | TokenTree::Token(_, Token::ModSep) => false, _ => true } } |
