]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #51762 - petrochenkov:oh-hi-mark, r=oli-obk
authorbors <bors@rust-lang.org>
Sat, 30 Jun 2018 09:19:21 +0000 (09:19 +0000)
committerbors <bors@rust-lang.org>
Sat, 30 Jun 2018 09:19:21 +0000 (09:19 +0000)
hygiene: Implement transparent marks and use them for call-site hygiene in proc-macros

Fixes https://github.com/rust-lang/rust/issues/50050

40 files changed:
src/libproc_macro/diagnostic.rs
src/libproc_macro/lib.rs
src/librustc/hir/map/definitions.rs
src/librustc/middle/stability.rs
src/librustc/ty/query/plumbing.rs
src/librustc_codegen_llvm/debuginfo/metadata.rs
src/librustc_codegen_llvm/debuginfo/mod.rs
src/librustc_errors/emitter.rs
src/librustc_metadata/creader.rs
src/librustc_metadata/cstore_impl.rs
src/librustc_metadata/encoder.rs
src/librustc_mir/borrow_check/nll/type_check/mod.rs
src/librustc_resolve/build_reduced_graph.rs
src/librustc_resolve/check_unused.rs
src/librustc_resolve/lib.rs
src/librustc_resolve/macros.rs
src/librustc_resolve/resolve_imports.rs
src/librustc_save_analysis/lib.rs
src/librustc_typeck/check_unused.rs
src/librustdoc/clean/mod.rs
src/libsyntax/codemap.rs
src/libsyntax/ext/base.rs
src/libsyntax/ext/expand.rs
src/libsyntax/ext/tt/macro_rules.rs
src/libsyntax/ext/tt/quoted.rs
src/libsyntax/parse/mod.rs
src/libsyntax/parse/parser.rs
src/libsyntax/tokenstream.rs
src/libsyntax_pos/hygiene.rs
src/libsyntax_pos/lib.rs
src/libsyntax_pos/symbol.rs
src/test/compile-fail-fulldeps/proc-macro/lints_in_proc_macros.rs
src/test/run-pass-fulldeps/proc-macro/auxiliary/call-site.rs [new file with mode: 0644]
src/test/run-pass-fulldeps/proc-macro/call-site.rs [new file with mode: 0644]
src/test/ui/hygiene/auxiliary/intercrate.rs
src/test/ui/hygiene/auxiliary/transparent-basic.rs [new file with mode: 0644]
src/test/ui/hygiene/dollar-crate-modern.rs [new file with mode: 0644]
src/test/ui/hygiene/generate-mod.rs [new file with mode: 0644]
src/test/ui/hygiene/generate-mod.stderr [new file with mode: 0644]
src/test/ui/hygiene/transparent-basic.rs [new file with mode: 0644]

