about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>2018-12-09 17:46:12 +0300
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>2018-12-19 23:17:54 +0300
commit69c66286a9a3240309cc5474478ad0a2186e2bce (patch)
tree3727676a66520a20a27bd2769c9cd21ffa03fe04
parent2bc67da378db40b23a426ea6384b2660c29a002c (diff)
downloadrust-69c66286a9a3240309cc5474478ad0a2186e2bce.tar.gz
rust-69c66286a9a3240309cc5474478ad0a2186e2bce.zip
Reintroduce special pretty-printing for `$crate` when it's necessary for proc macros
-rw-r--r--src/librustc_resolve/build_reduced_graph.rs11
-rw-r--r--src/libsyntax/print/pprust.rs27
-rw-r--r--src/libsyntax_pos/hygiene.rs26
-rw-r--r--src/test/pretty/issue-4264.pp4
-rw-r--r--src/test/ui/proc-macro/auxiliary/dollar-crate-external.rs16
-rw-r--r--src/test/ui/proc-macro/auxiliary/dollar-crate.rs18
-rw-r--r--src/test/ui/proc-macro/dollar-crate.rs32
-rw-r--r--src/test/ui/proc-macro/dollar-crate.stderr23
-rw-r--r--src/test/ui/proc-macro/dollar-crate.stdout260
9 files changed, 403 insertions, 14 deletions
diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs
index f082d776969..6cfa9f95082 100644
--- a/src/librustc_resolve/build_reduced_graph.rs
+++ b/src/librustc_resolve/build_reduced_graph.rs
@@ -1035,4 +1035,15 @@ impl<'a, 'b> Visitor<'a> for BuildReducedGraphVisitor<'a, 'b> {
         }
         visit::walk_attribute(self, attr);
     }
+
+    fn visit_ident(&mut self, ident: Ident) {
+        if ident.name == keywords::DollarCrate.name() {
+            let name = match self.resolver.resolve_crate_root(ident).kind {
+                ModuleKind::Def(_, name) if name != keywords::Invalid.name() => name,
+                _ => keywords::Crate.name(),
+            };
+            ident.span.ctxt().set_dollar_crate_name(name);
+        }
+        visit::walk_ident(self, ident);
+    }
 }
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 8250587f9e0..5e7707f4e5c 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -724,7 +724,11 @@ pub trait PrintState<'a> {
                 self.writer().word("::")?
             }
             if segment.ident.name != keywords::PathRoot.name() {
-                self.writer().word(segment.ident.as_str().get())?;
+                if segment.ident.name == keywords::DollarCrate.name() {
+                    self.print_dollar_crate(segment.ident)?;
+                } else {
+                    self.writer().word(segment.ident.as_str().get())?;
+                }
             }
         }
         Ok(())
@@ -837,6 +841,21 @@ pub trait PrintState<'a> {
     }
 
     fn nbsp(&mut self) -> io::Result<()> { self.writer().word(" ") }
+
+    // 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("::")?;
+        }
+        self.writer().word(name.as_str().get())
+    }
 }
 
 impl<'a> PrintState<'a> for State<'a> {
@@ -2446,7 +2465,11 @@ impl<'a> State<'a> {
                           -> io::Result<()>
     {
         if segment.ident.name != keywords::PathRoot.name() {
-            self.print_ident(segment.ident)?;
+            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)?;
             }
diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs
index 72b48ede58e..3dc884a94c0 100644
--- a/src/libsyntax_pos/hygiene.rs
+++ b/src/libsyntax_pos/hygiene.rs
@@ -18,11 +18,11 @@
 use GLOBALS;
 use Span;
 use edition::{Edition, DEFAULT_EDITION};
-use symbol::Symbol;
+use symbol::{keywords, Symbol};
 
 use serialize::{Encodable, Decodable, Encoder, Decoder};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use std::fmt;
+use std::{fmt, mem};
 
 /// A SyntaxContext represents a chain of macro expansions (represented by marks).
 #[derive(Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord, Hash)]
@@ -37,6 +37,8 @@ struct SyntaxContextData {
     opaque: SyntaxContext,
     // This context, but with all transparent marks filtered away.
     opaque_and_semitransparent: SyntaxContext,
+    // Name of the crate to which `$crate` with this context would resolve.
+    dollar_crate_name: Symbol,
 }
 
 /// A mark is a unique id associated with a macro expansion.
@@ -200,6 +202,7 @@ impl HygieneData {
                 prev_ctxt: SyntaxContext(0),
                 opaque: SyntaxContext(0),
                 opaque_and_semitransparent: SyntaxContext(0),
+                dollar_crate_name: keywords::DollarCrate.name(),
             }],
             markings: FxHashMap::default(),
             default_edition: DEFAULT_EDITION,
