Auto merge of #51762 - petrochenkov:oh-hi-mark, r=oli-obk
hygiene: Implement transparent marks and use them for call-site hygiene in proc-macros Fixes https://github.com/rust-lang/rust/issues/50050
This commit is contained in:
commit
bfc1ee4968
40 changed files with 549 additions and 169 deletions
|
|
@ -97,7 +97,7 @@ impl Diagnostic {
|
|||
/// 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);
|
||||
|
|
|
|||
|
|
@ -58,8 +58,7 @@ use syntax::parse::{self, token};
|
|||
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 @@ impl TokenStream {
|
|||
/// 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 @@ impl iter::FromIterator<TokenStream> for TokenStream {
|
|||
#[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 @@ pub mod token_stream {
|
|||
// 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 @@ impl 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 @@ impl TokenTree {
|
|||
}
|
||||
|
||||
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 mod __internal {
|
|||
}
|
||||
|
||||
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 @@ pub mod __internal {
|
|||
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 @@ pub mod __internal {
|
|||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -484,12 +484,7 @@ impl Definitions {
|
|||
#[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 @@ impl Definitions {
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use ty::{self, TyCtxt};
|
|||
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 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
|
|||
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 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
|
|||
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) {
|
||||
|
|
|
|||
|
|
@ -708,7 +708,7 @@ macro_rules! define_queries {
|
|||
|
||||
// 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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 @@ impl EmitterWriter {
|
|||
|
||||
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 @@ impl EmitterWriter {
|
|||
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 @@ impl EmitterWriter {
|
|||
}
|
||||
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 @@ impl EmitterWriter {
|
|||
|
||||
// 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 @@ impl EmitterWriter {
|
|||
// 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 @@ impl EmitterWriter {
|
|||
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 @@ impl EmitterWriter {
|
|||
// 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)?;
|
||||
|
|
|
|||
|
|
@ -569,9 +569,11 @@ impl<'a> CrateLoader<'a> {
|
|||
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)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -518,8 +518,11 @@ impl CrateStore for cstore::CStore {
|
|||
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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ use std::u32;
|
|||
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 @@ impl<'a, 'tcx> SpecializedEncoder<DefIndex> for EncodeContext<'a, 'tcx> {
|
|||
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, '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);
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ impl<'a> Resolver<'a> {
|
|||
|
||||
// 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!(),
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ impl<'a, 'b> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b> {
|
|||
// 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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1850,6 +1850,8 @@ impl<'a> Resolver<'a> {
|
|||
} else {
|
||||
ident.span.modern()
|
||||
}
|
||||
} else {
|
||||
ident = ident.modern_and_legacy();
|
||||
}
|
||||
|
||||
// Walk backwards up the ribs in scope.
|
||||
|
|
@ -1981,12 +1983,36 @@ impl<'a> Resolver<'a> {
|
|||
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 @@ impl<'a> Resolver<'a> {
|
|||
// 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 @@ impl<'a> Resolver<'a> {
|
|||
.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 @@ impl<'a> Resolver<'a> {
|
|||
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 @@ impl<'a> Resolver<'a> {
|
|||
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 @@ impl<'a> Resolver<'a> {
|
|||
}
|
||||
|
||||
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 @@ impl<'a> Resolver<'a> {
|
|||
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 @@ impl<'a> Resolver<'a> {
|
|||
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
|
||||
|
|
|
|||
|
|
@ -155,10 +155,9 @@ impl<'a> base::Resolver for Resolver<'a> {
|
|||
}
|
||||
});
|
||||
|
||||
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 @@ impl<'a> base::Resolver for Resolver<'a> {
|
|||
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 @@ impl<'a> base::Resolver for Resolver<'a> {
|
|||
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 @@ impl<'a> Resolver<'a> {
|
|||
/// 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 @@ impl<'a> Resolver<'a> {
|
|||
|
||||
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,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -698,7 +698,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
|
|||
"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 =
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use lint;
|
|||
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 {
|
||||
|
|
|
|||
|
|
@ -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 @@ impl 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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -443,7 +443,7 @@ impl CodeMap {
|
|||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 @@ impl SyntaxExtension {
|
|||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -738,13 +738,13 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
};
|
||||
|
||||
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 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
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 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
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 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
|
|||
// 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") {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use feature_gate::{self, emit_feature_err, Features, GateIssue};
|
|||
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 @@ impl Delimited {
|
|||
|
||||
/// 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 @@ impl Delimited {
|
|||
|
||||
/// 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))
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ use ast::{BinOpKind, UnOp};
|
|||
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 @@ impl<'a> Parser<'a> {
|
|||
|
||||
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 @@ impl<'a> Parser<'a> {
|
|||
} 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 @@ impl<'a> Parser<'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 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
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() {
|
||||
|
|
|
|||
|
|
@ -57,8 +57,8 @@ impl Delimited {
|
|||
|
||||
/// 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 @@ impl Delimited {
|
|||
|
||||
/// 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))
|
||||
};
|
||||
|
|
|
|||
|
|
@ -33,14 +33,17 @@ pub struct SyntaxContext(pub(super) u32);
|
|||
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 @@ impl HygieneData {
|
|||
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 @@ impl SyntaxContext {
|
|||
// 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 @@ impl SyntaxContext {
|
|||
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 @@ impl 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 @@ impl 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: modern,
|
||||
modern: SyntaxContext(len),
|
||||
prev_ctxt,
|
||||
opaque: new_opaque,
|
||||
opaque_and_semitransparent: new_opaque,
|
||||
});
|
||||
SyntaxContext(len)
|
||||
new_opaque
|
||||
});
|
||||
}
|
||||
|
||||
*data.markings.entry((self, mark)).or_insert_with(|| {
|
||||
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,
|
||||
opaque,
|
||||
opaque_and_semitransparent: new_opaque_and_semitransparent,
|
||||
});
|
||||
new_opaque_and_semitransparent
|
||||
});
|
||||
}
|
||||
|
||||
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 @@ impl SyntaxContext {
|
|||
|
||||
#[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]
|
||||
|
|
|
|||
|
|
@ -248,6 +248,13 @@ impl 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 @@ impl 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 @@ impl 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)]
|
||||
|
|
|
|||
|
|
@ -68,6 +68,15 @@ impl 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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
37
src/test/run-pass-fulldeps/proc-macro/auxiliary/call-site.rs
Normal file
37
src/test/run-pass-fulldeps/proc-macro/auxiliary/call-site.rs
Normal file
|
|
@ -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()
|
||||
}
|
||||
23
src/test/run-pass-fulldeps/proc-macro/call-site.rs
Normal file
23
src/test/run-pass-fulldeps/proc-macro/call-site.rs
Normal file
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -19,3 +19,38 @@ pub mod foo {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
16
src/test/ui/hygiene/auxiliary/transparent-basic.rs
Normal file
16
src/test/ui/hygiene/auxiliary/transparent-basic.rs
Normal file
|
|
@ -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;
|
||||
}
|
||||
35
src/test/ui/hygiene/dollar-crate-modern.rs
Normal file
35
src/test/ui/hygiene/dollar-crate-modern.rs
Normal file
|
|
@ -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() {}
|
||||
24
src/test/ui/hygiene/generate-mod.rs
Normal file
24
src/test/ui/hygiene/generate-mod.rs
Normal file
|
|
@ -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!();
|
||||
17
src/test/ui/hygiene/generate-mod.stderr
Normal file
17
src/test/ui/hygiene/generate-mod.stderr
Normal file
|
|
@ -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`.
|
||||
53
src/test/ui/hygiene/transparent-basic.rs
Normal file
53
src/test/ui/hygiene/transparent-basic.rs
Normal file
|
|
@ -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!();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue