Skip to content

Commit a3b3a6a

Browse files
committed
feat: Add deprecated_attributes_without_note
Signed-off-by: hashcatHitman <155700084+hashcatHitman@users.noreply.github.com>
1 parent a53092b commit a3b3a6a

6 files changed

Lines changed: 137 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6966,6 +6966,7 @@ Released 2018-09-13
69666966
[`default_trait_access`]: https://rust-lang.github.io/rust-clippy/main/index.html#default_trait_access
69676967
[`default_union_representation`]: https://rust-lang.github.io/rust-clippy/main/index.html#default_union_representation
69686968
[`definition_in_module_root`]: https://rust-lang.github.io/rust-clippy/main/index.html#definition_in_module_root
6969+
[`deprecated_attributes_without_note`]: https://rust-lang.github.io/rust-clippy/main/index.html#deprecated_attributes_without_note
69696970
[`deprecated_attributes_without_since`]: https://rust-lang.github.io/rust-clippy/main/index.html#deprecated_attributes_without_since
69706971
[`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/main/index.html#deprecated_cfg_attr
69716972
[`deprecated_clippy_cfg_attr`]: https://rust-lang.github.io/rust-clippy/main/index.html#deprecated_clippy_cfg_attr
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use super::{Attribute, DEPRECATED_ATTRIBUTES_WITHOUT_NOTE};
2+
use clippy_utils::diagnostics::span_lint_and_then;
3+
use clippy_utils::is_from_proc_macro;
4+
use rustc_ast::{MetaItemInner, MetaItemKind};
5+
use rustc_lint::{EarlyContext, LintContext as _};
6+
use rustc_span::sym;
7+
8+
pub(super) fn check<'cx>(cx: &EarlyContext<'cx>, items: Option<&[MetaItemInner]>, attr: &'cx Attribute) {
9+
// Check if a note is present
10+
if let Some(items) = items {
11+
for item_inner in items {
12+
if let Some(item) = MetaItemInner::meta_item(item_inner)
13+
&& let MetaItemKind::NameValue(_) = &item.kind
14+
&& item.path == sym::note
15+
{
16+
return;
17+
}
18+
}
19+
}
20+
21+
// Check if the attribute is in an external macro and therefore out of the developer's control
22+
if attr.span.in_external_macro(cx.sess().source_map()) || is_from_proc_macro(cx, attr) {
23+
return;
24+
}
25+
26+
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
27+
span_lint_and_then(
28+
cx,
29+
DEPRECATED_ATTRIBUTES_WITHOUT_NOTE,
30+
attr.span,
31+
"`deprecated` attribute without note",
32+
|diag| {
33+
diag.help("try adding a note at the end with `#[deprecated(note = \"..\", ..)]`");
34+
},
35+
);
36+
}

clippy_lints/src/attrs/mod.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
mod allow_attributes;
22
mod allow_attributes_without_reason;
33
mod blanket_clippy_restriction_lints;
4+
mod deprecated_attributes_without_note;
45
mod deprecated_attributes_without_since;
56
mod deprecated_cfg_attr;
67
mod deprecated_semver;
@@ -106,6 +107,28 @@ declare_clippy_lint! {
106107
"enabling the complete restriction group"
107108
}
108109

