
It's internal to resolve and always results in `Res::Err` outside of resolve. Instead put `DefKind::Fn`s themselves into the macro namespace, it's ok. Proc macro stubs are items placed into macro namespase for functions that define proc macros. https://github.com/rust-lang/rust/pull/52383 The rustdoc test is changed because the old test didn't actually reproduce the ICE it was supposed to reproduce.
1010 lines
33 KiB
Rust
1010 lines
33 KiB
Rust
use crate::ast::{self, Attribute, Name, PatKind};
|
|
use crate::attr::{HasAttrs, Stability, Deprecation};
|
|
use crate::source_map::{SourceMap, Spanned, respan};
|
|
use crate::edition::Edition;
|
|
use crate::ext::expand::{self, AstFragment, Invocation};
|
|
use crate::ext::hygiene::{Mark, SyntaxContext, Transparency};
|
|
use crate::mut_visit::{self, MutVisitor};
|
|
use crate::parse::{self, parser, DirectoryOwnership};
|
|
use crate::parse::token;
|
|
use crate::ptr::P;
|
|
use crate::symbol::{kw, sym, Ident, Symbol};
|
|
use crate::{ThinVec, MACRO_ARGUMENTS};
|
|
use crate::tokenstream::{self, TokenStream};
|
|
|
|
use errors::{DiagnosticBuilder, DiagnosticId};
|
|
use smallvec::{smallvec, SmallVec};
|
|
use syntax_pos::{Span, MultiSpan, DUMMY_SP};
|
|
use syntax_pos::hygiene::{ExpnInfo, ExpnFormat};
|
|
|
|
use rustc_data_structures::fx::FxHashMap;
|
|
use rustc_data_structures::sync::{self, Lrc};
|
|
use std::iter;
|
|
use std::path::PathBuf;
|
|
use std::rc::Rc;
|
|
use std::default::Default;
|
|
|
|
|
|
#[derive(Debug,Clone)]
|
|
pub enum Annotatable {
|
|
Item(P<ast::Item>),
|
|
TraitItem(P<ast::TraitItem>),
|
|
ImplItem(P<ast::ImplItem>),
|
|
ForeignItem(P<ast::ForeignItem>),
|
|
Stmt(P<ast::Stmt>),
|
|
Expr(P<ast::Expr>),
|
|
}
|
|
|
|
impl HasAttrs for Annotatable {
|
|
fn attrs(&self) -> &[Attribute] {
|
|
match *self {
|
|
Annotatable::Item(ref item) => &item.attrs,
|
|
Annotatable::TraitItem(ref trait_item) => &trait_item.attrs,
|
|
Annotatable::ImplItem(ref impl_item) => &impl_item.attrs,
|
|
Annotatable::ForeignItem(ref foreign_item) => &foreign_item.attrs,
|
|
Annotatable::Stmt(ref stmt) => stmt.attrs(),
|
|
Annotatable::Expr(ref expr) => &expr.attrs,
|
|
}
|
|
}
|
|
|
|
fn visit_attrs<F: FnOnce(&mut Vec<Attribute>)>(&mut self, f: F) {
|
|
match self {
|
|
Annotatable::Item(item) => item.visit_attrs(f),
|
|
Annotatable::TraitItem(trait_item) => trait_item.visit_attrs(f),
|
|
Annotatable::ImplItem(impl_item) => impl_item.visit_attrs(f),
|
|
Annotatable::ForeignItem(foreign_item) => foreign_item.visit_attrs(f),
|
|
Annotatable::Stmt(stmt) => stmt.visit_attrs(f),
|
|
Annotatable::Expr(expr) => expr.visit_attrs(f),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Annotatable {
|
|
pub fn span(&self) -> Span {
|
|
match *self {
|
|
Annotatable::Item(ref item) => item.span,
|
|
Annotatable::TraitItem(ref trait_item) => trait_item.span,
|
|
Annotatable::ImplItem(ref impl_item) => impl_item.span,
|
|
Annotatable::ForeignItem(ref foreign_item) => foreign_item.span,
|
|
Annotatable::Stmt(ref stmt) => stmt.span,
|
|
Annotatable::Expr(ref expr) => expr.span,
|
|
}
|
|
}
|
|
|
|
pub fn expect_item(self) -> P<ast::Item> {
|
|
match self {
|
|
Annotatable::Item(i) => i,
|
|
_ => panic!("expected Item")
|
|
}
|
|
}
|
|
|
|
pub fn map_item_or<F, G>(self, mut f: F, mut or: G) -> Annotatable
|
|
where F: FnMut(P<ast::Item>) -> P<ast::Item>,
|
|
G: FnMut(Annotatable) -> Annotatable
|
|
{
|
|
match self {
|
|
Annotatable::Item(i) => Annotatable::Item(f(i)),
|
|
_ => or(self)
|
|
}
|
|
}
|
|
|
|
pub fn expect_trait_item(self) -> ast::TraitItem {
|
|
match self {
|
|
Annotatable::TraitItem(i) => i.into_inner(),
|
|
_ => panic!("expected Item")
|
|
}
|
|
}
|
|
|
|
pub fn expect_impl_item(self) -> ast::ImplItem {
|
|
match self {
|
|
Annotatable::ImplItem(i) => i.into_inner(),
|
|
_ => panic!("expected Item")
|
|
}
|
|
}
|
|
|
|
pub fn expect_foreign_item(self) -> ast::ForeignItem {
|
|
match self {
|
|
Annotatable::ForeignItem(i) => i.into_inner(),
|
|
_ => panic!("expected foreign item")
|
|
}
|
|
}
|
|
|
|
pub fn expect_stmt(self) -> ast::Stmt {
|
|
match self {
|
|
Annotatable::Stmt(stmt) => stmt.into_inner(),
|
|
_ => panic!("expected statement"),
|
|
}
|
|
}
|
|
|
|
pub fn expect_expr(self) -> P<ast::Expr> {
|
|
match self {
|
|
Annotatable::Expr(expr) => expr,
|
|
_ => panic!("expected expression"),
|
|
}
|
|
}
|
|
|
|
pub fn derive_allowed(&self) -> bool {
|
|
match *self {
|
|
Annotatable::Item(ref item) => match item.node {
|
|
ast::ItemKind::Struct(..) |
|
|
ast::ItemKind::Enum(..) |
|
|
ast::ItemKind::Union(..) => true,
|
|
_ => false,
|
|
},
|
|
_ => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
// `meta_item` is the annotation, and `item` is the item being modified.
|
|
// FIXME Decorators should follow the same pattern too.
|
|
pub trait MultiItemModifier {
|
|
fn expand(&self,
|
|
ecx: &mut ExtCtxt<'_>,
|
|
span: Span,
|
|
meta_item: &ast::MetaItem,
|
|
item: Annotatable)
|
|
-> Vec<Annotatable>;
|
|
}
|
|
|
|
impl<F, T> MultiItemModifier for F
|
|
where F: Fn(&mut ExtCtxt<'_>, Span, &ast::MetaItem, Annotatable) -> T,
|
|
T: Into<Vec<Annotatable>>,
|
|
{
|
|
fn expand(&self,
|
|
ecx: &mut ExtCtxt<'_>,
|
|
span: Span,
|
|
meta_item: &ast::MetaItem,
|
|
item: Annotatable)
|
|
-> Vec<Annotatable> {
|
|
(*self)(ecx, span, meta_item, item).into()
|
|
}
|
|
}
|
|
|
|
impl Into<Vec<Annotatable>> for Annotatable {
|
|
fn into(self) -> Vec<Annotatable> {
|
|
vec![self]
|
|
}
|
|
}
|
|
|
|
pub trait ProcMacro {
|
|
fn expand<'cx>(&self,
|
|
ecx: &'cx mut ExtCtxt<'_>,
|
|
span: Span,
|
|
ts: TokenStream)
|
|
-> TokenStream;
|
|
}
|
|
|
|
impl<F> ProcMacro for F
|
|
where F: Fn(TokenStream) -> TokenStream
|
|
{
|
|
fn expand<'cx>(&self,
|
|
_ecx: &'cx mut ExtCtxt<'_>,
|
|
_span: Span,
|
|
ts: TokenStream)
|
|
-> TokenStream {
|
|
// FIXME setup implicit context in TLS before calling self.
|
|
(*self)(ts)
|
|
}
|
|
}
|
|
|
|
pub trait AttrProcMacro {
|
|
fn expand<'cx>(&self,
|
|
ecx: &'cx mut ExtCtxt<'_>,
|
|
span: Span,
|
|
annotation: TokenStream,
|
|
annotated: TokenStream)
|
|
-> TokenStream;
|
|
}
|
|
|
|
impl<F> AttrProcMacro for F
|
|
where F: Fn(TokenStream, TokenStream) -> TokenStream
|
|
{
|
|
fn expand<'cx>(&self,
|
|
_ecx: &'cx mut ExtCtxt<'_>,
|
|
_span: Span,
|
|
annotation: TokenStream,
|
|
annotated: TokenStream)
|
|
-> TokenStream {
|
|
// FIXME setup implicit context in TLS before calling self.
|
|
(*self)(annotation, annotated)
|
|
}
|
|
}
|
|
|
|
/// Represents a thing that maps token trees to Macro Results
|
|
pub trait TTMacroExpander {
|
|
fn expand<'cx>(
|
|
&self,
|
|
ecx: &'cx mut ExtCtxt<'_>,
|
|
span: Span,
|
|
input: TokenStream,
|
|
def_span: Option<Span>,
|
|
) -> Box<dyn MacResult+'cx>;
|
|
}
|
|
|
|
pub type MacroExpanderFn =
|
|
for<'cx> fn(&'cx mut ExtCtxt<'_>, Span, &[tokenstream::TokenTree])
|
|
-> Box<dyn MacResult+'cx>;
|
|
|
|
impl<F> TTMacroExpander for F
|
|
where F: for<'cx> Fn(&'cx mut ExtCtxt<'_>, Span, &[tokenstream::TokenTree])
|
|
-> Box<dyn MacResult+'cx>
|
|
{
|
|
fn expand<'cx>(
|
|
&self,
|
|
ecx: &'cx mut ExtCtxt<'_>,
|
|
span: Span,
|
|
input: TokenStream,
|
|
_def_span: Option<Span>,
|
|
) -> Box<dyn MacResult+'cx> {
|
|
struct AvoidInterpolatedIdents;
|
|
|
|
impl MutVisitor for AvoidInterpolatedIdents {
|
|
fn visit_tt(&mut self, tt: &mut tokenstream::TokenTree) {
|
|
if let tokenstream::TokenTree::Token(token) = tt {
|
|
if let token::Interpolated(nt) = &token.kind {
|
|
if let token::NtIdent(ident, is_raw) = **nt {
|
|
*tt = tokenstream::TokenTree::token(
|
|
token::Ident(ident.name, is_raw), ident.span
|
|
);
|
|
}
|
|
}
|
|
}
|
|
mut_visit::noop_visit_tt(tt, self)
|
|
}
|
|
|
|
fn visit_mac(&mut self, mac: &mut ast::Mac) {
|
|
mut_visit::noop_visit_mac(mac, self)
|
|
}
|
|
}
|
|
|
|
let input: Vec<_> =
|
|
input.trees().map(|mut tt| { AvoidInterpolatedIdents.visit_tt(&mut tt); tt }).collect();
|
|
(*self)(ecx, span, &input)
|
|
}
|
|
}
|
|
|
|
// Use a macro because forwarding to a simple function has type system issues
|
|
macro_rules! make_stmts_default {
|
|
($me:expr) => {
|
|
$me.make_expr().map(|e| smallvec![ast::Stmt {
|
|
id: ast::DUMMY_NODE_ID,
|
|
span: e.span,
|
|
node: ast::StmtKind::Expr(e),
|
|
}])
|
|
}
|
|
}
|
|
|
|
/// The result of a macro expansion. The return values of the various
|
|
/// methods are spliced into the AST at the callsite of the macro.
|
|
pub trait MacResult {
|
|
/// Creates an expression.
|
|
fn make_expr(self: Box<Self>) -> Option<P<ast::Expr>> {
|
|
None
|
|
}
|
|
/// Creates zero or more items.
|
|
fn make_items(self: Box<Self>) -> Option<SmallVec<[P<ast::Item>; 1]>> {
|
|
None
|
|
}
|
|
|
|
/// Creates zero or more impl items.
|
|
fn make_impl_items(self: Box<Self>) -> Option<SmallVec<[ast::ImplItem; 1]>> {
|
|
None
|
|
}
|
|
|
|
/// Creates zero or more trait items.
|
|
fn make_trait_items(self: Box<Self>) -> Option<SmallVec<[ast::TraitItem; 1]>> {
|
|
None
|
|
}
|
|
|
|
/// Creates zero or more items in an `extern {}` block
|
|
fn make_foreign_items(self: Box<Self>) -> Option<SmallVec<[ast::ForeignItem; 1]>> { None }
|
|
|
|
/// Creates a pattern.
|
|
fn make_pat(self: Box<Self>) -> Option<P<ast::Pat>> {
|
|
None
|
|
}
|
|
|
|
/// Creates zero or more statements.
|
|
///
|
|
/// By default this attempts to create an expression statement,
|
|
/// returning None if that fails.
|
|
fn make_stmts(self: Box<Self>) -> Option<SmallVec<[ast::Stmt; 1]>> {
|
|
make_stmts_default!(self)
|
|
}
|
|
|
|
fn make_ty(self: Box<Self>) -> Option<P<ast::Ty>> {
|
|
None
|
|
}
|
|
}
|
|
|
|
macro_rules! make_MacEager {
|
|
( $( $fld:ident: $t:ty, )* ) => {
|
|
/// `MacResult` implementation for the common case where you've already
|
|
/// built each form of AST that you might return.
|
|
#[derive(Default)]
|
|
pub struct MacEager {
|
|
$(
|
|
pub $fld: Option<$t>,
|
|
)*
|
|
}
|
|
|
|
impl MacEager {
|
|
$(
|
|
pub fn $fld(v: $t) -> Box<dyn MacResult> {
|
|
Box::new(MacEager {
|
|
$fld: Some(v),
|
|
..Default::default()
|
|
})
|
|
}
|
|
)*
|
|
}
|
|
}
|
|
}
|
|
|
|
make_MacEager! {
|
|
expr: P<ast::Expr>,
|
|
pat: P<ast::Pat>,
|
|
items: SmallVec<[P<ast::Item>; 1]>,
|
|
impl_items: SmallVec<[ast::ImplItem; 1]>,
|
|
trait_items: SmallVec<[ast::TraitItem; 1]>,
|
|
foreign_items: SmallVec<[ast::ForeignItem; 1]>,
|
|
stmts: SmallVec<[ast::Stmt; 1]>,
|
|
ty: P<ast::Ty>,
|
|
}
|
|
|
|
impl MacResult for MacEager {
|
|
fn make_expr(self: Box<Self>) -> Option<P<ast::Expr>> {
|
|
self.expr
|
|
}
|
|
|
|
fn make_items(self: Box<Self>) -> Option<SmallVec<[P<ast::Item>; 1]>> {
|
|
self.items
|
|
}
|
|
|
|
fn make_impl_items(self: Box<Self>) -> Option<SmallVec<[ast::ImplItem; 1]>> {
|
|
self.impl_items
|
|
}
|
|
|
|
fn make_trait_items(self: Box<Self>) -> Option<SmallVec<[ast::TraitItem; 1]>> {
|
|
self.trait_items
|
|
}
|
|
|
|
fn make_foreign_items(self: Box<Self>) -> Option<SmallVec<[ast::ForeignItem; 1]>> {
|
|
self.foreign_items
|
|
}
|
|
|
|
fn make_stmts(self: Box<Self>) -> Option<SmallVec<[ast::Stmt; 1]>> {
|
|
match self.stmts.as_ref().map_or(0, |s| s.len()) {
|
|
0 => make_stmts_default!(self),
|
|
_ => self.stmts,
|
|
}
|
|
}
|
|
|
|
fn make_pat(self: Box<Self>) -> Option<P<ast::Pat>> {
|
|
if let Some(p) = self.pat {
|
|
return Some(p);
|
|
}
|
|
if let Some(e) = self.expr {
|
|
if let ast::ExprKind::Lit(_) = e.node {
|
|
return Some(P(ast::Pat {
|
|
id: ast::DUMMY_NODE_ID,
|
|
span: e.span,
|
|
node: PatKind::Lit(e),
|
|
}));
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
fn make_ty(self: Box<Self>) -> Option<P<ast::Ty>> {
|
|
self.ty
|
|
}
|
|
}
|
|
|
|
/// Fill-in macro expansion result, to allow compilation to continue
|
|
/// after hitting errors.
|
|
#[derive(Copy, Clone)]
|
|
pub struct DummyResult {
|
|
expr_only: bool,
|
|
is_error: bool,
|
|
span: Span,
|
|
}
|
|
|
|
impl DummyResult {
|
|
/// Creates a default MacResult that can be anything.
|
|
///
|
|
/// Use this as a return value after hitting any errors and
|
|
/// calling `span_err`.
|
|
pub fn any(span: Span) -> Box<dyn MacResult+'static> {
|
|
Box::new(DummyResult { expr_only: false, is_error: true, span })
|
|
}
|
|
|
|
/// Same as `any`, but must be a valid fragment, not error.
|
|
pub fn any_valid(span: Span) -> Box<dyn MacResult+'static> {
|
|
Box::new(DummyResult { expr_only: false, is_error: false, span })
|
|
}
|
|
|
|
/// Creates a default MacResult that can only be an expression.
|
|
///
|
|
/// Use this for macros that must expand to an expression, so even
|
|
/// if an error is encountered internally, the user will receive
|
|
/// an error that they also used it in the wrong place.
|
|
pub fn expr(span: Span) -> Box<dyn MacResult+'static> {
|
|
Box::new(DummyResult { expr_only: true, is_error: true, span })
|
|
}
|
|
|
|
/// A plain dummy expression.
|
|
pub fn raw_expr(sp: Span, is_error: bool) -> P<ast::Expr> {
|
|
P(ast::Expr {
|
|
id: ast::DUMMY_NODE_ID,
|
|
node: if is_error { ast::ExprKind::Err } else { ast::ExprKind::Tup(Vec::new()) },
|
|
span: sp,
|
|
attrs: ThinVec::new(),
|
|
})
|
|
}
|
|
|
|
/// A plain dummy pattern.
|
|
pub fn raw_pat(sp: Span) -> ast::Pat {
|
|
ast::Pat {
|
|
id: ast::DUMMY_NODE_ID,
|
|
node: PatKind::Wild,
|
|
span: sp,
|
|
}
|
|
}
|
|
|
|
/// A plain dummy type.
|
|
pub fn raw_ty(sp: Span, is_error: bool) -> P<ast::Ty> {
|
|
P(ast::Ty {
|
|
id: ast::DUMMY_NODE_ID,
|
|
node: if is_error { ast::TyKind::Err } else { ast::TyKind::Tup(Vec::new()) },
|
|
span: sp
|
|
})
|
|
}
|
|
}
|
|
|
|
impl MacResult for DummyResult {
|
|
fn make_expr(self: Box<DummyResult>) -> Option<P<ast::Expr>> {
|
|
Some(DummyResult::raw_expr(self.span, self.is_error))
|
|
}
|
|
|
|
fn make_pat(self: Box<DummyResult>) -> Option<P<ast::Pat>> {
|
|
Some(P(DummyResult::raw_pat(self.span)))
|
|
}
|
|
|
|
fn make_items(self: Box<DummyResult>) -> Option<SmallVec<[P<ast::Item>; 1]>> {
|
|
// this code needs a comment... why not always just return the Some() ?
|
|
if self.expr_only {
|
|
None
|
|
} else {
|
|
Some(SmallVec::new())
|
|
}
|
|
}
|
|
|
|
fn make_impl_items(self: Box<DummyResult>) -> Option<SmallVec<[ast::ImplItem; 1]>> {
|
|
if self.expr_only {
|
|
None
|
|
} else {
|
|
Some(SmallVec::new())
|
|
}
|
|
}
|
|
|
|
fn make_trait_items(self: Box<DummyResult>) -> Option<SmallVec<[ast::TraitItem; 1]>> {
|
|
if self.expr_only {
|
|
None
|
|
} else {
|
|
Some(SmallVec::new())
|
|
}
|
|
}
|
|
|
|
fn make_foreign_items(self: Box<Self>) -> Option<SmallVec<[ast::ForeignItem; 1]>> {
|
|
if self.expr_only {
|
|
None
|
|
} else {
|
|
Some(SmallVec::new())
|
|
}
|
|
}
|
|
|
|
fn make_stmts(self: Box<DummyResult>) -> Option<SmallVec<[ast::Stmt; 1]>> {
|
|
Some(smallvec![ast::Stmt {
|
|
id: ast::DUMMY_NODE_ID,
|
|
node: ast::StmtKind::Expr(DummyResult::raw_expr(self.span, self.is_error)),
|
|
span: self.span,
|
|
}])
|
|
}
|
|
|
|
fn make_ty(self: Box<DummyResult>) -> Option<P<ast::Ty>> {
|
|
Some(DummyResult::raw_ty(self.span, self.is_error))
|
|
}
|
|
}
|
|
|
|
/// Represents different kinds of macro invocations that can be resolved.
|
|
#[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
|
|
pub enum MacroKind {
|
|
/// A bang macro - foo!()
|
|
Bang,
|
|
/// An attribute macro - #[foo]
|
|
Attr,
|
|
/// A derive attribute macro - #[derive(Foo)]
|
|
Derive,
|
|
}
|
|
|
|
impl MacroKind {
|
|
pub fn descr(self) -> &'static str {
|
|
match self {
|
|
MacroKind::Bang => "macro",
|
|
MacroKind::Attr => "attribute macro",
|
|
MacroKind::Derive => "derive macro",
|
|
}
|
|
}
|
|
|
|
pub fn article(self) -> &'static str {
|
|
match self {
|
|
MacroKind::Attr => "an",
|
|
_ => "a",
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A syntax extension kind.
|
|
pub enum SyntaxExtensionKind {
|
|
/// A token-based function-like macro.
|
|
Bang(
|
|
/// An expander with signature TokenStream -> TokenStream.
|
|
Box<dyn ProcMacro + sync::Sync + sync::Send>,
|
|
),
|
|
|
|
/// An AST-based function-like macro.
|
|
LegacyBang(
|
|
/// An expander with signature TokenStream -> AST.
|
|
Box<dyn TTMacroExpander + sync::Sync + sync::Send>,
|
|
),
|
|
|
|
/// A token-based attribute macro.
|
|
Attr(
|
|
/// An expander with signature (TokenStream, TokenStream) -> TokenStream.
|
|
/// The first TokenSteam is the attribute itself, the second is the annotated item.
|
|
/// The produced TokenSteam replaces the input TokenSteam.
|
|
Box<dyn AttrProcMacro + sync::Sync + sync::Send>,
|
|
),
|
|
|
|
/// An AST-based attribute macro.
|
|
LegacyAttr(
|
|
/// An expander with signature (AST, AST) -> AST.
|
|
/// The first AST fragment is the attribute itself, the second is the annotated item.
|
|
/// The produced AST fragment replaces the input AST fragment.
|
|
Box<dyn MultiItemModifier + sync::Sync + sync::Send>,
|
|
),
|
|
|
|
/// A trivial attribute "macro" that does nothing,
|
|
/// only keeps the attribute and marks it as inert,
|
|
/// thus making it ineligible for further expansion.
|
|
NonMacroAttr {
|
|
/// Suppresses the `unused_attributes` lint for this attribute.
|
|
mark_used: bool,
|
|
},
|
|
|
|
/// A token-based derive macro.
|
|
Derive(
|
|
/// An expander with signature TokenStream -> TokenStream (not yet).
|
|
/// The produced TokenSteam is appended to the input TokenSteam.
|
|
Box<dyn MultiItemModifier + sync::Sync + sync::Send>,
|
|
),
|
|
|
|
/// An AST-based derive macro.
|
|
LegacyDerive(
|
|
/// An expander with signature AST -> AST.
|
|
/// The produced AST fragment is appended to the input AST fragment.
|
|
Box<dyn MultiItemModifier + sync::Sync + sync::Send>,
|
|
),
|
|
}
|
|
|
|
/// A struct representing a macro definition in "lowered" form ready for expansion.
|
|
pub struct SyntaxExtension {
|
|
/// A syntax extension kind.
|
|
pub kind: SyntaxExtensionKind,
|
|
/// Span of the macro definition.
|
|
pub span: Span,
|
|
/// Hygienic properties of spans produced by this macro by default.
|
|
pub default_transparency: Transparency,
|
|
/// Whitelist of unstable features that are treated as stable inside this macro.
|
|
pub allow_internal_unstable: Option<Lrc<[Symbol]>>,
|
|
/// Suppresses the `unsafe_code` lint for code produced by this macro.
|
|
pub allow_internal_unsafe: bool,
|
|
/// Enables the macro helper hack (`ident!(...)` -> `$crate::ident!(...)`) for this macro.
|
|
pub local_inner_macros: bool,
|
|
/// The macro's stability info.
|
|
pub stability: Option<Stability>,
|
|
/// The macro's deprecation info.
|
|
pub deprecation: Option<Deprecation>,
|
|
/// Names of helper attributes registered by this macro.
|
|
pub helper_attrs: Vec<Symbol>,
|
|
/// Edition of the crate in which this macro is defined.
|
|
pub edition: Edition,
|
|
}
|
|
|
|
impl SyntaxExtensionKind {
|
|
/// When a syntax extension is constructed,
|
|
/// its transparency can often be inferred from its kind.
|
|
fn default_transparency(&self) -> Transparency {
|
|
match self {
|
|
SyntaxExtensionKind::Bang(..) |
|
|
SyntaxExtensionKind::Attr(..) |
|
|
SyntaxExtensionKind::Derive(..) |
|
|
SyntaxExtensionKind::NonMacroAttr { .. } => Transparency::Opaque,
|
|
SyntaxExtensionKind::LegacyBang(..) |
|
|
SyntaxExtensionKind::LegacyAttr(..) |
|
|
SyntaxExtensionKind::LegacyDerive(..) => Transparency::SemiTransparent,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl SyntaxExtension {
|
|
/// Returns which kind of macro calls this syntax extension.
|
|
pub fn macro_kind(&self) -> MacroKind {
|
|
match self.kind {
|
|
SyntaxExtensionKind::Bang(..) |
|
|
SyntaxExtensionKind::LegacyBang(..) => MacroKind::Bang,
|
|
SyntaxExtensionKind::Attr(..) |
|
|
SyntaxExtensionKind::LegacyAttr(..) |
|
|
SyntaxExtensionKind::NonMacroAttr { .. } => MacroKind::Attr,
|
|
SyntaxExtensionKind::Derive(..) |
|
|
SyntaxExtensionKind::LegacyDerive(..) => MacroKind::Derive,
|
|
}
|
|
}
|
|
|
|
/// Constructs a syntax extension with default properties.
|
|
pub fn default(kind: SyntaxExtensionKind, edition: Edition) -> SyntaxExtension {
|
|
SyntaxExtension {
|
|
span: DUMMY_SP,
|
|
default_transparency: kind.default_transparency(),
|
|
allow_internal_unstable: None,
|
|
allow_internal_unsafe: false,
|
|
local_inner_macros: false,
|
|
stability: None,
|
|
deprecation: None,
|
|
helper_attrs: Vec::new(),
|
|
edition,
|
|
kind,
|
|
}
|
|
}
|
|
|
|
fn expn_format(&self, symbol: Symbol) -> ExpnFormat {
|
|
match self.kind {
|
|
SyntaxExtensionKind::Bang(..) |
|
|
SyntaxExtensionKind::LegacyBang(..) => ExpnFormat::MacroBang(symbol),
|
|
_ => ExpnFormat::MacroAttribute(symbol),
|
|
}
|
|
}
|
|
|
|
pub fn expn_info(&self, call_site: Span, format: &str) -> ExpnInfo {
|
|
ExpnInfo {
|
|
call_site,
|
|
format: self.expn_format(Symbol::intern(format)),
|
|
def_site: Some(self.span),
|
|
default_transparency: self.default_transparency,
|
|
allow_internal_unstable: self.allow_internal_unstable.clone(),
|
|
allow_internal_unsafe: self.allow_internal_unsafe,
|
|
local_inner_macros: self.local_inner_macros,
|
|
edition: self.edition,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub type NamedSyntaxExtension = (Name, SyntaxExtension);
|
|
|
|
pub trait Resolver {
|
|
fn next_node_id(&mut self) -> ast::NodeId;
|
|
|
|
fn get_module_scope(&mut self, id: ast::NodeId) -> Mark;
|
|
|
|
fn resolve_dollar_crates(&mut self);
|
|
fn visit_ast_fragment_with_placeholders(&mut self, mark: Mark, fragment: &AstFragment,
|
|
derives: &[Mark]);
|
|
fn add_builtin(&mut self, ident: ast::Ident, ext: Lrc<SyntaxExtension>);
|
|
|
|
fn resolve_imports(&mut self);
|
|
|
|
fn resolve_macro_invocation(&mut self, invoc: &Invocation, invoc_id: Mark, force: bool)
|
|
-> Result<Option<Lrc<SyntaxExtension>>, Determinacy>;
|
|
fn resolve_macro_path(&mut self, path: &ast::Path, kind: MacroKind, invoc_id: Mark,
|
|
derives_in_scope: Vec<ast::Path>, force: bool)
|
|
-> Result<Lrc<SyntaxExtension>, Determinacy>;
|
|
|
|
fn check_unused_macros(&self);
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
|
pub enum Determinacy {
|
|
Determined,
|
|
Undetermined,
|
|
}
|
|
|
|
impl Determinacy {
|
|
pub fn determined(determined: bool) -> Determinacy {
|
|
if determined { Determinacy::Determined } else { Determinacy::Undetermined }
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct ModuleData {
|
|
pub mod_path: Vec<ast::Ident>,
|
|
pub directory: PathBuf,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct ExpansionData {
|
|
pub mark: Mark,
|
|
pub depth: usize,
|
|
pub module: Rc<ModuleData>,
|
|
pub directory_ownership: DirectoryOwnership,
|
|
}
|
|
|
|
/// One of these is made during expansion and incrementally updated as we go;
|
|
/// when a macro expansion occurs, the resulting nodes have the `backtrace()
|
|
/// -> expn_info` of their expansion context stored into their span.
|
|
pub struct ExtCtxt<'a> {
|
|
pub parse_sess: &'a parse::ParseSess,
|
|
pub ecfg: expand::ExpansionConfig<'a>,
|
|
pub root_path: PathBuf,
|
|
pub resolver: &'a mut dyn Resolver,
|
|
pub current_expansion: ExpansionData,
|
|
pub expansions: FxHashMap<Span, Vec<String>>,
|
|
}
|
|
|
|
impl<'a> ExtCtxt<'a> {
|
|
pub fn new(parse_sess: &'a parse::ParseSess,
|
|
ecfg: expand::ExpansionConfig<'a>,
|
|
resolver: &'a mut dyn Resolver)
|
|
-> ExtCtxt<'a> {
|
|
ExtCtxt {
|
|
parse_sess,
|
|
ecfg,
|
|
root_path: PathBuf::new(),
|
|
resolver,
|
|
current_expansion: ExpansionData {
|
|
mark: Mark::root(),
|
|
depth: 0,
|
|
module: Rc::new(ModuleData { mod_path: Vec::new(), directory: PathBuf::new() }),
|
|
directory_ownership: DirectoryOwnership::Owned { relative: None },
|
|
},
|
|
expansions: FxHashMap::default(),
|
|
}
|
|
}
|
|
|
|
/// Returns a `Folder` for deeply expanding all macros in an AST node.
|
|
pub fn expander<'b>(&'b mut self) -> expand::MacroExpander<'b, 'a> {
|
|
expand::MacroExpander::new(self, false)
|
|
}
|
|
|
|
/// Returns a `Folder` that deeply expands all macros and assigns all `NodeId`s in an AST node.
|
|
/// Once `NodeId`s are assigned, the node may not be expanded, removed, or otherwise modified.
|
|
pub fn monotonic_expander<'b>(&'b mut self) -> expand::MacroExpander<'b, 'a> {
|
|
expand::MacroExpander::new(self, true)
|
|
}
|
|
|
|
pub fn new_parser_from_tts(&self, tts: &[tokenstream::TokenTree]) -> parser::Parser<'a> {
|
|
parse::stream_to_parser(self.parse_sess, tts.iter().cloned().collect(), MACRO_ARGUMENTS)
|
|
}
|
|
pub fn source_map(&self) -> &'a SourceMap { self.parse_sess.source_map() }
|
|
pub fn parse_sess(&self) -> &'a parse::ParseSess { self.parse_sess }
|
|
pub fn cfg(&self) -> &ast::CrateConfig { &self.parse_sess.config }
|
|
pub fn call_site(&self) -> Span {
|
|
match self.current_expansion.mark.expn_info() {
|
|
Some(expn_info) => expn_info.call_site,
|
|
None => DUMMY_SP,
|
|
}
|
|
}
|
|
pub fn backtrace(&self) -> SyntaxContext {
|
|
SyntaxContext::empty().apply_mark(self.current_expansion.mark)
|
|
}
|
|
|
|
/// Returns span for the macro which originally caused the current expansion to happen.
|
|
///
|
|
/// Stops backtracing at include! boundary.
|
|
pub fn expansion_cause(&self) -> Option<Span> {
|
|
let mut ctxt = self.backtrace();
|
|
let mut last_macro = None;
|
|
loop {
|
|
if ctxt.outer_expn_info().map_or(None, |info| {
|
|
if info.format.name() == sym::include {
|
|
// Stop going up the backtrace once include! is encountered
|
|
return None;
|
|
}
|
|
ctxt = info.call_site.ctxt();
|
|
last_macro = Some(info.call_site);
|
|
Some(())
|
|
}).is_none() {
|
|
break
|
|
}
|
|
}
|
|
last_macro
|
|
}
|
|
|
|
pub fn struct_span_warn<S: Into<MultiSpan>>(&self,
|
|
sp: S,
|
|
msg: &str)
|
|
-> DiagnosticBuilder<'a> {
|
|
self.parse_sess.span_diagnostic.struct_span_warn(sp, msg)
|
|
}
|
|
pub fn struct_span_err<S: Into<MultiSpan>>(&self,
|
|
sp: S,
|
|
msg: &str)
|
|
-> DiagnosticBuilder<'a> {
|
|
self.parse_sess.span_diagnostic.struct_span_err(sp, msg)
|
|
}
|
|
pub fn struct_span_fatal<S: Into<MultiSpan>>(&self,
|
|
sp: S,
|
|
msg: &str)
|
|
-> DiagnosticBuilder<'a> {
|
|
self.parse_sess.span_diagnostic.struct_span_fatal(sp, msg)
|
|
}
|
|
|
|
/// Emit `msg` attached to `sp`, and stop compilation immediately.
|
|
///
|
|
/// `span_err` should be strongly preferred where-ever possible:
|
|
/// this should *only* be used when:
|
|
///
|
|
/// - continuing has a high risk of flow-on errors (e.g., errors in
|
|
/// declaring a macro would cause all uses of that macro to
|
|
/// complain about "undefined macro"), or
|
|
/// - there is literally nothing else that can be done (however,
|
|
/// in most cases one can construct a dummy expression/item to
|
|
/// substitute; we never hit resolve/type-checking so the dummy
|
|
/// value doesn't have to match anything)
|
|
pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
|
|
self.parse_sess.span_diagnostic.span_fatal(sp, msg).raise();
|
|
}
|
|
|
|
/// Emit `msg` attached to `sp`, without immediately stopping
|
|
/// compilation.
|
|
///
|
|
/// Compilation will be stopped in the near future (at the end of
|
|
/// the macro expansion phase).
|
|
pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
|
|
self.parse_sess.span_diagnostic.span_err(sp, msg);
|
|
}
|
|
pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) {
|
|
self.parse_sess.span_diagnostic.span_err_with_code(sp, msg, code);
|
|
}
|
|
pub fn mut_span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str)
|
|
-> DiagnosticBuilder<'a> {
|
|
self.parse_sess.span_diagnostic.mut_span_err(sp, msg)
|
|
}
|
|
pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
|
|
self.parse_sess.span_diagnostic.span_warn(sp, msg);
|
|
}
|
|
pub fn span_unimpl<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
|
|
self.parse_sess.span_diagnostic.span_unimpl(sp, msg);
|
|
}
|
|
pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
|
|
self.parse_sess.span_diagnostic.span_bug(sp, msg);
|
|
}
|
|
pub fn trace_macros_diag(&mut self) {
|
|
for (sp, notes) in self.expansions.iter() {
|
|
let mut db = self.parse_sess.span_diagnostic.span_note_diag(*sp, "trace_macro");
|
|
for note in notes {
|
|
db.note(note);
|
|
}
|
|
db.emit();
|
|
}
|
|
// Fixme: does this result in errors?
|
|
self.expansions.clear();
|
|
}
|
|
pub fn bug(&self, msg: &str) -> ! {
|
|
self.parse_sess.span_diagnostic.bug(msg);
|
|
}
|
|
pub fn trace_macros(&self) -> bool {
|
|
self.ecfg.trace_mac
|
|
}
|
|
pub fn set_trace_macros(&mut self, x: bool) {
|
|
self.ecfg.trace_mac = x
|
|
}
|
|
pub fn ident_of(&self, st: &str) -> ast::Ident {
|
|
ast::Ident::from_str(st)
|
|
}
|
|
pub fn std_path(&self, components: &[Symbol]) -> Vec<ast::Ident> {
|
|
let def_site = DUMMY_SP.apply_mark(self.current_expansion.mark);
|
|
iter::once(Ident::new(kw::DollarCrate, def_site))
|
|
.chain(components.iter().map(|&s| Ident::with_empty_ctxt(s)))
|
|
.collect()
|
|
}
|
|
pub fn name_of(&self, st: &str) -> ast::Name {
|
|
Symbol::intern(st)
|
|
}
|
|
|
|
pub fn check_unused_macros(&self) {
|
|
self.resolver.check_unused_macros();
|
|
}
|
|
}
|
|
|
|
/// Extracts a string literal from the macro expanded version of `expr`,
|
|
/// emitting `err_msg` if `expr` is not a string literal. This does not stop
|
|
/// compilation on error, merely emits a non-fatal error and returns `None`.
|
|
pub fn expr_to_spanned_string<'a>(
|
|
cx: &'a mut ExtCtxt<'_>,
|
|
mut expr: P<ast::Expr>,
|
|
err_msg: &str,
|
|
) -> Result<Spanned<(Symbol, ast::StrStyle)>, Option<DiagnosticBuilder<'a>>> {
|
|
// Update `expr.span`'s ctxt now in case expr is an `include!` macro invocation.
|
|
expr.span = expr.span.apply_mark(cx.current_expansion.mark);
|
|
|
|
// we want to be able to handle e.g., `concat!("foo", "bar")`
|
|
cx.expander().visit_expr(&mut expr);
|
|
Err(match expr.node {
|
|
ast::ExprKind::Lit(ref l) => match l.node {
|
|
ast::LitKind::Str(s, style) => return Ok(respan(expr.span, (s, style))),
|
|
ast::LitKind::Err(_) => None,
|
|
_ => Some(cx.struct_span_err(l.span, err_msg))
|
|
},
|
|
ast::ExprKind::Err => None,
|
|
_ => Some(cx.struct_span_err(expr.span, err_msg))
|
|
})
|
|
}
|
|
|
|
pub fn expr_to_string(cx: &mut ExtCtxt<'_>, expr: P<ast::Expr>, err_msg: &str)
|
|
-> Option<(Symbol, ast::StrStyle)> {
|
|
expr_to_spanned_string(cx, expr, err_msg)
|
|
.map_err(|err| err.map(|mut err| err.emit()))
|
|
.ok()
|
|
.map(|s| s.node)
|
|
}
|
|
|
|
/// Non-fatally assert that `tts` is empty. Note that this function
|
|
/// returns even when `tts` is non-empty, macros that *need* to stop
|
|
/// compilation should call
|
|
/// `cx.parse_sess.span_diagnostic.abort_if_errors()` (this should be
|
|
/// done as rarely as possible).
|
|
pub fn check_zero_tts(cx: &ExtCtxt<'_>,
|
|
sp: Span,
|
|
tts: &[tokenstream::TokenTree],
|
|
name: &str) {
|
|
if !tts.is_empty() {
|
|
cx.span_err(sp, &format!("{} takes no arguments", name));
|
|
}
|
|
}
|
|
|
|
/// Interpreting `tts` as a comma-separated sequence of expressions,
|
|
/// expect exactly one string literal, or emit an error and return `None`.
|
|
pub fn get_single_str_from_tts(cx: &mut ExtCtxt<'_>,
|
|
sp: Span,
|
|
tts: &[tokenstream::TokenTree],
|
|
name: &str)
|
|
-> Option<String> {
|
|
let mut p = cx.new_parser_from_tts(tts);
|
|
if p.token == token::Eof {
|
|
cx.span_err(sp, &format!("{} takes 1 argument", name));
|
|
return None
|
|
}
|
|
let ret = panictry!(p.parse_expr());
|
|
let _ = p.eat(&token::Comma);
|
|
|
|
if p.token != token::Eof {
|
|
cx.span_err(sp, &format!("{} takes 1 argument", name));
|
|
}
|
|
expr_to_string(cx, ret, "argument must be a string literal").map(|(s, _)| {
|
|
s.to_string()
|
|
})
|
|
}
|
|
|
|
/// Extracts comma-separated expressions from `tts`. If there is a
|
|
/// parsing error, emit a non-fatal error and return `None`.
|
|
pub fn get_exprs_from_tts(cx: &mut ExtCtxt<'_>,
|
|
sp: Span,
|
|
tts: &[tokenstream::TokenTree]) -> Option<Vec<P<ast::Expr>>> {
|
|
let mut p = cx.new_parser_from_tts(tts);
|
|
let mut es = Vec::new();
|
|
while p.token != token::Eof {
|
|
let mut expr = panictry!(p.parse_expr());
|
|
cx.expander().visit_expr(&mut expr);
|
|
es.push(expr);
|
|
if p.eat(&token::Comma) {
|
|
continue;
|
|
}
|
|
if p.token != token::Eof {
|
|
cx.span_err(sp, "expected token: `,`");
|
|
return None;
|
|
}
|
|
}
|
|
Some(es)
|
|
}
|