Skip to content

Commit

Permalink
Replace cfg_attr in AST with rustc-cfg-placeholder for accurate s…
Browse files Browse the repository at this point in the history
…pan 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 +
   |
```
  • Loading branch information
estebank committed Dec 3, 2024
1 parent 5e1440a commit 4ff5353
Show file tree
Hide file tree
Showing 34 changed files with 210 additions and 76 deletions.
4 changes: 4 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3006,6 +3006,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 @@ -226,6 +226,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 @@ -340,6 +340,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::is_builtin_attr(attr)
Expand Down
20 changes: 19 additions & 1 deletion compiler/rustc_expand/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,25 @@ impl<'a> StripUnconfigured<'a> {
}

if !attr::cfg_matches(&cfg_predicate, &self.sess, self.lint_node_id, self.features) {
return vec![];
// `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.
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 }));
let attr: ast::Attribute = ast::Attribute {
kind,
id: self.sess.psess.attr_id_generator.mk_attr_id(),
style: ast::AttrStyle::Outer,
span: cfg_attr.span,
};
return vec![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 @@ -750,6 +750,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
template!(Word, List: r#""...""#), DuplicatesOk,
EncodeCrossCrate::No, 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 @@ -927,8 +927,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/context/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,8 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: &
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 @@ -2907,7 +2907,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 @@ -706,6 +706,7 @@ pub enum BuiltinLintDiag {
ty: String,
},
UnusedExternCrate {
span: Span,
removal_span: Span,
},
ExternCrateNotIdiomatic {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,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
3 changes: 3 additions & 0 deletions compiler/rustc_resolve/src/check_unused.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,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 Expand Up @@ -221,6 +222,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for UnusedImportCheckVisitor<'a, 'ra, 'tcx> {
// compiler and we don't need to consider them.
ast::ItemKind::Use(..) if item.span.is_dummy() => return,
ast::ItemKind::ExternCrate(orig_name) => {
tracing::info!(?item);
self.extern_crate_items.push(ExternCrateToLint {
id: item.id,
span: item.span,
Expand Down Expand Up @@ -397,6 +399,7 @@ impl Resolver<'_, '_> {
}
}
ImportKind::ExternCrate { id, .. } => {
tracing::info!("import={import:#?}");
let def_id = self.local_def_id(id);
if self.extern_crate_map.get(&def_id).map_or(true, |&cnum| {
!tcx.is_compiler_builtins(cnum)
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 @@ -1667,6 +1667,8 @@ symbols! {
rustc_box,
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
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
19 changes: 15 additions & 4 deletions tests/ui/lint/unused/lint-unused-extern-crate.stderr
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
error: unused extern crate
error: unused `extern crate`
--> $DIR/lint-unused-extern-crate.rs:11:1
|
LL | extern crate lint_unused_extern_crate5;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused
|
note: the lint level is defined here
--> $DIR/lint-unused-extern-crate.rs:7:9
|
LL | #![deny(unused_extern_crates)]
| ^^^^^^^^^^^^^^^^^^^^
help: remove the unused `extern crate`
|
LL - extern crate lint_unused_extern_crate5;
LL +
|

error: unused extern crate
error: unused `extern crate`
--> $DIR/lint-unused-extern-crate.rs:29:5
|
LL | extern crate lint_unused_extern_crate2;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused
|
help: remove the unused `extern crate`
|
LL - extern crate lint_unused_extern_crate2;
LL +
|

error: aborting due to 2 previous errors

Loading

0 comments on commit 4ff5353

Please sign in to comment.