110+
declare_clippy_lint! {
111+
/// ### What it does
112+
/// Checks for attributes that deprecate items without a `note` field.
113+
///
114+
/// ### Why restrict this?
115+
/// This is typically used to provide an explanation about the deprecation
116+
/// and preferred alternatives.
117+
///
118+
/// ### Example
119+
/// ```no_run
120+
/// #[deprecated]
121+
/// ```
122+
/// Use instead:
123+
/// ```no_run
124+
/// #[deprecated(note = "foo was rarely used. Users should instead use bar")]
125+
/// ```
126+
#[clippy::version = "1.100.0"]
127+
pub DEPRECATED_ATTRIBUTES_WITHOUT_NOTE,
128+
restriction,
129+
"ensures that all `deprecated` attributes have a `note` field"
130+
}
131+
109132
declare_clippy_lint! {
110133
/// ### What it does
111134
/// Checks for attributes that deprecate items without a `since` field.
@@ -504,6 +527,7 @@ declare_clippy_lint! {
504527
}
505528

506529
impl_lint_pass!(Attributes => [
530+
DEPRECATED_ATTRIBUTES_WITHOUT_NOTE,
507531
DEPRECATED_ATTRIBUTES_WITHOUT_SINCE,
508532
INLINE_ALWAYS,
509533
REPR_PACKED_WITHOUT_ABI,
@@ -618,6 +642,7 @@ impl EarlyLintPass for PostExpansionEarlyAttributes {
618642
}
619643
if matches!(name, sym::deprecated) {
620644
deprecated_attributes_without_since::check(cx, Some(items), attr);
645+
deprecated_attributes_without_note::check(cx, Some(items), attr);
621646
}
622647
if is_lint_level(name) {
623648
blanket_clippy_restriction_lints::check(cx, name, items);
@@ -634,6 +659,11 @@ impl EarlyLintPass for PostExpansionEarlyAttributes {
634659
}
635660
}
636661
} else if attr.has_name(sym::deprecated) {
662+
if let AttrKind::Normal(normal_attr) = &attr.kind
663+
&& !matches!(normal_attr.item.args, AttrArgs::Eq { .. })
664+
{
665+
deprecated_attributes_without_note::check(cx, None, attr);
666+
}
637667
deprecated_attributes_without_since::check(cx, None, attr);
638668
}
639669

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
1919
crate::attrs::ALLOW_ATTRIBUTES_INFO,
2020
crate::attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON_INFO,
2121
crate::attrs::BLANKET_CLIPPY_RESTRICTION_LINTS_INFO,
22+
crate::attrs::DEPRECATED_ATTRIBUTES_WITHOUT_NOTE_INFO,
2223
crate::attrs::DEPRECATED_ATTRIBUTES_WITHOUT_SINCE_INFO,
2324
crate::attrs::DEPRECATED_CFG_ATTR_INFO,
2425
crate::attrs::DEPRECATED_CLIPPY_CFG_ATTR_INFO,
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//@aux-build:proc_macros.rs
2+
#![deny(clippy::deprecated_attributes_without_note)]
3+
4+
extern crate proc_macros;
5+
use proc_macros::{external, with_span};
6+
7+
// These should trigger the lint
8+
#[deprecated]
9+
//~^ deprecated_attributes_without_note
10+
fn foo() {}
11+
12+
#[deprecated(since = "1.42.100")]
13+
//~^ deprecated_attributes_without_note
14+
fn quux() {}
15+
16+
// These should be fine
17+
#[allow(deprecated)]
18+
#[allow(dead_code, reason = "This should be allowed")]
19+
#[expect(dead_code)]
20+
#[warn(dyn_drop, reason = "Warnings can also have reasons")]
21+
#[warn(deref_nullptr)]
22+
#[deny(deref_nullptr)]
23+
#[forbid(deref_nullptr)]
24+
fn main() {
25+
external! {
26+
#[deprecated]
27+
fn a() {}
28+
}
29+
with_span! {
30+
span
31+
#[deprecated]
32+
fn b() {}
33+
}
34+
}
35+
36+
#[deprecated(since = "TBD", note = "use qux instead")]
37+
fn baz() {}
38+
39+
#[deprecated(note = "use quux instead", since = "0.0.1")]
40+
fn qux() {}
41+
42+
#[deprecated(note = "I don't feel like maintaining this anymore, sorry")]
43+
fn bar() {}
44+
45+
#[deprecated = "probably a bad idea to use this"]
46+
fn weird() {}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error: `deprecated` attribute without note
2+
--> tests/ui/deprecated_attributes_without_note.rs:8:1
3+
|
4+
LL | #[deprecated]
5+
| ^^^^^^^^^^^^^
6+
|
7+
= help: try adding a note at the end with `#[deprecated(note = "..", ..)]`
8+
note: the lint level is defined here
9+
--> tests/ui/deprecated_attributes_without_note.rs:2:9
10+
|
11+
LL | #![deny(clippy::deprecated_attributes_without_note)]
12+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13+
14+
error: `deprecated` attribute without note
15+
--> tests/ui/deprecated_attributes_without_note.rs:12:1
16+
|
17+
LL | #[deprecated(since = "1.42.100")]
18+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
19+
|
20+
= help: try adding a note at the end with `#[deprecated(note = "..", ..)]`
21+
22+
error: aborting due to 2 previous errors
23+

0 commit comments

Comments
 (0)