@@ -258,6 +261,7 @@ impl SyntaxContext {
                 prev_ctxt: SyntaxContext::empty(),
                 opaque: SyntaxContext::empty(),
                 opaque_and_semitransparent: SyntaxContext::empty(),
+                dollar_crate_name: keywords::DollarCrate.name(),
             });
             SyntaxContext(data.syntax_contexts.len() as u32 - 1)
         })
@@ -324,6 +328,7 @@ impl SyntaxContext {
                         prev_ctxt,
                         opaque: new_opaque,
                         opaque_and_semitransparent: new_opaque,
+                        dollar_crate_name: keywords::DollarCrate.name(),
                     });
                     new_opaque
                 });
@@ -341,6 +346,7 @@ impl SyntaxContext {
                         prev_ctxt,
                         opaque,
                         opaque_and_semitransparent: new_opaque_and_semitransparent,
+                        dollar_crate_name: keywords::DollarCrate.name(),
                     });
                     new_opaque_and_semitransparent
                 });
@@ -356,6 +362,7 @@ impl SyntaxContext {
                     prev_ctxt,
                     opaque,
                     opaque_and_semitransparent,
+                    dollar_crate_name: keywords::DollarCrate.name(),
                 });
                 new_opaque_and_semitransparent_and_transparent
             })
