Change:
Original rustfix issue: When rustc outputs warnings like "you don't need this extern crate line anymore, delete it," it doesn't include accompanying attributes. This leads rustfix to apply its suggestion to delete the extern crate line, but leave the attribute, which then gets applied to the next item. For example: becomes which applies the attribute to which is definitely not what was desired.
Here's where we issue the diagnostic: Assuming the cfg attribute is actually preserved by earlier passes, then correcting for this case should be easy (look for it in and extend the span correspondingly), but I don't remember offhand whether that's true and don't have time to investigate right now. If it isn't, then this is similar-in-spirit to in that we "just" want the span of an attribute, but would need to do some nontrivial rearchitecting work to make it available.

Discussion:
let hir_id = tcx.hir.definitions().def_index_to_hir_id(extern_crate.def_id.index); let id = tcx.hir.hir_to_node_id(hir_id); let msg = "unused extern crate"; <ins> // Removal suggestion span needs to include attributes (Issue #54400) let span_with_attrs = tcx.get_attrs(extern_crate.def_id).iter() .map(|attr| attr.span) .fold(span, |acc, attr_span| acc.to(attr_span)); </ins> tcx.struct_span_lint_node(lint, id, span, msg) .span_suggestion_short_with_applicability( <del> span, </del> <ins> span_with_attrs, </ins> "remove it", String::new(), Applicability::MachineApplicable)

<ins> // Copyright 2018 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. // aux-build:edition-lint-paths.rs // run-rustfix // compile-flags:--extern edition_lint_paths --cfg blandiloquence // edition:2018 #![deny(rust_2018_idioms)] #![allow(dead_code)] // The suggestion span should include the attribute. //~^ ERROR unused extern crate fn main() {} </ins>

<ins> // Copyright 2018 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. // aux-build:edition-lint-paths.rs // run-rustfix // compile-flags:--extern edition_lint_paths --cfg blandiloquence // edition:2018 #![deny(rust_2018_idioms)] #![allow(dead_code)] // The suggestion span should include the attribute. #[cfg(blandiloquence)] //~ HELP remove it extern crate edition_lint_paths; //~^ ERROR unused extern crate fn main() {} </ins>

<ins> error: unused extern crate --> $DIR/issue-54400-unused-extern-crate-attr-span.rs:22:1 | LL | / #[cfg(blandiloquence)] //~ HELP remove it LL | | extern crate edition_lint_paths; | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | |________________________________| |                                  help: remove it | note: lint level defined here --> $DIR/issue-54400-unused-extern-crate-attr-span.rs:16:9 | LL | #![deny(rust_2018_idioms)] |         ^^^^^^^^^^^^^^^^ = note: #[deny(unused_extern_crates)] implied by #[deny(rust_2018_idioms)] error: aborting due to previous error </ins>