Skip to content

Replace evaluated cfg_attr in AST with a placeholder attribute for accurate span tracking #133823

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3143,6 +3143,10 @@ impl AttrItem {
|| self.path == sym::allow
|| self.path == sym::deny
}

pub fn is_cfg_placeholder(&self) -> bool {
self.path == sym::rustc_cfg_placeholder
}
}

/// `TraitRef`s appear in impls.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/attr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ impl Attribute {

pub fn token_trees(&self) -> Vec<TokenTree> {
match self.kind {
AttrKind::Normal(ref normal) if normal.item.is_cfg_placeholder() => vec![],
AttrKind::Normal(ref normal) => normal
.tokens
.as_ref()
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,10 @@ impl<'a> AstValidator<'a> {
sym::deny,
sym::expect,
sym::forbid,
// `cfg` and `cfg_attr` get replaced with an inert `rustc_cfg_placeholder` to
// keep the attribute "spot" in the AST. This allows suggestions to remove an
// item to provide a correct suggestion when `#[cfg_attr]`s are present.
sym::rustc_cfg_placeholder,
sym::warn,
];
!arr.contains(&attr.name_or_empty()) && rustc_attr_parsing::is_builtin_attr(*attr)
Expand Down
25 changes: 23 additions & 2 deletions compiler/rustc_expand/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,27 @@ impl<'a> StripUnconfigured<'a> {
}
}

/// `cfg` and `cfg_attr` gets replaced with an inert `rustc_cfg_placeholder` to keep the
/// attribute "spot" in the AST. This allows suggestions to remove an item to provide a
/// correct suggestion when `#[cfg_attr]`s are present.
fn mk_placeholder(&self, cfg_attr: &ast::Attribute) -> ast::Attribute {
let item = ast::AttrItem {
unsafety: ast::Safety::Default,
path: ast::Path::from_ident(rustc_span::symbol::Ident::with_dummy_span(
sym::rustc_cfg_placeholder,
)),
args: ast::AttrArgs::Empty,
tokens: None,
};
let kind = ast::AttrKind::Normal(P(ast::NormalAttr { item, tokens: None }));
ast::Attribute {
kind,
id: self.sess.psess.attr_id_generator.mk_attr_id(),
style: cfg_attr.style,
span: cfg_attr.span,
}
}

/// Parse and expand a single `cfg_attr` attribute into a list of attributes
/// when the configuration predicate is true, or otherwise expand into an
/// empty list of attributes.
Expand All @@ -278,7 +299,7 @@ impl<'a> StripUnconfigured<'a> {
let Some((cfg_predicate, expanded_attrs)) =
rustc_parse::parse_cfg_attr(cfg_attr, &self.sess.psess)
else {
return vec![];
return vec![self.mk_placeholder(cfg_attr)];
};

// Lint on zero attributes in source.
Expand All @@ -292,7 +313,7 @@ impl<'a> StripUnconfigured<'a> {
}

if !attr::cfg_matches(&cfg_predicate, &self.sess, self.lint_node_id, self.features) {
return vec![];
return vec![self.mk_placeholder(cfg_attr)];
}

if recursive {
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,12 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
template!(Word, List: r#""...""#), DuplicatesOk,
EncodeCrossCrate::Yes, INTERNAL_UNSTABLE
),
// placeholder that replaces `cfg_attr` in the item's attribute list
ungated!(
rustc_cfg_placeholder, Normal, template!(Word /* irrelevant */), DuplicatesOk,
EncodeCrossCrate::No
),


// ==========================================================================
// Internal attributes, Diagnostics related:
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -945,8 +945,9 @@ lint_unused_doc_comment = unused doc comment
.label = rustdoc does not generate documentation for macro invocations
.help = to document an item produced by a macro, the macro must produce the documentation as part of its expansion

lint_unused_extern_crate = unused extern crate
.suggestion = remove it
lint_unused_extern_crate = unused `extern crate`
.label = unused
.suggestion = remove the unused `extern crate`

lint_unused_import_braces = braces around {$node} is unnecessary

Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_lint/src/early/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,8 @@ pub(super) fn decorate_lint(
BuiltinLintDiag::ByteSliceInPackedStructWithDerive { ty } => {
lints::ByteSliceInPackedStructWithDerive { ty }.decorate_lint(diag);
}
BuiltinLintDiag::UnusedExternCrate { removal_span } => {
lints::UnusedExternCrate { removal_span }.decorate_lint(diag);
BuiltinLintDiag::UnusedExternCrate { span, removal_span } => {
lints::UnusedExternCrate { span, removal_span }.decorate_lint(diag);
}
BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span } => {
let suggestion_span = vis_span.between(ident_span);
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3001,7 +3001,9 @@ pub(crate) struct ByteSliceInPackedStructWithDerive {
#[derive(LintDiagnostic)]
#[diag(lint_unused_extern_crate)]
pub(crate) struct UnusedExternCrate {
#[suggestion(code = "", applicability = "machine-applicable")]
#[label]
pub span: Span,
#[suggestion(code = "", applicability = "machine-applicable", style = "verbose")]
pub removal_span: Span,
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_lint_defs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,7 @@ pub enum BuiltinLintDiag {
ty: String,
},
UnusedExternCrate {
span: Span,
removal_span: Span,
},
ExternCrateNotIdiomatic {
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| sym::prelude_import
| sym::panic_handler
| sym::allow_internal_unsafe
| sym::rustc_cfg_placeholder // Inert, it's a placeholder in the AST.
| sym::fundamental
| sym::lang
| sym::needs_allocator
Expand Down Expand Up @@ -575,6 +576,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
// conditional compilation
sym::cfg,
sym::cfg_attr,
sym::rustc_cfg_placeholder,
// testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`)
sym::test,
sym::ignore,
Expand Down Expand Up @@ -2641,7 +2643,7 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
// 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`)
// 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::rustc_cfg_placeholder];
let spans = self
.tcx
.hir_attrs(where_predicate.hir_id)
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_resolve/src/check_unused.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> {
extern_crate.id,
span,
BuiltinLintDiag::UnusedExternCrate {
span: extern_crate.span,
removal_span: extern_crate.span_with_attributes,
},
);
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1745,6 +1745,8 @@ symbols! {
rustc_autodiff,
rustc_builtin_macro,
rustc_capture_analysis,
// We ensure that the attribute can't be written by end users by adding `-` to the name.
rustc_cfg_placeholder: "rustc-cfg-placeholder",
rustc_clean,
rustc_coherence_is_core,
rustc_coinductive,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@ fn check_duplicated_attr(
}
let Some(ident) = attr.ident() else { return };
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::rustc_on_unimplemented
|| name == sym::reason
|| name == sym::rustc_cfg_placeholder
{
// FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg
// conditions are the same.
// `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected.
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/editions/edition-extern-crate-allowed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
#![warn(rust_2018_idioms)]

extern crate edition_extern_crate_allowed;
//~^ WARNING unused extern crate
//~^ WARNING unused `extern crate`

fn main() {}
8 changes: 6 additions & 2 deletions tests/ui/editions/edition-extern-crate-allowed.stderr
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
warning: unused extern crate
warning: unused `extern crate`
--> $DIR/edition-extern-crate-allowed.rs:7:1
|
LL | extern crate edition_extern_crate_allowed;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused
|
note: the lint level is defined here
--> $DIR/edition-extern-crate-allowed.rs:5:9
|
LL | #![warn(rust_2018_idioms)]
| ^^^^^^^^^^^^^^^^
= note: `#[warn(unused_extern_crates)]` implied by `#[warn(rust_2018_idioms)]`
help: remove the unused `extern crate`
|
LL - extern crate edition_extern_crate_allowed;
|

warning: 1 warning emitted

2 changes: 1 addition & 1 deletion tests/ui/imports/extern-crate-used.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ extern crate core as iso3;
extern crate core as iso4;

// Doesn't introduce its extern prelude entry, so it's still considered unused.
extern crate core; //~ ERROR unused extern crate
extern crate core; //~ ERROR unused `extern crate`

mod m {
use iso1::any as are_you_okay1;
Expand Down
9 changes: 7 additions & 2 deletions tests/ui/imports/extern-crate-used.stderr
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
error: unused extern crate
error: unused `extern crate`
--> $DIR/extern-crate-used.rs:18:1
|
LL | extern crate core;
| ^^^^^^^^^^^^^^^^^^ help: remove it
| ^^^^^^^^^^^^^^^^^^ unused
|
note: the lint level is defined here
--> $DIR/extern-crate-used.rs:6:9
|
LL | #![deny(unused_extern_crates)]
| ^^^^^^^^^^^^^^^^^^^^
help: remove the unused `extern crate`
|
LL - extern crate core;
LL +
|

error: aborting due to 1 previous error

12 changes: 6 additions & 6 deletions tests/ui/lint/unnecessary-extern-crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
#![feature(test)]

extern crate core;
//~^ ERROR unused extern crate
//~^ ERROR unused `extern crate`
//~| HELP remove
extern crate core as x;
//~^ ERROR unused extern crate
//~^ ERROR unused `extern crate`
//~| HELP remove

extern crate proc_macro;
Expand All @@ -29,11 +29,11 @@ mod foo {
pub(super) extern crate alloc as d;

extern crate core;
//~^ ERROR unused extern crate
//~^ ERROR unused `extern crate`
//~| HELP remove

extern crate core as x;
//~^ ERROR unused extern crate
//~^ ERROR unused `extern crate`
//~| HELP remove

pub extern crate test;
Expand All @@ -42,11 +42,11 @@ mod foo {

mod bar {
extern crate core;
//~^ ERROR unused extern crate
//~^ ERROR unused `extern crate`
//~| HELP remove

extern crate core as x;
//~^ ERROR unused extern crate
//~^ ERROR unused `extern crate`
//~| HELP remove

pub(in crate::foo::bar) extern crate alloc as e;
Expand Down
53 changes: 41 additions & 12 deletions tests/ui/lint/unnecessary-extern-crate.stderr
Original file line number Diff line number Diff line change
@@ -1,44 +1,73 @@
error: unused extern crate
error: unused `extern crate`
--> $DIR/unnecessary-extern-crate.rs:6:1
|
LL | extern crate core;
| ^^^^^^^^^^^^^^^^^^ help: remove it
| ^^^^^^^^^^^^^^^^^^ unused
|
note: the lint level is defined here
--> $DIR/unnecessary-extern-crate.rs:3:9
|
LL | #![deny(unused_extern_crates)]
| ^^^^^^^^^^^^^^^^^^^^
help: remove the unused `extern crate`
|
LL - extern crate core;
|

error: unused extern crate
error: unused `extern crate`
--> $DIR/unnecessary-extern-crate.rs:9:1
|
LL | extern crate core as x;
| ^^^^^^^^^^^^^^^^^^^^^^^ help: remove it
| ^^^^^^^^^^^^^^^^^^^^^^^ unused
|
help: remove the unused `extern crate`
|
LL - extern crate core as x;
|

error: unused extern crate
error: unused `extern crate`
--> $DIR/unnecessary-extern-crate.rs:31:5
|
LL | extern crate core;
| ^^^^^^^^^^^^^^^^^^ help: remove it
| ^^^^^^^^^^^^^^^^^^ unused
|
help: remove the unused `extern crate`
|
LL - extern crate core;
|

error: unused extern crate
error: unused `extern crate`
--> $DIR/unnecessary-extern-crate.rs:35:5
|
LL | extern crate core as x;
| ^^^^^^^^^^^^^^^^^^^^^^^ help: remove it
| ^^^^^^^^^^^^^^^^^^^^^^^ unused
|
help: remove the unused `extern crate`
|
LL - extern crate core as x;
|

error: unused extern crate
error: unused `extern crate`
--> $DIR/unnecessary-extern-crate.rs:44:9
|
LL | extern crate core;
| ^^^^^^^^^^^^^^^^^^ help: remove it
| ^^^^^^^^^^^^^^^^^^ unused
|
help: remove the unused `extern crate`
|
LL - extern crate core;
|

error: unused extern crate
error: unused `extern crate`
--> $DIR/unnecessary-extern-crate.rs:48:9
|
LL | extern crate core as x;
| ^^^^^^^^^^^^^^^^^^^^^^^ help: remove it
| ^^^^^^^^^^^^^^^^^^^^^^^ unused
|
help: remove the unused `extern crate`
|
LL - extern crate core as x;
|

error: aborting due to 6 previous errors

4 changes: 2 additions & 2 deletions tests/ui/lint/unused/lint-unused-extern-crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#![allow(unused_variables)]
#![allow(deprecated)]

extern crate lint_unused_extern_crate5; //~ ERROR: unused extern crate
extern crate lint_unused_extern_crate5; //~ ERROR: unused `extern crate`

pub extern crate lint_unused_extern_crate4; // no error, it is re-exported

Expand All @@ -26,7 +26,7 @@ use other::*;

mod foo {
// Test that this is unused even though an earlier `extern crate` is used.
extern crate lint_unused_extern_crate2; //~ ERROR unused extern crate
extern crate lint_unused_extern_crate2; //~ ERROR unused `extern crate`
}

fn main() {
Expand Down
Loading
Loading