index c39aec896e6b4d4c091a582e8342febb64ded67a..a82e3dcb0600cbf8b78d51f28196e2903e3f11c6 100644 (file)
@@ -97,7 +97,7 @@ pub fn level(&self) -> Level {
     /// Emit the diagnostic.
     #[unstable(feature = "proc_macro", issue = "38356")]
     pub fn emit(self) {
-        ::__internal::with_sess(move |(sess, _)| {
+        ::__internal::with_sess(move |sess, _| {
             let handler = &sess.span_diagnostic;
             let level = __internal::level_to_internal_level(self.level);
             let mut diag = rustc::DiagnosticBuilder::new(handler, level, &*self.message);
index befc1ba064aa2e7199afa2b76765366838970d43..fb5cbf473a387e782f593d4b20dc4e305db1b370 100644 (file)
@@ -58,8 +58,7 @@
 use syntax::symbol::{keywords, Symbol};
 use syntax::tokenstream;
 use syntax::parse::lexer::{self, comments};
-use syntax_pos::{FileMap, Pos, SyntaxContext, FileName};
-use syntax_pos::hygiene::Mark;
+use syntax_pos::{FileMap, Pos, FileName};
 
 /// The main type provided by this crate, representing an abstract stream of
 /// tokens, or, more specifically, a sequence of token trees.
@@ -109,6 +108,7 @@ pub fn is_empty(&self) -> bool {
 /// Attempts to break the string into tokens and parse those tokens into a token stream.
 /// May fail for a number of reasons, for example, if the string contains unbalanced delimiters
 /// or characters not existing in the language.
+/// All tokens in the parsed stream get `Span::call_site()` spans.
 ///
 /// NOTE: Some errors may cause panics instead of returning `LexError`. We reserve the right to
 /// change these errors into `LexError`s later.
@@ -117,17 +117,10 @@ impl FromStr for TokenStream {
     type Err = LexError;
 
     fn from_str(src: &str) -> Result<TokenStream, LexError> {
-        __internal::with_sess(|(sess, mark)| {
-            let src = src.to_string();
-            let name = FileName::ProcMacroSourceCode;
-            let expn_info = mark.expn_info().unwrap();
-            let call_site = expn_info.call_site;
-            // notify the expansion info that it is unhygienic
-            let mark = Mark::fresh(mark);
-            mark.set_expn_info(expn_info);
-            let span = call_site.with_ctxt(SyntaxContext::empty().apply_mark(mark));
-            let stream = parse::parse_stream_from_source_str(name, src, sess, Some(span));
-            Ok(__internal::token_stream_wrap(stream))
+        __internal::with_sess(|sess, data| {
+            Ok(__internal::token_stream_wrap(parse::parse_stream_from_source_str(
+                FileName::ProcMacroSourceCode, src.to_string(), sess, Some(data.call_site.0)
+            )))
         })
     }
 }
@@ -184,8 +177,6 @@ fn from_iter<I: IntoIterator<Item = TokenStream>>(streams: I) -> Self {
 #[unstable(feature = "proc_macro", issue = "38356")]
 pub mod token_stream {
     use syntax::tokenstream;
-    use syntax_pos::DUMMY_SP;
-
     use {TokenTree, TokenStream, Delimiter};
 
     /// An iterator over `TokenStream`'s `TokenTree`s.
@@ -214,7 +205,7 @@ fn next(&mut self) -> Option<TokenTree> {
                 // need to flattened during iteration over stream's token trees.
                 // Eventually this needs to be removed in favor of keeping original token trees
                 // and not doing the roundtrip through AST.
-                if tree.span().0 == DUMMY_SP {
+                if tree.span().0.is_dummy() {
                     if let TokenTree::Group(ref group) = tree {
                         if group.delimiter() == Delimiter::None {
                             self.cursor.insert(group.stream.clone().0);
@@ -284,10 +275,7 @@ impl Span {
     /// A span that resolves at the macro definition site.
     #[unstable(feature = "proc_macro", issue = "38356")]
     pub fn def_site() -> Span {
-        ::__internal::with_sess(|(_, mark)| {
-            let call_site = mark.expn_info().unwrap().call_site;
-            Span(call_site.with_ctxt(SyntaxContext::empty().apply_mark(mark)))
-        })
+        ::__internal::with_sess(|_, data| data.def_site)
     }
 
     /// The span of the invocation of the current procedural macro.
@@ -296,7 +284,7 @@ pub fn def_site() -> Span {
     /// at the macro call site will be able to refer to them as well.
     #[unstable(feature = "proc_macro", issue = "38356")]
     pub fn call_site() -> Span {
-        ::__internal::with_sess(|(_, mark)| Span(mark.expn_info().unwrap().call_site))
+        ::__internal::with_sess(|_, data| data.call_site)
     }
 
     /// The original source file into which this span points.
@@ -1243,7 +1231,7 @@ macro_rules! op {
             }
 
             Interpolated(_) => {
-                __internal::with_sess(|(sess, _)| {
+                __internal::with_sess(|sess, _| {
                     let tts = token.interpolated_to_tokenstream(sess, span);
                     tt!(Group::new(Delimiter::None, TokenStream(tts)))
                 })
@@ -1354,20 +1342,21 @@ pub mod __internal {
     pub use quote::{LiteralKind, SpannedSymbol, Quoter, unquote};
 
     use std::cell::Cell;
+    use std::ptr;
 
     use syntax::ast;
     use syntax::ext::base::ExtCtxt;
-    use syntax::ext::hygiene::Mark;
     use syntax::ptr::P;
     use syntax::parse::{self, ParseSess};
     use syntax::parse::token::{self, Token};
     use syntax::tokenstream;
     use syntax_pos::{BytePos, Loc, DUMMY_SP};
+    use syntax_pos::hygiene::{Mark, SyntaxContext, Transparency};
 
-    use super::{TokenStream, LexError};
+    use super::{TokenStream, LexError, Span};
 
     pub fn lookup_char_pos(pos: BytePos) -> Loc {
-        with_sess(|(sess, _)| sess.codemap().lookup_char_pos(pos))
+        with_sess(|sess, _| sess.codemap().lookup_char_pos(pos))
     }
 
     pub fn new_token_stream(item: P<ast::Item>) -> TokenStream {
@@ -1380,7 +1369,7 @@ pub fn token_stream_wrap(inner: tokenstream::TokenStream) -> TokenStream {
     }
 
     pub fn token_stream_parse_items(stream: TokenStream) -> Result<Vec<P<ast::Item>>, LexError> {
-        with_sess(move |(sess, _)| {
+        with_sess(move |sess, _| {
             let mut parser = parse::stream_to_parser(sess, stream.0);
             let mut items = Vec::new();
 
@@ -1411,16 +1400,30 @@ fn register_bang_proc_macro(&mut self,
                                     expand: fn(TokenStream) -> TokenStream);
     }
 
+    #[derive(Clone, Copy)]
+    pub struct ProcMacroData {
+        pub def_site: Span,
+        pub call_site: Span,
+    }
+
+    #[derive(Clone, Copy)]
+    struct ProcMacroSess {
+        parse_sess: *const ParseSess,
+        data: ProcMacroData,
+    }
+
     // Emulate scoped_thread_local!() here essentially
     thread_local! {
-        static CURRENT_SESS: Cell<(*const ParseSess, Mark)> =
-            Cell::new((0 as *const _, Mark::root()));
+        static CURRENT_SESS: Cell<ProcMacroSess> = Cell::new(ProcMacroSess {
+            parse_sess: ptr::null(),
+            data: ProcMacroData { def_site: Span(DUMMY_SP), call_site: Span(DUMMY_SP) },
+        });
     }
 
     pub fn set_sess<F, R>(cx: &ExtCtxt, f: F) -> R
         where F: FnOnce() -> R
     {
-        struct Reset { prev: (*const ParseSess, Mark) }
+        struct Reset { prev: ProcMacroSess }
 
         impl Drop for Reset {
             fn drop(&mut self) {
@@ -1430,24 +1433,42 @@ fn drop(&mut self) {
 
         CURRENT_SESS.with(|p| {
             let _reset = Reset { prev: p.get() };
-            p.set((cx.parse_sess, cx.current_expansion.mark));
+
+            // No way to determine def location for a proc macro right now, so use call location.
+            let location = cx.current_expansion.mark.expn_info().unwrap().call_site;
+            // Opaque mark was already created by expansion, now create its transparent twin.
+            // We can't use the call-site span literally here, even if it appears to provide
+            // correct name resolution, because it has all the `ExpnInfo` wrong, so the edition
+            // checks, lint macro checks, macro backtraces will all break.
+            let opaque_mark = cx.current_expansion.mark;
+            let transparent_mark = Mark::fresh_cloned(opaque_mark);
+            transparent_mark.set_transparency(Transparency::Transparent);
+
+            let to_span = |mark| Span(location.with_ctxt(SyntaxContext::empty().apply_mark(mark)));
+            p.set(ProcMacroSess {
+                parse_sess: cx.parse_sess,
+                data: ProcMacroData {
+                    def_site: to_span(opaque_mark),
+                    call_site: to_span(transparent_mark),
+                },
+            });
             f()
         })
     }
 
     pub fn in_sess() -> bool
     {
-        let p = CURRENT_SESS.with(|p| p.get());
-        !p.0.is_null()
+        !CURRENT_SESS.with(|sess| sess.get()).parse_sess.is_null()
     }
 
     pub fn with_sess<F, R>(f: F) -> R
-        where F: FnOnce((&ParseSess, Mark)) -> R
+        where F: FnOnce(&ParseSess, &ProcMacroData) -> R
     {
-        let p = CURRENT_SESS.with(|p| p.get());
-        assert!(!p.0.is_null(), "proc_macro::__internal::with_sess() called \
-                                 before set_parse_sess()!");
-        f(unsafe { (&*p.0, p.1) })
+        let sess = CURRENT_SESS.with(|sess| sess.get());
+        if sess.parse_sess.is_null() {
+            panic!("procedural macro API is used outside of a procedural macro");
+        }
+        f(unsafe { &*sess.parse_sess }, &sess.data)
     }
 }
 
index ca2789f04f2e2fe2a12df24b93b6f55129be0704..fa01a1ccca59baf0998c4bc8840e780251440839 100644 (file)
@@ -484,12 +484,7 @@ pub fn def_index_to_hir_id(&self, def_index: DefIndex) -> hir::HirId {
     #[inline]
     pub fn opt_span(&self, def_id: DefId) -> Option<Span> {
         if def_id.krate == LOCAL_CRATE {
-            let span = self.def_index_to_span.get(&def_id.index).cloned().unwrap_or(DUMMY_SP);
-            if span != DUMMY_SP {
-                Some(span)
-            } else {
-                None
-            }
+            self.def_index_to_span.get(&def_id.index).cloned()
         } else {
             None
         }
@@ -586,8 +581,8 @@ pub fn create_def_with_parent(&mut self,
             self.opaque_expansions_that_defined.insert(index, expansion);
         }
 
-        // The span is added if it isn't DUMMY_SP
-        if span != DUMMY_SP {
+        // The span is added if it isn't dummy
+        if !span.is_dummy() {
             self.def_index_to_span.insert(index, span);
         }
 
index a289a2c21ce7f421c3d2876a8284e118c46adf68..9bf5c4d72b70cb05f8b6977c78291029e0f0961a 100644 (file)
@@ -20,7 +20,7 @@
 use middle::privacy::AccessLevels;
 use session::DiagnosticMessageId;
 use syntax::symbol::Symbol;
-use syntax_pos::{Span, MultiSpan, DUMMY_SP};
+use syntax_pos::{Span, MultiSpan};
 use syntax::ast;
 use syntax::ast::{NodeId, Attribute};
 use syntax::feature_gate::{GateIssue, emit_feature_err, find_lang_feature_accepted_version};
@@ -687,7 +687,7 @@ pub fn check_stability(self, def_id: DefId, id: Option<NodeId>, span: Span) {
                 let msp: MultiSpan = span.into();
                 let cm = &self.sess.parse_sess.codemap();
                 let span_key = msp.primary_span().and_then(|sp: Span|
-                    if sp != DUMMY_SP {
+                    if !sp.is_dummy() {
                         let file = cm.lookup_char_pos(sp.lo()).file;
                         if file.name.is_macros() {
                             None
@@ -725,7 +725,7 @@ fn visit_item(&mut self, item: &'tcx hir::Item) {
         match item.node {
             hir::ItemExternCrate(_) => {
                 // compiler-generated `extern crate` items have a dummy span.
-                if item.span == DUMMY_SP { return }
+                if item.span.is_dummy() { return }
 
                 let def_id = self.tcx.hir.local_def_id(item.id);
                 let cnum = match self.tcx.extern_mod_stmt_cnum(def_id) {
index e17c6fba74c6ed2fc0c406e19287bef25ad5d891..d783b9574efb2fdbe21bcbd1d22d23fbf369a286 100644 (file)
@@ -708,7 +708,7 @@ pub fn describe(&self, tcx: TyCtxt) -> String {
 
             // FIXME(eddyb) Get more valid Span's on queries.
             pub fn default_span(&self, tcx: TyCtxt<'_, $tcx, '_>, span: Span) -> Span {
-                if span != DUMMY_SP {
+                if !span.is_dummy() {
                     return span;
                 }
                 // The def_span query is used to calculate default_span,
index 1eec57c9c877c04f547bc37d412322e4cc857b4e..6d727f7b048c6541276c85ac3129c3793dd6faaa 100644 (file)
@@ -1662,7 +1662,7 @@ pub fn create_global_var_metadata(cx: &CodegenCx,
     let var_scope = get_namespace_for_item(cx, def_id);
     let span = tcx.def_span(def_id);
 
-    let (file_metadata, line_number) = if span != syntax_pos::DUMMY_SP {
+    let (file_metadata, line_number) = if !span.is_dummy() {
         let loc = span_start(cx, span);
         (file_metadata(cx, &loc.file.name, LOCAL_CRATE), loc.line as c_uint)
     } else {
index 803966145f773268c1a513909be6e05e0c357a60..068dd9821ac97029dad1295b389a0555d5057bc9 100644 (file)
@@ -219,7 +219,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
     let span = mir.span;
 
     // This can be the case for functions inlined from another crate
-    if span == syntax_pos::DUMMY_SP {
+    if span.is_dummy() {
         // FIXME(simulacrum): Probably can't happen; remove.
         return FunctionDebugContext::FunctionWithoutDebugInfo;
     }
index 92e72fe91d3e8dad19aca526b966492f3ab4211f..e79a3a87738ec407903ecb1690f515d797659442 100644 (file)
@@ -10,7 +10,7 @@
 
 use self::Destination::*;
 
-use syntax_pos::{DUMMY_SP, FileMap, Span, MultiSpan};
+use syntax_pos::{FileMap, Span, MultiSpan};
 
 use {Level, CodeSuggestion, DiagnosticBuilder, SubDiagnostic, CodeMapperDyn, DiagnosticId};
 use snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, StyledString, Style};
@@ -216,7 +216,7 @@ fn add_annotation_to_file(file_vec: &mut Vec<FileWithAnnotatedLines>,
 
         if let Some(ref cm) = self.cm {
             for span_label in msp.span_labels() {
-                if span_label.span == DUMMY_SP {
+                if span_label.span.is_dummy() {
                     continue;
                 }
 
@@ -730,7 +730,7 @@ fn get_multispan_max_line_num(&mut self, msp: &MultiSpan) -> usize {
         let mut max = 0;
         if let Some(ref cm) = self.cm {
             for primary_span in msp.primary_spans() {
-                if primary_span != &DUMMY_SP {
+                if !primary_span.is_dummy() {
                     let hi = cm.lookup_char_pos(primary_span.hi());
                     if hi.line > max {
                         max = hi.line;
@@ -739,7 +739,7 @@ fn get_multispan_max_line_num(&mut self, msp: &MultiSpan) -> usize {
             }
             if !self.short_message {
                 for span_label in msp.span_labels() {
-                    if span_label.span != DUMMY_SP {
+                    if !span_label.span.is_dummy() {
                         let hi = cm.lookup_char_pos(span_label.span.hi());
                         if hi.line > max {
                             max = hi.line;
@@ -778,7 +778,7 @@ fn fix_multispan_in_std_macros(&mut self,
 
             // First, find all the spans in <*macros> and point instead at their use site
             for sp in span.primary_spans() {
-                if *sp == DUMMY_SP {
+                if sp.is_dummy() {
                     continue;
                 }
                 let call_sp = cm.call_span_if_macro(*sp);
@@ -790,7 +790,7 @@ fn fix_multispan_in_std_macros(&mut self,
                     // Only show macro locations that are local
                     // and display them like a span_note
                     if let Some(def_site) = trace.def_site_span {
-                        if def_site == DUMMY_SP {
+                        if def_site.is_dummy() {
                             continue;
                         }
                         if always_backtrace {
@@ -830,7 +830,7 @@ fn fix_multispan_in_std_macros(&mut self,
                 span.push_span_label(label_span, label_text);
             }
             for sp_label in span.span_labels() {
-                if sp_label.span == DUMMY_SP {
+                if sp_label.span.is_dummy() {
                     continue;
                 }
                 if cm.span_to_filename(sp_label.span.clone()).is_macros() &&
@@ -1003,7 +1003,7 @@ fn emit_message_default(&mut self,
         // Make sure our primary file comes first
         let (primary_lo, cm) = if let (Some(cm), Some(ref primary_span)) =
             (self.cm.as_ref(), msp.primary_span().as_ref()) {
-            if primary_span != &&DUMMY_SP {
+            if !primary_span.is_dummy() {
                 (cm.lookup_char_pos(primary_span.lo()), cm)
             } else {
                 emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?;
index 1a13335a0e49d34bfc44b62f02b7011ecd4126b1..cdeee92cb073fc4914539e6ce61af79bc02144ce 100644 (file)
@@ -569,9 +569,11 @@ fn register_attr_proc_macro(&mut self,
             fn register_bang_proc_macro(&mut self,
                                         name: &str,
                                         expand: fn(TokenStream) -> TokenStream) {
-                let expand = SyntaxExtension::ProcMacro(
-                    Box::new(BangProcMacro { inner: expand }), false, self.edition
-                );
+                let expand = SyntaxExtension::ProcMacro {
+                    expander: Box::new(BangProcMacro { inner: expand }),
+                    allow_internal_unstable: false,
+                    edition: self.edition,
+                };
                 self.extensions.push((Symbol::intern(name), Lrc::new(expand)));
             }
         }
index d0e4e54e63d677f86f615ff5004b4620d3c5b644..23da82f5a4514241ca5fc56fed10f80fef37bd6d 100644 (file)
@@ -518,8 +518,11 @@ fn load_macro_untracked(&self, id: DefId, sess: &Session) -> LoadedMacro {
             return LoadedMacro::ProcMacro(proc_macros[id.index.to_proc_macro_index()].1.clone());
         } else if data.name == "proc_macro" &&
                   self.get_crate_data(id.krate).item_name(id.index) == "quote" {
-            let ext = SyntaxExtension::ProcMacro(Box::new(::proc_macro::__internal::Quoter),
-                                                 true, data.root.edition);
+            let ext = SyntaxExtension::ProcMacro {
+                expander: Box::new(::proc_macro::__internal::Quoter),
+                allow_internal_unstable: true,
+                edition: data.root.edition,
+            };
             return LoadedMacro::ProcMacro(Lrc::new(ext));
         }
 
index d8a224d3badd8692390396d37d31e30bcc501e76..93294075272584cdc4de217562d9c797f577b3bd 100644 (file)
@@ -41,7 +41,7 @@
 use syntax::ast::{self, CRATE_NODE_ID};
 use syntax::attr;
 use syntax::symbol::keywords;
-use syntax_pos::{self, hygiene, FileName, FileMap, Span, DUMMY_SP};
+use syntax_pos::{self, hygiene, FileName, FileMap, Span};
 
 use rustc::hir::{self, PatKind};
 use rustc::hir::itemlikevisit::ItemLikeVisitor;
@@ -147,7 +147,7 @@ fn specialized_encode(&mut self, def_index: &DefIndex) -> Result<(), Self::Error
 
 impl<'a, 'tcx> SpecializedEncoder<Span> for EncodeContext<'a, 'tcx> {
     fn specialized_encode(&mut self, span: &Span) -> Result<(), Self::Error> {
-        if *span == DUMMY_SP {
+        if span.is_dummy() {
             return TAG_INVALID_SPAN.encode(self)
         }
 
index 2da2b10edb8032355f06089d3df72b409ea68e1d..9b6e3e0cab6baa5c8ba650f585fc54c80e7db08b 100644 (file)
@@ -190,7 +190,7 @@ struct TypeVerifier<'a, 'b: 'a, 'gcx: 'b + 'tcx, 'tcx: 'b> {
 
 impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> {
     fn visit_span(&mut self, span: &Span) {
-        if *span != DUMMY_SP {
+        if !span.is_dummy() {
             self.last_span = *span;
         }
     }
@@ -1601,7 +1601,7 @@ fn typeck_mir(&mut self, mir: &Mir<'tcx>) {
                 statement_index: 0,
             };
             for stmt in &block_data.statements {
-                if stmt.source_info.span != DUMMY_SP {
+                if !stmt.source_info.span.is_dummy() {
                     self.last_span = stmt.source_info.span;
                 }
                 self.check_stmt(mir, stmt, location);
index af7678aa06f7e5e90e69fc584b81496d7098e53d..a770f078404702c10508367dd858d5ec26829e1b 100644 (file)
@@ -156,7 +156,7 @@ fn build_reduced_graph_for_use_tree(&mut self,
 
                     // Disallow `use $crate;`
                     if source.name == keywords::DollarCrate.name() && path.segments.len() == 1 {
-                        let crate_root = self.resolve_crate_root(source.span.ctxt(), true);
+                        let crate_root = self.resolve_crate_root(source);
                         let crate_name = match crate_root.kind {
                             ModuleKind::Def(_, name) => name,
                             ModuleKind::Block(..) => unreachable!(),
index 590ce168d5d0fdac64d6dd81967a5aa308d5d881..0c4b9a546cbd18617a52bdb3d9f670d08638f985 100644 (file)
@@ -86,7 +86,7 @@ fn visit_item(&mut self, item: &'a ast::Item) {
         // because this means that they were generated in some fashion by the
         // compiler and we don't need to consider them.
         if let ast::ItemKind::Use(..) = item.node {
-            if item.vis.node == ast::VisibilityKind::Public || item.span.source_equal(&DUMMY_SP) {
+            if item.vis.node == ast::VisibilityKind::Public || item.span.is_dummy() {
                 return;
             }
         }
@@ -129,7 +129,7 @@ pub fn check_crate(resolver: &mut Resolver, krate: &ast::Crate) {
         match directive.subclass {
             _ if directive.used.get() ||
                  directive.vis.get() == ty::Visibility::Public ||
-                 directive.span.source_equal(&DUMMY_SP) => {}
+                 directive.span.is_dummy() => {}
             ImportDirectiveSubclass::ExternCrate(_) => {
                 resolver.maybe_unused_extern_crates.push((directive.id, directive.span));
             }
index 2ef374277714e86f96f9eaa2b3242c62e7ced8f0..9f36f888b581b97ddb5ee8bdde90a9fb5dcdf836 100644 (file)
@@ -1850,6 +1850,8 @@ fn resolve_ident_in_lexical_scope(&mut self,
             } else {
                 ident.span.modern()
             }
+        } else {
+            ident = ident.modern_and_legacy();
         }
 
         // Walk backwards up the ribs in scope.
@@ -1981,12 +1983,36 @@ fn resolve_ident_in_module(&mut self,
         result
     }
 
-    fn resolve_crate_root(&mut self, mut ctxt: SyntaxContext, legacy: bool) -> Module<'a> {
-        let mark = if legacy {
+    fn resolve_crate_root(&mut self, ident: Ident) -> Module<'a> {
+        let mut ctxt = ident.span.ctxt();
+        let mark = if ident.name == keywords::DollarCrate.name() {
             // When resolving `$crate` from a `macro_rules!` invoked in a `macro`,
             // we don't want to pretend that the `macro_rules!` definition is in the `macro`
             // as described in `SyntaxContext::apply_mark`, so we ignore prepended modern marks.
-            ctxt.marks().into_iter().find(|&mark| mark.transparency() != Transparency::Opaque)
+            // FIXME: This is only a guess and it doesn't work correctly for `macro_rules!`
+            // definitions actually produced by `macro` and `macro` definitions produced by
+            // `macro_rules!`, but at least such configurations are not stable yet.
+            ctxt = ctxt.modern_and_legacy();
+            let mut iter = ctxt.marks().into_iter().rev().peekable();
+            let mut result = None;
+            // Find the last modern mark from the end if it exists.
+            while let Some(&mark) = iter.peek() {
+                if mark.transparency() == Transparency::Opaque {
+                    result = Some(mark);
+                    iter.next();
+                } else {
+                    break;
+                }
+            }
+            // Then find the last legacy mark from the end if it exists.
+            for mark in iter {
+                if mark.transparency() == Transparency::SemiTransparent {
+                    result = Some(mark);
+                } else {
+                    break;
+                }
+            }
+            result
         } else {
             ctxt = ctxt.modern();
             ctxt.adjust(Mark::root())
@@ -2627,6 +2653,7 @@ fn fresh_binding(&mut self,
         // must not add it if it's in the bindings map
         // because that breaks the assumptions later
         // passes make about or-patterns.)
+        let ident = ident.modern_and_legacy();
         let mut def = Def::Local(pat_id);
         match bindings.get(&ident).cloned() {
             Some(id) if id == outer_pat_id => {
@@ -2857,7 +2884,7 @@ fn smart_resolve_path_fragment(&mut self,
                     .map(|suggestion| import_candidate_to_paths(&suggestion)).collect::<Vec<_>>();
                 enum_candidates.sort();
                 for (sp, variant_path, enum_path) in enum_candidates {
-                    if sp == DUMMY_SP {
+                    if sp.is_dummy() {
                         let msg = format!("there is an enum variant `{}`, \
                                         try using `{}`?",
                                         variant_path,
@@ -3345,14 +3372,11 @@ fn resolve_path(
             if ns == TypeNS {
                 if (i == 0 && name == keywords::CrateRoot.name()) ||
                    (i == 0 && name == keywords::Crate.name()) ||
+                   (i == 0 && name == keywords::DollarCrate.name()) ||
                    (i == 1 && name == keywords::Crate.name() &&
                               path[0].name == keywords::CrateRoot.name()) {
-                    // `::a::b` or `::crate::a::b`
-                    module = Some(self.resolve_crate_root(ident.span.ctxt(), false));
-                    continue
-                } else if i == 0 && name == keywords::DollarCrate.name() {
-                    // `$crate::a::b`
-                    module = Some(self.resolve_crate_root(ident.span.ctxt(), true));
+                    // `::a::b`, `crate::a::b`, `::crate::a::b` or `$crate::a::b`
+                    module = Some(self.resolve_crate_root(ident));
                     continue
                 } else if i == 1 && !ident.is_path_segment_keyword() {
                     let prev_name = path[0].name;
@@ -3784,7 +3808,8 @@ fn with_resolved_label<F>(&mut self, label: Option<Label>, id: NodeId, f: F)
             self.unused_labels.insert(id, label.ident.span);
             let def = Def::Label(id);
             self.with_label_rib(|this| {
-                this.label_ribs.last_mut().unwrap().bindings.insert(label.ident, def);
+                let ident = label.ident.modern_and_legacy();
+                this.label_ribs.last_mut().unwrap().bindings.insert(ident, def);
                 f(this);
             });
         } else {
@@ -3815,7 +3840,10 @@ fn resolve_expr(&mut self, expr: &Expr, parent: Option<&Expr>) {
             }
 
             ExprKind::Break(Some(label), _) | ExprKind::Continue(Some(label)) => {
-                match self.search_label(label.ident, |rib, id| rib.bindings.get(&id).cloned()) {
+                let def = self.search_label(label.ident, |rib, ident| {
+                    rib.bindings.get(&ident.modern_and_legacy()).cloned()
+                });
+                match def {
                     None => {
                         // Search again for close matches...
                         // Picks the first label that is "close enough", which is not necessarily
@@ -4281,7 +4309,7 @@ fn report_errors(&mut self, krate: &Crate) {
             let mut err = struct_span_err!(self.session, span, E0659, "`{}` is ambiguous", name);
             err.span_note(b1.span, &msg1);
             match b2.def() {
-                Def::Macro(..) if b2.span == DUMMY_SP =>
+                Def::Macro(..) if b2.span.is_dummy() =>
                     err.note(&format!("`{}` is also a builtin macro", name)),
                 _ => err.span_note(b2.span, &msg2),
             };
@@ -4394,14 +4422,14 @@ fn report_conflict<'b>(&mut self,
                           container));
 
         err.span_label(span, format!("`{}` re{} here", name, new_participle));
-        if old_binding.span != DUMMY_SP {
+        if !old_binding.span.is_dummy() {
             err.span_label(self.session.codemap().def_span(old_binding.span),
                            format!("previous {} of the {} `{}` here", old_noun, old_kind, name));
         }
 
         // See https://github.com/rust-lang/rust/issues/32354
         if old_binding.is_import() || new_binding.is_import() {
-            let binding = if new_binding.is_import() && new_binding.span != DUMMY_SP {
+            let binding = if new_binding.is_import() && !new_binding.span.is_dummy() {
                 new_binding
             } else {
                 old_binding
index c9d00f80b0ba325b31d073fc997b24d203d2baf0..0523765ea189750cb879d61588967d3511855fa1 100644 (file)
@@ -155,10 +155,9 @@ fn fold_qpath(&mut self, mut qself: Option<ast::QSelf>, mut path: ast::Path)
                     }
                 });
 
-                let ident = path.segments[0].ident;
-                if ident.name == keywords::DollarCrate.name() {
+                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::CrateRoot.name();
-                    let module = self.0.resolve_crate_root(ident.span.ctxt(), true);
                     if !module.is_local() {
                         let span = path.segments[0].ident.span;
                         path.segments.insert(1, match module.kind {
@@ -333,7 +332,9 @@ fn resolve_invoc(&mut self, invoc: &mut Invocation, scope: Mark, force: bool)
         self.unused_macros.remove(&def_id);
         let ext = self.get_macro(def);
         if ext.is_modern() {
-            invoc.expansion_data.mark.set_transparency(Transparency::Opaque);
+            let transparency =
+                if ext.is_transparent() { Transparency::Transparent } else { Transparency::Opaque };
+            invoc.expansion_data.mark.set_transparency(transparency);
         } else if def_id.krate == BUILTIN_MACROS_CRATE {
             invoc.expansion_data.mark.set_is_builtin(true);
         }
@@ -351,8 +352,8 @@ fn resolve_macro(&mut self, scope: Mark, path: &ast::Path, kind: MacroKind, forc
     fn check_unused_macros(&self) {
         for did in self.unused_macros.iter() {
             let id_span = match *self.macro_map[did] {
-                SyntaxExtension::NormalTT { def_info, .. } => def_info,
-                SyntaxExtension::DeclMacro(.., osp, _) => osp,
+                SyntaxExtension::NormalTT { def_info, .. } |
+                SyntaxExtension::DeclMacro { def_info, .. } => def_info,
                 _ => None,
             };
             if let Some((id, span)) = id_span {
@@ -849,8 +850,6 @@ pub fn define_macro(&mut self,
     /// Error if `ext` is a Macros 1.1 procedural macro being imported by `#[macro_use]`
     fn err_if_macro_use_proc_macro(&mut self, name: Name, use_span: Span,
                                    binding: &NameBinding<'a>) {
-        use self::SyntaxExtension::*;
-
         let krate = binding.def().def_id().krate;
 
         // Plugin-based syntax extensions are exempt from this check
@@ -860,15 +859,16 @@ fn err_if_macro_use_proc_macro(&mut self, name: Name, use_span: Span,
 
         match *ext {
             // If `ext` is a procedural macro, check if we've already warned about it
-            AttrProcMacro(..) | ProcMacro(..) =>
+            SyntaxExtension::AttrProcMacro(..) | SyntaxExtension::ProcMacro { .. } =>
                 if !self.warned_proc_macros.insert(name) { return; },
             _ => return,
         }
 
         let warn_msg = match *ext {
-            AttrProcMacro(..) => "attribute procedural macros cannot be \
-                                  imported with `#[macro_use]`",
-            ProcMacro(..) => "procedural macros cannot be imported with `#[macro_use]`",
+            SyntaxExtension::AttrProcMacro(..) =>
+                "attribute procedural macros cannot be imported with `#[macro_use]`",
+            SyntaxExtension::ProcMacro { .. } =>
+                "procedural macros cannot be imported with `#[macro_use]`",
             _ => return,
         };
 
index 8ac5e248dade4db5da31745e32d3ab44259d47f3..0ee17ebc4870423b29068b2823be42d5668f630a 100644 (file)
@@ -698,7 +698,7 @@ fn finalize_import(&mut self, directive: &'b ImportDirective<'b>) -> Option<(Spa
                                          "crate root imports need to be explicitly named: \
                                           `use crate as name;`".to_string()));
                         } else {
-                            Some(self.resolve_crate_root(source.span.ctxt().modern(), false))
+                            Some(self.resolve_crate_root(source))
                         }
                     } else if is_extern && !source.is_path_segment_keyword() {
                         let crate_id =
index 89d30fd666a883d88d67c80b9ff4c046cc68ff23..c07db44b36ccfd9e0001225890a8244bdddc7be6 100644 (file)
@@ -1157,7 +1157,7 @@ fn escape(s: String) -> String {
 // Helper function to determine if a span came from a
 // macro expansion or syntax extension.
 fn generated_code(span: Span) -> bool {
-    span.ctxt() != NO_EXPANSION || span == DUMMY_SP
+    span.ctxt() != NO_EXPANSION || span.is_dummy()
 }
 
 // DefId::index is a newtype and so the JSON serialisation is ugly. Therefore
index 41adde0d4a1887c16cc599f870289814b5b26486..ae5ca5441adf67675d3d3df6d23edc592d04f518 100644 (file)
@@ -12,7 +12,7 @@
 use rustc::ty::TyCtxt;
 
 use syntax::ast;
-use syntax_pos::{Span, DUMMY_SP};
+use syntax_pos::Span;
 
 use rustc::hir::def_id::{DefId, LOCAL_CRATE};
 use rustc::hir::itemlikevisit::ItemLikeVisitor;
@@ -39,7 +39,7 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
 
 impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for CheckVisitor<'a, 'tcx> {
     fn visit_item(&mut self, item: &hir::Item) {
-        if item.vis == hir::Public || item.span == DUMMY_SP {
+        if item.vis == hir::Public || item.span.is_dummy() {
             return;
         }
         if let hir::ItemUse(ref path, _) = item.node {
index 8cdffcd558d75cff4c1c5ab6e2ab82945fe776fa..65babbffffef11e93d0c83e3cf234a5602d4a222 100644 (file)
@@ -1219,7 +1219,7 @@ fn macro_resolve(cx: &DocContext, path_str: &str) -> Option<Def> {
     let res = resolver
         .resolve_macro_to_def_inner(mark, &path, MacroKind::Bang, false);
     if let Ok(def) = res {
-        if let SyntaxExtension::DeclMacro(..) = *resolver.get_macro(def) {
+        if let SyntaxExtension::DeclMacro { .. } = *resolver.get_macro(def) {
             Some(def)
         } else {
             None
@@ -3464,7 +3464,7 @@ pub fn empty() -> Span {
 
 impl Clean<Span> for syntax_pos::Span {
     fn clean(&self, cx: &DocContext) -> Span {
-        if *self == DUMMY_SP {
+        if self.is_dummy() {
             return Span::empty();
         }
 
index 1d5429bdf8f7d124d498d9f0243b2ce6e49aa8e3..ea6b39504e81d72463c90b4fd32d1fce8490c6dc 100644 (file)
@@ -443,7 +443,7 @@ pub fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span> {
     }
 
     pub fn span_to_string(&self, sp: Span) -> String {
-        if self.files.borrow().file_maps.is_empty() && sp.source_equal(&DUMMY_SP) {
+        if self.files.borrow().file_maps.is_empty() && sp.is_dummy() {
             return "no-location".to_string();
         }
 
index 9afce74f53cc4001fcccb6b1635e244061163467..e2424de4d1449a25a81d4bcac7618cb5e8bf4fa9 100644 (file)
@@ -597,11 +597,11 @@ pub enum SyntaxExtension {
     MultiModifier(Box<MultiItemModifier + sync::Sync + sync::Send>),
 
     /// A function-like procedural macro. TokenStream -> TokenStream.
-    ProcMacro(
-        /* expander: */ Box<ProcMacro + sync::Sync + sync::Send>,
-        /* allow_internal_unstable: */ bool,
-        /* edition: */ Edition,
-    ),
+    ProcMacro {
+        expander: Box<ProcMacro + sync::Sync + sync::Send>,
+        allow_internal_unstable: bool,
+        edition: Edition,
+    },
 
     /// An attribute-like procedural macro. TokenStream, TokenStream -> TokenStream.
     /// The first TokenSteam is the attribute, the second is the annotated item.
@@ -646,19 +646,22 @@ pub enum SyntaxExtension {
     BuiltinDerive(BuiltinDeriveFn),
 
     /// A declarative macro, e.g. `macro m() {}`.
-    ///
-    /// The second element is the definition site span.
-    DeclMacro(Box<TTMacroExpander + sync::Sync + sync::Send>, Option<(ast::NodeId, Span)>, Edition),
+    DeclMacro {
+        expander: Box<TTMacroExpander + sync::Sync + sync::Send>,
+        def_info: Option<(ast::NodeId, Span)>,
+        is_transparent: bool,
+        edition: Edition,
+    }
 }
 
 impl SyntaxExtension {
     /// Return which kind of macro calls this syntax extension.
     pub fn kind(&self) -> MacroKind {
         match *self {
-            SyntaxExtension::DeclMacro(..) |
+            SyntaxExtension::DeclMacro { .. } |
             SyntaxExtension::NormalTT { .. } |
             SyntaxExtension::IdentTT(..) |
-            SyntaxExtension::ProcMacro(..) =>
+            SyntaxExtension::ProcMacro { .. } =>
                 MacroKind::Bang,
             SyntaxExtension::MultiDecorator(..) |
             SyntaxExtension::MultiModifier(..) |
@@ -672,19 +675,26 @@ pub fn kind(&self) -> MacroKind {
 
     pub fn is_modern(&self) -> bool {
         match *self {
-            SyntaxExtension::DeclMacro(..) |
-            SyntaxExtension::ProcMacro(..) |
+            SyntaxExtension::DeclMacro { .. } |
+            SyntaxExtension::ProcMacro { .. } |
             SyntaxExtension::AttrProcMacro(..) |
             SyntaxExtension::ProcMacroDerive(..) => true,
             _ => false,
         }
     }
 
+    pub fn is_transparent(&self) -> bool {
+        match *self {
+            SyntaxExtension::DeclMacro { is_transparent, .. } => is_transparent,
+            _ => false,
+        }
+    }
+
     pub fn edition(&self) -> Edition {
         match *self {
             SyntaxExtension::NormalTT { edition, .. } |
-            SyntaxExtension::DeclMacro(.., edition) |
-            SyntaxExtension::ProcMacro(.., edition) |
+            SyntaxExtension::DeclMacro { edition, .. } |
+            SyntaxExtension::ProcMacro { edition, .. } |
             SyntaxExtension::AttrProcMacro(.., edition) |
             SyntaxExtension::ProcMacroDerive(.., edition) => edition,
             // Unstable legacy stuff
index 9cd410d424394643bafb156d6f7c44a613b2b80c..f29bff20f3dd6a4d671c81a472375d1210786ef5 100644 (file)
@@ -738,13 +738,13 @@ fn expand_bang_invoc(&mut self,
         };
 
         let opt_expanded = match *ext {
-            DeclMacro(ref expand, def_span, edition) => {
-                if let Err(dummy_span) = validate_and_set_expn_info(self, def_span.map(|(_, s)| s),
+            DeclMacro { ref expander, def_info, edition, .. } => {
+                if let Err(dummy_span) = validate_and_set_expn_info(self, def_info.map(|(_, s)| s),
                                                                     false, false, false, None,
                                                                     edition) {
                     dummy_span
                 } else {
-                    kind.make_from(expand.expand(self.cx, span, mac.node.stream()))
+                    kind.make_from(expander.expand(self.cx, span, mac.node.stream()))
                 }
             }
 
@@ -804,7 +804,7 @@ fn expand_bang_invoc(&mut self,
                 kind.dummy(span)
             }
 
-            ProcMacro(ref expandfun, allow_internal_unstable, edition) => {
+            SyntaxExtension::ProcMacro { ref expander, allow_internal_unstable, edition } => {
                 if ident.name != keywords::Invalid.name() {
                     let msg =
                         format!("macro {}! expects no ident argument, given '{}'", path, ident);
@@ -826,7 +826,7 @@ fn expand_bang_invoc(&mut self,
                         edition,
                     });
 
-                    let tok_result = expandfun.expand(self.cx, span, mac.node.stream());
+                    let tok_result = expander.expand(self.cx, span, mac.node.stream());
                     let result = self.parse_ast_fragment(tok_result, kind, path, span);
                     self.gate_proc_macro_expansion(span, &result);
                     result
@@ -1297,7 +1297,7 @@ fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
                 // Detect if this is an inline module (`mod m { ... }` as opposed to `mod m;`).
                 // In the non-inline case, `inner` is never the dummy span (c.f. `parse_item_mod`).
                 // Thus, if `inner` is the dummy span, we know the module is inline.
-                let inline_module = item.span.contains(inner) || inner == DUMMY_SP;
+                let inline_module = item.span.contains(inner) || inner.is_dummy();
 
                 if inline_module {
                     if let Some(path) = attr::first_attr_value_str_by_name(&item.attrs, "path") {
index 3b3892729d93cdcc0315b36b7f8229e6b615642d..70fc9dada428ea7f8c60f17720fec27a010cb49f 100644 (file)
@@ -312,7 +312,14 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item, edition:
             edition,
         }
     } else {
-        SyntaxExtension::DeclMacro(expander, Some((def.id, def.span)), edition)
+        let is_transparent = attr::contains_name(&def.attrs, "rustc_transparent_macro");
+
+        SyntaxExtension::DeclMacro {
+            expander,
+            def_info: Some((def.id, def.span)),
+            is_transparent,
+            edition,
+        }
     }
 }
 
index 01b971976a763c3d49bf72f5283e2303e52f0442..82b0fae3e9c68e7ce93e82ca7d0d8926d56799b1 100644 (file)
@@ -14,7 +14,7 @@
 use parse::{token, ParseSess};
 use print::pprust;
 use symbol::keywords;
-use syntax_pos::{BytePos, Span, DUMMY_SP};
+use syntax_pos::{BytePos, Span};
 use tokenstream;
 
 use std::iter::Peekable;
@@ -41,8 +41,8 @@ pub fn close_token(&self) -> token::Token {
 
     /// Return a `self::TokenTree` with a `Span` corresponding to the opening delimiter.
     pub fn open_tt(&self, span: Span) -> TokenTree {
-        let open_span = if span == DUMMY_SP {
-            DUMMY_SP
+        let open_span = if span.is_dummy() {
+            span
         } else {
             span.with_lo(span.lo() + BytePos(self.delim.len() as u32))
         };
@@ -51,8 +51,8 @@ pub fn open_tt(&self, span: Span) -> TokenTree {
 
     /// Return a `self::TokenTree` with a `Span` corresponding to the closing delimiter.
     pub fn close_tt(&self, span: Span) -> TokenTree {
-        let close_span = if span == DUMMY_SP {
-            DUMMY_SP
+        let close_span = if span.is_dummy() {
+            span
         } else {
             span.with_lo(span.hi() - BytePos(self.delim.len() as u32))
         };
index cce8da1dcbd53f9291aa08bab5d6e032920e9938..c443f240780317ef2671b3c1ecfdd4fbf22b1e2a 100644 (file)
@@ -13,7 +13,7 @@
 use rustc_data_structures::sync::{Lrc, Lock};
 use ast::{self, CrateConfig};
 use codemap::{CodeMap, FilePathMapping};
-use syntax_pos::{self, Span, FileMap, NO_EXPANSION, FileName};
+use syntax_pos::{Span, FileMap, FileName};
 use errors::{Handler, ColorConfig, DiagnosticBuilder};
 use feature_gate::UnstableFeatures;
 use parse::parser::Parser;
@@ -188,8 +188,8 @@ fn filemap_to_parser(sess: & ParseSess, filemap: Lrc<FileMap>) -> Parser {
     let end_pos = filemap.end_pos;
     let mut parser = stream_to_parser(sess, filemap_to_stream(sess, filemap, None));
 
-    if parser.token == token::Eof && parser.span == syntax_pos::DUMMY_SP {
-        parser.span = Span::new(end_pos, end_pos, NO_EXPANSION);
+    if parser.token == token::Eof && parser.span.is_dummy() {
+        parser.span = Span::new(end_pos, end_pos, parser.span.ctxt());
     }
 
     parser
index e04fec797e02cd6ce3d90ef07551c5cfcda68e75..673157d0ffa0db871c61f7730fc79d91af923086 100644 (file)
@@ -43,7 +43,7 @@
 use ast::{RangeEnd, RangeSyntax};
 use {ast, attr};
 use codemap::{self, CodeMap, Spanned, respan};
-use syntax_pos::{self, Span, MultiSpan, BytePos, FileName, DUMMY_SP, edition::Edition};
+use syntax_pos::{self, Span, MultiSpan, BytePos, FileName, edition::Edition};
 use errors::{self, Applicability, DiagnosticBuilder};
 use parse::{self, SeqSep, classify, token};
 use parse::lexer::TokenAndSpan;
@@ -567,7 +567,7 @@ pub fn new(sess: &'a ParseSess,
 
         if let Some(directory) = directory {
             parser.directory = directory;
-        } else if !parser.span.source_equal(&DUMMY_SP) {
+        } else if !parser.span.is_dummy() {
             if let FileName::Real(mut path) = sess.codemap().span_to_unmapped_path(parser.span) {
                 path.pop();
                 parser.directory.path = Cow::from(path);
@@ -584,7 +584,7 @@ fn next_tok(&mut self) -> TokenAndSpan {
         } else {
             self.token_cursor.next()
         };
-        if next.sp == syntax_pos::DUMMY_SP {
+        if next.sp.is_dummy() {
             // Tweak the location for better diagnostics, but keep syntactic context intact.
             next.sp = self.prev_span.with_ctxt(next.sp.ctxt());
         }
@@ -6138,7 +6138,7 @@ fn parse_mod_items(&mut self, term: &token::Token, inner_lo: Span) -> PResult<'a
             return Err(err);
         }
 
-        let hi = if self.span == syntax_pos::DUMMY_SP {
+        let hi = if self.span.is_dummy() {
             inner_lo
         } else {
             self.prev_span
@@ -6369,7 +6369,7 @@ fn submod_path(&mut self,
                 }
                 let mut err = self.diagnostic().struct_span_err(id_sp,
                     "cannot declare a new module at this location");
-                if id_sp != syntax_pos::DUMMY_SP {
+                if !id_sp.is_dummy() {
                     let src_path = self.sess.codemap().span_to_filename(id_sp);
                     if let FileName::Real(src_path) = src_path {
                         if let Some(stem) = src_path.file_stem() {
index 455cc4391dd3b535877d8b79d03a926859833a95..8736fcf9729a634d2f78a2f601abba147c938282 100644 (file)
@@ -57,8 +57,8 @@ pub fn close_token(&self) -> token::Token {
 
     /// Returns the opening delimiter as a token tree.
     pub fn open_tt(&self, span: Span) -> TokenTree {
-        let open_span = if span == DUMMY_SP {
-            DUMMY_SP
+        let open_span = if span.is_dummy() {
+            span
         } else {
             span.with_hi(span.lo() + BytePos(self.delim.len() as u32))
         };
@@ -67,8 +67,8 @@ pub fn open_tt(&self, span: Span) -> TokenTree {
 
     /// Returns the closing delimiter as a token tree.
     pub fn close_tt(&self, span: Span) -> TokenTree {
-        let close_span = if span == DUMMY_SP {
-            DUMMY_SP
+        let close_span = if span.is_dummy() {
+            span
         } else {
             span.with_lo(span.hi() - BytePos(self.delim.len() as u32))
         };
index 99d8b1b172d24505aaa6cc54c29a0d0c6774b306..33d02d0b10a7bd5283558892069b7d78f65cb1cf 100644 (file)
 pub struct SyntaxContextData {
     pub outer_mark: Mark,
     pub prev_ctxt: SyntaxContext,
-    pub modern: SyntaxContext,
+    // This context, but with all transparent and semi-transparent marks filtered away.
+    pub opaque: SyntaxContext,
+    // This context, but with all transparent marks filtered away.
+    pub opaque_and_semitransparent: SyntaxContext,
 }
 
 /// A mark is a unique id associated with a macro expansion.
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
 pub struct Mark(u32);
 
-#[derive(Debug)]
+#[derive(Clone, Debug)]
 struct MarkData {
     parent: Mark,
     transparency: Transparency,
@@ -50,11 +53,10 @@ struct MarkData {
 
 /// A property of a macro expansion that determines how identifiers
 /// produced by that expansion are resolved.
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)]
 pub enum Transparency {
     /// Identifier produced by a transparent expansion is always resolved at call-site.
     /// Call-site spans in procedural macros, hygiene opt-out in `macro` should use this.
-    /// (Not used yet.)
     Transparent,
     /// Identifier produced by a semi-transparent expansion may be resolved
     /// either at call-site or at definition-site.
@@ -69,16 +71,26 @@ pub enum Transparency {
 }
 
 impl Mark {
+    fn fresh_with_data(mark_data: MarkData, data: &mut HygieneData) -> Self {
+        data.marks.push(mark_data);
+        Mark(data.marks.len() as u32 - 1)
+    }
+
     pub fn fresh(parent: Mark) -> Self {
         HygieneData::with(|data| {
-            data.marks.push(MarkData {
+            Mark::fresh_with_data(MarkData {
                 parent,
                 // By default expansions behave like `macro_rules`.
                 transparency: Transparency::SemiTransparent,
                 is_builtin: false,
                 expn_info: None,
-            });
-            Mark(data.marks.len() as u32 - 1)
+            }, data)
+        })
+    }
+
+    pub fn fresh_cloned(clone_from: Mark) -> Self {
+        HygieneData::with(|data| {
+            Mark::fresh_with_data(data.marks[clone_from.0 as usize].clone(), data)
         })
     }
 
@@ -207,7 +219,8 @@ pub fn new() -> Self {
             syntax_contexts: vec![SyntaxContextData {
                 outer_mark: Mark::root(),
                 prev_ctxt: SyntaxContext(0),
-                modern: SyntaxContext(0),
+                opaque: SyntaxContext(0),
+                opaque_and_semitransparent: SyntaxContext(0),
             }],
             markings: HashMap::new(),
             default_edition: Edition::Edition2015,
@@ -239,7 +252,7 @@ pub const fn empty() -> Self {
     // Allocate a new SyntaxContext with the given ExpnInfo. This is used when
     // deserializing Spans from the incr. comp. cache.
     // FIXME(mw): This method does not restore MarkData::parent or
-    // SyntaxContextData::prev_ctxt or SyntaxContextData::modern. These things
+    // SyntaxContextData::prev_ctxt or SyntaxContextData::opaque. These things
     // don't seem to be used after HIR lowering, so everything should be fine
     // as long as incremental compilation does not kick in before that.
     pub fn allocate_directly(expansion_info: ExpnInfo) -> Self {
@@ -256,7 +269,8 @@ pub fn allocate_directly(expansion_info: ExpnInfo) -> Self {
             data.syntax_contexts.push(SyntaxContextData {
                 outer_mark: mark,
                 prev_ctxt: SyntaxContext::empty(),
-                modern: SyntaxContext::empty(),
+                opaque: SyntaxContext::empty(),
+                opaque_and_semitransparent: SyntaxContext::empty(),
             });
             SyntaxContext(data.syntax_contexts.len() as u32 - 1)
         })
@@ -269,7 +283,13 @@ pub fn apply_mark(self, mark: Mark) -> SyntaxContext {
         }
 
         let call_site_ctxt =
-            mark.expn_info().map_or(SyntaxContext::empty(), |info| info.call_site.ctxt()).modern();
+            mark.expn_info().map_or(SyntaxContext::empty(), |info| info.call_site.ctxt());
+        let call_site_ctxt = if mark.transparency() == Transparency::SemiTransparent {
+            call_site_ctxt.modern()
+        } else {
+            call_site_ctxt.modern_and_legacy()
+        };
+
         if call_site_ctxt == SyntaxContext::empty() {
             return self.apply_mark_internal(mark);
         }
@@ -293,26 +313,53 @@ pub fn apply_mark(self, mark: Mark) -> SyntaxContext {
     fn apply_mark_internal(self, mark: Mark) -> SyntaxContext {
         HygieneData::with(|data| {
             let syntax_contexts = &mut data.syntax_contexts;
-            let mut modern = syntax_contexts[self.0 as usize].modern;
-            if data.marks[mark.0 as usize].transparency == Transparency::Opaque {
-                modern = *data.markings.entry((modern, mark)).or_insert_with(|| {
-                    let len = syntax_contexts.len() as u32;
+            let transparency = data.marks[mark.0 as usize].transparency;
+
+            let mut opaque = syntax_contexts[self.0 as usize].opaque;
+            let mut opaque_and_semitransparent =
+                syntax_contexts[self.0 as usize].opaque_and_semitransparent;
+
+            if transparency >= Transparency::Opaque {
+                let prev_ctxt = opaque;
+                opaque = *data.markings.entry((prev_ctxt, mark)).or_insert_with(|| {
+                    let new_opaque = SyntaxContext(syntax_contexts.len() as u32);
+                    syntax_contexts.push(SyntaxContextData {
+                        outer_mark: mark,
+                        prev_ctxt,
+                        opaque: new_opaque,
+                        opaque_and_semitransparent: new_opaque,
+                    });
+                    new_opaque
+                });
+            }
+
+            if transparency >= Transparency::SemiTransparent {
+                let prev_ctxt = opaque_and_semitransparent;
+                opaque_and_semitransparent =
+                        *data.markings.entry((prev_ctxt, mark)).or_insert_with(|| {
+                    let new_opaque_and_semitransparent =
+                        SyntaxContext(syntax_contexts.len() as u32);
                     syntax_contexts.push(SyntaxContextData {
                         outer_mark: mark,
-                        prev_ctxt: modern,
-                        modern: SyntaxContext(len),
+                        prev_ctxt,
+                        opaque,
+                        opaque_and_semitransparent: new_opaque_and_semitransparent,
                     });
-                    SyntaxContext(len)
+                    new_opaque_and_semitransparent
                 });
             }
 
-            *data.markings.entry((self, mark)).or_insert_with(|| {
+            let prev_ctxt = self;
+            *data.markings.entry((prev_ctxt, mark)).or_insert_with(|| {
+                let new_opaque_and_semitransparent_and_transparent =
+                    SyntaxContext(syntax_contexts.len() as u32);
                 syntax_contexts.push(SyntaxContextData {
                     outer_mark: mark,
-                    prev_ctxt: self,
-                    modern,
+                    prev_ctxt,
+                    opaque,
+                    opaque_and_semitransparent,
                 });
-                SyntaxContext(syntax_contexts.len() as u32 - 1)
+                new_opaque_and_semitransparent_and_transparent
             })
         })
     }
@@ -452,7 +499,12 @@ pub fn reverse_glob_adjust(&mut self, expansion: Mark, mut glob_ctxt: SyntaxCont
 
     #[inline]
     pub fn modern(self) -> SyntaxContext {
-        HygieneData::with(|data| data.syntax_contexts[self.0 as usize].modern)
+        HygieneData::with(|data| data.syntax_contexts[self.0 as usize].opaque)
+    }
+
+    #[inline]
+    pub fn modern_and_legacy(self) -> SyntaxContext {
+        HygieneData::with(|data| data.syntax_contexts[self.0 as usize].opaque_and_semitransparent)
     }
 
     #[inline]
index 55dec31511c37f506825d111b00dffe50e051c62..491ce720f36c5f0eacd0408dd07a6c99f5e1a65e 100644 (file)
@@ -248,6 +248,13 @@ pub fn with_ctxt(self, ctxt: SyntaxContext) -> Span {
         self.data().with_ctxt(ctxt)
     }
 
+    /// Returns `true` if this is a dummy span with any hygienic context.
+    #[inline]
+    pub fn is_dummy(self) -> bool {
+        let span = self.data();
+        span.lo.0 == 0 && span.hi.0 == 0
+    }
+
     /// Returns a new span representing an empty span at the beginning of this span
     #[inline]
     pub fn shrink_to_lo(self) -> Span {
@@ -263,7 +270,7 @@ pub fn shrink_to_hi(self) -> Span {
 
     /// Returns `self` if `self` is not the dummy span, and `other` otherwise.
     pub fn substitute_dummy(self, other: Span) -> Span {
-        if self.source_equal(&DUMMY_SP) { other } else { self }
+        if self.is_dummy() { other } else { self }
     }
 
     /// Return true if `self` fully encloses `other`.
@@ -491,6 +498,12 @@ pub fn modern(self) -> Span {
         let span = self.data();
         span.with_ctxt(span.ctxt.modern())
     }
+
+    #[inline]
+    pub fn modern_and_legacy(self) -> Span {
+        let span = self.data();
+        span.with_ctxt(span.ctxt.modern_and_legacy())
+    }
 }
 
 #[derive(Clone, Debug)]
index bb64dad12085dfaa0e18e8674efef1a10c7c060d..9a0c92f679366929916399b3aa08c655fe863dbb 100644 (file)
@@ -68,6 +68,15 @@ pub fn modern(self) -> Ident {
         Ident::new(self.name, self.span.modern())
     }
 
+    /// "Normalize" ident for use in comparisons using "local variable hygiene".
+    /// Identifiers with same string value become same if they came from the same non-transparent
+    /// macro (e.g. `macro` or `macro_rules!` items) and stay different if they came from different
+    /// non-transparent macros.
+    /// Technically, this operation strips all transparent marks from ident's syntactic context.
+    pub fn modern_and_legacy(self) -> Ident {
+        Ident::new(self.name, self.span.modern_and_legacy())
+    }
+
     pub fn gensym(self) -> Ident {
         Ident::new(self.name.gensymed(), self.span)
     }
index 98e50183097cce111dae113a677ad97defa18c50..6473b69b4591d3169163caed83305dbe33d85dfd 100644 (file)
@@ -23,5 +23,5 @@ fn main() {
     bang_proc_macro2!();
     //~^ ERROR cannot find value `foobar2` in this scope
     //~^^ did you mean `foobar`?
-    println!("{}", x); //~ ERROR cannot find value `x` in this scope
+    println!("{}", x);
 }
diff --git a/src/test/run-pass-fulldeps/proc-macro/auxiliary/call-site.rs b/src/test/run-pass-fulldeps/proc-macro/auxiliary/call-site.rs
new file mode 100644 (file)
index 0000000..ab4e082
--- /dev/null
@@ -0,0 +1,37 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+#![feature(proc_macro)]
+
+extern crate proc_macro;
+use proc_macro::*;
+
+#[proc_macro]
+pub fn check(input: TokenStream) -> TokenStream {
+    // Parsed `x2` can refer to `x2` from `input`
+    let parsed1: TokenStream = "let x3 = x2;".parse().unwrap();
+    // `x3` parsed from one string can refer to `x3` parsed from another string.
+    let parsed2: TokenStream = "let x4 = x3;".parse().unwrap();
+    // Manually assembled `x4` can refer to parsed `x4`.
+    let manual: Vec<TokenTree> = vec![
+        Ident::new("let", Span::call_site()).into(),
+        Ident::new("x5", Span::call_site()).into(),
+        Punct::new('=', Spacing::Alone).into(),
+        Ident::new("x4", Span::call_site()).into(),
+        Punct::new(';', Spacing::Alone).into(),
+    ];
+    input.into_iter().chain(parsed1.into_iter())
+                     .chain(parsed2.into_iter())
+                     .chain(manual.into_iter())
+                     .collect()
+}
diff --git a/src/test/run-pass-fulldeps/proc-macro/call-site.rs b/src/test/run-pass-fulldeps/proc-macro/call-site.rs
new file mode 100644 (file)
index 0000000..f0d4897
--- /dev/null
@@ -0,0 +1,23 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:call-site.rs
+// ignore-stage1
+
+#![feature(proc_macro, proc_macro_non_items)]
+
+extern crate call_site;
+use call_site::*;
+
+fn main() {
+    let x1 = 10;
+    call_site::check!(let x2 = x1;);
+    let x6 = x5;
+}
index aa67e5c5f4db7bcaf1d43feb2d228b63982cca6e..f917fabbe0c589180a5fc5bddea68df675ce2d93 100644 (file)
@@ -19,3 +19,38 @@ fn f() -> u32 { 1 }
         }
     }
 }
+
+pub struct SomeType;
+
+// `$crate`
+pub macro uses_dollar_crate_modern() {
+    type Alias = $crate::SomeType;
+}
+
+pub macro define_uses_dollar_crate_modern_nested($uses_dollar_crate_modern_nested: ident) {
+    macro $uses_dollar_crate_modern_nested() {
+        type AliasCrateModernNested = $crate::SomeType;
+    }
+}
+
+#[macro_export]
+macro_rules! define_uses_dollar_crate_legacy_nested {
+    () => {
+        macro_rules! uses_dollar_crate_legacy_nested {
+            () => {
+                type AliasLegacyNested = $crate::SomeType;
+            }
+        }
+    }
+}
+
+// `crate`
+pub macro uses_crate_modern() {
+    type AliasCrate = crate::SomeType;
+}
+
+pub macro define_uses_crate_modern_nested($uses_crate_modern_nested: ident) {
+    macro $uses_crate_modern_nested() {
+        type AliasCrateModernNested = crate::SomeType;
+    }
+}
diff --git a/src/test/ui/hygiene/auxiliary/transparent-basic.rs b/src/test/ui/hygiene/auxiliary/transparent-basic.rs
new file mode 100644 (file)
index 0000000..ba65c5f
--- /dev/null
@@ -0,0 +1,16 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(decl_macro, rustc_attrs)]
+
+#[rustc_transparent_macro]
+pub macro dollar_crate() {
+    let s = $crate::S;
+}
diff --git a/src/test/ui/hygiene/dollar-crate-modern.rs b/src/test/ui/hygiene/dollar-crate-modern.rs
new file mode 100644 (file)
index 0000000..6e536ab
--- /dev/null
@@ -0,0 +1,35 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Make sure `$crate` and `crate` work in for basic cases of nested macros.
+
+// compile-pass
+// aux-build:intercrate.rs
+
+#![feature(decl_macro, crate_in_paths)]
+
+extern crate intercrate;
+
+// `$crate`
+intercrate::uses_dollar_crate_modern!();
+
+intercrate::define_uses_dollar_crate_modern_nested!(uses_dollar_crate_modern_nested);
+uses_dollar_crate_modern_nested!();
+
+intercrate::define_uses_dollar_crate_legacy_nested!();
+uses_dollar_crate_legacy_nested!();
+
+// `crate`
+intercrate::uses_crate_modern!();
+
+intercrate::define_uses_crate_modern_nested!(uses_crate_modern_nested);
+uses_crate_modern_nested!();
+
+fn main() {}
diff --git a/src/test/ui/hygiene/generate-mod.rs b/src/test/ui/hygiene/generate-mod.rs
new file mode 100644 (file)
index 0000000..9040985
--- /dev/null
@@ -0,0 +1,24 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// This is an equivalent of issue #50504, but for declarative macros.
+
+#![feature(decl_macro, rustc_attrs)]
+
+#[rustc_transparent_macro]
+macro genmod() {
+    mod m {
+        type A = S; //~ ERROR cannot find type `S` in this scope
+    }
+}
+
+struct S;
+
+genmod!();
diff --git a/src/test/ui/hygiene/generate-mod.stderr b/src/test/ui/hygiene/generate-mod.stderr
new file mode 100644 (file)
index 0000000..e79f852
--- /dev/null
@@ -0,0 +1,17 @@
+error[E0412]: cannot find type `S` in this scope
+  --> $DIR/generate-mod.rs:18:18
+   |
+LL |         type A = S; //~ ERROR cannot find type `S` in this scope
+   |                  ^ did you mean `A`?
+...
+LL | genmod!();
+   | ---------- in this macro invocation
+
+error[E0601]: `main` function not found in crate `generate_mod`
+   |
+   = note: consider adding a `main` function to `$DIR/generate-mod.rs`
+
+error: aborting due to 2 previous errors
+
+Some errors occurred: E0412, E0601.
+For more information about an error, try `rustc --explain E0412`.
diff --git a/src/test/ui/hygiene/transparent-basic.rs b/src/test/ui/hygiene/transparent-basic.rs
new file mode 100644 (file)
index 0000000..81ece1f
--- /dev/null
@@ -0,0 +1,53 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-pass
+// aux-build:transparent-basic.rs
+
+#![feature(decl_macro, rustc_attrs)]
+
+extern crate transparent_basic;
+
+#[rustc_transparent_macro]
+macro binding() {
+    let x = 10;
+}
+
+#[rustc_transparent_macro]
+macro label() {
+    break 'label
+}
+
+macro_rules! legacy {
+    () => {
+        binding!();
+        let y = x;
+    }
+}
+
+fn legacy_interaction1() {
+    legacy!();
+}
+
+struct S;
+
+fn check_dollar_crate() {
+    // `$crate::S` inside the macro resolves to `S` from this crate.
+    transparent_basic::dollar_crate!();
+}
+
+fn main() {
+    binding!();
+    let y = x;
+
+    'label: loop {
+        label!();
+    }
+}