Skip to content

Add expansion info to crate metadata #43847

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 13 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/librustc_metadata/creader.rs
Original file line number Diff line number Diff line change
@@ -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
5 changes: 4 additions & 1 deletion src/librustc_metadata/cstore.rs
Original file line number Diff line number Diff line change
@@ -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<Vec<(ast::Name, Rc<SyntaxExtension>)>>,
// Foreign items imported from a dylib (Windows only)
pub dllimport_foreign_items: FxHashSet<DefIndex>,

pub hygiene_data_import_info: RefCell<Option<hygiene::ImportedHygieneData>>,
pub hygiene_data_being_decoded: Cell<bool>,
}

pub struct CStore {
56 changes: 53 additions & 3 deletions src/librustc_metadata/decoder.rs
Original file line number Diff line number Diff line change
@@ -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<CrateNum> for DecodeContext<'a, 'tcx> {
}
}

impl<'a, 'tcx> SpecializedDecoder<hygiene::Mark> for DecodeContext<'a, 'tcx> {
fn specialized_decode(&mut self) -> Result<hygiene::Mark, Self::Error> {
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<SyntaxContext> for DecodeContext<'a, 'tcx> {
fn specialized_decode(&mut self) -> Result<SyntaxContext, Self::Error> {
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<Span> for DecodeContext<'a, 'tcx> {
fn specialized_decode(&mut self) -> Result<Span, Self::Error> {
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<Span> 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())
}
}
46 changes: 43 additions & 3 deletions src/librustc_metadata/encoder.rs
Original file line number Diff line number Diff line change
@@ -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<ty::GenericPredicates<'tcx>> for EncodeContext
}
}

impl<'a, 'tcx> SpecializedEncoder<hygiene::SyntaxContext> 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<hygiene::Mark> 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<hygiene::HygieneDataMap> {
let data = hygiene::HygieneData::safe_with(|data| data.to_map());
self.lazy(&data)
}

fn encode_crate_root(&mut self) -> Lazy<CrateRoot> {
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.
3 changes: 2 additions & 1 deletion src/librustc_metadata/schema.rs
Original file line number Diff line number Diff line change
@@ -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<TraitImpls>,
pub exported_symbols: LazySeq<DefIndex>,
pub index: LazySeq<index::Index>,
pub hygiene_data: Lazy<hygiene::HygieneDataMap>,
}

#[derive(RustcEncodable, RustcDecodable)]
280 changes: 261 additions & 19 deletions src/libsyntax_pos/hygiene.rs
Original file line number Diff line number Diff line change
@@ -18,27 +18,27 @@
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,
pub modern: SyntaxContext,
}

/// 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<ExpnInfo> {
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<MarkData>,
syntax_contexts: Vec<SyntaxContextData>,
markings: HashMap<(SyntaxContext, Mark), SyntaxContext>,
gensym_to_ctxt: HashMap<Symbol, SyntaxContext>,
used_marks: Vec<Mark>,
used_syntax_contexts: Vec<SyntaxContext>,
}

#[derive(RustcEncodable, RustcDecodable)]
pub struct HygieneDataMap {
marks: HashMap<Mark, MarkData>,
syntax_contexts: HashMap<SyntaxContext, SyntaxContextData>,
gensym_to_ctxt: HashMap<Symbol, SyntaxContext>,
}

