Skip to content

Commit 4ff5353

Browse files
committed
Replace cfg_attr in AST with rustc-cfg-placeholder for accurate span tracking
Previously, when evaluating a `#[cfg_attr(..)]` to false, the entire attribute was removed from the AST. Afterwards, we insert in its place a `#[rustc-cfg-placeholder]` attribute so that checks for attributes can still know about their placement. This is particularly relevant when we suggest removing items with `cfg_attr`s (fix #56328). We use `rustc-cfg-placeholder` as it is an ident that can't be written by the end user to begin with. We tweak the wording of the existing "unused `extern crate`" lint. ``` warning: unused `extern crate` --> $DIR/removing-extern-crate.rs:9:1 | LL | extern crate removing_extern_crate as foo; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused | note: the lint level is defined here --> $DIR/removing-extern-crate.rs:6:9 | LL | #![warn(rust_2018_idioms)] | ^^^^^^^^^^^^^^^^ = note: `#[warn(unused_extern_crates)]` implied by `#[warn(rust_2018_idioms)]` help: remove the unused `extern crate` | LL - #[cfg_attr(test, macro_use)] LL - extern crate removing_extern_crate as foo; LL + | ```
1 parent 5e1440a commit 4ff5353

34 files changed

+210
-76
lines changed

compiler/rustc_ast/src/ast.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3006,6 +3006,10 @@ impl AttrItem {
30063006
|| self.path == sym::allow
30073007
|| self.path == sym::deny
30083008
}
3009+
3010+
pub fn is_cfg_placeholder(&self) -> bool {
3011+
self.path == sym::rustc_cfg_placeholder
3012+
}
30093013
}
30103014

30113015
/// `TraitRef`s appear in impls.

compiler/rustc_ast/src/attr/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ impl Attribute {
226226

227227
pub fn token_trees(&self) -> Vec<TokenTree> {
228228
match self.kind {
229+
AttrKind::Normal(ref normal) if normal.item.is_cfg_placeholder() => vec![],
229230
AttrKind::Normal(ref normal) => normal
230231
.tokens
231232
.as_ref()

compiler/rustc_ast_passes/src/ast_validation.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,10 @@ impl<'a> AstValidator<'a> {
340340
sym::deny,
341341
sym::expect,
342342
sym::forbid,
343+
// `cfg` and `cfg_attr` get replaced with an inert `rustc_cfg_placeholder` to
344+
// keep the attribute "spot" in the AST. This allows suggestions to remove an
345+
// item to provide a correct suggestion when `#[cfg_attr]`s are present.
346+
sym::rustc_cfg_placeholder,
343347
sym::warn,
344348
];
345349
!arr.contains(&attr.name_or_empty()) && rustc_attr::is_builtin_attr(attr)

compiler/rustc_expand/src/config.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,25 @@ impl<'a> StripUnconfigured<'a> {
296296
}
297297

298298
if !attr::cfg_matches(&cfg_predicate, &self.sess, self.lint_node_id, self.features) {
299-
return vec![];
299+
// `cfg` and `cfg_attr` gets replaced with an inert `rustc_cfg_placeholder` to keep the
300+
// attribute "spot" in the AST. This allows suggestions to remove an item to provide a
301+
// correct suggestion when `#[cfg_attr]`s are present.
302+
let item = ast::AttrItem {
303+
unsafety: ast::Safety::Default,
304+
path: ast::Path::from_ident(rustc_span::symbol::Ident::with_dummy_span(
305+
sym::rustc_cfg_placeholder,
306+
)),
307+
args: ast::AttrArgs::Empty,
308+
tokens: None,
309+
};
310+
let kind = ast::AttrKind::Normal(P(ast::NormalAttr { item, tokens: None }));
311+
let attr: ast::Attribute = ast::Attribute {
312+
kind,
313+
id: self.sess.psess.attr_id_generator.mk_attr_id(),
314+
style: ast::AttrStyle::Outer,
315+
span: cfg_attr.span,
316+
};
317+
return vec![attr];
300318
}
301319

302320
if recursive {

compiler/rustc_feature/src/builtin_attrs.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
750750
template!(Word, List: r#""...""#), DuplicatesOk,
751751
EncodeCrossCrate::No, INTERNAL_UNSTABLE
752752
),
753+
// placeholder that replaces `cfg_attr` in the item's attribute list
754+
ungated!(
755+
rustc_cfg_placeholder, Normal, template!(Word /* irrelevant */), DuplicatesOk,
756+
EncodeCrossCrate::No
757+
),
758+
753759

754760
// ==========================================================================
755761
// Internal attributes, Diagnostics related:

compiler/rustc_lint/messages.ftl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -927,8 +927,9 @@ lint_unused_doc_comment = unused doc comment
927927
.label = rustdoc does not generate documentation for macro invocations
928928
.help = to document an item produced by a macro, the macro must produce the documentation as part of its expansion
929929
930-
lint_unused_extern_crate = unused extern crate
931-
.suggestion = remove it
930+
lint_unused_extern_crate = unused `extern crate`
931+
.label = unused
932+
.suggestion = remove the unused `extern crate`
932933
933934
lint_unused_import_braces = braces around {$node} is unnecessary
934935

compiler/rustc_lint/src/context/diagnostics.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,8 +288,8 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: &
288288
BuiltinLintDiag::ByteSliceInPackedStructWithDerive { ty } => {
289289
lints::ByteSliceInPackedStructWithDerive { ty }.decorate_lint(diag);
290290
}
291-
BuiltinLintDiag::UnusedExternCrate { removal_span } => {
292-
lints::UnusedExternCrate { removal_span }.decorate_lint(diag);
291+
BuiltinLintDiag::UnusedExternCrate { span, removal_span } => {
292+
lints::UnusedExternCrate { span, removal_span }.decorate_lint(diag);
293293
}
294294
BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span } => {
295295
let suggestion_span = vis_span.between(ident_span);

compiler/rustc_lint/src/lints.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2907,7 +2907,9 @@ pub(crate) struct ByteSliceInPackedStructWithDerive {
29072907
#[derive(LintDiagnostic)]
29082908
#[diag(lint_unused_extern_crate)]
29092909
pub(crate) struct UnusedExternCrate {
2910-
#[suggestion(code = "", applicability = "machine-applicable")]
2910+
#[label]
2911+
pub span: Span,
2912+
#[suggestion(code = "", applicability = "machine-applicable", style = "verbose")]
29112913
pub removal_span: Span,
29122914
}
29132915

compiler/rustc_lint_defs/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,7 @@ pub enum BuiltinLintDiag {
706706
ty: String,
707707
},
708708
UnusedExternCrate {
709+
span: Span,
709710
removal_span: Span,
710711
},
711712
ExternCrateNotIdiomatic {

compiler/rustc_passes/src/check_attr.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
273273
| sym::prelude_import
274274
| sym::panic_handler
275275
| sym::allow_internal_unsafe
276+
| sym::rustc_cfg_placeholder // Inert, it's a placeholder in the AST.
276277
| sym::fundamental
277278
| sym::lang
278279
| sym::needs_allocator

0 commit comments

Comments
 (0)