Skip to content

Commit f34804d

Browse files
Add new duplicated_attributes lint
1 parent 453242c commit f34804d

File tree

4 files changed

+100
-1
lines changed

4 files changed

+100
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5157,6 +5157,7 @@ Released 2018-09-13
51575157
[`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref
51585158
[`duplicate_mod`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_mod
51595159
[`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
5160+
[`duplicated_attributes`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicated_attributes
51605161
[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
51615162
[`eager_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#eager_transmute
51625163
[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use super::DUPLICATED_ATTRIBUTES;
2+
use clippy_utils::diagnostics::span_lint_and_then;
3+
use rustc_ast::{Attribute, MetaItem};
4+
use rustc_data_structures::fx::FxHashMap;
5+
use rustc_lint::EarlyContext;
6+
use rustc_span::{sym, Span};
7+
use std::collections::hash_map::Entry;
8+
9+
fn emit_if_duplicated(
10+
cx: &EarlyContext<'_>,
11+
attr: &MetaItem,
12+
attr_paths: &mut FxHashMap<String, Span>,
13+
complete_path: String,
14+
) {
15+
match attr_paths.entry(complete_path) {
16+
Entry::Vacant(v) => {
17+
v.insert(attr.span);
18+
},
19+
Entry::Occupied(o) => {
20+
span_lint_and_then(cx, DUPLICATED_ATTRIBUTES, attr.span, "duplicated attribute", |diag| {
21+
diag.span_note(*o.get(), "first defined here");
22+
diag.span_help(attr.span, "remove this attribute");
23+
});
24+
},
25+
}
26+
}
27+
28+
fn check_duplicated_attr(
29+
cx: &EarlyContext<'_>,
30+
attr: &MetaItem,
31+
attr_paths: &mut FxHashMap<String, Span>,
32+
parent: &mut Vec<String>,
33+
) {
34+
let Some(ident) = attr.ident() else { return };
35+
let name = ident.name;
36+
if name == sym::doc || name == sym::cfg_attr {
37+
// FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg
38+
// conditions are the same.
39+
return;
40+
}
41+
if let Some(value) = attr.value_str() {
42+
emit_if_duplicated(cx, attr, attr_paths, format!("{}:{name}={value}", parent.join(":")));
43+
} else if let Some(sub_attrs) = attr.meta_item_list() {
44+
parent.push(name.as_str().to_string());
45+
for sub_attr in sub_attrs {
46+
if let Some(meta) = sub_attr.meta_item() {
47+
check_duplicated_attr(cx, meta, attr_paths, parent);
48+
}
49+
}
50+
parent.pop();
51+
} else {
52+
emit_if_duplicated(cx, attr, attr_paths, format!("{}:{name}", parent.join(":")));
53+
}
54+
}
55+
56+
pub fn check(cx: &EarlyContext<'_>, attrs: &[Attribute]) {
57+
let mut attr_paths = FxHashMap::default();
58+
59+
for attr in attrs {
60+
if let Some(meta) = attr.meta() {
61+
check_duplicated_attr(cx, &meta, &mut attr_paths, &mut Vec::new());
62+
}
63+
}
64+
}

clippy_lints/src/attrs/mod.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod allow_attributes_without_reason;
44
mod blanket_clippy_restriction_lints;
55
mod deprecated_cfg_attr;
66
mod deprecated_semver;
7+
mod duplicated_attributes;
78
mod empty_line_after;
89
mod inline_always;
910
mod maybe_misused_cfg;
@@ -16,7 +17,7 @@ mod useless_attribute;
1617
mod utils;
1718

1819
use clippy_config::msrvs::Msrv;
19-
use rustc_ast::{Attribute, MetaItemKind, NestedMetaItem};
20+
use rustc_ast::{Attribute, Crate, MetaItemKind, NestedMetaItem};
2021
use rustc_hir::{ImplItem, Item, ItemKind, TraitItem};
2122
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
2223
use rustc_session::{declare_lint_pass, impl_lint_pass};
@@ -489,6 +490,32 @@ declare_clippy_lint! {
489490
"item has both inner and outer attributes"
490491
}
491492

493+
declare_clippy_lint! {
494+
/// ### What it does
495+
/// Checks for attributes that appear two or more times.
496+
///
497+
/// ### Why is this bad?
498+
/// Repeating an attribute on the same item (or globally on the same crate)
499+
/// is unnecessary and doesn't have an effect.
500+
///
501+
/// ### Example
502+
/// ```no_run
503+
/// #[allow(dead_code)]
504+
/// #[allow(dead_code)]
505+
/// fn foo() {}
506+
/// ```
507+
///
508+
/// Use instead:
509+
/// ```no_run
510+
/// #[allow(dead_code)]
511+
/// fn foo() {}
512+
/// ```
513+
#[clippy::version = "1.78.0"]
514+
pub DUPLICATED_ATTRIBUTES,
515+
suspicious,
516+
"duplicated attribute"
517+
}
518+
492519
declare_lint_pass!(Attributes => [
493520
ALLOW_ATTRIBUTES_WITHOUT_REASON,
494521
INLINE_ALWAYS,
@@ -568,12 +595,18 @@ impl_lint_pass!(EarlyAttributes => [
568595
DEPRECATED_CLIPPY_CFG_ATTR,
569596
UNNECESSARY_CLIPPY_CFG,
570597
MIXED_ATTRIBUTES_STYLE,
598+
DUPLICATED_ATTRIBUTES,
571599
]);
572600

573601
impl EarlyLintPass for EarlyAttributes {
602+
fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
603+
duplicated_attributes::check(cx, &krate.attrs);
604+
}
605+
574606
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
575607
empty_line_after::check(cx, item);
576608
mixed_attributes_style::check(cx, item);
609+
duplicated_attributes::check(cx, &item.attrs);
577610
}
578611

579612
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
5454
crate::attrs::DEPRECATED_CFG_ATTR_INFO,
5555
crate::attrs::DEPRECATED_CLIPPY_CFG_ATTR_INFO,
5656
crate::attrs::DEPRECATED_SEMVER_INFO,
57+
crate::attrs::DUPLICATED_ATTRIBUTES_INFO,
5758
crate::attrs::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO,
5859
crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO,
5960
crate::attrs::INLINE_ALWAYS_INFO,

0 commit comments

Comments
 (0)