Skip to content

Commit 9dd4e4c

Browse files
committed
expand: Leave traces when expanding cfg_attr attributes
1 parent 9bad8ac commit 9dd4e4c

File tree

10 files changed

+136
-19
lines changed

10 files changed

+136
-19
lines changed

compiler/rustc_ast_passes/src/ast_validation.rs

+1
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@ impl<'a> AstValidator<'a> {
336336
sym::allow,
337337
sym::cfg,
338338
sym::cfg_attr,
339+
sym::cfg_attr_trace,
339340
sym::deny,
340341
sym::expect,
341342
sym::forbid,

compiler/rustc_ast_pretty/src/pprust/state.rs

+13-6
Original file line numberDiff line numberDiff line change
@@ -578,11 +578,12 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
578578
let mut printed = false;
579579
for attr in attrs {
580580
if attr.style == kind {
581-
self.print_attribute_inline(attr, is_inline);
582-
if is_inline {
583-
self.nbsp();
581+
if self.print_attribute_inline(attr, is_inline) {
582+
if is_inline {
583+
self.nbsp();
584+
}
585+
printed = true;
584586
}
585-
printed = true;
586587
}
587588
}
588589
if printed && trailing_hardbreak && !is_inline {
@@ -591,7 +592,12 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
591592
printed
592593
}
593594

594-
fn print_attribute_inline(&mut self, attr: &ast::Attribute, is_inline: bool) {
595+
fn print_attribute_inline(&mut self, attr: &ast::Attribute, is_inline: bool) -> bool {
596+
if attr.has_name(sym::cfg_attr_trace) {
597+
// It's not a valid identifier, so avoid printing it
598+
// to keep the printed code reasonably parse-able.
599+
return false;
600+
}
595601
if !is_inline {
596602
self.hardbreak_if_not_bol();
597603
}
@@ -610,6 +616,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
610616
self.hardbreak()
611617
}
612618
}
619+
true
613620
}
614621

615622
fn print_attr_item(&mut self, item: &ast::AttrItem, span: Span) {
@@ -2047,7 +2054,7 @@ impl<'a> State<'a> {
20472054
}
20482055

20492056
fn print_attribute(&mut self, attr: &ast::Attribute) {
2050-
self.print_attribute_inline(attr, false)
2057+
self.print_attribute_inline(attr, false);
20512058
}
20522059

20532060
fn print_meta_list_item(&mut self, item: &ast::MetaItemInner) {

compiler/rustc_expand/src/config.rs

+25-10
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
//! Conditional compilation stripping.
22
3+
use std::iter;
4+
35
use rustc_ast::ptr::P;
46
use rustc_ast::token::{Delimiter, Token, TokenKind};
57
use rustc_ast::tokenstream::{
68
AttrTokenStream, AttrTokenTree, LazyAttrTokenStream, Spacing, TokenTree,
79
};
810
use rustc_ast::{
9-
self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, MetaItemInner, NodeId,
11+
self as ast, AttrKind, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, MetaItemInner,
12+
NodeId, NormalAttr,
1013
};
1114
use rustc_attr_parsing as attr;
1215
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
@@ -275,10 +278,23 @@ impl<'a> StripUnconfigured<'a> {
275278
pub(crate) fn expand_cfg_attr(&self, cfg_attr: &Attribute, recursive: bool) -> Vec<Attribute> {
276279
validate_attr::check_attribute_safety(&self.sess.psess, AttributeSafety::Normal, &cfg_attr);
277280

281+
// A trace attribute left in AST in place of the original `cfg_attr` attribute.
282+
// It can later be used by lints or other diagnostics.
283+
let mut trace_attr = cfg_attr.clone();
284+
match &mut trace_attr.kind {
285+
AttrKind::Normal(normal) => {
286+
let NormalAttr { item, tokens } = &mut **normal;
287+
item.path.segments[0].ident.name = sym::cfg_attr_trace;
288+
// This makes the trace attributes unobservable to token-based proc macros.
289+
*tokens = Some(LazyAttrTokenStream::new(AttrTokenStream::default()));
290+
}
291+
AttrKind::DocComment(..) => unreachable!(),
292+
}
293+
278294
let Some((cfg_predicate, expanded_attrs)) =
279295
rustc_parse::parse_cfg_attr(cfg_attr, &self.sess.psess)
280296
else {
281-
return vec![];
297+
return vec![trace_attr];
282298
};
283299

284300
// Lint on zero attributes in source.
@@ -292,22 +308,21 @@ impl<'a> StripUnconfigured<'a> {
292308
}
293309

294310
if !attr::cfg_matches(&cfg_predicate, &self.sess, self.lint_node_id, self.features) {
295-
return vec![];
311+
return vec![trace_attr];
296312
}
297313

298314
if recursive {
299315
// We call `process_cfg_attr` recursively in case there's a
300316
// `cfg_attr` inside of another `cfg_attr`. E.g.
301317
// `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
302-
expanded_attrs
318+
let expanded_attrs = expanded_attrs
303319
.into_iter()
304-
.flat_map(|item| self.process_cfg_attr(&self.expand_cfg_attr_item(cfg_attr, item)))
305-
.collect()
320+
.flat_map(|item| self.process_cfg_attr(&self.expand_cfg_attr_item(cfg_attr, item)));
321+
iter::once(trace_attr).chain(expanded_attrs).collect()
306322
} else {
307-
expanded_attrs
308-
.into_iter()
309-
.map(|item| self.expand_cfg_attr_item(cfg_attr, item))
310-
.collect()
323+
let expanded_attrs =
324+
expanded_attrs.into_iter().map(|item| self.expand_cfg_attr_item(cfg_attr, item));
325+
iter::once(trace_attr).chain(expanded_attrs).collect()
311326
}
312327
}
313328

compiler/rustc_feature/src/builtin_attrs.rs

+8
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,14 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
752752
template!(Word, List: r#""...""#), DuplicatesOk,
753753
EncodeCrossCrate::Yes, INTERNAL_UNSTABLE
754754
),
755+
// Trace that is left when a `cfg_attr` attribute is expanded.
756+
// The attribute is not gated, to avoid stability errors, but it cannot be used in stable or
757+
// unstable code directly because `sym::cfg_attr_trace` is not a valid identifier, it can only
758+
// be generated by the compiler.
759+
ungated!(
760+
cfg_attr_trace, Normal, template!(Word /* irrelevant */), DuplicatesOk,
761+
EncodeCrossCrate::No
762+
),
755763

756764
// ==========================================================================
757765
// Internal attributes, Diagnostics related:

compiler/rustc_parse/src/validate_attr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_span::{Span, Symbol, sym};
1616
use crate::{errors, parse_in};
1717

1818
pub fn check_attr(psess: &ParseSess, attr: &Attribute) {
19-
if attr.is_doc_comment() {
19+
if attr.is_doc_comment() || attr.has_name(sym::cfg_attr_trace) {
2020
return;
2121
}
2222

compiler/rustc_passes/src/check_attr.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
272272
| sym::forbid
273273
| sym::cfg
274274
| sym::cfg_attr
275+
| sym::cfg_attr_trace
275276
// need to be fixed
276277
| sym::cfi_encoding // FIXME(cfi_encoding)
277278
| sym::pointee // FIXME(derive_coerce_pointee)
@@ -575,6 +576,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
575576
// conditional compilation
576577
sym::cfg,
577578
sym::cfg_attr,
579+
sym::cfg_attr_trace,
578580
// testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`)
579581
sym::test,
580582
sym::ignore,
@@ -2641,7 +2643,7 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
26412643
// only `#[cfg]` and `#[cfg_attr]` are allowed, but it should be removed
26422644
// if we allow more attributes (e.g., tool attributes and `allow/deny/warn`)
26432645
// in where clauses. After that, only `self.check_attributes` should be enough.
2644-
const ATTRS_ALLOWED: &[Symbol] = &[sym::cfg, sym::cfg_attr];
2646+
const ATTRS_ALLOWED: &[Symbol] = &[sym::cfg, sym::cfg_attr, sym::cfg_attr_trace];
26452647
let spans = self
26462648
.tcx
26472649
.hir_attrs(where_predicate.hir_id)

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,7 @@ symbols! {
591591
cfg_accessible,
592592
cfg_attr,
593593
cfg_attr_multi,
594+
cfg_attr_trace: "<cfg_attr>", // must not be a valid identifier
594595
cfg_boolean_literals,
595596
cfg_contract_checks,
596597
cfg_doctest,

src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@ fn check_duplicated_attr(
3636
}
3737
let Some(ident) = attr.ident() else { return };
3838
let name = ident.name;
39-
if name == sym::doc || name == sym::cfg_attr || name == sym::rustc_on_unimplemented || name == sym::reason {
39+
if name == sym::doc
40+
|| name == sym::cfg_attr
41+
|| name == sym::cfg_attr_trace
42+
|| name == sym::rustc_on_unimplemented
43+
|| name == sym::reason {
4044
// FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg
4145
// conditions are the same.
4246
// `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected.

tests/ui/proc-macro/cfg-attr-trace.rs

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Ensure that `cfg_attr_trace` attributes aren't observable by proc-macros.
2+
3+
//@ check-pass
4+
//@ proc-macro: test-macros.rs
5+
6+
#![feature(cfg_eval)]
7+
8+
#[macro_use]
9+
extern crate test_macros;
10+
11+
#[cfg_eval]
12+
#[test_macros::print_attr]
13+
#[cfg_attr(FALSE, test_macros::print_attr)]
14+
#[cfg_attr(all(), test_macros::print_attr)]
15+
struct S;
16+
17+
fn main() {}
+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
PRINT-ATTR INPUT (DISPLAY): #[test_macros::print_attr] struct S;
2+
PRINT-ATTR DEEP-RE-COLLECTED (DISPLAY): #[test_macros :: print_attr] struct S;
3+
PRINT-ATTR INPUT (DEBUG): TokenStream [
4+
Punct {
5+
ch: '#',
6+
spacing: Alone,
7+
span: #0 bytes(271..272),
8+
},
9+
Group {
10+
delimiter: Bracket,
11+
stream: TokenStream [
12+
Ident {
13+
ident: "test_macros",
14+
span: #0 bytes(289..300),
15+
},
16+
Punct {
17+
ch: ':',
18+
spacing: Joint,
19+
span: #0 bytes(300..301),
20+
},
21+
Punct {
22+
ch: ':',
23+
spacing: Alone,
24+
span: #0 bytes(301..302),
25+
},
26+
Ident {
27+
ident: "print_attr",
28+
span: #0 bytes(302..312),
29+
},
30+
],
31+
span: #0 bytes(272..314),
32+
},
33+
Ident {
34+
ident: "struct",
35+
span: #0 bytes(315..321),
36+
},
37+
Ident {
38+
ident: "S",
39+
span: #0 bytes(322..323),
40+
},
41+
Punct {
42+
ch: ';',
43+
spacing: Alone,
44+
span: #0 bytes(323..324),
45+
},
46+
]
47+
PRINT-ATTR INPUT (DISPLAY): struct S;
48+
PRINT-ATTR INPUT (DEBUG): TokenStream [
49+
Ident {
50+
ident: "struct",
51+
span: #0 bytes(315..321),
52+
},
53+
Ident {
54+
ident: "S",
55+
span: #0 bytes(322..323),
56+
},
57+
Punct {
58+
ch: ';',
59+
spacing: Alone,
60+
span: #0 bytes(323..324),
61+
},
62+
]

0 commit comments

Comments
 (0)