Skip to content
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
131 changes: 82 additions & 49 deletions clippy_lints/src/attrs/deprecated_cfg_attr.rs
Original file line number Diff line number Diff line change
@@ -1,70 +1,66 @@
use super::{Attribute, DEPRECATED_CFG_ATTR, DEPRECATED_CLIPPY_CFG_ATTR, unnecessary_clippy_cfg};
use clippy_utils::diagnostics::span_lint_and_sugg;
use super::{Attribute, DEPRECATED_CFG_ATTR, DEPRECATED_CLIPPY_CFG_ATTR};
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::msrvs::{self, MsrvStack};
use clippy_utils::sym;
use rustc_ast::AttrStyle;
use rustc_errors::Applicability;
use rustc_lint::EarlyContext;
use rustc_hir::attrs::CfgEntry;
use rustc_hir::def_id::{LOCAL_CRATE, LocalDefId};
use rustc_lint::{EarlyContext, LateContext};

pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &MsrvStack) {
pub(super) fn check_rustfmt(cx: &EarlyContext<'_>, attrs: &[Attribute], msrv: &MsrvStack) {
for attr in attrs {
check_rustfmt_attr(cx, attr, msrv);
}
}

fn check_rustfmt_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &MsrvStack) {
// check cfg_attr
if attr.has_name(sym::cfg_attr)
if attr.has_name(sym::cfg_attr_trace)
&& let Some(items) = attr.meta_item_list()
&& items.len() == 2
&& let Some(feature_item) = items[0].meta_item()
{
// check for `rustfmt`
if feature_item.has_name(sym::rustfmt)
&& msrv.meets(msrvs::TOOL_ATTRIBUTES)
// check for `rustfmt_skip` and `rustfmt::skip`
&& let Some(skip_item) = &items[1].meta_item()
&& (skip_item.has_name(sym::rustfmt_skip)
|| skip_item
.path
.segments
.last()
.expect("empty path in attribute")
.ident
.name
== sym::skip)
// Only lint outer attributes, because custom inner attributes are unstable
// Tracking issue: https://github.com/rust-lang/rust/issues/54726
&& attr.style == AttrStyle::Outer
{
span_lint_and_sugg(
cx,
DEPRECATED_CFG_ATTR,
attr.span,
"`cfg_attr` is deprecated for rustfmt and got replaced by tool attributes",
"use",
"#[rustfmt::skip]".to_string(),
Applicability::MachineApplicable,
);
} else {
check_deprecated_cfg_recursively(cx, feature_item);
if let Some(behind_cfg_attr) = items[1].meta_item() {
unnecessary_clippy_cfg::check(cx, feature_item, behind_cfg_attr, attr);
}
}
}
}

pub(super) fn check_clippy(cx: &EarlyContext<'_>, attr: &Attribute) {
if attr.has_name(sym::cfg)
&& let Some(list) = attr.meta_item_list()
&& feature_item.has_name(sym::rustfmt)
&& msrv.meets(msrvs::TOOL_ATTRIBUTES)
// check for `rustfmt_skip` and `rustfmt::skip`
&& let Some(skip_item) = &items[1].meta_item()
&& (skip_item.has_name(sym::rustfmt_skip)
|| skip_item
.path
.segments
.last()
.expect("empty path in attribute")
.ident
.name
== sym::skip)
// Only lint outer attributes, because custom inner attributes are unstable
// Tracking issue: https://github.com/rust-lang/rust/issues/54726
&& attr.style == AttrStyle::Outer
{
for item in list.iter().filter_map(|item| item.meta_item()) {
check_deprecated_cfg_recursively(cx, item);
}
span_lint_and_then(
cx,
DEPRECATED_CFG_ATTR,
attr.span,
"`cfg_attr` is deprecated for rustfmt",
|diag| {
diag.span_suggestion_verbose(
attr.span,
"use the `rustfmt::skip` tool attribute instead",
"#[rustfmt::skip]",
Applicability::MachineApplicable,
);
},
);
}
}

