diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index f2b85832dac31..ebba5c81fe077 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -729,11 +729,6 @@ impl Punct { /// which can be further configured with the `set_span` method below. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] pub fn new(ch: char, spacing: Spacing) -> Punct { - const LEGAL_CHARS: &[char] = &['=', '<', '>', '!', '~', '+', '-', '*', '/', '%', '^', - '&', '|', '@', '.', ',', ';', ':', '#', '$', '?', '\'']; - if !LEGAL_CHARS.contains(&ch) { - panic!("unsupported character `{:?}`", ch) - } Punct(bridge::client::Punct::new(ch, spacing)) } @@ -800,16 +795,6 @@ impl fmt::Debug for Punct { pub struct Ident(bridge::client::Ident); impl Ident { - fn is_valid(string: &str) -> bool { - let mut chars = string.chars(); - if let Some(start) = chars.next() { - (start == '_' || start.is_xid_start()) - && chars.all(|cont| cont == '_' || cont.is_xid_continue()) - } else { - false - } - } - /// Creates a new `Ident` with the given `string` as well as the specified /// `span`. /// The `string` argument must be a valid identifier permitted by the @@ -831,18 +816,12 @@ impl Ident { /// tokens, requires a `Span` to be specified at construction. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] pub fn new(string: &str, span: Span) -> Ident { - if !Ident::is_valid(string) { - panic!("`{:?}` is not a valid identifier", string) - } Ident(bridge::client::Ident::new(string, span.0, false)) } /// Same as `Ident::new`, but creates a raw identifier (`r#ident`). #[unstable(feature = "proc_macro_raw_ident", issue = "54723")] pub fn new_raw(string: &str, span: Span) -> Ident { - if !Ident::is_valid(string) { - panic!("`{:?}` is not a valid identifier", string) - } Ident(bridge::client::Ident::new(string, span.0, true)) } diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index a24f2fa4bc652..5c6845181afd1 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -1622,8 +1622,7 @@ impl<'a> State<'a> { if i > 0 { self.s.word("::")? } - if segment.ident.name != keywords::PathRoot.name() && - segment.ident.name != keywords::DollarCrate.name() { + if segment.ident.name != keywords::PathRoot.name() { self.print_ident(segment.ident)?; segment.with_generic_args(|generic_args| { self.print_generic_args(generic_args, segment.infer_types, @@ -1636,8 +1635,7 @@ impl<'a> State<'a> { } pub fn print_path_segment(&mut self, segment: &hir::PathSegment) -> io::Result<()> { - if segment.ident.name != keywords::PathRoot.name() && - segment.ident.name != keywords::DollarCrate.name() { + if segment.ident.name != keywords::PathRoot.name() { self.print_ident(segment.ident)?; segment.with_generic_args(|generic_args| { self.print_generic_args(generic_args, segment.infer_types, false) @@ -1664,8 +1662,7 @@ impl<'a> State<'a> { if i > 0 { self.s.word("::")? } - if segment.ident.name != keywords::PathRoot.name() && - segment.ident.name != keywords::DollarCrate.name() { + if segment.ident.name != keywords::PathRoot.name() { self.print_ident(segment.ident)?; segment.with_generic_args(|generic_args| { self.print_generic_args(generic_args, diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index f082d776969eb..6cfa9f95082e0 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/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 36f4497b77fe5..794e5741d62ca 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -1173,10 +1173,6 @@ impl<'a> ModuleData<'a> { } } - fn is_local(&self) -> bool { - self.normal_ancestor_id.is_local() - } - fn nearest_item_scope(&'a self) -> Module<'a> { if self.is_trait() { self.parent.unwrap() } else { self } } diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 32f0d84342edd..81633c8f57f9e 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -10,7 +10,7 @@ use {AmbiguityError, AmbiguityKind, AmbiguityErrorMisc}; use {CrateLint, Resolver, ResolutionError, ScopeSet, Weak}; -use {Module, ModuleKind, NameBinding, NameBindingKind, PathResult, Segment, ToNameBinding}; +use {Module, NameBinding, NameBindingKind, PathResult, Segment, ToNameBinding}; use {is_known_tool, resolve_error}; use ModuleOrUniformRoot; use Namespace::*; @@ -30,8 +30,6 @@ use syntax::ext::expand::{AstFragment, Invocation, InvocationKind}; use syntax::ext::hygiene::{self, Mark}; use syntax::ext::tt::macro_rules; use syntax::feature_gate::{feature_err, is_builtin_attr_name, GateIssue}; -use syntax::fold::{self, Folder}; -use syntax::ptr::P; use syntax::symbol::{Symbol, keywords}; use syntax::util::lev_distance::find_best_match_for_name; use syntax_pos::{Span, DUMMY_SP}; @@ -138,58 +136,6 @@ impl<'a> base::Resolver for Resolver<'a> { mark } - fn eliminate_crate_var(&mut self, item: P) -> P { - struct EliminateCrateVar<'b, 'a: 'b>( - &'b mut Resolver<'a>, Span - ); - - impl<'a, 'b> Folder for EliminateCrateVar<'a, 'b> { - fn fold_path(&mut self, path: ast::Path) -> ast::Path { - match self.fold_qpath(None, path) { - (None, path) => path, - _ => unreachable!(), - } - } - - fn fold_qpath(&mut self, mut qself: Option, mut path: ast::Path) - -> (Option, ast::Path) { - qself = qself.map(|ast::QSelf { ty, path_span, position }| { - ast::QSelf { - ty: self.fold_ty(ty), - path_span: self.new_span(path_span), - position, - } - }); - - if path.segments[0].ident.name == keywords::DollarCrate.name() { - let module = self.0.resolve_crate_root(path.segments[0].ident); - path.segments[0].ident.name = keywords::PathRoot.name(); - if !module.is_local() { - let span = path.segments[0].ident.span; - path.segments.insert(1, match module.kind { - ModuleKind::Def(_, name) => ast::PathSegment::from_ident( - ast::Ident::with_empty_ctxt(name).with_span_pos(span) - ), - _ => unreachable!(), - }); - if let Some(qself) = &mut qself { - qself.position += 1; - } - } - } - (qself, path) - } - - fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac { - fold::noop_fold_mac(mac, self) - } - } - - let ret = EliminateCrateVar(self, item.span).fold_item(item); - assert!(ret.len() == 1); - ret.into_iter().next().unwrap() - } - fn visit_ast_fragment_with_placeholders(&mut self, mark: Mark, fragment: &AstFragment, derives: &[Mark]) { let invocation = self.invocations[&mark]; @@ -259,7 +205,6 @@ impl<'a> base::Resolver for Resolver<'a> { self.definitions.add_parent_module_of_macro_def(invoc.expansion_data.mark, normal_module_def_id); invoc.expansion_data.mark.set_default_transparency(ext.default_transparency()); - invoc.expansion_data.mark.set_is_builtin(def_id.krate == CrateNum::BuiltinMacros); } Ok(Some(ext)) diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 310cf27689bc5..b807a65f6aed6 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) -> P; 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) -> P { 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 03c7aa9682477..57ccc3e981720 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 ed7466574596f..badcc4ed8762d 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 41165c7e36d58..5e7707f4e5c3c 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 c11ef33f931d8..013ecd3d343c2 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 } } diff --git a/src/libsyntax_ext/deriving/custom.rs b/src/libsyntax_ext/deriving/custom.rs index 5c82d1911383e..cc2fa68568713 100644 --- a/src/libsyntax_ext/deriving/custom.rs +++ b/src/libsyntax_ext/deriving/custom.rs @@ -74,7 +74,6 @@ impl MultiItemModifier for ProcMacroDerive { // Mark attributes as known, and used. MarkAttrs(&self.attrs).visit_item(&item); - let item = ecx.resolver.eliminate_crate_var(item); let token = Token::interpolated(token::NtItem(item)); let input = tokenstream::TokenTree::Token(DUMMY_SP, token).into(); diff --git a/src/libsyntax_ext/proc_macro_server.rs b/src/libsyntax_ext/proc_macro_server.rs index a04d6c92b7817..ca960cbe41b5d 100644 --- a/src/libsyntax_ext/proc_macro_server.rs +++ b/src/libsyntax_ext/proc_macro_server.rs @@ -81,29 +81,23 @@ impl FromInternal<(TokenStream, &'_ ParseSess, &'_ mut Vec)> $($field $(: $value)*,)* span, }) - ) + ); + ($ty:ident::$method:ident($($value:expr),*)) => ( + TokenTree::$ty(self::$ty::$method($($value,)* span)) + ); } macro_rules! op { ($a:expr) => { - tt!(Punct { ch: $a, joint }) + tt!(Punct::new($a, joint)) }; ($a:expr, $b:expr) => {{ - stack.push(tt!(Punct { ch: $b, joint })); - tt!(Punct { - ch: $a, - joint: true - }) + stack.push(tt!(Punct::new($b, joint))); + tt!(Punct::new($a, true)) }}; ($a:expr, $b:expr, $c:expr) => {{ - stack.push(tt!(Punct { ch: $c, joint })); - stack.push(tt!(Punct { - ch: $b, - joint: true - })); - tt!(Punct { - ch: $a, - joint: true - }) + stack.push(tt!(Punct::new($c, joint))); + stack.push(tt!(Punct::new($b, true))); + tt!(Punct::new($a, true)) }}; } @@ -156,20 +150,13 @@ impl FromInternal<(TokenStream, &'_ ParseSess, &'_ mut Vec)> Question => op!('?'), SingleQuote => op!('\''), - Ident(ident, is_raw) => tt!(Ident { - sym: ident.name, - is_raw - }), + Ident(ident, false) if ident.name == keywords::DollarCrate.name() => + tt!(Ident::dollar_crate()), + Ident(ident, is_raw) => tt!(Ident::new(ident.name, is_raw)), Lifetime(ident) => { let ident = ident.without_first_quote(); - stack.push(tt!(Ident { - sym: ident.name, - is_raw: false - })); - tt!(Punct { - ch: '\'', - joint: true - }) + stack.push(tt!(Ident::new(ident.name, false))); + tt!(Punct::new('\'', true)) } Literal(lit, suffix) => tt!(Literal { lit, suffix }), DocComment(c) => { @@ -193,15 +180,9 @@ impl FromInternal<(TokenStream, &'_ ParseSess, &'_ mut Vec)> span: DelimSpan::from_single(span), })); if style == ast::AttrStyle::Inner { - stack.push(tt!(Punct { - ch: '!', - joint: false - })); + stack.push(tt!(Punct::new('!', false))); } - tt!(Punct { - ch: '#', - joint: false - }) + tt!(Punct::new('#', false)) } Interpolated(_) => { @@ -237,7 +218,7 @@ impl ToInternal for TokenTree { ) .into(); } - TokenTree::Ident(self::Ident { sym, span, is_raw }) => { + TokenTree::Ident(self::Ident { sym, is_raw, span }) => { let token = Ident(ast::Ident::new(sym, span), is_raw); return tokenstream::TokenTree::Token(span, token).into(); } @@ -338,11 +319,52 @@ pub struct Punct { span: Span, } +impl Punct { + fn new(ch: char, joint: bool, span: Span) -> Punct { + const LEGAL_CHARS: &[char] = &['=', '<', '>', '!', '~', '+', '-', '*', '/', '%', '^', + '&', '|', '@', '.', ',', ';', ':', '#', '$', '?', '\'']; + if !LEGAL_CHARS.contains(&ch) { + panic!("unsupported character `{:?}`", ch) + } + Punct { ch, joint, span } + } +} + #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct Ident { sym: Symbol, - span: Span, is_raw: bool, + span: Span, +} + +impl Ident { + fn is_valid(string: &str) -> bool { + let mut chars = string.chars(); + if let Some(start) = chars.next() { + (start == '_' || start.is_xid_start()) + && chars.all(|cont| cont == '_' || cont.is_xid_continue()) + } else { + false + } + } + fn new(sym: Symbol, is_raw: bool, span: Span) -> Ident { + let string = sym.as_str().get(); + if !Self::is_valid(string) { + panic!("`{:?}` is not a valid identifier", string) + } + if is_raw { + let normalized_sym = Symbol::intern(string); + if normalized_sym == keywords::Underscore.name() || + ast::Ident::with_empty_ctxt(normalized_sym).is_path_segment_keyword() { + panic!("`{:?}` is not a valid raw identifier", string) + } + } + Ident { sym, is_raw, span } + } + fn dollar_crate(span: Span) -> Ident { + // `$crate` is accepted as an ident only if it comes from the compiler. + Ident { sym: keywords::DollarCrate.name(), is_raw: false, span } + } } // FIXME(eddyb) `Literal` should not expose internal `Debug` impls. @@ -492,11 +514,7 @@ impl server::Group for Rustc<'_> { impl server::Punct for Rustc<'_> { fn new(&mut self, ch: char, spacing: Spacing) -> Self::Punct { - Punct { - ch, - joint: spacing == Spacing::Joint, - span: server::Span::call_site(self), - } + Punct::new(ch, spacing == Spacing::Joint, server::Span::call_site(self)) } fn as_char(&mut self, punct: Self::Punct) -> char { punct.ch @@ -518,14 +536,7 @@ impl server::Punct for Rustc<'_> { impl server::Ident for Rustc<'_> { fn new(&mut self, string: &str, span: Self::Span, is_raw: bool) -> Self::Ident { - let sym = Symbol::intern(string); - if is_raw - && (sym == keywords::Underscore.name() - || ast::Ident::with_empty_ctxt(sym).is_path_segment_keyword()) - { - panic!("`{:?}` is not a valid raw identifier", string) - } - Ident { sym, span, is_raw } + Ident::new(Symbol::intern(string), is_raw, span) } fn span(&mut self, ident: Self::Ident) -> Self::Span { ident.span diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 74f63b5e2c604..3dc884a94c0dc 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. @@ -47,7 +49,6 @@ pub struct Mark(u32); struct MarkData { parent: Mark, default_transparency: Transparency, - is_builtin: bool, expn_info: Option, } @@ -77,7 +78,6 @@ impl Mark { parent, // By default expansions behave like `macro_rules`. default_transparency: Transparency::SemiTransparent, - is_builtin: false, expn_info: None, }); Mark(data.marks.len() as u32 - 1) @@ -121,18 +121,6 @@ impl Mark { HygieneData::with(|data| data.marks[self.0 as usize].default_transparency = transparency) } - #[inline] - pub fn is_builtin(self) -> bool { - assert_ne!(self, Mark::root()); - HygieneData::with(|data| data.marks[self.0 as usize].is_builtin) - } - - #[inline] - pub fn set_is_builtin(self, is_builtin: bool) { - assert_ne!(self, Mark::root()); - HygieneData::with(|data| data.marks[self.0 as usize].is_builtin = is_builtin) - } - pub fn is_descendant_of(mut self, ancestor: Mark) -> bool { HygieneData::with(|data| { while self != ancestor { @@ -206,7 +194,6 @@ impl HygieneData { // If the root is opaque, then loops searching for an opaque mark // will automatically stop after reaching it. default_transparency: Transparency::Opaque, - is_builtin: true, expn_info: None, }], syntax_contexts: vec![SyntaxContextData { @@ -215,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, @@ -262,7 +250,6 @@ impl SyntaxContext { data.marks.push(MarkData { parent: Mark::root(), default_transparency: Transparency::SemiTransparent, - is_builtin: false, expn_info: Some(expansion_info), }); @@ -274,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) }) @@ -340,6 +328,7 @@ impl SyntaxContext { prev_ctxt, opaque: new_opaque, opaque_and_semitransparent: new_opaque, + dollar_crate_name: keywords::DollarCrate.name(), }); new_opaque }); @@ -357,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 }); @@ -372,6 +362,7 @@ impl SyntaxContext { prev_ctxt, opaque, opaque_and_semitransparent, + dollar_crate_name: keywords::DollarCrate.name(), }); new_opaque_and_semitransparent_and_transparent }) @@ -526,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 5f42b86c82a81..3b01ab3a47b6a 100644 --- a/src/test/pretty/issue-4264.pp +++ b/src/test/pretty/issue-4264.pp @@ -39,8 +39,8 @@ - ((::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 0000000000000..8f15a2b975bf3 --- /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 new file mode 100644 index 0000000000000..d0ea850d4e323 --- /dev/null +++ b/src/test/ui/proc-macro/auxiliary/dollar-crate.rs @@ -0,0 +1,28 @@ +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro] +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 new file mode 100644 index 0000000000000..1460e9a3b2d55 --- /dev/null +++ b/src/test/ui/proc-macro/dollar-crate.rs @@ -0,0 +1,40 @@ +// edition:2018 +// aux-build:dollar-crate.rs +// aux-build:dollar-crate-external.rs + +// 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)" + +extern crate dollar_crate; +extern crate dollar_crate_external; + +type S = u8; + +mod local { + use crate::dollar_crate; + + 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 + }; + } + + local!(); +} + +mod external { + use crate::dollar_crate_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 0000000000000..08de3c7d1a666 --- /dev/null +++ b/src/test/ui/proc-macro/dollar-crate.stderr @@ -0,0 +1,29 @@ +error[E0428]: the name `D` is defined multiple times + --> $DIR/dollar-crate.rs:27:13 + | +LL | struct D($crate::S); //~ ERROR the name `D` is defined multiple times + | ^^^^^^^^^^^^^^^^^^^^ + | | + | `D` redefined here + | previous definition of the type `D` 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:37:5 + | +LL | dollar_crate_external::external!(); //~ ERROR the name `D` is defined multiple times + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `D` redefined here + | previous definition of the type `D` 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 0000000000000..c47b3603f41c0 --- /dev/null +++ b/src/test/ui/proc-macro/dollar-crate.stdout @@ -0,0 +1,240 @@ +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: #2 bytes(LO..HI) + }, + Ident { + ident: "A", + 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) + } +] +DERIVE INPUT (PRETTY-PRINTED): struct D(crate::S); +DERIVE INPUT: TokenStream [ + Ident { + ident: "struct", + span: #2 bytes(LO..HI) + }, + Ident { + ident: "D", + 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) + } +] +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: #10 bytes(LO..HI) + }, + Ident { + ident: "A", + 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) + } +] +DERIVE INPUT (PRETTY-PRINTED): struct D(::dollar_crate_external::S); +DERIVE INPUT: TokenStream [ + Ident { + ident: "struct", + span: #10 bytes(LO..HI) + }, + Ident { + ident: "D", + 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) + } +]