rust/compiler/rustc_expand/src/base.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1465 lines
50 KiB
Rust
Raw Normal View History

#![deny(rustc::untranslatable_diagnostic)]
use crate::errors;
use crate::expand::{self, AstFragment, Invocation};
use crate::module::DirOwnership;
use rustc_ast::attr::MarkedAttrs;
use rustc_ast::mut_visit::DummyAstNode;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Nonterminal};
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::visit::{AssocCtxt, Visitor};
use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind};
use rustc_attr::{self as attr, Deprecation, Stability};
2023-03-06 10:56:23 +00:00
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::sync::{self, Lrc};
use rustc_errors::{Applicability, DiagCtxt, DiagnosticBuilder, ErrorGuaranteed, PResult};
use rustc_feature::Features;
Extend `proc_macro_back_compat` lint to `procedural-masquerade` We now lint on *any* use of `procedural-masquerade` crate. While this crate still exists, its main reverse dependency (`cssparser`) no longer depends on it. Any crates still depending off should stop doing so, as it only exists to support very old Rust versions. If a crate actually needs to support old versions of rustc via `procedural-masquerade`, then they'll just need to accept the warning until we remove it entirely (at the same time as the back-compat hack). The latest version of `procedural-masquerade` does not work with the latest rustc, but trying to check for the version seems like more trouble than it's worth. While working on this, I realized that the `proc-macro-hack` check was never actually doing anything. The corresponding enum variant in `proc-macro-hack` is named `Value` or `Nested` - it has never been called `Input`. Due to a strange Crater issue, the Crater run that tested adding this did *not* end up testing it - some of the crates that would have failed did not actually have their tests checked, making it seem as though the `proc-macro-hack` check was working. The Crater issue is being discussed at https://rust-lang.zulipchat.com/#narrow/stream/242791-t-infra/topic/Nearly.20identical.20Crater.20runs.20processed.20a.20crate.20differently/near/230406661 Despite the `proc-macro-hack` check not actually doing anything, we haven't gotten any reports from users about their build being broken. I went ahead and removed it entirely, since it's clear that no one is being affected by the `proc-macro-hack` regression in practice.
2021-03-15 15:54:25 -04:00
use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT;
2023-03-06 10:56:23 +00:00
use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics, RegisteredTools};
use rustc_parse::{parser, MACRO_ARGUMENTS};
use rustc_session::errors::report_lit_error;
use rustc_session::{parse::ParseSess, Limit, Session};
2021-05-02 21:19:28 +02:00
use rustc_span::def_id::{CrateNum, DefId, LocalDefId};
2020-01-01 19:40:49 +01:00
use rustc_span::edition::Edition;
2021-06-25 20:43:04 +02:00
use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId};
use rustc_span::source_map::SourceMap;
2020-01-01 19:30:57 +01:00
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{BytePos, FileName, Span, DUMMY_SP};
use smallvec::{smallvec, SmallVec};
use std::default::Default;
use std::iter;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use thin_vec::ThinVec;
pub(crate) use rustc_span::hygiene::MacroKind;
// When adding new variants, make sure to
// adjust the `visit_*` / `flat_map_*` calls in `InvocationCollector`
// to use `assign_id!`
2015-01-28 08:34:18 -05:00
#[derive(Debug, Clone)]
pub enum Annotatable {
Item(P<ast::Item>),
TraitItem(P<ast::AssocItem>),
ImplItem(P<ast::AssocItem>),
ForeignItem(P<ast::ForeignItem>),
Stmt(P<ast::Stmt>),
Expr(P<ast::Expr>),
Arm(ast::Arm),
ExprField(ast::ExprField),
PatField(ast::PatField),
GenericParam(ast::GenericParam),
Param(ast::Param),
FieldDef(ast::FieldDef),
Variant(ast::Variant),
Crate(ast::Crate),
}
Implement token-based handling of attributes during expansion This PR modifies the macro expansion infrastructure to handle attributes in a fully token-based manner. As a result: * Derives macros no longer lose spans when their input is modified by eager cfg-expansion. This is accomplished by performing eager cfg-expansion on the token stream that we pass to the derive proc-macro * Inner attributes now preserve spans in all cases, including when we have multiple inner attributes in a row. This is accomplished through the following changes: * New structs `AttrAnnotatedTokenStream` and `AttrAnnotatedTokenTree` are introduced. These are very similar to a normal `TokenTree`, but they also track the position of attributes and attribute targets within the stream. They are built when we collect tokens during parsing. An `AttrAnnotatedTokenStream` is converted to a regular `TokenStream` when we invoke a macro. * Token capturing and `LazyTokenStream` are modified to work with `AttrAnnotatedTokenStream`. A new `ReplaceRange` type is introduced, which is created during the parsing of a nested AST node to make the 'outer' AST node aware of the attributes and attribute target stored deeper in the token stream. * When we need to perform eager cfg-expansion (either due to `#[derive]` or `#[cfg_eval]`), we tokenize and reparse our target, capturing additional information about the locations of `#[cfg]` and `#[cfg_attr]` attributes at any depth within the target. This is a performance optimization, allowing us to perform less work in the typical case where captured tokens never have eager cfg-expansion run.
2020-11-28 18:33:17 -05:00
impl Annotatable {
pub fn span(&self) -> Span {
2022-12-23 17:34:23 +00:00
match self {
Annotatable::Item(item) => item.span,
Annotatable::TraitItem(trait_item) => trait_item.span,
Annotatable::ImplItem(impl_item) => impl_item.span,
Annotatable::ForeignItem(foreign_item) => foreign_item.span,
Annotatable::Stmt(stmt) => stmt.span,
Annotatable::Expr(expr) => expr.span,
Annotatable::Arm(arm) => arm.span,
Annotatable::ExprField(field) => field.span,
Annotatable::PatField(fp) => fp.pat.span,
Annotatable::GenericParam(gp) => gp.ident.span,
Annotatable::Param(p) => p.span,
Annotatable::FieldDef(sf) => sf.span,
Annotatable::Variant(v) => v.span,
Annotatable::Crate(c) => c.spans.inner_span,
}
}
pub fn visit_attrs(&mut self, f: impl FnOnce(&mut AttrVec)) {
match self {
Overhaul `syntax::fold::Folder`. This commit changes `syntax::fold::Folder` from a functional style (where most methods take a `T` and produce a new `T`) to a more imperative style (where most methods take and modify a `&mut T`), and renames it `syntax::mut_visit::MutVisitor`. The first benefit is speed. The functional style does not require any reallocations, due to the use of `P::map` and `MoveMap::move_{,flat_}map`. However, every field in the AST must be overwritten; even those fields that are unchanged are overwritten with the same value. This causes a lot of unnecessary memory writes. The imperative style reduces instruction counts by 1--3% across a wide range of workloads, particularly incremental workloads. The second benefit is conciseness; the imperative style is usually more concise. E.g. compare the old functional style: ``` fn fold_abc(&mut self, abc: ABC) { ABC { a: fold_a(abc.a), b: fold_b(abc.b), c: abc.c, } } ``` with the imperative style: ``` fn visit_abc(&mut self, ABC { a, b, c: _ }: &mut ABC) { visit_a(a); visit_b(b); } ``` (The reductions get larger in more complex examples.) Overall, the patch removes over 200 lines of code -- even though the new code has more comments -- and a lot of the remaining lines have fewer characters. Some notes: - The old style used methods called `fold_*`. The new style mostly uses methods called `visit_*`, but there are a few methods that map a `T` to something other than a `T`, which are called `flat_map_*` (`T` maps to multiple `T`s) or `filter_map_*` (`T` maps to 0 or 1 `T`s). - `move_map.rs`/`MoveMap`/`move_map`/`move_flat_map` are renamed `map_in_place.rs`/`MapInPlace`/`map_in_place`/`flat_map_in_place` to reflect their slightly changed signatures. - Although this commit renames the `fold` module as `mut_visit`, it keeps it in the `fold.rs` file, so as not to confuse git. The next commit will rename the file.
2019-02-05 15:20:55 +11:00
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),
Annotatable::Arm(arm) => arm.visit_attrs(f),
Annotatable::ExprField(field) => field.visit_attrs(f),
Annotatable::PatField(fp) => fp.visit_attrs(f),
Annotatable::GenericParam(gp) => gp.visit_attrs(f),
Annotatable::Param(p) => p.visit_attrs(f),
Annotatable::FieldDef(sf) => sf.visit_attrs(f),
Annotatable::Variant(v) => v.visit_attrs(f),
Annotatable::Crate(c) => c.visit_attrs(f),
}
}
pub fn visit_with<'a, V: Visitor<'a>>(&'a self, visitor: &mut V) {
match self {
Annotatable::Item(item) => visitor.visit_item(item),
Annotatable::TraitItem(item) => visitor.visit_assoc_item(item, AssocCtxt::Trait),
Annotatable::ImplItem(item) => visitor.visit_assoc_item(item, AssocCtxt::Impl),
Annotatable::ForeignItem(foreign_item) => visitor.visit_foreign_item(foreign_item),
Annotatable::Stmt(stmt) => visitor.visit_stmt(stmt),
Annotatable::Expr(expr) => visitor.visit_expr(expr),
Annotatable::Arm(arm) => visitor.visit_arm(arm),
Annotatable::ExprField(field) => visitor.visit_expr_field(field),
Annotatable::PatField(fp) => visitor.visit_pat_field(fp),
Annotatable::GenericParam(gp) => visitor.visit_generic_param(gp),
Annotatable::Param(p) => visitor.visit_param(p),
Annotatable::FieldDef(sf) => visitor.visit_field_def(sf),
Annotatable::Variant(v) => visitor.visit_variant(v),
Annotatable::Crate(c) => visitor.visit_crate(c),
}
}
pub fn to_tokens(&self) -> TokenStream {
match self {
Annotatable::Item(node) => TokenStream::from_ast(node),
Annotatable::TraitItem(node) | Annotatable::ImplItem(node) => {
TokenStream::from_ast(node)
}
Annotatable::ForeignItem(node) => TokenStream::from_ast(node),
Annotatable::Stmt(node) => {
assert!(!matches!(node.kind, ast::StmtKind::Empty));
TokenStream::from_ast(node)
}
Annotatable::Expr(node) => TokenStream::from_ast(node),
Annotatable::Arm(..)
| Annotatable::ExprField(..)
| Annotatable::PatField(..)
| Annotatable::GenericParam(..)
| Annotatable::Param(..)
| Annotatable::FieldDef(..)
| Annotatable::Variant(..)
| Annotatable::Crate(..) => panic!("unexpected annotatable"),
}
}
pub fn expect_item(self) -> P<ast::Item> {
match self {
Annotatable::Item(i) => i,
_ => panic!("expected Item"),
}
}
pub fn expect_trait_item(self) -> P<ast::AssocItem> {
match self {
Annotatable::TraitItem(i) => i,
_ => panic!("expected Item"),
}
}
pub fn expect_impl_item(self) -> P<ast::AssocItem> {
match self {
Annotatable::ImplItem(i) => i,
_ => panic!("expected Item"),
}
}
pub fn expect_foreign_item(self) -> P<ast::ForeignItem> {
match self {
Annotatable::ForeignItem(i) => i,
_ => 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 expect_arm(self) -> ast::Arm {
match self {
Annotatable::Arm(arm) => arm,
_ => panic!("expected match arm"),
}
}
pub fn expect_expr_field(self) -> ast::ExprField {
match self {
Annotatable::ExprField(field) => field,
_ => panic!("expected field"),
}
}
pub fn expect_pat_field(self) -> ast::PatField {
match self {
Annotatable::PatField(fp) => fp,
_ => panic!("expected field pattern"),
}
}
pub fn expect_generic_param(self) -> ast::GenericParam {
match self {
Annotatable::GenericParam(gp) => gp,
_ => panic!("expected generic parameter"),
}
}
pub fn expect_param(self) -> ast::Param {
match self {
Annotatable::Param(param) => param,
_ => panic!("expected parameter"),
}
}
pub fn expect_field_def(self) -> ast::FieldDef {
match self {
Annotatable::FieldDef(sf) => sf,
_ => panic!("expected struct field"),
}
}
pub fn expect_variant(self) -> ast::Variant {
match self {
Annotatable::Variant(v) => v,
_ => panic!("expected variant"),
}
}
pub fn expect_crate(self) -> ast::Crate {
match self {
Annotatable::Crate(krate) => krate,
_ => panic!("expected krate"),
}
}
}
/// Result of an expansion that may need to be retried.
/// Consider using this for non-`MultiItemModifier` expanders as well.
pub enum ExpandResult<T, U> {
/// Expansion produced a result (possibly dummy).
Ready(T),
/// Expansion could not produce a result and needs to be retried.
Retry(U),
}
pub trait MultiItemModifier {
/// `meta_item` is the attribute, and `item` is the item being modified.
fn expand(
&self,
2019-02-07 02:33:01 +09:00
ecx: &mut ExtCtxt<'_>,
span: Span,
meta_item: &ast::MetaItem,
item: Annotatable,
2022-09-20 11:55:07 +00:00
is_derive_const: bool,
) -> ExpandResult<Vec<Annotatable>, Annotatable>;
}
2019-12-30 21:38:43 +03:00
impl<F> MultiItemModifier for F
2019-02-07 02:33:01 +09:00
where
2019-12-30 21:38:43 +03:00
F: Fn(&mut ExtCtxt<'_>, Span, &ast::MetaItem, Annotatable) -> Vec<Annotatable>,
{
fn expand(
&self,
2019-02-07 02:33:01 +09:00
ecx: &mut ExtCtxt<'_>,
span: Span,
meta_item: &ast::MetaItem,
item: Annotatable,
2022-09-20 11:55:07 +00:00
_is_derive_const: bool,
) -> ExpandResult<Vec<Annotatable>, Annotatable> {
ExpandResult::Ready(self(ecx, span, meta_item, item))
}
}
pub trait BangProcMacro {
2020-03-17 10:09:18 +01:00
fn expand<'cx>(
&self,
ecx: &'cx mut ExtCtxt<'_>,
span: Span,
ts: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed>;
}
impl<F> BangProcMacro for F
where
F: Fn(TokenStream) -> TokenStream,
{
2020-03-17 10:09:18 +01:00
fn expand<'cx>(
&self,
_ecx: &'cx mut ExtCtxt<'_>,
_span: Span,
ts: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed> {
// FIXME setup implicit context in TLS before calling self.
2020-09-28 23:10:48 +03:00
Ok(self(ts))
}
}
pub trait AttrProcMacro {
fn expand<'cx>(
&self,
2019-02-07 02:33:01 +09:00
ecx: &'cx mut ExtCtxt<'_>,
span: Span,
annotation: TokenStream,
annotated: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed>;
}
impl<F> AttrProcMacro for F
where
F: Fn(TokenStream, TokenStream) -> TokenStream,
{
fn expand<'cx>(
&self,
2019-02-07 02:33:01 +09:00
_ecx: &'cx mut ExtCtxt<'_>,
2016-09-06 17:57:58 +12:00
_span: Span,
annotation: TokenStream,
annotated: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed> {
// FIXME setup implicit context in TLS before calling self.
2020-09-28 23:10:48 +03:00
Ok(self(annotation, annotated))
}
}
/// Represents a thing that maps token trees to Macro Results
pub trait TTMacroExpander {
fn expand<'cx>(
&self,
2019-02-07 02:33:01 +09:00
ecx: &'cx mut ExtCtxt<'_>,
span: Span,
input: TokenStream,
) -> Box<dyn MacResult + 'cx>;
}
pub type MacroExpanderFn =
for<'cx> fn(&'cx mut ExtCtxt<'_>, Span, TokenStream) -> Box<dyn MacResult + 'cx>;
impl<F> TTMacroExpander for F
where
F: for<'cx> Fn(&'cx mut ExtCtxt<'_>, Span, TokenStream) -> Box<dyn MacResult + 'cx>,
{
fn expand<'cx>(
&self,
2019-02-07 02:33:01 +09:00
ecx: &'cx mut ExtCtxt<'_>,
span: Span,
input: TokenStream,
) -> Box<dyn MacResult + 'cx> {
2020-09-28 23:10:48 +03:00
self(ecx, span, input)
}
}
2013-07-08 15:55:14 -07:00
// Use a macro because forwarding to a simple function has type system issues
macro_rules! make_stmts_default {
($me:expr) => {
2018-08-13 22:15:16 +03:00
$me.make_expr().map(|e| {
smallvec![ast::Stmt {
2016-06-17 02:30:01 +00:00
id: ast::DUMMY_NODE_ID,
span: e.span,
2019-09-26 17:34:50 +01:00
kind: ast::StmtKind::Expr(e),
2018-08-13 22:15:16 +03:00
}]
})
};
}
/// 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 {
2019-02-08 14:53:55 +01:00
/// Creates an expression.
2014-09-13 19:06:01 +03:00
fn make_expr(self: Box<Self>) -> Option<P<ast::Expr>> {
None
}
2021-08-08 11:49:13 -03:00
2019-02-08 14:53:55 +01:00
/// Creates zero or more items.
2018-08-30 11:42:16 +02:00
fn make_items(self: Box<Self>) -> Option<SmallVec<[P<ast::Item>; 1]>> {
None
}
2019-02-08 14:53:55 +01:00
/// Creates zero or more impl items.
fn make_impl_items(self: Box<Self>) -> Option<SmallVec<[P<ast::AssocItem>; 1]>> {
None
}
2019-02-08 14:53:55 +01:00
/// Creates zero or more trait items.
fn make_trait_items(self: Box<Self>) -> Option<SmallVec<[P<ast::AssocItem>; 1]>> {
None
}
2019-02-08 14:53:55 +01:00
/// Creates zero or more items in an `extern {}` block
fn make_foreign_items(self: Box<Self>) -> Option<SmallVec<[P<ast::ForeignItem>; 1]>> {
2018-08-30 11:42:16 +02:00
None
}
2019-02-08 14:53:55 +01:00
/// Creates a pattern.
2014-09-13 19:06:01 +03:00
fn make_pat(self: Box<Self>) -> Option<P<ast::Pat>> {
2014-05-19 13:32:51 -07:00
None
}
2019-02-08 14:53:55 +01:00
/// Creates zero or more statements.
///
/// By default this attempts to create an expression statement,
/// returning None if that fails.
2018-08-30 11:42:16 +02:00
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
}
fn make_arms(self: Box<Self>) -> Option<SmallVec<[ast::Arm; 1]>> {
None
}
fn make_expr_fields(self: Box<Self>) -> Option<SmallVec<[ast::ExprField; 1]>> {
None
}
fn make_pat_fields(self: Box<Self>) -> Option<SmallVec<[ast::PatField; 1]>> {
None
}
fn make_generic_params(self: Box<Self>) -> Option<SmallVec<[ast::GenericParam; 1]>> {
None
}
fn make_params(self: Box<Self>) -> Option<SmallVec<[ast::Param; 1]>> {
None
}
fn make_field_defs(self: Box<Self>) -> Option<SmallVec<[ast::FieldDef; 1]>> {
None
}
fn make_variants(self: Box<Self>) -> Option<SmallVec<[ast::Variant; 1]>> {
None
}
fn make_crate(self: Box<Self>) -> Option<ast::Crate> {
// Fn-like macros cannot produce a crate.
unreachable!()
}
}
2013-07-08 15:55:14 -07:00
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>,
2018-08-30 11:42:16 +02:00
items: SmallVec<[P<ast::Item>; 1]>,
impl_items: SmallVec<[P<ast::AssocItem>; 1]>,
trait_items: SmallVec<[P<ast::AssocItem>; 1]>,
foreign_items: SmallVec<[P<ast::ForeignItem>; 1]>,
2018-08-30 11:42:16 +02:00
stmts: SmallVec<[ast::Stmt; 1]>,
ty: P<ast::Ty>,
2014-05-19 13:32:51 -07:00
}
impl MacResult for MacEager {
fn make_expr(self: Box<Self>) -> Option<P<ast::Expr>> {
self.expr
2014-05-19 13:32:51 -07:00
}
2018-08-30 11:42:16 +02:00
fn make_items(self: Box<Self>) -> Option<SmallVec<[P<ast::Item>; 1]>> {
self.items
2014-05-19 13:32:51 -07:00
}
fn make_impl_items(self: Box<Self>) -> Option<SmallVec<[P<ast::AssocItem>; 1]>> {
self.impl_items
}
fn make_trait_items(self: Box<Self>) -> Option<SmallVec<[P<ast::AssocItem>; 1]>> {
self.trait_items
}
fn make_foreign_items(self: Box<Self>) -> Option<SmallVec<[P<ast::ForeignItem>; 1]>> {
self.foreign_items
}
2018-08-30 11:42:16 +02:00
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 matches!(e.kind, ast::ExprKind::Lit(_) | ast::ExprKind::IncludedBytes(_)) {
return Some(P(ast::Pat {
id: ast::DUMMY_NODE_ID,
span: e.span,
2019-09-26 16:18:31 +01:00
kind: PatKind::Lit(e),
tokens: None,
}));
}
}
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.
2015-03-30 09:38:59 -04:00
#[derive(Copy, Clone)]
pub struct DummyResult {
2018-12-20 03:57:48 +03:00
is_error: bool,
span: Span,
2012-07-06 14:29:50 -07:00
}
impl DummyResult {
2019-02-08 14:53:55 +01:00
/// Creates a default MacResult that can be anything.
///
/// Use this as a return value after hitting any errors and
/// calling `span_err`.
2018-12-20 03:57:48 +03:00
pub fn any(span: Span) -> Box<dyn MacResult + 'static> {
Box::new(DummyResult { is_error: true, span })
2018-12-20 03:57:48 +03:00
}
/// Same as `any`, but must be a valid fragment, not error.
pub fn any_valid(span: Span) -> Box<dyn MacResult + 'static> {
Box::new(DummyResult { is_error: false, span })
}
/// A plain dummy expression.
2018-12-20 03:57:48 +03:00
pub fn raw_expr(sp: Span, is_error: bool) -> P<ast::Expr> {
2014-09-13 19:06:01 +03:00
P(ast::Expr {
id: ast::DUMMY_NODE_ID,
kind: if is_error { ast::ExprKind::Err } else { ast::ExprKind::Tup(ThinVec::new()) },
span: sp,
2019-12-03 16:38:34 +01:00
attrs: ast::AttrVec::new(),
2020-05-19 16:56:20 -04:00
tokens: None,
2014-09-13 19:06:01 +03:00
})
}
2014-05-19 13:32:51 -07:00
/// A plain dummy pattern.
2014-09-13 19:06:01 +03:00
pub fn raw_pat(sp: Span) -> ast::Pat {
ast::Pat { id: ast::DUMMY_NODE_ID, kind: PatKind::Wild, span: sp, tokens: None }
2014-05-19 13:32:51 -07:00
}
/// A plain dummy type.
2018-12-20 03:57:48 +03:00
pub fn raw_ty(sp: Span, is_error: bool) -> P<ast::Ty> {
P(ast::Ty {
id: ast::DUMMY_NODE_ID,
kind: if is_error { ast::TyKind::Err } else { ast::TyKind::Tup(ThinVec::new()) },
span: sp,
tokens: None,
})
}
}
impl MacResult for DummyResult {
2014-09-13 19:06:01 +03:00
fn make_expr(self: Box<DummyResult>) -> Option<P<ast::Expr>> {
2018-12-20 03:57:48 +03:00
Some(DummyResult::raw_expr(self.span, self.is_error))
}
2014-09-13 19:06:01 +03:00
fn make_pat(self: Box<DummyResult>) -> Option<P<ast::Pat>> {
Some(P(DummyResult::raw_pat(self.span)))
2014-05-19 13:32:51 -07:00
}
2018-08-30 11:42:16 +02:00
fn make_items(self: Box<DummyResult>) -> Option<SmallVec<[P<ast::Item>; 1]>> {
Some(SmallVec::new())
}
fn make_impl_items(self: Box<DummyResult>) -> Option<SmallVec<[P<ast::AssocItem>; 1]>> {
Some(SmallVec::new())
}
fn make_trait_items(self: Box<DummyResult>) -> Option<SmallVec<[P<ast::AssocItem>; 1]>> {
Some(SmallVec::new())
}
fn make_foreign_items(self: Box<Self>) -> Option<SmallVec<[P<ast::ForeignItem>; 1]>> {
Some(SmallVec::new())
}
2018-08-30 11:42:16 +02:00
fn make_stmts(self: Box<DummyResult>) -> Option<SmallVec<[ast::Stmt; 1]>> {
2018-08-13 22:15:16 +03:00
Some(smallvec![ast::Stmt {
2016-06-17 02:30:01 +00:00
id: ast::DUMMY_NODE_ID,
2019-09-26 17:34:50 +01:00
kind: ast::StmtKind::Expr(DummyResult::raw_expr(self.span, self.is_error)),
2016-06-17 02:30:01 +00:00
span: self.span,
2018-08-13 22:15:16 +03:00
}])
}
fn make_ty(self: Box<DummyResult>) -> Option<P<ast::Ty>> {
2018-12-20 03:57:48 +03:00
Some(DummyResult::raw_ty(self.span, self.is_error))
}
fn make_arms(self: Box<DummyResult>) -> Option<SmallVec<[ast::Arm; 1]>> {
Some(SmallVec::new())
}
fn make_expr_fields(self: Box<DummyResult>) -> Option<SmallVec<[ast::ExprField; 1]>> {
Some(SmallVec::new())
}
fn make_pat_fields(self: Box<DummyResult>) -> Option<SmallVec<[ast::PatField; 1]>> {
Some(SmallVec::new())
}
fn make_generic_params(self: Box<DummyResult>) -> Option<SmallVec<[ast::GenericParam; 1]>> {
Some(SmallVec::new())
}
fn make_params(self: Box<DummyResult>) -> Option<SmallVec<[ast::Param; 1]>> {
Some(SmallVec::new())
}
fn make_field_defs(self: Box<DummyResult>) -> Option<SmallVec<[ast::FieldDef; 1]>> {
Some(SmallVec::new())
}
fn make_variants(self: Box<DummyResult>) -> Option<SmallVec<[ast::Variant; 1]>> {
Some(SmallVec::new())
}
fn make_crate(self: Box<DummyResult>) -> Option<ast::Crate> {
Some(DummyAstNode::dummy())
}
}
/// A syntax extension kind.
pub enum SyntaxExtensionKind {
/// A token-based function-like macro.
Bang(
/// An expander with signature TokenStream -> TokenStream.
Box<dyn BangProcMacro + sync::DynSync + sync::DynSend>,
),
/// An AST-based function-like macro.
LegacyBang(
/// An expander with signature TokenStream -> AST.
Box<dyn TTMacroExpander + sync::DynSync + sync::DynSend>,
),
/// 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::DynSync + sync::DynSend>,
),
/// 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::DynSync + sync::DynSend>,
),
/// A trivial attribute "macro" that does nothing,
/// only keeps the attribute and marks it as inert,
/// thus making it ineligible for further expansion.
2021-08-05 17:58:59 -05:00
NonMacroAttr,
/// A token-based derive macro.
Derive(
/// An expander with signature TokenStream -> TokenStream.
/// The produced TokenSteam is appended to the input TokenSteam.
///
/// FIXME: The text above describes how this should work. Currently it
/// is handled identically to `LegacyDerive`. It should be migrated to
/// a token-based representation like `Bang` and `Attr`, instead of
/// using `MultiItemModifier`.
Box<dyn MultiItemModifier + sync::DynSync + sync::DynSend>,
),
/// 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::DynSync + sync::DynSend>,
),
}
/// 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,
/// List of unstable features that are treated as stable inside this macro.
pub allow_internal_unstable: Option<Lrc<[Symbol]>>,
/// 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,
/// Built-in macros have a couple of special properties like availability
/// in `#[no_implicit_prelude]` modules, so we have to keep this flag.
pub builtin_name: Option<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,
/// Should debuginfo for the macro be collapsed to the outermost expansion site (in other
/// words, was the macro definition annotated with `#[collapse_debuginfo]`)?
pub collapse_debuginfo: bool,
}
impl SyntaxExtension {
2019-02-08 14:53:55 +01:00
/// 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(..)
2021-08-05 17:58:59 -05:00
| SyntaxExtensionKind::NonMacroAttr => MacroKind::Attr,
SyntaxExtensionKind::Derive(..) | SyntaxExtensionKind::LegacyDerive(..) => {
MacroKind::Derive
2019-12-22 17:42:04 -05:00
}
}
}
2017-03-22 08:39:51 +00:00
/// Constructs a syntax extension with default properties.
pub fn default(kind: SyntaxExtensionKind, edition: Edition) -> SyntaxExtension {
SyntaxExtension {
span: DUMMY_SP,
allow_internal_unstable: None,
stability: None,
deprecation: None,
helper_attrs: Vec::new(),
edition,
builtin_name: None,
kind,
allow_internal_unsafe: false,
local_inner_macros: false,
collapse_debuginfo: false,
2018-06-24 19:54:23 +03:00
}
}
/// Constructs a syntax extension with the given properties
/// and other properties converted from attributes.
pub fn new(
sess: &Session,
features: &Features,
kind: SyntaxExtensionKind,
span: Span,
helper_attrs: Vec<Symbol>,
edition: Edition,
2020-04-19 13:00:18 +02:00
name: Symbol,
attrs: &[ast::Attribute],
) -> SyntaxExtension {
let allow_internal_unstable =
attr::allow_internal_unstable(sess, attrs).collect::<Vec<Symbol>>();
let allow_internal_unsafe = attr::contains_name(attrs, sym::allow_internal_unsafe);
let local_inner_macros = attr::find_by_name(attrs, sym::macro_export)
.and_then(|macro_export| macro_export.meta_item_list())
.is_some_and(|l| attr::list_contains_name(&l, sym::local_inner_macros));
let collapse_debuginfo = attr::contains_name(attrs, sym::collapse_debuginfo);
tracing::debug!(?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe);
let (builtin_name, helper_attrs) = attr::find_by_name(attrs, sym::rustc_builtin_macro)
.map(|attr| {
// Override `helper_attrs` passed above if it's a built-in macro,
// marking `proc_macro_derive` macros as built-in is not a realistic use case.
parse_macro_name_and_helper_attrs(sess.dcx(), attr, "built-in").map_or_else(
|| (Some(name), Vec::new()),
|(name, helper_attrs)| (Some(name), helper_attrs),
)
})
.unwrap_or_else(|| (None, helper_attrs));
let stability = attr::find_stability(sess, attrs, span);
let const_stability = attr::find_const_stability(sess, attrs, span);
let body_stability = attr::find_body_stability(sess, attrs);
if let Some((_, sp)) = const_stability {
sess.dcx().emit_err(errors::MacroConstStability {
span: sp,
head_span: sess.source_map().guess_head_span(span),
});
}
if let Some((_, sp)) = body_stability {
sess.dcx().emit_err(errors::MacroBodyStability {
span: sp,
head_span: sess.source_map().guess_head_span(span),
});
}
SyntaxExtension {
kind,
span,
allow_internal_unstable: (!allow_internal_unstable.is_empty())
.then(|| allow_internal_unstable.into()),
stability: stability.map(|(s, _)| s),
deprecation: attr::find_deprecation(sess, features, attrs).map(|(d, _)| d),
helper_attrs,
edition,
builtin_name,
allow_internal_unsafe,
local_inner_macros,
collapse_debuginfo,
}
}
pub fn dummy_bang(edition: Edition) -> SyntaxExtension {
fn expander<'cx>(
_: &'cx mut ExtCtxt<'_>,
span: Span,
_: TokenStream,
) -> Box<dyn MacResult + 'cx> {
DummyResult::any(span)
}
SyntaxExtension::default(SyntaxExtensionKind::LegacyBang(Box::new(expander)), edition)
}
pub fn dummy_derive(edition: Edition) -> SyntaxExtension {
fn expander(
_: &mut ExtCtxt<'_>,
_: Span,
_: &ast::MetaItem,
_: Annotatable,
) -> Vec<Annotatable> {
Vec::new()
}
SyntaxExtension::default(SyntaxExtensionKind::Derive(Box::new(expander)), edition)
}
2021-08-05 17:58:59 -05:00
pub fn non_macro_attr(edition: Edition) -> SyntaxExtension {
SyntaxExtension::default(SyntaxExtensionKind::NonMacroAttr, edition)
}
pub fn expn_data(
&self,
2021-06-25 20:43:04 +02:00
parent: LocalExpnId,
call_site: Span,
descr: Symbol,
macro_def_id: Option<DefId>,
2021-06-28 19:29:55 +02:00
parent_module: Option<DefId>,
) -> ExpnData {
ExpnData::new(
ExpnKind::Macro(self.macro_kind(), descr),
2021-06-25 20:43:04 +02:00
parent.to_expn_id(),
call_site,
self.span,
self.allow_internal_unstable.clone(),
self.edition,
macro_def_id,
2021-06-28 19:29:55 +02:00
parent_module,
self.allow_internal_unsafe,
self.local_inner_macros,
self.collapse_debuginfo,
)
}
}
/// Error type that denotes indeterminacy.
pub struct Indeterminate;
2022-09-20 11:55:07 +00:00
pub type DeriveResolutions = Vec<(ast::Path, Annotatable, Option<Lrc<SyntaxExtension>>, bool)>;
2020-06-27 23:51:28 +03:00
pub trait ResolverExpand {
fn next_node_id(&mut self) -> NodeId;
2021-05-02 21:19:28 +02:00
fn invocation_parent(&self, id: LocalExpnId) -> LocalDefId;
fn resolve_dollar_crates(&mut self);
2021-06-25 20:43:04 +02:00
fn visit_ast_fragment_with_placeholders(
&mut self,
expn_id: LocalExpnId,
fragment: &AstFragment,
);
2021-01-10 14:36:30 +03:00
fn register_builtin_macro(&mut self, name: Symbol, ext: SyntaxExtensionKind);
fn expansion_for_ast_pass(
&mut self,
call_site: Span,
pass: AstPass,
features: &[Symbol],
parent_module_id: Option<NodeId>,
2021-06-25 20:43:04 +02:00
) -> LocalExpnId;
2016-11-10 10:11:25 +00:00
fn resolve_imports(&mut self);
fn resolve_macro_invocation(
&mut self,
invoc: &Invocation,
2021-06-25 20:43:04 +02:00
eager_expansion_root: LocalExpnId,
force: bool,
) -> Result<Lrc<SyntaxExtension>, Indeterminate>;
2022-04-17 04:01:17 +02:00
fn record_macro_rule_usage(&mut self, mac_id: NodeId, rule_index: usize);
fn check_unused_macros(&mut self);
2020-11-19 01:50:16 +03:00
// Resolver interfaces for specific built-in macros.
/// Does `#[derive(...)]` attribute with the given `ExpnId` have built-in `Copy` inside it?
2021-06-25 20:43:04 +02:00
fn has_derive_copy(&self, expn_id: LocalExpnId) -> bool;
/// Resolve paths inside the `#[derive(...)]` attribute with the given `ExpnId`.
fn resolve_derives(
&mut self,
2021-06-25 20:43:04 +02:00
expn_id: LocalExpnId,
force: bool,
derive_paths: &dyn Fn() -> DeriveResolutions,
) -> Result<(), Indeterminate>;
/// Take resolutions for paths inside the `#[derive(...)]` attribute with the given `ExpnId`
/// back from resolver.
2021-06-25 20:43:04 +02:00
fn take_derive_resolutions(&mut self, expn_id: LocalExpnId) -> Option<DeriveResolutions>;
2020-11-19 01:50:16 +03:00
/// Path resolution logic for `#[cfg_accessible(path)]`.
2021-06-25 20:43:04 +02:00
fn cfg_accessible(
&mut self,
expn_id: LocalExpnId,
path: &ast::Path,
) -> Result<bool, Indeterminate>;
Implement span quoting for proc-macros This PR implements span quoting, allowing proc-macros to produce spans pointing *into their own crate*. This is used by the unstable `proc_macro::quote!` macro, allowing us to get error messages like this: ``` error[E0412]: cannot find type `MissingType` in this scope --> $DIR/auxiliary/span-from-proc-macro.rs:37:20 | LL | pub fn error_from_attribute(_args: TokenStream, _input: TokenStream) -> TokenStream { | ----------------------------------------------------------------------------------- in this expansion of procedural macro `#[error_from_attribute]` ... LL | field: MissingType | ^^^^^^^^^^^ not found in this scope | ::: $DIR/span-from-proc-macro.rs:8:1 | LL | #[error_from_attribute] | ----------------------- in this macro invocation ``` Here, `MissingType` occurs inside the implementation of the proc-macro `#[error_from_attribute]`. Previosuly, this would always result in a span pointing at `#[error_from_attribute]` This will make many proc-macro-related error message much more useful - when a proc-macro generates code containing an error, users will get an error message pointing directly at that code (within the macro definition), instead of always getting a span pointing at the macro invocation site. This is implemented as follows: * When a proc-macro crate is being *compiled*, it causes the `quote!` macro to get run. This saves all of the sapns in the input to `quote!` into the metadata of *the proc-macro-crate* (which we are currently compiling). The `quote!` macro then expands to a call to `proc_macro::Span::recover_proc_macro_span(id)`, where `id` is an opaque identifier for the span in the crate metadata. * When the same proc-macro crate is *run* (e.g. it is loaded from disk and invoked by some consumer crate), the call to `proc_macro::Span::recover_proc_macro_span` causes us to load the span from the proc-macro crate's metadata. The proc-macro then produces a `TokenStream` containing a `Span` pointing into the proc-macro crate itself. The recursive nature of 'quote!' can be difficult to understand at first. The file `src/test/ui/proc-macro/quote-debug.stdout` shows the output of the `quote!` macro, which should make this eaier to understand. This PR also supports custom quoting spans in custom quote macros (e.g. the `quote` crate). All span quoting goes through the `proc_macro::quote_span` method, which can be called by a custom quote macro to perform span quoting. An example of this usage is provided in `src/test/ui/proc-macro/auxiliary/custom-quote.rs` Custom quoting currently has a few limitations: In order to quote a span, we need to generate a call to `proc_macro::Span::recover_proc_macro_span`. However, proc-macros support renaming the `proc_macro` crate, so we can't simply hardcode this path. Previously, the `quote_span` method used the path `crate::Span` - however, this only works when it is called by the builtin `quote!` macro in the same crate. To support being called from arbitrary crates, we need access to the name of the `proc_macro` crate to generate a path. This PR adds an additional argument to `quote_span` to specify the name of the `proc_macro` crate. Howver, this feels kind of hacky, and we may want to change this before stabilizing anything quote-related. Additionally, using `quote_span` currently requires enabling the `proc_macro_internals` feature. The builtin `quote!` macro has an `#[allow_internal_unstable]` attribute, but this won't work for custom quote implementations. This will likely require some additional tricks to apply `allow_internal_unstable` to the span of `proc_macro::Span::recover_proc_macro_span`.
2020-08-02 19:52:16 -04:00
/// Decodes the proc-macro quoted span in the specified crate, with the specified id.
/// No caching is performed.
fn get_proc_macro_quoted_span(&self, krate: CrateNum, id: usize) -> Span;
2021-07-16 22:22:08 +02:00
/// The order of items in the HIR is unrelated to the order of
/// items in the AST. However, we generate proc macro harnesses
/// based on the AST order, and later refer to these harnesses
/// from the HIR. This field keeps track of the order in which
/// we generated proc macros harnesses, so that we can map
/// HIR proc macros items back to their harness items.
fn declare_proc_macro(&mut self, id: NodeId);
fn append_stripped_cfg_item(&mut self, parent_node: NodeId, name: Ident, cfg: ast::MetaItem);
/// Tools registered with `#![register_tool]` and used by tool attributes and lints.
2023-03-06 10:56:23 +00:00
fn registered_tools(&self) -> &RegisteredTools;
}
pub trait LintStoreExpand {
fn pre_expansion_lint(
&self,
sess: &Session,
features: &Features,
2023-03-06 10:56:23 +00:00
registered_tools: &RegisteredTools,
node_id: NodeId,
attrs: &[Attribute],
items: &[P<Item>],
name: Symbol,
);
}
type LintStoreExpandDyn<'a> = Option<&'a (dyn LintStoreExpand + 'a)>;
#[derive(Debug, Clone, Default)]
pub struct ModuleData {
/// Path to the module starting from the crate name, like `my_crate::foo::bar`.
2020-04-19 13:00:18 +02:00
pub mod_path: Vec<Ident>,
/// Stack of paths to files loaded by out-of-line module items,
/// used to detect and report recursive module inclusions.
pub file_path_stack: Vec<PathBuf>,
/// Directory to search child module files in,
/// often (but not necessarily) the parent of the top file path on the `file_path_stack`.
pub dir_path: PathBuf,
}
impl ModuleData {
pub fn with_dir_path(&self, dir_path: PathBuf) -> ModuleData {
ModuleData {
mod_path: self.mod_path.clone(),
file_path_stack: self.file_path_stack.clone(),
dir_path,
}
}
}
#[derive(Clone)]
pub struct ExpansionData {
2021-06-25 20:43:04 +02:00
pub id: LocalExpnId,
pub depth: usize,
pub module: Rc<ModuleData>,
pub dir_ownership: DirOwnership,
/// Some parent node that is close to this macro call
pub lint_node_id: NodeId,
Display an extra note for trailing semicolon lint with trailing macro Currently, we parse macros at the end of a block (e.g. `fn foo() { my_macro!() }`) as expressions, rather than statements. This means that a macro invoked in this position cannot expand to items or semicolon-terminated expressions. In the future, we might want to start parsing these kinds of macros as statements. This would make expansion more 'token-based' (i.e. macro expansion behaves (almost) as if you just textually replaced the macro invocation with its output). However, this is a breaking change (see PR #78991), so it will require further discussion. Since the current behavior will not be changing any time soon, we need to address the interaction with the `SEMICOLON_IN_EXPRESSIONS_FROM_MACROS` lint. Since we are parsing the result of macro expansion as an expression, we will emit a lint if there's a trailing semicolon in the macro output. However, this results in a somewhat confusing message for users, since it visually looks like there should be no problem with having a semicolon at the end of a block (e.g. `fn foo() { my_macro!() }` => `fn foo() { produced_expr; }`) To help reduce confusion, this commit adds a note explaining that the macro is being interpreted as an expression. Additionally, we suggest adding a semicolon after the macro *invocation* - this will cause us to parse the macro call as a statement. We do *not* use a structured suggestion for this, since the user may actually want to remove the semicolon from the macro definition (allowing the block to evaluate to the expression produced by the macro).
2021-07-22 11:24:42 -05:00
pub is_trailing_mac: bool,
}
2014-06-09 13:12:30 -07:00
/// 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_data` of their expansion context stored into their span.
2013-12-25 11:10:33 -07:00
pub struct ExtCtxt<'a> {
pub sess: &'a Session,
pub ecfg: expand::ExpansionConfig<'a>,
pub num_standard_library_imports: usize,
pub reduced_recursion_limit: Option<Limit>,
pub root_path: PathBuf,
2020-06-27 23:51:28 +03:00
pub resolver: &'a mut dyn ResolverExpand,
pub current_expansion: ExpansionData,
/// Error recovery mode entered when expansion is stuck
/// (or during eager expansion, but that's a hack).
pub force_mode: bool,
pub expansions: FxIndexMap<Span, Vec<String>>,
/// Used for running pre-expansion lints on freshly loaded modules.
pub(super) lint_store: LintStoreExpandDyn<'a>,
/// Used for storing lints generated during expansion, like `NAMED_ARGUMENTS_USED_POSITIONALLY`
pub buffered_early_lint: Vec<BufferedEarlyLint>,
/// When we 'expand' an inert attribute, we leave it
/// in the AST, but insert it here so that we know
/// not to expand it again.
pub(super) expanded_inert_attrs: MarkedAttrs,
}
2013-03-15 15:24:24 -04:00
2013-12-25 11:10:33 -07:00
impl<'a> ExtCtxt<'a> {
2019-10-14 10:08:26 +02:00
pub fn new(
sess: &'a Session,
ecfg: expand::ExpansionConfig<'a>,
2020-06-27 23:51:28 +03:00
resolver: &'a mut dyn ResolverExpand,
lint_store: LintStoreExpandDyn<'a>,
) -> ExtCtxt<'a> {
2013-12-27 17:17:36 -07:00
ExtCtxt {
sess,
ecfg,
num_standard_library_imports: 0,
reduced_recursion_limit: None,
resolver,
lint_store,
2020-03-16 00:43:37 +01:00
root_path: PathBuf::new(),
current_expansion: ExpansionData {
2021-06-25 20:43:04 +02:00
id: LocalExpnId::ROOT,
depth: 0,
module: Default::default(),
dir_ownership: DirOwnership::Owned { relative: None },
lint_node_id: ast::CRATE_NODE_ID,
Display an extra note for trailing semicolon lint with trailing macro Currently, we parse macros at the end of a block (e.g. `fn foo() { my_macro!() }`) as expressions, rather than statements. This means that a macro invoked in this position cannot expand to items or semicolon-terminated expressions. In the future, we might want to start parsing these kinds of macros as statements. This would make expansion more 'token-based' (i.e. macro expansion behaves (almost) as if you just textually replaced the macro invocation with its output). However, this is a breaking change (see PR #78991), so it will require further discussion. Since the current behavior will not be changing any time soon, we need to address the interaction with the `SEMICOLON_IN_EXPRESSIONS_FROM_MACROS` lint. Since we are parsing the result of macro expansion as an expression, we will emit a lint if there's a trailing semicolon in the macro output. However, this results in a somewhat confusing message for users, since it visually looks like there should be no problem with having a semicolon at the end of a block (e.g. `fn foo() { my_macro!() }` => `fn foo() { produced_expr; }`) To help reduce confusion, this commit adds a note explaining that the macro is being interpreted as an expression. Additionally, we suggest adding a semicolon after the macro *invocation* - this will cause us to parse the macro call as a statement. We do *not* use a structured suggestion for this, since the user may actually want to remove the semicolon from the macro definition (allowing the block to evaluate to the expression produced by the macro).
2021-07-22 11:24:42 -05:00
is_trailing_mac: false,
},
force_mode: false,
expansions: FxIndexMap::default(),
expanded_inert_attrs: MarkedAttrs::new(),
buffered_early_lint: vec![],
}
}
pub fn dcx(&self) -> &'a DiagCtxt {
self.sess.dcx()
}
/// 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)
}
2019-02-08 14:53:55 +01:00
/// 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, stream: TokenStream) -> parser::Parser<'a> {
rustc_parse::stream_to_parser(&self.sess.parse_sess, stream, MACRO_ARGUMENTS)
}
2018-08-18 12:14:09 +02:00
pub fn source_map(&self) -> &'a SourceMap {
self.sess.parse_sess.source_map()
2018-08-18 12:14:09 +02:00
}
2019-10-14 10:08:26 +02:00
pub fn parse_sess(&self) -> &'a ParseSess {
&self.sess.parse_sess
2019-10-14 10:08:26 +02:00
}
pub fn call_site(&self) -> Span {
self.current_expansion.id.expn_data().call_site
2017-03-17 04:04:41 +00:00
}
/// Returns the current expansion kind's description.
pub(crate) fn expansion_descr(&self) -> String {
let expn_data = self.current_expansion.id.expn_data();
expn_data.kind.descr()
}
/// Equivalent of `Span::def_site` from the proc macro API,
/// except that the location is taken from the span passed as an argument.
pub fn with_def_site_ctxt(&self, span: Span) -> Span {
2021-06-25 20:43:04 +02:00
span.with_def_site_ctxt(self.current_expansion.id.to_expn_id())
}
/// Equivalent of `Span::call_site` from the proc macro API,
/// except that the location is taken from the span passed as an argument.
pub fn with_call_site_ctxt(&self, span: Span) -> Span {
2021-06-25 20:43:04 +02:00
span.with_call_site_ctxt(self.current_expansion.id.to_expn_id())
}
/// Equivalent of `Span::mixed_site` from the proc macro API,
/// except that the location is taken from the span passed as an argument.
pub fn with_mixed_site_ctxt(&self, span: Span) -> Span {
2021-06-25 20:43:04 +02:00
span.with_mixed_site_ctxt(self.current_expansion.id.to_expn_id())
}
/// Returns span for the macro which originally caused the current expansion to happen.
///
/// Stops backtracing at include! boundary.
2017-05-15 09:41:05 +00:00
pub fn expansion_cause(&self) -> Option<Span> {
self.current_expansion.id.expansion_cause()
}
pub fn trace_macros_diag(&mut self) {
for (span, notes) in self.expansions.iter() {
let mut db = self.dcx().create_note(errors::TraceMacro { span: *span });
for note in notes {
db.note(note.clone());
}
db.emit();
}
// Fixme: does this result in errors?
self.expansions.clear();
}
pub fn trace_macros(&self) -> bool {
self.ecfg.trace_mac
}
2013-12-28 22:35:38 -07:00
pub fn set_trace_macros(&mut self, x: bool) {
self.ecfg.trace_mac = x
}
2020-04-19 13:00:18 +02:00
pub fn std_path(&self, components: &[Symbol]) -> Vec<Ident> {
let def_site = self.with_def_site_ctxt(DUMMY_SP);
2019-05-11 17:41:37 +03:00
iter::once(Ident::new(kw::DollarCrate, def_site))
.chain(components.iter().map(|&s| Ident::with_dummy_span(s)))
.collect()
}
pub fn def_site_path(&self, components: &[Symbol]) -> Vec<Ident> {
let def_site = self.with_def_site_ctxt(DUMMY_SP);
components.iter().map(|&s| Ident::new(s, def_site)).collect()
}
2017-05-11 10:26:07 +02:00
pub fn check_unused_macros(&mut self) {
2017-05-11 10:26:07 +02:00
self.resolver.check_unused_macros();
}
}
/// Resolves a `path` mentioned inside Rust code, returning an absolute path.
///
/// This unifies the logic used for resolving `include_X!`.
pub fn resolve_path(
parse_sess: &Session,
path: impl Into<PathBuf>,
span: Span,
) -> PResult<'_, PathBuf> {
let path = path.into();
// Relative paths are resolved relative to the file in which they are found
// after macro expansion (that is, they are unhygienic).
if !path.is_absolute() {
let callsite = span.source_callsite();
let mut result = match parse_sess.source_map().span_to_filename(callsite) {
FileName::Real(name) => name
.into_local_path()
.expect("attempting to resolve a file path in an external file"),
FileName::DocTest(path, _) => path,
other => {
return Err(parse_sess.dcx().create_err(errors::ResolveRelativePath {
span,
path: parse_sess.source_map().filename_for_diagnostics(&other).to_string(),
}));
}
};
result.pop();
result.push(path);
Ok(result)
} else {
Ok(path)
}
}
2019-02-08 14:53:55 +01:00
/// Extracts a string literal from the macro expanded version of `expr`,
/// returning a diagnostic error of `err_msg` if `expr` is not a string literal.
/// The returned bool indicates whether an applicable suggestion has already been
/// added to the diagnostic to avoid emitting multiple suggestions. `Err(None)`
/// indicates that an ast error was encountered.
// FIXME(Nilstrieb) Make this function setup translatable
#[allow(rustc::untranslatable_diagnostic)]
pub fn expr_to_spanned_string<'a>(
2019-02-07 02:33:01 +09:00
cx: &'a mut ExtCtxt<'_>,
expr: P<ast::Expr>,
err_msg: &'static str,
) -> Result<(Symbol, ast::StrStyle, Span), Option<(DiagnosticBuilder<'a>, bool)>> {
// Perform eager expansion on the expression.
// We want to be able to handle e.g., `concat!("foo", "bar")`.
let expr = cx.expander().fully_expand_fragment(AstFragment::Expr(expr)).make_expr();
Err(match expr.kind {
ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) {
Ok(ast::LitKind::Str(s, style)) => return Ok((s, style, expr.span)),
Ok(ast::LitKind::ByteStr(..)) => {
let mut err = cx.dcx().struct_span_err(expr.span, err_msg);
let span = expr.span.shrink_to_lo();
err.span_suggestion(
span.with_hi(span.lo() + BytePos(1)),
"consider removing the leading `b`",
"",
Applicability::MaybeIncorrect,
);
Some((err, true))
}
Ok(ast::LitKind::Err) => None,
Err(err) => {
report_lit_error(&cx.sess.parse_sess, err, token_lit, expr.span);
None
}
_ => Some((cx.dcx().struct_span_err(expr.span, err_msg), false)),
},
ast::ExprKind::Err => None,
_ => Some((cx.dcx().struct_span_err(expr.span, err_msg), false)),
})
}
/// 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`.
2019-02-07 02:33:01 +09:00
pub fn expr_to_string(
cx: &mut ExtCtxt<'_>,
expr: P<ast::Expr>,
err_msg: &'static str,
) -> Option<(Symbol, ast::StrStyle)> {
expr_to_spanned_string(cx, expr, err_msg)
2020-02-02 09:47:58 +10:00
.map_err(|err| {
Make `DiagnosticBuilder::emit` consuming. This works for most of its call sites. This is nice, because `emit` very much makes sense as a consuming operation -- indeed, `DiagnosticBuilderState` exists to ensure no diagnostic is emitted twice, but it uses runtime checks. For the small number of call sites where a consuming emit doesn't work, the commit adds `DiagnosticBuilder::emit_without_consuming`. (This will be removed in subsequent commits.) Likewise, `emit_unless` becomes consuming. And `delay_as_bug` becomes consuming, while `delay_as_bug_without_consuming` is added (which will also be removed in subsequent commits.) All this requires significant changes to `DiagnosticBuilder`'s chaining methods. Currently `DiagnosticBuilder` method chaining uses a non-consuming `&mut self -> &mut Self` style, which allows chaining to be used when the chain ends in `emit()`, like so: ``` struct_err(msg).span(span).emit(); ``` But it doesn't work when producing a `DiagnosticBuilder` value, requiring this: ``` let mut err = self.struct_err(msg); err.span(span); err ``` This style of chaining won't work with consuming `emit` though. For that, we need to use to a `self -> Self` style. That also would allow `DiagnosticBuilder` production to be chained, e.g.: ``` self.struct_err(msg).span(span) ``` However, removing the `&mut self -> &mut Self` style would require that individual modifications of a `DiagnosticBuilder` go from this: ``` err.span(span); ``` to this: ``` err = err.span(span); ``` There are *many* such places. I have a high tolerance for tedious refactorings, but even I gave up after a long time trying to convert them all. Instead, this commit has it both ways: the existing `&mut self -> Self` chaining methods are kept, and new `self -> Self` chaining methods are added, all of which have a `_mv` suffix (short for "move"). Changes to the existing `forward!` macro lets this happen with very little additional boilerplate code. I chose to add the suffix to the new chaining methods rather than the existing ones, because the number of changes required is much smaller that way. This doubled chainging is a bit clumsy, but I think it is worthwhile because it allows a *lot* of good things to subsequently happen. In this commit, there are many `mut` qualifiers removed in places where diagnostics are emitted without being modified. In subsequent commits: - chaining can be used more, making the code more concise; - more use of chaining also permits the removal of redundant diagnostic APIs like `struct_err_with_code`, which can be replaced easily with `struct_err` + `code_mv`; - `emit_without_diagnostic` can be removed, which simplifies a lot of machinery, removing the need for `DiagnosticBuilderState`.
2024-01-03 12:17:35 +11:00
err.map(|(err, _)| {
2020-02-02 09:47:58 +10:00
err.emit();
})
})
.ok()
.map(|(symbol, style, _)| (symbol, style))
2016-09-02 22:01:35 +00:00
}
/// 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.diagnostic().abort_if_errors()`
/// (this should be done as rarely as possible).
pub fn check_zero_tts(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream, name: &str) {
if !tts.is_empty() {
cx.dcx().emit_err(errors::TakesNoArguments { span, name });
}
}
2020-03-17 08:29:34 +01:00
/// Parse an expression. On error, emit it, advancing to `Eof`, and return `None`.
pub fn parse_expr(p: &mut parser::Parser<'_>) -> Option<P<ast::Expr>> {
2020-03-17 08:29:34 +01:00
match p.parse_expr() {
Ok(e) => return Some(e),
Make `DiagnosticBuilder::emit` consuming. This works for most of its call sites. This is nice, because `emit` very much makes sense as a consuming operation -- indeed, `DiagnosticBuilderState` exists to ensure no diagnostic is emitted twice, but it uses runtime checks. For the small number of call sites where a consuming emit doesn't work, the commit adds `DiagnosticBuilder::emit_without_consuming`. (This will be removed in subsequent commits.) Likewise, `emit_unless` becomes consuming. And `delay_as_bug` becomes consuming, while `delay_as_bug_without_consuming` is added (which will also be removed in subsequent commits.) All this requires significant changes to `DiagnosticBuilder`'s chaining methods. Currently `DiagnosticBuilder` method chaining uses a non-consuming `&mut self -> &mut Self` style, which allows chaining to be used when the chain ends in `emit()`, like so: ``` struct_err(msg).span(span).emit(); ``` But it doesn't work when producing a `DiagnosticBuilder` value, requiring this: ``` let mut err = self.struct_err(msg); err.span(span); err ``` This style of chaining won't work with consuming `emit` though. For that, we need to use to a `self -> Self` style. That also would allow `DiagnosticBuilder` production to be chained, e.g.: ``` self.struct_err(msg).span(span) ``` However, removing the `&mut self -> &mut Self` style would require that individual modifications of a `DiagnosticBuilder` go from this: ``` err.span(span); ``` to this: ``` err = err.span(span); ``` There are *many* such places. I have a high tolerance for tedious refactorings, but even I gave up after a long time trying to convert them all. Instead, this commit has it both ways: the existing `&mut self -> Self` chaining methods are kept, and new `self -> Self` chaining methods are added, all of which have a `_mv` suffix (short for "move"). Changes to the existing `forward!` macro lets this happen with very little additional boilerplate code. I chose to add the suffix to the new chaining methods rather than the existing ones, because the number of changes required is much smaller that way. This doubled chainging is a bit clumsy, but I think it is worthwhile because it allows a *lot* of good things to subsequently happen. In this commit, there are many `mut` qualifiers removed in places where diagnostics are emitted without being modified. In subsequent commits: - chaining can be used more, making the code more concise; - more use of chaining also permits the removal of redundant diagnostic APIs like `struct_err_with_code`, which can be replaced easily with `struct_err` + `code_mv`; - `emit_without_diagnostic` can be removed, which simplifies a lot of machinery, removing the need for `DiagnosticBuilderState`.
2024-01-03 12:17:35 +11:00
Err(err) => {
err.emit();
}
2020-03-17 08:29:34 +01:00
}
while p.token != token::Eof {
p.bump();
}
None
}
/// Interpreting `tts` as a comma-separated sequence of expressions,
2019-02-08 14:53:55 +01:00
/// expect exactly one string literal, or emit an error and return `None`.
2019-02-07 02:33:01 +09:00
pub fn get_single_str_from_tts(
cx: &mut ExtCtxt<'_>,
span: Span,
tts: TokenStream,
name: &str,
) -> Option<Symbol> {
let mut p = cx.new_parser_from_tts(tts);
2014-11-02 23:10:09 -08:00
if p.token == token::Eof {
cx.dcx().emit_err(errors::OnlyOneArgument { span, name });
2014-11-02 23:10:09 -08:00
return None;
}
2020-03-17 08:29:34 +01:00
let ret = parse_expr(&mut p)?;
let _ = p.eat(&token::Comma);
if p.token != token::Eof {
cx.dcx().emit_err(errors::OnlyOneArgument { span, name });
}
expr_to_string(cx, ret, "argument must be a string literal").map(|(s, _)| s)
}
2020-03-17 08:29:34 +01:00
/// Extracts comma-separated expressions from `tts`.
/// On error, emit it, and return `None`.
pub fn get_exprs_from_tts(cx: &mut ExtCtxt<'_>, tts: TokenStream) -> Option<Vec<P<ast::Expr>>> {
let mut p = cx.new_parser_from_tts(tts);
let mut es = Vec::new();
2014-10-27 19:22:52 +11:00
while p.token != token::Eof {
2020-03-17 08:29:34 +01:00
let expr = parse_expr(&mut p)?;
// Perform eager expansion on the expression.
// We want to be able to handle e.g., `concat!("foo", "bar")`.
let expr = cx.expander().fully_expand_fragment(AstFragment::Expr(expr)).make_expr();
Overhaul `syntax::fold::Folder`. This commit changes `syntax::fold::Folder` from a functional style (where most methods take a `T` and produce a new `T`) to a more imperative style (where most methods take and modify a `&mut T`), and renames it `syntax::mut_visit::MutVisitor`. The first benefit is speed. The functional style does not require any reallocations, due to the use of `P::map` and `MoveMap::move_{,flat_}map`. However, every field in the AST must be overwritten; even those fields that are unchanged are overwritten with the same value. This causes a lot of unnecessary memory writes. The imperative style reduces instruction counts by 1--3% across a wide range of workloads, particularly incremental workloads. The second benefit is conciseness; the imperative style is usually more concise. E.g. compare the old functional style: ``` fn fold_abc(&mut self, abc: ABC) { ABC { a: fold_a(abc.a), b: fold_b(abc.b), c: abc.c, } } ``` with the imperative style: ``` fn visit_abc(&mut self, ABC { a, b, c: _ }: &mut ABC) { visit_a(a); visit_b(b); } ``` (The reductions get larger in more complex examples.) Overall, the patch removes over 200 lines of code -- even though the new code has more comments -- and a lot of the remaining lines have fewer characters. Some notes: - The old style used methods called `fold_*`. The new style mostly uses methods called `visit_*`, but there are a few methods that map a `T` to something other than a `T`, which are called `flat_map_*` (`T` maps to multiple `T`s) or `filter_map_*` (`T` maps to 0 or 1 `T`s). - `move_map.rs`/`MoveMap`/`move_map`/`move_flat_map` are renamed `map_in_place.rs`/`MapInPlace`/`map_in_place`/`flat_map_in_place` to reflect their slightly changed signatures. - Although this commit renames the `fold` module as `mut_visit`, it keeps it in the `fold.rs` file, so as not to confuse git. The next commit will rename the file.
2019-02-05 15:20:55 +11:00
es.push(expr);
if p.eat(&token::Comma) {
continue;
}
2014-10-27 19:22:52 +11:00
if p.token != token::Eof {
cx.dcx().emit_err(errors::ExpectedCommaInList { span: p.token.span });
return None;
}
}
Some(es)
}
Extend `proc_macro_back_compat` lint to `procedural-masquerade` We now lint on *any* use of `procedural-masquerade` crate. While this crate still exists, its main reverse dependency (`cssparser`) no longer depends on it. Any crates still depending off should stop doing so, as it only exists to support very old Rust versions. If a crate actually needs to support old versions of rustc via `procedural-masquerade`, then they'll just need to accept the warning until we remove it entirely (at the same time as the back-compat hack). The latest version of `procedural-masquerade` does not work with the latest rustc, but trying to check for the version seems like more trouble than it's worth. While working on this, I realized that the `proc-macro-hack` check was never actually doing anything. The corresponding enum variant in `proc-macro-hack` is named `Value` or `Nested` - it has never been called `Input`. Due to a strange Crater issue, the Crater run that tested adding this did *not* end up testing it - some of the crates that would have failed did not actually have their tests checked, making it seem as though the `proc-macro-hack` check was working. The Crater issue is being discussed at https://rust-lang.zulipchat.com/#narrow/stream/242791-t-infra/topic/Nearly.20identical.20Crater.20runs.20processed.20a.20crate.20differently/near/230406661 Despite the `proc-macro-hack` check not actually doing anything, we haven't gotten any reports from users about their build being broken. I went ahead and removed it entirely, since it's clear that no one is being affected by the `proc-macro-hack` regression in practice.
2021-03-15 15:54:25 -04:00
pub fn parse_macro_name_and_helper_attrs(
2023-12-18 10:15:45 +11:00
dcx: &rustc_errors::DiagCtxt,
attr: &Attribute,
macro_type: &str,
) -> Option<(Symbol, Vec<Symbol>)> {
// Once we've located the `#[proc_macro_derive]` attribute, verify
// that it's of the form `#[proc_macro_derive(Foo)]` or
// `#[proc_macro_derive(Foo, attributes(A, ..))]`
2022-04-27 08:51:33 +01:00
let list = attr.meta_item_list()?;
if list.len() != 1 && list.len() != 2 {
2023-12-18 10:15:45 +11:00
dcx.emit_err(errors::AttrNoArguments { span: attr.span });
return None;
}
2022-02-19 00:48:49 +01:00
let Some(trait_attr) = list[0].meta_item() else {
2023-12-18 10:15:45 +11:00
dcx.emit_err(errors::NotAMetaItem { span: list[0].span() });
2022-02-19 00:48:49 +01:00
return None;
};
let trait_ident = match trait_attr.ident() {
Some(trait_ident) if trait_attr.is_word() => trait_ident,
_ => {
2023-12-18 10:15:45 +11:00
dcx.emit_err(errors::OnlyOneWord { span: trait_attr.span });
return None;
}
};
if !trait_ident.name.can_be_raw() {
2023-12-18 10:15:45 +11:00
dcx.emit_err(errors::CannotBeNameOfMacro {
span: trait_attr.span,
trait_ident,
macro_type,
});
}
let attributes_attr = list.get(1);
let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr {
if !attr.has_name(sym::attributes) {
2023-12-18 10:15:45 +11:00
dcx.emit_err(errors::ArgumentNotAttributes { span: attr.span() });
}
attr.meta_item_list()
.unwrap_or_else(|| {
2023-12-18 10:15:45 +11:00
dcx.emit_err(errors::AttributesWrongForm { span: attr.span() });
&[]
})
.iter()
.filter_map(|attr| {
2022-02-19 00:48:49 +01:00
let Some(attr) = attr.meta_item() else {
2023-12-18 10:15:45 +11:00
dcx.emit_err(errors::AttributeMetaItem { span: attr.span() });
2022-02-19 00:48:49 +01:00
return None;
};
let ident = match attr.ident() {
Some(ident) if attr.is_word() => ident,
_ => {
2023-12-18 10:15:45 +11:00
dcx.emit_err(errors::AttributeSingleWord { span: attr.span });
return None;
}
};
if !ident.name.can_be_raw() {
2023-12-18 10:15:45 +11:00
dcx.emit_err(errors::HelperAttributeNameInvalid {
span: attr.span,
name: ident,
});
}
Some(ident.name)
})
.collect()
} else {
Vec::new()
};
Some((trait_ident.name, proc_attrs))
}
Extend `proc_macro_back_compat` lint to `procedural-masquerade` We now lint on *any* use of `procedural-masquerade` crate. While this crate still exists, its main reverse dependency (`cssparser`) no longer depends on it. Any crates still depending off should stop doing so, as it only exists to support very old Rust versions. If a crate actually needs to support old versions of rustc via `procedural-masquerade`, then they'll just need to accept the warning until we remove it entirely (at the same time as the back-compat hack). The latest version of `procedural-masquerade` does not work with the latest rustc, but trying to check for the version seems like more trouble than it's worth. While working on this, I realized that the `proc-macro-hack` check was never actually doing anything. The corresponding enum variant in `proc-macro-hack` is named `Value` or `Nested` - it has never been called `Input`. Due to a strange Crater issue, the Crater run that tested adding this did *not* end up testing it - some of the crates that would have failed did not actually have their tests checked, making it seem as though the `proc-macro-hack` check was working. The Crater issue is being discussed at https://rust-lang.zulipchat.com/#narrow/stream/242791-t-infra/topic/Nearly.20identical.20Crater.20runs.20processed.20a.20crate.20differently/near/230406661 Despite the `proc-macro-hack` check not actually doing anything, we haven't gotten any reports from users about their build being broken. I went ahead and removed it entirely, since it's clear that no one is being affected by the `proc-macro-hack` regression in practice.
2021-03-15 15:54:25 -04:00
/// This nonterminal looks like some specific enums from
/// `proc-macro-hack` and `procedural-masquerade` crates.
/// We need to maintain some special pretty-printing behavior for them due to incorrect
/// asserts in old versions of those crates and their wide use in the ecosystem.
/// See issue #73345 for more details.
/// FIXME(#73933): Remove this eventually.
fn pretty_printing_compatibility_hack(item: &Item, sess: &Session) -> bool {
Extend `proc_macro_back_compat` lint to `procedural-masquerade` We now lint on *any* use of `procedural-masquerade` crate. While this crate still exists, its main reverse dependency (`cssparser`) no longer depends on it. Any crates still depending off should stop doing so, as it only exists to support very old Rust versions. If a crate actually needs to support old versions of rustc via `procedural-masquerade`, then they'll just need to accept the warning until we remove it entirely (at the same time as the back-compat hack). The latest version of `procedural-masquerade` does not work with the latest rustc, but trying to check for the version seems like more trouble than it's worth. While working on this, I realized that the `proc-macro-hack` check was never actually doing anything. The corresponding enum variant in `proc-macro-hack` is named `Value` or `Nested` - it has never been called `Input`. Due to a strange Crater issue, the Crater run that tested adding this did *not* end up testing it - some of the crates that would have failed did not actually have their tests checked, making it seem as though the `proc-macro-hack` check was working. The Crater issue is being discussed at https://rust-lang.zulipchat.com/#narrow/stream/242791-t-infra/topic/Nearly.20identical.20Crater.20runs.20processed.20a.20crate.20differently/near/230406661 Despite the `proc-macro-hack` check not actually doing anything, we haven't gotten any reports from users about their build being broken. I went ahead and removed it entirely, since it's clear that no one is being affected by the `proc-macro-hack` regression in practice.
2021-03-15 15:54:25 -04:00
let name = item.ident.name;
if name == sym::ProceduralMasqueradeDummyType {
if let ast::ItemKind::Enum(enum_def, _) = &item.kind {
if let [variant] = &*enum_def.variants {
if variant.ident.name == sym::Input {
let filename = sess.source_map().span_to_filename(item.ident.span);
if let FileName::Real(real) = filename {
if let Some(c) = real
.local_path()
.unwrap_or(Path::new(""))
.components()
.flat_map(|c| c.as_os_str().to_str())
.find(|c| c.starts_with("rental") || c.starts_with("allsorts-rental"))
{
let crate_matches = if c.starts_with("allsorts-rental") {
true
} else {
2022-11-18 10:30:47 +01:00
let mut version = c.trim_start_matches("rental-").split('.');
version.next() == Some("0")
&& version.next() == Some("5")
&& version
.next()
.and_then(|c| c.parse::<u32>().ok())
.is_some_and(|v| v < 6)
};
if crate_matches {
sess.parse_sess.buffer_lint_with_diagnostic(
PROC_MACRO_BACK_COMPAT,
item.ident.span,
ast::CRATE_NODE_ID,
"using an old version of `rental`",
BuiltinLintDiagnostics::ProcMacroBackCompat(
"older versions of the `rental` crate will stop compiling in future versions of Rust; \
please update to `rental` v0.5.6, or switch to one of the `rental` alternatives".to_string()
)
);
return true;
}
}
}
Extend `proc_macro_back_compat` lint to `procedural-masquerade` We now lint on *any* use of `procedural-masquerade` crate. While this crate still exists, its main reverse dependency (`cssparser`) no longer depends on it. Any crates still depending off should stop doing so, as it only exists to support very old Rust versions. If a crate actually needs to support old versions of rustc via `procedural-masquerade`, then they'll just need to accept the warning until we remove it entirely (at the same time as the back-compat hack). The latest version of `procedural-masquerade` does not work with the latest rustc, but trying to check for the version seems like more trouble than it's worth. While working on this, I realized that the `proc-macro-hack` check was never actually doing anything. The corresponding enum variant in `proc-macro-hack` is named `Value` or `Nested` - it has never been called `Input`. Due to a strange Crater issue, the Crater run that tested adding this did *not* end up testing it - some of the crates that would have failed did not actually have their tests checked, making it seem as though the `proc-macro-hack` check was working. The Crater issue is being discussed at https://rust-lang.zulipchat.com/#narrow/stream/242791-t-infra/topic/Nearly.20identical.20Crater.20runs.20processed.20a.20crate.20differently/near/230406661 Despite the `proc-macro-hack` check not actually doing anything, we haven't gotten any reports from users about their build being broken. I went ahead and removed it entirely, since it's clear that no one is being affected by the `proc-macro-hack` regression in practice.
2021-03-15 15:54:25 -04:00
}
}
}
}
false
}
pub(crate) fn ann_pretty_printing_compatibility_hack(ann: &Annotatable, sess: &Session) -> bool {
let item = match ann {
Annotatable::Item(item) => item,
Annotatable::Stmt(stmt) => match &stmt.kind {
ast::StmtKind::Item(item) => item,
_ => return false,
},
_ => return false,
};
pretty_printing_compatibility_hack(item, sess)
}
pub(crate) fn nt_pretty_printing_compatibility_hack(nt: &Nonterminal, sess: &Session) -> bool {
let item = match nt {
Nonterminal::NtItem(item) => item,
Nonterminal::NtStmt(stmt) => match &stmt.kind {
ast::StmtKind::Item(item) => item,
_ => return false,
},
_ => return false,
};
pretty_printing_compatibility_hack(item, sess)
}