expand: Leave traces when expanding cfg_attr
attributes
This commit is contained in:
parent
9bad8ac498
commit
9dd4e4cad1
10 changed files with 136 additions and 19 deletions
|
@ -336,6 +336,7 @@ impl<'a> AstValidator<'a> {
|
||||||
sym::allow,
|
sym::allow,
|
||||||
sym::cfg,
|
sym::cfg,
|
||||||
sym::cfg_attr,
|
sym::cfg_attr,
|
||||||
|
sym::cfg_attr_trace,
|
||||||
sym::deny,
|
sym::deny,
|
||||||
sym::expect,
|
sym::expect,
|
||||||
sym::forbid,
|
sym::forbid,
|
||||||
|
|
|
@ -578,20 +578,26 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
||||||
let mut printed = false;
|
let mut printed = false;
|
||||||
for attr in attrs {
|
for attr in attrs {
|
||||||
if attr.style == kind {
|
if attr.style == kind {
|
||||||
self.print_attribute_inline(attr, is_inline);
|
if self.print_attribute_inline(attr, is_inline) {
|
||||||
if is_inline {
|
if is_inline {
|
||||||
self.nbsp();
|
self.nbsp();
|
||||||
}
|
}
|
||||||
printed = true;
|
printed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if printed && trailing_hardbreak && !is_inline {
|
if printed && trailing_hardbreak && !is_inline {
|
||||||
self.hardbreak_if_not_bol();
|
self.hardbreak_if_not_bol();
|
||||||
}
|
}
|
||||||
printed
|
printed
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_attribute_inline(&mut self, attr: &ast::Attribute, is_inline: bool) {
|
fn print_attribute_inline(&mut self, attr: &ast::Attribute, is_inline: bool) -> bool {
|
||||||
|
if attr.has_name(sym::cfg_attr_trace) {
|
||||||
|
// It's not a valid identifier, so avoid printing it
|
||||||
|
// to keep the printed code reasonably parse-able.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if !is_inline {
|
if !is_inline {
|
||||||
self.hardbreak_if_not_bol();
|
self.hardbreak_if_not_bol();
|
||||||
}
|
}
|
||||||
|
@ -610,6 +616,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
||||||
self.hardbreak()
|
self.hardbreak()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_attr_item(&mut self, item: &ast::AttrItem, span: Span) {
|
fn print_attr_item(&mut self, item: &ast::AttrItem, span: Span) {
|
||||||
|
@ -2047,7 +2054,7 @@ impl<'a> State<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_attribute(&mut self, attr: &ast::Attribute) {
|
fn print_attribute(&mut self, attr: &ast::Attribute) {
|
||||||
self.print_attribute_inline(attr, false)
|
self.print_attribute_inline(attr, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_meta_list_item(&mut self, item: &ast::MetaItemInner) {
|
fn print_meta_list_item(&mut self, item: &ast::MetaItemInner) {
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
//! Conditional compilation stripping.
|
//! Conditional compilation stripping.
|
||||||
|
|
||||||
|
use std::iter;
|
||||||
|
|
||||||
use rustc_ast::ptr::P;
|
use rustc_ast::ptr::P;
|
||||||
use rustc_ast::token::{Delimiter, Token, TokenKind};
|
use rustc_ast::token::{Delimiter, Token, TokenKind};
|
||||||
use rustc_ast::tokenstream::{
|
use rustc_ast::tokenstream::{
|
||||||
AttrTokenStream, AttrTokenTree, LazyAttrTokenStream, Spacing, TokenTree,
|
AttrTokenStream, AttrTokenTree, LazyAttrTokenStream, Spacing, TokenTree,
|
||||||
};
|
};
|
||||||
use rustc_ast::{
|
use rustc_ast::{
|
||||||
self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, MetaItemInner, NodeId,
|
self as ast, AttrKind, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, MetaItemInner,
|
||||||
|
NodeId, NormalAttr,
|
||||||
};
|
};
|
||||||
use rustc_attr_parsing as attr;
|
use rustc_attr_parsing as attr;
|
||||||
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
|
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
|
||||||
|
@ -275,10 +278,23 @@ impl<'a> StripUnconfigured<'a> {
|
||||||
pub(crate) fn expand_cfg_attr(&self, cfg_attr: &Attribute, recursive: bool) -> Vec<Attribute> {
|
pub(crate) fn expand_cfg_attr(&self, cfg_attr: &Attribute, recursive: bool) -> Vec<Attribute> {
|
||||||
validate_attr::check_attribute_safety(&self.sess.psess, AttributeSafety::Normal, &cfg_attr);
|
validate_attr::check_attribute_safety(&self.sess.psess, AttributeSafety::Normal, &cfg_attr);
|
||||||
|
|
||||||
|
// A trace attribute left in AST in place of the original `cfg_attr` attribute.
|
||||||
|
// It can later be used by lints or other diagnostics.
|
||||||
|
let mut trace_attr = cfg_attr.clone();
|
||||||
|
match &mut trace_attr.kind {
|
||||||
|
AttrKind::Normal(normal) => {
|
||||||
|
let NormalAttr { item, tokens } = &mut **normal;
|
||||||
|
item.path.segments[0].ident.name = sym::cfg_attr_trace;
|
||||||
|
// This makes the trace attributes unobservable to token-based proc macros.
|
||||||
|
*tokens = Some(LazyAttrTokenStream::new(AttrTokenStream::default()));
|
||||||
|
}
|
||||||
|
AttrKind::DocComment(..) => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
let Some((cfg_predicate, expanded_attrs)) =
|
let Some((cfg_predicate, expanded_attrs)) =
|
||||||
rustc_parse::parse_cfg_attr(cfg_attr, &self.sess.psess)
|
rustc_parse::parse_cfg_attr(cfg_attr, &self.sess.psess)
|
||||||
else {
|
else {
|
||||||
return vec![];
|
return vec![trace_attr];
|
||||||
};
|
};
|
||||||
|
|
||||||
// Lint on zero attributes in source.
|
// Lint on zero attributes in source.
|
||||||
|
@ -292,22 +308,21 @@ impl<'a> StripUnconfigured<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !attr::cfg_matches(&cfg_predicate, &self.sess, self.lint_node_id, self.features) {
|
if !attr::cfg_matches(&cfg_predicate, &self.sess, self.lint_node_id, self.features) {
|
||||||
return vec![];
|
return vec![trace_attr];
|
||||||
}
|
}
|
||||||
|
|
||||||
if recursive {
|
if recursive {
|
||||||
// We call `process_cfg_attr` recursively in case there's a
|
// We call `process_cfg_attr` recursively in case there's a
|
||||||
// `cfg_attr` inside of another `cfg_attr`. E.g.
|
// `cfg_attr` inside of another `cfg_attr`. E.g.
|
||||||
// `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
|
// `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
|
||||||
expanded_attrs
|
let expanded_attrs = expanded_attrs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|item| self.process_cfg_attr(&self.expand_cfg_attr_item(cfg_attr, item)))
|
.flat_map(|item| self.process_cfg_attr(&self.expand_cfg_attr_item(cfg_attr, item)));
|
||||||
.collect()
|
iter::once(trace_attr).chain(expanded_attrs).collect()
|
||||||
} else {
|
} else {
|
||||||
expanded_attrs
|
let expanded_attrs =
|
||||||
.into_iter()
|
expanded_attrs.into_iter().map(|item| self.expand_cfg_attr_item(cfg_attr, item));
|
||||||
.map(|item| self.expand_cfg_attr_item(cfg_attr, item))
|
iter::once(trace_attr).chain(expanded_attrs).collect()
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -752,6 +752,14 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||||
template!(Word, List: r#""...""#), DuplicatesOk,
|
template!(Word, List: r#""...""#), DuplicatesOk,
|
||||||
EncodeCrossCrate::Yes, INTERNAL_UNSTABLE
|
EncodeCrossCrate::Yes, INTERNAL_UNSTABLE
|
||||||
),
|
),
|
||||||
|
// Trace that is left when a `cfg_attr` attribute is expanded.
|
||||||
|
// The attribute is not gated, to avoid stability errors, but it cannot be used in stable or
|
||||||
|
// unstable code directly because `sym::cfg_attr_trace` is not a valid identifier, it can only
|
||||||
|
// be generated by the compiler.
|
||||||
|
ungated!(
|
||||||
|
cfg_attr_trace, Normal, template!(Word /* irrelevant */), DuplicatesOk,
|
||||||
|
EncodeCrossCrate::No
|
||||||
|
),
|
||||||
|
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
// Internal attributes, Diagnostics related:
|
// Internal attributes, Diagnostics related:
|
||||||
|
|
|
@ -16,7 +16,7 @@ use rustc_span::{Span, Symbol, sym};
|
||||||
use crate::{errors, parse_in};
|
use crate::{errors, parse_in};
|
||||||
|
|
||||||
pub fn check_attr(psess: &ParseSess, attr: &Attribute) {
|
pub fn check_attr(psess: &ParseSess, attr: &Attribute) {
|
||||||
if attr.is_doc_comment() {
|
if attr.is_doc_comment() || attr.has_name(sym::cfg_attr_trace) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -272,6 +272,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
||||||
| sym::forbid
|
| sym::forbid
|
||||||
| sym::cfg
|
| sym::cfg
|
||||||
| sym::cfg_attr
|
| sym::cfg_attr
|
||||||
|
| sym::cfg_attr_trace
|
||||||
// need to be fixed
|
// need to be fixed
|
||||||
| sym::cfi_encoding // FIXME(cfi_encoding)
|
| sym::cfi_encoding // FIXME(cfi_encoding)
|
||||||
| sym::pointee // FIXME(derive_coerce_pointee)
|
| sym::pointee // FIXME(derive_coerce_pointee)
|
||||||
|
@ -575,6 +576,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
||||||
// conditional compilation
|
// conditional compilation
|
||||||
sym::cfg,
|
sym::cfg,
|
||||||
sym::cfg_attr,
|
sym::cfg_attr,
|
||||||
|
sym::cfg_attr_trace,
|
||||||
// testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`)
|
// testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`)
|
||||||
sym::test,
|
sym::test,
|
||||||
sym::ignore,
|
sym::ignore,
|
||||||
|
@ -2641,7 +2643,7 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
|
||||||
// only `#[cfg]` and `#[cfg_attr]` are allowed, but it should be removed
|
// only `#[cfg]` and `#[cfg_attr]` are allowed, but it should be removed
|
||||||
// if we allow more attributes (e.g., tool attributes and `allow/deny/warn`)
|
// if we allow more attributes (e.g., tool attributes and `allow/deny/warn`)
|
||||||
// in where clauses. After that, only `self.check_attributes` should be enough.
|
// in where clauses. After that, only `self.check_attributes` should be enough.
|
||||||
const ATTRS_ALLOWED: &[Symbol] = &[sym::cfg, sym::cfg_attr];
|
const ATTRS_ALLOWED: &[Symbol] = &[sym::cfg, sym::cfg_attr, sym::cfg_attr_trace];
|
||||||
let spans = self
|
let spans = self
|
||||||
.tcx
|
.tcx
|
||||||
.hir_attrs(where_predicate.hir_id)
|
.hir_attrs(where_predicate.hir_id)
|
||||||
|
|
|
@ -591,6 +591,7 @@ symbols! {
|
||||||
cfg_accessible,
|
cfg_accessible,
|
||||||
cfg_attr,
|
cfg_attr,
|
||||||
cfg_attr_multi,
|
cfg_attr_multi,
|
||||||
|
cfg_attr_trace: "<cfg_attr>", // must not be a valid identifier
|
||||||
cfg_boolean_literals,
|
cfg_boolean_literals,
|
||||||
cfg_contract_checks,
|
cfg_contract_checks,
|
||||||
cfg_doctest,
|
cfg_doctest,
|
||||||
|
|
|
@ -36,7 +36,11 @@ fn check_duplicated_attr(
|
||||||
}
|
}
|
||||||
let Some(ident) = attr.ident() else { return };
|
let Some(ident) = attr.ident() else { return };
|
||||||
let name = ident.name;
|
let name = ident.name;
|
||||||
if name == sym::doc || name == sym::cfg_attr || name == sym::rustc_on_unimplemented || name == sym::reason {
|
if name == sym::doc
|
||||||
|
|| name == sym::cfg_attr
|
||||||
|
|| name == sym::cfg_attr_trace
|
||||||
|
|| name == sym::rustc_on_unimplemented
|
||||||
|
|| name == sym::reason {
|
||||||
// FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg
|
// FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg
|
||||||
// conditions are the same.
|
// conditions are the same.
|
||||||
// `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected.
|
// `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected.
|
||||||
|
|
17
tests/ui/proc-macro/cfg-attr-trace.rs
Normal file
17
tests/ui/proc-macro/cfg-attr-trace.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// Ensure that `cfg_attr_trace` attributes aren't observable by proc-macros.
|
||||||
|
|
||||||
|
//@ check-pass
|
||||||
|
//@ proc-macro: test-macros.rs
|
||||||
|
|
||||||
|
#![feature(cfg_eval)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate test_macros;
|
||||||
|
|
||||||
|
#[cfg_eval]
|
||||||
|
#[test_macros::print_attr]
|
||||||
|
#[cfg_attr(FALSE, test_macros::print_attr)]
|
||||||
|
#[cfg_attr(all(), test_macros::print_attr)]
|
||||||
|
struct S;
|
||||||
|
|
||||||
|
fn main() {}
|
62
tests/ui/proc-macro/cfg-attr-trace.stdout
Normal file
62
tests/ui/proc-macro/cfg-attr-trace.stdout
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
PRINT-ATTR INPUT (DISPLAY): #[test_macros::print_attr] struct S;
|
||||||
|
PRINT-ATTR DEEP-RE-COLLECTED (DISPLAY): #[test_macros :: print_attr] struct S;
|
||||||
|
PRINT-ATTR INPUT (DEBUG): TokenStream [
|
||||||
|
Punct {
|
||||||
|
ch: '#',
|
||||||
|
spacing: Alone,
|
||||||
|
span: #0 bytes(271..272),
|
||||||
|
},
|
||||||
|
Group {
|
||||||
|
delimiter: Bracket,
|
||||||
|
stream: TokenStream [
|
||||||
|
Ident {
|
||||||
|
ident: "test_macros",
|
||||||
|
span: #0 bytes(289..300),
|
||||||
|
},
|
||||||
|
Punct {
|
||||||
|
ch: ':',
|
||||||
|
spacing: Joint,
|
||||||
|
span: #0 bytes(300..301),
|
||||||
|
},
|
||||||
|
Punct {
|
||||||
|
ch: ':',
|
||||||
|
spacing: Alone,
|
||||||
|
span: #0 bytes(301..302),
|
||||||
|
},
|
||||||
|
Ident {
|
||||||
|
ident: "print_attr",
|
||||||
|
span: #0 bytes(302..312),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: #0 bytes(272..314),
|
||||||
|
},
|
||||||
|
Ident {
|
||||||
|
ident: "struct",
|
||||||
|
span: #0 bytes(315..321),
|
||||||
|
},
|
||||||
|
Ident {
|
||||||
|
ident: "S",
|
||||||
|
span: #0 bytes(322..323),
|
||||||
|
},
|
||||||
|
Punct {
|
||||||
|
ch: ';',
|
||||||
|
spacing: Alone,
|
||||||
|
span: #0 bytes(323..324),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
PRINT-ATTR INPUT (DISPLAY): struct S;
|
||||||
|
PRINT-ATTR INPUT (DEBUG): TokenStream [
|
||||||
|
Ident {
|
||||||
|
ident: "struct",
|
||||||
|
span: #0 bytes(315..321),
|
||||||
|
},
|
||||||
|
Ident {
|
||||||
|
ident: "S",
|
||||||
|
span: #0 bytes(322..323),
|
||||||
|
},
|
||||||
|
Punct {
|
||||||
|
ch: ';',
|
||||||
|
spacing: Alone,
|
||||||
|
span: #0 bytes(323..324),
|
||||||
|
},
|
||||||
|
]
|
Loading…
Add table
Add a link
Reference in a new issue