summaryrefslogtreecommitdiff
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
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)
+ }
+]