fn check_deprecated_cfg_recursively(cx: &EarlyContext<'_>, attr: &rustc_ast::MetaItem) {
pub(super) fn check(cx: &EarlyContext<'_>, attr: &rustc_ast::MetaItem) {
if let Some(ident) = attr.ident() {
if matches!(ident.name, sym::any | sym::all | sym::not) {
let Some(list) = attr.meta_item_list() else { return };
for item in list.iter().filter_map(|item| item.meta_item()) {
check_deprecated_cfg_recursively(cx, item);
check(cx, item);
}
} else {
check_cargo_clippy_attr(cx, attr);
Expand All @@ -85,3 +81,40 @@ fn check_cargo_clippy_attr(cx: &EarlyContext<'_>, item: &rustc_ast::MetaItem) {
);
}
}

pub fn check_stripped(cx: &LateContext<'_>) {
for stripped in cx.tcx.stripped_cfg_items(LOCAL_CRATE) {
if let Some(parent_module) = stripped.parent_module.as_local() {
check_cfg_entry(cx, &stripped.cfg.0, parent_module);
}
}
}

fn check_cfg_entry(cx: &LateContext<'_>, cfg: &CfgEntry, parent_module: LocalDefId) {
match cfg {
&CfgEntry::NameValue {
name: sym::feature,
name_span: _,
value: Some((sym::cargo_clippy, _)),
span,
} => {
span_lint_hir_and_then(
cx,
DEPRECATED_CLIPPY_CFG_ATTR,
cx.tcx.local_def_id_to_hir_id(parent_module),
span,
"`feature = \"cargo-clippy\"` was replaced by `clippy`",
|diag| {
diag.span_suggestion(span, "replace with", "clippy", Applicability::MachineApplicable);
},
);
},
CfgEntry::All(children, _) | CfgEntry::Any(children, _) => {
for child in children {
check_cfg_entry(cx, child, parent_module);
}
},
CfgEntry::Not(child, _) => check_cfg_entry(cx, child, parent_module),
_ => {},
}
}
112 changes: 73 additions & 39 deletions clippy_lints/src/attrs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,11 @@ declare_clippy_lint! {
/// are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes.
///
/// ### Known problems
/// This lint doesn't detect crate level inner attributes, because they get
/// processed before the PreExpansionPass lints get executed. See
/// [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765)
/// Does not detect attributes applied to macro invocations such as
/// ```no_run
/// #[cfg_attr(rustfmt, rustfmt_skip)]
/// println!("..");
/// ```
///
/// ### Example
/// ```no_run
Expand Down Expand Up @@ -314,11 +316,16 @@ declare_clippy_lint! {

declare_clippy_lint! {
/// ### What it does
/// Checks for `any` and `all` combinators in `cfg` with only one condition.
/// Checks for `any` and `all` combinators in `cfg` or `cfg_attr` with only one condition.
///
/// ### Why is this bad?
/// If there is only one condition, no need to wrap it into `any` or `all` combinators.
///
/// ### Known Problems
/// Only attributes that are attached to items included in the current compilation will be linted,
/// for the examples below the lint will only fire when `Bar` is compiled - in this case when on
/// a `unix` target.
///
/// ### Example
/// ```no_run
/// #[cfg(any(unix))]
Expand Down Expand Up @@ -474,22 +481,27 @@ declare_clippy_lint! {
"ignored tests without messages"
}

pub struct Attributes {
pub struct LateAttributes {
msrv: Msrv,
}

impl_lint_pass!(Attributes => [
impl_lint_pass!(LateAttributes => [
INLINE_ALWAYS,
REPR_PACKED_WITHOUT_ABI,
DEPRECATED_CLIPPY_CFG_ATTR,
]);

impl Attributes {
impl LateAttributes {
pub fn new(conf: &'static Conf) -> Self {
Self { msrv: conf.msrv }
}
}

impl<'tcx> LateLintPass<'tcx> for Attributes {
impl<'tcx> LateLintPass<'tcx> for LateAttributes {
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
deprecated_cfg_attr::check_stripped(cx);
}

fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
let attrs = cx.tcx.hir_attrs(item.hir_id());
if let ItemKind::Fn { ident, .. } = item.kind
Expand Down Expand Up @@ -526,35 +538,6 @@ impl EarlyAttributes {
}

impl_lint_pass!(EarlyAttributes => [
DEPRECATED_CFG_ATTR,
NON_MINIMAL_CFG,
DEPRECATED_CLIPPY_CFG_ATTR,
UNNECESSARY_CLIPPY_CFG,
]);

impl EarlyLintPass for EarlyAttributes {
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
deprecated_cfg_attr::check(cx, attr, &self.msrv);
deprecated_cfg_attr::check_clippy(cx, attr);
non_minimal_cfg::check(cx, attr);
}

extract_msrv_attr!();
}

pub struct PostExpansionEarlyAttributes {
msrv: MsrvStack,
}

impl PostExpansionEarlyAttributes {
pub fn new(conf: &'static Conf) -> Self {
Self {
msrv: MsrvStack::new(conf.msrv),
}
}
}

impl_lint_pass!(PostExpansionEarlyAttributes => [
ALLOW_ATTRIBUTES,
ALLOW_ATTRIBUTES_WITHOUT_REASON,
DEPRECATED_SEMVER,
Expand All @@ -564,9 +547,13 @@ impl_lint_pass!(PostExpansionEarlyAttributes => [
SHOULD_PANIC_WITHOUT_EXPECT,
MIXED_ATTRIBUTES_STYLE,
DUPLICATED_ATTRIBUTES,
DEPRECATED_CFG_ATTR,
DEPRECATED_CLIPPY_CFG_ATTR,
UNNECESSARY_CLIPPY_CFG,
NON_MINIMAL_CFG,
]);

impl EarlyLintPass for PostExpansionEarlyAttributes {
impl EarlyLintPass for EarlyAttributes {
fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &ast::Crate) {
blanket_clippy_restriction_lints::check_command_line(cx);
duplicated_attributes::check(cx, &krate.attrs);
Expand All @@ -585,6 +572,15 @@ impl EarlyLintPass for PostExpansionEarlyAttributes {
if is_lint_level(ident.name, attr.id) {
blanket_clippy_restriction_lints::check(cx, ident.name, items);
}
if matches!(ident.name, sym::cfg_trace | sym::cfg_attr_trace)
&& let Some(meta_item) = items.first().and_then(|item| item.meta_item())
{
non_minimal_cfg::check(cx, items, &self.msrv);
deprecated_cfg_attr::check(cx, meta_item);
if let Some(behind_cfg) = items.get(1).and_then(|item| item.meta_item()) {
unnecessary_clippy_cfg::check(cx, meta_item, behind_cfg, attr);
}
}
if items.is_empty() || !attr.has_name(sym::deprecated) {
return;
}
Expand Down Expand Up @@ -620,14 +616,52 @@ impl EarlyLintPass for PostExpansionEarlyAttributes {
}

fn check_item(&mut self, cx: &EarlyContext<'_>, item: &'_ ast::Item) {
match item.kind {
match &item.kind {
ast::ItemKind::ExternCrate(..) | ast::ItemKind::Use(..) => useless_attribute::check(cx, item, &item.attrs),
ast::ItemKind::Struct(.., variant) => {
for field in variant.fields() {
deprecated_cfg_attr::check_rustfmt(cx, &field.attrs, &self.msrv);
}
},
ast::ItemKind::ForeignMod(foreign) => {
for item in &foreign.items {
deprecated_cfg_attr::check_rustfmt(cx, &item.attrs, &self.msrv);
}
},
_ => {},
}

deprecated_cfg_attr::check_rustfmt(cx, &item.attrs, &self.msrv);
mixed_attributes_style::check(cx, item.span, &item.attrs);
duplicated_attributes::check(cx, &item.attrs);
}

fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) {
match &stmt.kind {
ast::StmtKind::Let(local) => deprecated_cfg_attr::check_rustfmt(cx, &local.attrs, &self.msrv),
ast::StmtKind::Semi(expr) => deprecated_cfg_attr::check_rustfmt(cx, &expr.attrs, &self.msrv),
_ => {},
}
}

fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) {
deprecated_cfg_attr::check_rustfmt(cx, &arm.attrs, &self.msrv);
}

fn check_trait_item(&mut self, cx: &EarlyContext<'_>, item: &ast::AssocItem) {
deprecated_cfg_attr::check_rustfmt(cx, &item.attrs, &self.msrv);
}

fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &ast::AssocItem) {
deprecated_cfg_attr::check_rustfmt(cx, &item.attrs, &self.msrv);
}

fn check_variant(&mut self, cx: &EarlyContext<'_>, variant: &ast::Variant) {
deprecated_cfg_attr::check_rustfmt(cx, &variant.attrs, &self.msrv);
for field in variant.data.fields() {
deprecated_cfg_attr::check_rustfmt(cx, &field.attrs, &self.msrv);
}
}

extract_msrv_attr!();
}
Loading