diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 39bdf88925e44..2cf5c92ea6721 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -293,6 +293,8 @@ impl<'a> CrateLoader<'a> { // Initialize this with an empty set. The field is populated below // after we were able to deserialize its contents. dllimport_foreign_items: FxHashSet(), + hygiene_data_import_info: RefCell::new(None), + hygiene_data_being_decoded: Cell::new(false), }; let dllimports: FxHashSet<_> = cmeta diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs index 9e47e96aee4ef..38578d9c286f9 100644 --- a/src/librustc_metadata/cstore.rs +++ b/src/librustc_metadata/cstore.rs @@ -27,7 +27,7 @@ use owning_ref::ErasedBoxRef; use syntax::{ast, attr}; use syntax::ext::base::SyntaxExtension; use syntax::symbol::Symbol; -use syntax_pos; +use syntax_pos::{self, hygiene}; pub use rustc::middle::cstore::{NativeLibrary, NativeLibraryKind, LinkagePreference}; pub use rustc::middle::cstore::NativeLibraryKind::*; @@ -87,6 +87,9 @@ pub struct CrateMetadata { pub proc_macros: Option)>>, // Foreign items imported from a dylib (Windows only) pub dllimport_foreign_items: FxHashSet, + + pub hygiene_data_import_info: RefCell>, + pub hygiene_data_being_decoded: Cell, } pub struct CStore { diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 65cf15e5a0ec7..a8176bf7a1654 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -45,7 +45,7 @@ use syntax::ast::{self, Ident}; use syntax::codemap; use syntax::symbol::{InternedString, Symbol}; use syntax::ext::base::MacroKind; -use syntax_pos::{self, Span, BytePos, Pos, DUMMY_SP, NO_EXPANSION}; +use syntax_pos::{self, hygiene, Span, SyntaxContext, BytePos, Pos, DUMMY_SP}; pub struct DecodeContext<'a, 'tcx: 'a> { opaque: opaque::Decoder<'a>, @@ -237,15 +237,44 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { } } +impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { + fn specialized_decode(&mut self) -> Result { + let mark = u32::decode(self)?; + + if !self.cdata().hygiene_data_being_decoded.get() && mark != 0 { + let imported_hygiene = self.cdata().imported_hygiene_data(); + + Ok(imported_hygiene.translate_mark(hygiene::Mark::from_u32(mark))) + } else { + Ok(hygiene::Mark::from_u32(mark)) + } + } +} + +impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { + fn specialized_decode(&mut self) -> Result { + let ctxt = u32::decode(self)?; + + if !self.cdata().hygiene_data_being_decoded.get() && ctxt != 0 { + let imported_hygiene = self.cdata().imported_hygiene_data(); + + Ok(imported_hygiene.translate_ctxt(SyntaxContext::from_u32(ctxt))) + } else { + Ok(SyntaxContext::from_u32(ctxt)) + } + } +} + impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { fn specialized_decode(&mut self) -> Result { let lo = BytePos::decode(self)?; let hi = BytePos::decode(self)?; + let ctxt = SyntaxContext::decode(self)?; let sess = if let Some(sess) = self.sess { sess } else { - return Ok(Span::new(lo, hi, NO_EXPANSION)); + return Ok(Span::new(lo, hi, ctxt)); }; let (lo, hi) = if lo > hi { @@ -292,7 +321,7 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { let lo = (lo - filemap.original_start_pos) + filemap.translated_filemap.start_pos; let hi = (hi - filemap.original_start_pos) + filemap.translated_filemap.start_pos; - Ok(Span::new(lo, hi, NO_EXPANSION)) + Ok(Span::new(lo, hi, ctxt)) } } @@ -1224,4 +1253,25 @@ impl<'a, 'tcx> CrateMetadata { *self.codemap_import_info.borrow_mut() = imported_filemaps; self.codemap_import_info.borrow() } + + pub fn imported_hygiene_data(&'a self) -> Ref<'a, hygiene::ImportedHygieneData> { + { + let hygiene_data = self.hygiene_data_import_info.borrow(); + if hygiene_data.is_some() { + return Ref::map(hygiene_data, |d| d.as_ref().unwrap()); + } + } + + self.hygiene_data_being_decoded.set(true); + + let external_hygiene_data = self.root.hygiene_data.decode(self); + + // This shouldn't borrow twice, but there is no way to downgrade RefMut to Ref. + *self.hygiene_data_import_info.borrow_mut() = + Some(hygiene::extend_hygiene_data(external_hygiene_data)); + + self.hygiene_data_being_decoded.set(false); + + Ref::map(self.hygiene_data_import_info.borrow(), |d| d.as_ref().unwrap()) + } } diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 6b49be3e12192..41cb54117dbb7 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -29,7 +29,7 @@ use rustc::ty::{self, Ty, TyCtxt, ReprOptions}; use rustc::session::config::{self, CrateTypeProcMacro}; use rustc::util::nodemap::{FxHashMap, NodeSet}; -use rustc_serialize::{Encodable, Encoder, SpecializedEncoder, opaque}; +use rustc_serialize::{Encodable, Encoder, SpecializedEncoder, UseSpecializedEncodable, opaque}; use std::hash::Hash; use std::intrinsics; @@ -42,7 +42,7 @@ use syntax::ast::{self, CRATE_NODE_ID}; use syntax::codemap::Spanned; use syntax::attr; use syntax::symbol::Symbol; -use syntax_pos; +use syntax_pos::{self, hygiene}; use rustc::hir::{self, PatKind}; use rustc::hir::itemlikevisit::ItemLikeVisitor; @@ -61,6 +61,13 @@ pub struct EncodeContext<'a, 'tcx: 'a> { pub metadata_hashes: EncodedMetadataHashes, pub compute_ich: bool, + // We need to encode hygiene info after all spans and possibly other data structures + // that reference it have been encoded already, since we only encode those elements of it + // that are actually used to avoid excessive memory usage. Thus, we need to keep track of + // whether we already encoded the hygiene info (and thus committed to a specific set of + // information to encode) to make sure we can catch bugs introduced by further changes + // quickly. + already_encoded_hygiene_data: bool, } macro_rules! encoder_methods { @@ -136,6 +143,26 @@ impl<'a, 'tcx> SpecializedEncoder> for EncodeContext } } +impl<'a, 'tcx> SpecializedEncoder for EncodeContext<'a, 'tcx> { + fn specialized_encode(&mut self, ctxt: &hygiene::SyntaxContext) -> Result<(), Self::Error> { + if self.already_encoded_hygiene_data { + bug!("trying to encode syntax context `{:?}` after encoding hygiene data!", ctxt); + } else { + ctxt.default_encode(self) + } + } +} + +impl<'a, 'tcx> SpecializedEncoder for EncodeContext<'a, 'tcx> { + fn specialized_encode(&mut self, mark: &hygiene::Mark) -> Result<(), Self::Error> { + if self.already_encoded_hygiene_data { + bug!("trying to encode mark `{:?}` after encoding hygiene data!", mark); + } else { + mark.default_encode(self) + } + } +} + impl<'a, 'tcx> EncodeContext<'a, 'tcx> { pub fn position(&self) -> usize { @@ -323,6 +350,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy_seq_ref(adapted.iter().map(|rc| &**rc)) } + fn encode_hygiene_data(&mut self) -> Lazy { + let data = hygiene::HygieneData::safe_with(|data| data.to_map()); + self.lazy(&data) + } + fn encode_crate_root(&mut self) -> Lazy { let mut i = self.position(); @@ -397,6 +429,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let index = items.write_index(&mut self.opaque.cursor); let index_bytes = self.position() - i; + // Encode hygiene data + i = self.position(); + let hygiene_data = self.encode_hygiene_data(); + let hygiene_data_bytes = self.position() - i; + self.already_encoded_hygiene_data = true; + let tcx = self.tcx; let link_meta = self.link_meta; let is_proc_macro = tcx.sess.crate_types.borrow().contains(&CrateTypeProcMacro); @@ -421,7 +459,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } else { None }, - crate_deps, dylib_dependency_formats, lang_items, @@ -432,6 +469,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { impls, exported_symbols, index, + hygiene_data, }); let total_bytes = self.position(); @@ -459,6 +497,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { println!(" def-path table bytes: {}", def_path_table_bytes); println!(" item bytes: {}", item_bytes); println!(" index bytes: {}", index_bytes); + println!(" hygiene data bytes: {}", hygiene_data_bytes); println!(" zero bytes: {}", zero_bytes); println!(" total bytes: {}", total_bytes); } @@ -1678,6 +1717,7 @@ pub fn encode_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, predicate_shorthands: Default::default(), metadata_hashes: EncodedMetadataHashes::new(), compute_ich, + already_encoded_hygiene_data: false, }; // Encode the rustc version string in a predictable location. diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs index dad0d26d2715d..26e90913d0a9e 100644 --- a/src/librustc_metadata/schema.rs +++ b/src/librustc_metadata/schema.rs @@ -24,7 +24,7 @@ use rustc_back::PanicStrategy; use rustc_serialize as serialize; use syntax::{ast, attr}; use syntax::symbol::Symbol; -use syntax_pos::{self, Span}; +use syntax_pos::{self, hygiene, Span}; use std::marker::PhantomData; use std::mem; @@ -208,6 +208,7 @@ pub struct CrateRoot { pub impls: LazySeq, pub exported_symbols: LazySeq, pub index: LazySeq, + pub hygiene_data: Lazy, } #[derive(RustcEncodable, RustcDecodable)] diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 4790fa0a7edc2..b8a4eaaef4977 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -18,16 +18,16 @@ use Span; use symbol::{Ident, Symbol}; -use serialize::{Encodable, Decodable, Encoder, Decoder}; +use serialize::{Decoder, Encoder, UseSpecializedDecodable, UseSpecializedEncodable}; use std::cell::RefCell; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet, VecDeque}; use std::fmt; /// A SyntaxContext represents a chain of macro expansions (represented by marks). #[derive(Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord, Hash)] pub struct SyntaxContext(pub(super) u32); -#[derive(Copy, Clone, Default)] +#[derive(Copy, Clone, Default, RustcEncodable, RustcDecodable)] pub struct SyntaxContextData { pub outer_mark: Mark, pub prev_ctxt: SyntaxContext, @@ -35,10 +35,10 @@ pub struct SyntaxContextData { } /// A mark is a unique id associated with a macro expansion. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] pub struct Mark(u32); -#[derive(Default)] +#[derive(Clone, Default, RustcEncodable, RustcDecodable)] struct MarkData { parent: Mark, modern: bool, @@ -66,6 +66,14 @@ impl Mark { Mark(raw) } + pub fn translate(&self, offset: u32) -> Mark { + if self.0 != 0 { + Mark(self.0 + offset) + } else { + Mark(self.0) + } + } + pub fn expn_info(self) -> Option { HygieneData::with(|data| data.marks[self.0 as usize].expn_info.clone()) } @@ -106,11 +114,24 @@ impl Mark { } } -struct HygieneData { +pub struct HygieneData { marks: Vec, syntax_contexts: Vec, markings: HashMap<(SyntaxContext, Mark), SyntaxContext>, gensym_to_ctxt: HashMap, + used_marks: Vec, + used_syntax_contexts: Vec, +} + +#[derive(RustcEncodable, RustcDecodable)] +pub struct HygieneDataMap { + marks: HashMap, + syntax_contexts: HashMap, + gensym_to_ctxt: HashMap, +} + +thread_local! { + static HYGIENE_DATA: RefCell = RefCell::new(HygieneData::new()); } impl HygieneData { @@ -120,22 +141,229 @@ impl HygieneData { syntax_contexts: vec![SyntaxContextData::default()], markings: HashMap::new(), gensym_to_ctxt: HashMap::new(), + used_marks: Vec::new(), + used_syntax_contexts: Vec::new(), } } fn with T>(f: F) -> T { - thread_local! { - static HYGIENE_DATA: RefCell = RefCell::new(HygieneData::new()); - } HYGIENE_DATA.with(|data| f(&mut *data.borrow_mut())) } + + pub fn safe_with T>(f: F) -> T { + HYGIENE_DATA.with(|data| f(&*data.borrow())) + } + + pub fn to_map(&self) -> HygieneDataMap { + let mut marks = HashMap::new(); + let mut syntax_contexts = HashMap::new(); + + let mut mark_queue: VecDeque<_> = self.used_marks.iter().cloned().collect(); + let mut ctxt_queue: VecDeque<_> = self.used_syntax_contexts.iter().cloned().collect(); + ctxt_queue.extend(self.gensym_to_ctxt.values()); + let gensym_to_ctxt = self.gensym_to_ctxt.clone(); + + let mut visited_marks = HashSet::new(); + let mut visited_ctxts = HashSet::new(); + + while !(mark_queue.is_empty() && ctxt_queue.is_empty()) { + let next_mark = mark_queue.pop_front().and_then(|mark| + // skip default mark and already visited marks + if visited_marks.contains(&mark) || mark.0 == 0 { + None + } else { + visited_marks.insert(mark); + Some(mark) + }); + let next_ctxt = ctxt_queue.pop_front().and_then(|ctxt| + // skip default context and already visited contexts + if visited_ctxts.contains(&ctxt) || ctxt.0 == 0 { + None + } else { + visited_ctxts.insert(ctxt); + Some(ctxt) + }); + + if let Some(mark) = next_mark { + let data = &self.marks[mark.0 as usize]; + + mark_queue.push_back(data.parent); + if let Some(ref info) = data.expn_info { + ctxt_queue.push_back(info.call_site.ctxt()); + + if let Some(span) = info.callee.span { + ctxt_queue.push_back(span.ctxt()); + } + } + + marks.insert(mark, data.clone()); + } + + if let Some(ctxt) = next_ctxt { + let data = self.syntax_contexts[ctxt.0 as usize]; + + mark_queue.push_back(data.outer_mark); + ctxt_queue.push_back(data.prev_ctxt); + ctxt_queue.push_back(data.modern); + + syntax_contexts.insert(ctxt, data); + } + } + + HygieneDataMap { + marks, + syntax_contexts, + gensym_to_ctxt, + } + } } pub fn clear_markings() { HygieneData::with(|data| data.markings = HashMap::new()); } +fn register_mark_use(mark: Mark) { + HygieneData::with(|data| if !data.used_marks.contains(&mark) { + data.used_marks.push(mark); + }); +} + +fn register_syntax_context_use(ctxt: SyntaxContext) { + HygieneData::with(|data| if !data.used_syntax_contexts.contains(&ctxt) { + data.used_syntax_contexts.push(ctxt) + }); +} + +/// Holds information about a HygieneData imported from another crate. +/// See `imported_hygiene_data()` in `rustc_metadata` for more information. +#[derive(Default)] +pub struct ImportedHygieneData { + /// Map an external crate's syntax contexts to the current crate's. + ctxt_map: HashMap, + /// Map an external crate's marks to the current crate's. + mark_map: HashMap, +} + +impl ImportedHygieneData { + fn insert_ctxt(&mut self, external: SyntaxContext, target: SyntaxContext) { + assert!(!self.ctxt_map.contains_key(&external)); + self.ctxt_map.insert(external, target); + } + + fn insert_mark(&mut self, external: Mark, target: Mark) { + assert!(!self.mark_map.contains_key(&external)); + self.mark_map.insert(external, target); + } + + pub fn translate_ctxt(&self, external: SyntaxContext) -> SyntaxContext { + if external.0 != 0 { + self.ctxt_map[&external] + } else { + external + } + } + + pub fn translate_mark(&self, external: Mark) -> Mark { + if external.0 != 0 { + self.mark_map[&external] + } else { + external + } + } + + pub fn translate_span(&self, external: Span) -> Span { + Span::new(external.lo(), external.hi(), self.translate_ctxt(external.ctxt())) + } + + fn translate_mark_data(&self, data: MarkData) -> MarkData { + MarkData { + parent: self.translate_mark(data.parent), + modern: data.modern, + expn_info: data.expn_info.as_ref().map(|info| { + ExpnInfo { + call_site: self.translate_span(info.call_site), + callee: NameAndSpan { + format: info.callee.format.clone(), + allow_internal_unstable: info.callee.allow_internal_unstable, + allow_internal_unsafe: info.callee.allow_internal_unsafe, + span: info.callee.span.map(|span| self.translate_span(span)), + }, + } + }), + } + } + + fn translate_ctxt_data(&self, data: SyntaxContextData) -> SyntaxContextData { + SyntaxContextData { + outer_mark: self.translate_mark(data.outer_mark), + prev_ctxt: self.translate_ctxt(data.prev_ctxt), + modern: self.translate_ctxt(data.modern), + } + } +} + +pub fn extend_hygiene_data(extend_with: HygieneDataMap) -> ImportedHygieneData { + HygieneData::with(move |data| { + let mut imported_map = ImportedHygieneData::default(); + let mark_offset = data.marks.len() as u32; + let ctxt_offset = data.syntax_contexts.len() as u32; + + let HygieneDataMap { + mut marks, + mut syntax_contexts, + mut gensym_to_ctxt, + } = extend_with; + + let marks: Vec<_> = marks + .drain() + .enumerate() + .map(|(index_offset, (mark, data))| { + let index_offset = index_offset as u32; + imported_map.insert_mark(mark, Mark(mark_offset + index_offset)); + data + }) + .collect(); + + let syntax_contexts: Vec<_> = syntax_contexts + .drain() + .enumerate() + .map(|(index_offset, (ctxt, data))| { + let index_offset = index_offset as u32; + imported_map.insert_ctxt(ctxt, SyntaxContext(ctxt_offset + index_offset)); + data + }) + .collect(); + + for mark in marks { + data.marks.push(imported_map.translate_mark_data(mark)); + } + + for ctxt in syntax_contexts { + data.syntax_contexts.push(imported_map.translate_ctxt_data(ctxt)); + } + + data.gensym_to_ctxt + .extend(gensym_to_ctxt + .drain() + .map(|(symbol, ctxt)| (symbol, imported_map.translate_ctxt(ctxt)))); + + imported_map + }) +} + impl SyntaxContext { + pub fn from_u32(raw: u32) -> SyntaxContext { + SyntaxContext(raw) + } + + pub fn translate(&self, offset: u32) -> SyntaxContext { + if self.0 != 0 { + SyntaxContext(self.0 + offset) + } else { + SyntaxContext(self.0) + } + } + pub const fn empty() -> Self { SyntaxContext(0) } @@ -286,7 +514,7 @@ impl fmt::Debug for SyntaxContext { } /// Extra information for tracking spans of macro and syntax sugar expansion -#[derive(Clone, Hash, Debug)] +#[derive(Clone, Hash, Debug, RustcEncodable, RustcDecodable)] pub struct ExpnInfo { /// The location of the actual macro invocation or syntax sugar , e.g. /// `let x = foo!();` or `if let Some(y) = x {}` @@ -302,7 +530,7 @@ pub struct ExpnInfo { pub callee: NameAndSpan } -#[derive(Clone, Hash, Debug)] +#[derive(Clone, Hash, Debug, RustcEncodable, RustcDecodable)] pub struct NameAndSpan { /// The format with which the macro was invoked. pub format: ExpnFormat, @@ -330,7 +558,7 @@ impl NameAndSpan { } /// The source of expansion. -#[derive(Clone, Hash, Debug, PartialEq, Eq)] +#[derive(Clone, Hash, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)] pub enum ExpnFormat { /// e.g. #[derive(...)] MacroAttribute(Symbol), @@ -341,7 +569,7 @@ pub enum ExpnFormat { } /// The kind of compiler desugaring. -#[derive(Clone, Hash, Debug, PartialEq, Eq)] +#[derive(Clone, Hash, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)] pub enum CompilerDesugaringKind { BackArrow, DotFill, @@ -360,15 +588,29 @@ impl CompilerDesugaringKind { } } -impl Encodable for SyntaxContext { - fn encode(&self, _: &mut E) -> Result<(), E::Error> { - Ok(()) // FIXME(jseyfried) intercrate hygiene +impl UseSpecializedDecodable for SyntaxContext { + fn default_decode(d: &mut D) -> Result { + d.read_u32().map(|u| SyntaxContext(u)) + } +} + +impl UseSpecializedEncodable for SyntaxContext { + fn default_encode(&self, s: &mut S) -> Result<(), S::Error> { + register_syntax_context_use(*self); + s.emit_u32(self.0) + } +} + +impl UseSpecializedDecodable for Mark { + fn default_decode(d: &mut D) -> Result { + d.read_u32().map(Mark::from_u32) } } -impl Decodable for SyntaxContext { - fn decode(_: &mut D) -> Result { - Ok(SyntaxContext::empty()) // FIXME(jseyfried) intercrate hygiene +impl UseSpecializedEncodable for Mark { + fn default_encode(&self, s: &mut S) -> Result<(), S::Error> { + register_mark_use(*self); + s.emit_u32(self.0) } } diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index 2000db9703cf9..9f4172037892d 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -316,13 +316,17 @@ impl Default for Span { impl serialize::UseSpecializedEncodable for Span { fn default_encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_struct("Span", 2, |s| { + s.emit_struct("Span", 3, |s| { s.emit_struct_field("lo", 0, |s| { self.lo().encode(s) })?; s.emit_struct_field("hi", 1, |s| { self.hi().encode(s) + })?; + + s.emit_struct_field("ctxt", 2, |s| { + self.ctxt().encode(s) }) }) } diff --git a/src/test/compile-fail-fulldeps/auxiliary/macro_spans_lib.rs b/src/test/compile-fail-fulldeps/auxiliary/macro_spans_lib.rs new file mode 100644 index 0000000000000..eeba255923407 --- /dev/null +++ b/src/test/compile-fail-fulldeps/auxiliary/macro_spans_lib.rs @@ -0,0 +1,26 @@ +// Copyright 2017 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_export] +macro_rules! abc { + ($name:ident) => { + mod $name { + macro_rules! $name { + ($name2:ident) => { + struct $name { + $name2: $name, + } + } + } + + $name!(some_field); + } + } +} diff --git a/src/test/compile-fail-fulldeps/macro-spans.rs b/src/test/compile-fail-fulldeps/macro-spans.rs new file mode 100644 index 0000000000000..3a9994c7505af --- /dev/null +++ b/src/test/compile-fail-fulldeps/macro-spans.rs @@ -0,0 +1,18 @@ +// Copyright 2017 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:macro_spans_lib.rs + +#[macro_use] +extern crate macro_spans_lib; + +abc!(Name); //~ ERROR recursive type `Name::Name` has infinite size + +fn main() { }