thread_local! {
static HYGIENE_DATA: RefCell<HygieneData> = 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: FnOnce(&mut HygieneData) -> T>(f: F) -> T {
thread_local! {
static HYGIENE_DATA: RefCell<HygieneData> = RefCell::new(HygieneData::new());
}
HYGIENE_DATA.with(|data| f(&mut *data.borrow_mut()))
}

pub fn safe_with<T, F: FnOnce(&HygieneData) -> 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<SyntaxContext, SyntaxContext>,
/// Map an external crate's marks to the current crate's.
mark_map: HashMap<Mark, Mark>,
}

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(...)] <item>
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<E: Encoder>(&self, _: &mut E) -> Result<(), E::Error> {
Ok(()) // FIXME(jseyfried) intercrate hygiene
impl UseSpecializedDecodable for SyntaxContext {
fn default_decode<D: Decoder>(d: &mut D) -> Result<SyntaxContext, D::Error> {
d.read_u32().map(|u| SyntaxContext(u))
}
}

impl UseSpecializedEncodable for SyntaxContext {
fn default_encode<S: Encoder>(&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: Decoder>(d: &mut D) -> Result<Mark, D::Error> {
d.read_u32().map(Mark::from_u32)
}
}

impl Decodable for SyntaxContext {
fn decode<D: Decoder>(_: &mut D) -> Result<SyntaxContext, D::Error> {
Ok(SyntaxContext::empty()) // FIXME(jseyfried) intercrate hygiene
impl UseSpecializedEncodable for Mark {
fn default_encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
register_mark_use(*self);
s.emit_u32(self.0)
}
}

6 changes: 5 additions & 1 deletion src/libsyntax_pos/lib.rs
Original file line number Diff line number Diff line change
@@ -316,13 +316,17 @@ impl Default for Span {

impl serialize::UseSpecializedEncodable for Span {
fn default_encode<S: Encoder>(&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)
})
})
}
26 changes: 26 additions & 0 deletions src/test/compile-fail-fulldeps/auxiliary/macro_spans_lib.rs
Original file line number Diff line number Diff line change
@@ -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 <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.

#[macro_export]
macro_rules! abc {
($name:ident) => {
mod $name {
macro_rules! $name {
($name2:ident) => {
struct $name {
$name2: $name,
}
}
}

$name!(some_field);
}
}
}
18 changes: 18 additions & 0 deletions src/test/compile-fail-fulldeps/macro-spans.rs
Original file line number Diff line number Diff line change
@@ -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 <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:macro_spans_lib.rs

#[macro_use]
extern crate macro_spans_lib;

abc!(Name); //~ ERROR recursive type `Name::Name` has infinite size
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test failed.

[00:56:33] ---- [compile-fail] compile-fail-fulldeps/macro-spans.rs stdout ----
[00:56:33] 	
[00:56:33] error: /checkout/src/test/compile-fail-fulldeps/macro-spans.rs:16: expected error not found: recursive type `Name::Name` has infinite size
[00:56:33] 
[00:56:33] error: 0 unexpected errors found, 1 expected errors not found
[00:56:33] status: exit code: 101
[00:56:33] command: "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2/bin/rustc" "/checkout/src/test/compile-fail-fulldeps/macro-spans.rs" "-L" "/checkout/obj/build/x86_64-unknown-linux-gnu/test/compile-fail-fulldeps" "--target=x86_64-unknown-linux-gnu" "--error-format" "json" "-C" "prefer-dynamic" "-o" "/checkout/obj/build/x86_64-unknown-linux-gnu/test/compile-fail-fulldeps/macro-spans.stage2-x86_64-unknown-linux-gnu" "-Crpath" "-O" "-Lnative=/checkout/obj/build/x86_64-unknown-linux-gnu/native/rust-test-helpers" "-L" "/checkout/obj/build/x86_64-unknown-linux-gnu/test/compile-fail-fulldeps/macro-spans.stage2-x86_64-unknown-linux-gnu.compile-fail.libaux" "-A" "unused"
[00:56:33] not found errors (from test file): [
[00:56:33]     Error {
[00:56:33]         line_num: 16,
[00:56:33]         kind: Some(
[00:56:33]             Error
[00:56:33]         ),
[00:56:33]         msg: "recursive type `Name::Name` has infinite size"
[00:56:33]     }
[00:56:33] ]
[00:56:33] 
[00:56:33] thread '[compile-fail] compile-fail-fulldeps/macro-spans.rs' panicked at 'explicit panic', /checkout/src/tools/compiletest/src/runtest.rs:1084:12
[00:56:33] note: Run with `RUST_BACKTRACE=1` for a backtrace.
[00:56:33] 
[00:56:33] 
[00:56:33] failures:
[00:56:33]     [compile-fail] compile-fail-fulldeps/macro-spans.rs
[00:56:33] 
[00:56:33] test result: FAILED. 52 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out


fn main() { }