@@ -510,6 +517,21 @@ impl SyntaxContext {
     pub fn outer(self) -> Mark {
         HygieneData::with(|data| data.syntax_contexts[self.0 as usize].outer_mark)
     }
+
+    pub fn dollar_crate_name(self) -> Symbol {
+        HygieneData::with(|data| data.syntax_contexts[self.0 as usize].dollar_crate_name)
+    }
+
+    pub fn set_dollar_crate_name(self, dollar_crate_name: Symbol) {
+        HygieneData::with(|data| {
+            let prev_dollar_crate_name = mem::replace(
+                &mut data.syntax_contexts[self.0 as usize].dollar_crate_name, dollar_crate_name
+            );
+            assert!(dollar_crate_name == prev_dollar_crate_name ||
+                    prev_dollar_crate_name == keywords::DollarCrate.name(),
+                    "$crate name is reset for a syntax context");
+        })
+    }
 }
 
 impl fmt::Debug for SyntaxContext {
diff --git a/src/test/pretty/issue-4264.pp b/src/test/pretty/issue-4264.pp
index 5f42b86c82a..3b01ab3a47b 100644
--- a/src/test/pretty/issue-4264.pp
+++ b/src/test/pretty/issue-4264.pp
@@ -39,8 +39,8 @@ pub fn bar() ({
 
 
 
-                  ((::fmt::format as
-                       for<'r> fn(std::fmt::Arguments<'r>) -> std::string::String {std::fmt::format})(((<::fmt::Arguments>::new_v1
+                  (($crate::fmt::format as
+                       for<'r> fn(std::fmt::Arguments<'r>) -> std::string::String {std::fmt::format})(((<$crate::fmt::Arguments>::new_v1
                                                                                                            as
                                                                                                            fn(&[&str], &[std::fmt::ArgumentV1<'_>]) -> std::fmt::Arguments<'_> {std::fmt::Arguments<'_>::new_v1})((&([("test"
                                                                                                                                                                                                                           as
diff --git a/src/test/ui/proc-macro/auxiliary/dollar-crate-external.rs b/src/test/ui/proc-macro/auxiliary/dollar-crate-external.rs
new file mode 100644
index 00000000000..8f15a2b975b
--- /dev/null
+++ b/src/test/ui/proc-macro/auxiliary/dollar-crate-external.rs
@@ -0,0 +1,16 @@
+pub type S = u8;
+
+#[macro_export]
+macro_rules! external {
+    () => {
+        dollar_crate::m! {
+            struct M($crate::S);
+        }
+
+        #[dollar_crate::a]
+        struct A($crate::S);
+
+        #[derive(dollar_crate::d)]
+        struct D($crate::S);
+    };
+}
diff --git a/src/test/ui/proc-macro/auxiliary/dollar-crate.rs b/src/test/ui/proc-macro/auxiliary/dollar-crate.rs
index b0727a33332..d0ea850d4e3 100644
--- a/src/test/ui/proc-macro/auxiliary/dollar-crate.rs
+++ b/src/test/ui/proc-macro/auxiliary/dollar-crate.rs
@@ -7,6 +7,22 @@ extern crate proc_macro;
 use proc_macro::TokenStream;
 
 #[proc_macro]
-pub fn normalize(input: TokenStream) -> TokenStream {
+pub fn m(input: TokenStream) -> TokenStream {
+    println!("PROC MACRO INPUT (PRETTY-PRINTED): {}", input);
+    println!("PROC MACRO INPUT: {:#?}", input);
+    input.into_iter().collect()
+}
+
+#[proc_macro_attribute]
+pub fn a(_args: TokenStream, input: TokenStream) -> TokenStream {
+    println!("ATTRIBUTE INPUT (PRETTY-PRINTED): {}", input);
+    println!("ATTRIBUTE INPUT: {:#?}", input);
+    input.into_iter().collect()
+}
+
+#[proc_macro_derive(d)]
+pub fn d(input: TokenStream) -> TokenStream {
+    println!("DERIVE INPUT (PRETTY-PRINTED): {}", input);
+    println!("DERIVE INPUT: {:#?}", input);
     input.into_iter().collect()
 }
diff --git a/src/test/ui/proc-macro/dollar-crate.rs b/src/test/ui/proc-macro/dollar-crate.rs
index b8b1ddd5d1c..3f4a01cb2dc 100644
--- a/src/test/ui/proc-macro/dollar-crate.rs
+++ b/src/test/ui/proc-macro/dollar-crate.rs
@@ -1,16 +1,34 @@
-// compile-pass
+// edition:2018
+// compile-flags:--extern dollar_crate --extern dollar_crate_external
 // aux-build:dollar-crate.rs
+// aux-build:dollar-crate-external.rs
 
-extern crate dollar_crate;
+// Anonymize unstable non-dummy spans while still showing dummy spans `0..0`.
+// normalize-stdout-test "bytes\([^0]\w*\.\.(\w+)\)" -> "bytes(LO..$1)"
+// normalize-stdout-test "bytes\((\w+)\.\.[^0]\w*\)" -> "bytes($1..HI)"
 
 type S = u8;
 
-macro_rules! check { () => {
-    dollar_crate::normalize! {
-        type A = $crate::S;
+mod local {
+    macro_rules! local {
+        () => {
+            dollar_crate::m! {
+                struct M($crate::S);
+            }
+
+            #[dollar_crate::a]
+            struct A($crate::S);
+
+            #[derive(dollar_crate::d)]
+            struct D($crate::S); //~ ERROR the name `D` is defined multiple times
+        };
     }
-}}
 
-check!();
+    local!();
+}
+
+mod external {
+    dollar_crate_external::external!(); //~ ERROR the name `D` is defined multiple times
+}
 
 fn main() {}
diff --git a/src/test/ui/proc-macro/dollar-crate.stderr b/src/test/ui/proc-macro/dollar-crate.stderr
new file mode 100644
index 00000000000..171562a5aff
--- /dev/null
+++ b/src/test/ui/proc-macro/dollar-crate.stderr
@@ -0,0 +1,23 @@
+error[E0428]: the name `D` is defined multiple times
+  --> $DIR/dollar-crate.rs:23:13
+   |
+LL |             struct D($crate::S); //~ ERROR the name `D` is defined multiple times
+   |             ^^^^^^^^^^^^^^^^^^^^ `D` redefined here
+...
+LL |     local!();
+   |     --------- in this macro invocation
+   |
+   = note: `D` must be defined only once in the type namespace of this module
+
+error[E0428]: the name `D` is defined multiple times
+  --> $DIR/dollar-crate.rs:31:5
+   |
+LL |     dollar_crate_external::external!(); //~ ERROR the name `D` is defined multiple times
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `D` redefined here
+   |
+   = note: `D` must be defined only once in the type namespace of this module
+   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0428`.
diff --git a/src/test/ui/proc-macro/dollar-crate.stdout b/src/test/ui/proc-macro/dollar-crate.stdout
new file mode 100644
index 00000000000..8e254854e32
--- /dev/null
+++ b/src/test/ui/proc-macro/dollar-crate.stdout
@@ -0,0 +1,260 @@
+PROC MACRO INPUT (PRETTY-PRINTED): struct M ( $crate :: S ) ;
+PROC MACRO INPUT: TokenStream [
+    Ident {
+        ident: "struct",
+        span: #2 bytes(LO..HI)
+    },
+    Ident {
+        ident: "M",
+        span: #2 bytes(LO..HI)
+    },
+    Group {
+        delimiter: Parenthesis,
+        stream: TokenStream [
+            Ident {
+                ident: "$crate",
+                span: #2 bytes(LO..HI)
+            },
+            Punct {
+                ch: ':',
+                spacing: Joint,
+                span: #2 bytes(LO..HI)
+            },
+            Punct {
+                ch: ':',
+                spacing: Alone,
+                span: #2 bytes(LO..HI)
+            },
+            Ident {
+                ident: "S",
+                span: #2 bytes(LO..HI)
+            }
+        ],
+        span: #2 bytes(LO..HI)
+    },
+    Punct {
+        ch: ';',
+        spacing: Alone,
+        span: #2 bytes(LO..HI)
+    }
+]
+ATTRIBUTE INPUT (PRETTY-PRINTED): struct A(crate::S);
+ATTRIBUTE INPUT: TokenStream [
+    Ident {
+        ident: "struct",
+        span: #0 bytes(0..0)
+    },
+    Ident {
+        ident: "A",
+        span: #0 bytes(0..0)
+    },
+    Group {
+        delimiter: Parenthesis,
+        stream: TokenStream [
+            Ident {
+                ident: "crate",
+                span: #0 bytes(0..0)
+            },
+            Punct {
+                ch: ':',
+                spacing: Joint,
+                span: #0 bytes(0..0)
+            },
+            Punct {
+                ch: ':',
+                spacing: Alone,
+                span: #0 bytes(0..0)
+            },
+            Ident {
+                ident: "S",
+                span: #0 bytes(0..0)
+            }
+        ],
+        span: #0 bytes(0..0)
+    },
+    Punct {
+        ch: ';',
+        spacing: Alone,
+        span: #0 bytes(0..0)
+    }
+]
+DERIVE INPUT (PRETTY-PRINTED): struct D(crate::S);
+DERIVE INPUT: TokenStream [
+    Ident {
+        ident: "struct",
+        span: #0 bytes(0..0)
+    },
+    Ident {
+        ident: "D",
+        span: #0 bytes(0..0)
+    },
+    Group {
+        delimiter: Parenthesis,
+        stream: TokenStream [
+            Ident {
+                ident: "crate",
+                span: #0 bytes(0..0)
+            },
+            Punct {
+                ch: ':',
+                spacing: Joint,
+                span: #0 bytes(0..0)
+            },
+            Punct {
+                ch: ':',
+                spacing: Alone,
+                span: #0 bytes(0..0)
+            },
+            Ident {
+                ident: "S",
+                span: #0 bytes(0..0)
+            }
+        ],
+        span: #0 bytes(0..0)
+    },
+    Punct {
+        ch: ';',
+        spacing: Alone,
+        span: #0 bytes(0..0)
+    }
+]
+PROC MACRO INPUT (PRETTY-PRINTED): struct M ( $crate :: S ) ;
+PROC MACRO INPUT: TokenStream [
+    Ident {
+        ident: "struct",
+        span: #10 bytes(LO..HI)
+    },
+    Ident {
+        ident: "M",
+        span: #10 bytes(LO..HI)
+    },
+    Group {
+        delimiter: Parenthesis,
+        stream: TokenStream [
+            Ident {
+                ident: "$crate",
+                span: #10 bytes(LO..HI)
+            },
+            Punct {
+                ch: ':',
+                spacing: Joint,
+                span: #10 bytes(LO..HI)
+            },
+            Punct {
+                ch: ':',
+                spacing: Alone,
+                span: #10 bytes(LO..HI)
+            },
+            Ident {
+                ident: "S",
+                span: #10 bytes(LO..HI)
+            }
+        ],
+        span: #10 bytes(LO..HI)
+    },
+    Punct {
+        ch: ';',
+        spacing: Alone,
+        span: #10 bytes(LO..HI)
+    }
+]
+ATTRIBUTE INPUT (PRETTY-PRINTED): struct A(::dollar_crate_external::S);
+ATTRIBUTE INPUT: TokenStream [
+    Ident {
+        ident: "struct",
+        span: #0 bytes(0..0)
+    },
+    Ident {
+        ident: "A",
+        span: #0 bytes(0..0)
+    },
+    Group {
+        delimiter: Parenthesis,
+        stream: TokenStream [
+            Punct {
+                ch: ':',
+                spacing: Joint,
+                span: #0 bytes(0..0)
+            },
+            Punct {
+                ch: ':',
+                spacing: Alone,
+                span: #0 bytes(0..0)
+            },
+            Ident {
+                ident: "dollar_crate_external",
+                span: #0 bytes(0..0)
+            },
+            Punct {
+                ch: ':',
+                spacing: Joint,
+                span: #0 bytes(0..0)
+            },
+            Punct {
+                ch: ':',
+                spacing: Alone,
+                span: #0 bytes(0..0)
+            },
+            Ident {
+                ident: "S",
+                span: #0 bytes(0..0)
+            }
+        ],
+        span: #0 bytes(0..0)
+    },
+    Punct {
+        ch: ';',
+        spacing: Alone,
+        span: #0 bytes(0..0)
+    }
+]
+DERIVE INPUT (PRETTY-PRINTED): struct D(::dollar_crate_external::S);
+DERIVE INPUT: TokenStream [
+    Ident {
+        ident: "struct",
+        span: #0 bytes(0..0)
+    },
+    Ident {
+        ident: "D",
+        span: #0 bytes(0..0)
+    },
+    Group {
+        delimiter: Parenthesis,
+        stream: TokenStream [
+            Punct {
+                ch: ':',
+                spacing: Joint,
+                span: #0 bytes(0..0)
+            },
+            Punct {
+                ch: ':',
+                spacing: Alone,
+                span: #0 bytes(0..0)
+            },
+            Ident {
+                ident: "dollar_crate_external",
+                span: #0 bytes(0..0)
+            },
+            Punct {
+                ch: ':',
+                spacing: Joint,
+                span: #0 bytes(0..0)
+            },
+            Punct {
+                ch: ':',
+                spacing: Alone,
+                span: #0 bytes(0..0)
+            },
+            Ident {
+                ident: "S",
+                span: #0 bytes(0..0)
+            }
+        ],
+        span: #0 bytes(0..0)
+    },
+    Punct {
+        ch: ';',
+        spacing: Alone,
+        span: #0 bytes(0..0)
+    }
+]