Skip to content

Commit aa7e4c3

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

6 files changed

Lines changed: 147 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: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use super::{Attribute, DEPRECATED_ATTRIBUTES_WITHOUT_NOTE};
2+
use clippy_utils::diagnostics::span_lint_and_sugg;
3+
use clippy_utils::source::snippet_with_applicability;
4+
use rustc_ast::{MetaItemInner, MetaItemKind};
5+
use rustc_errors::Applicability;
6+
use rustc_lint::EarlyContext;
7+
use rustc_span::sym;
8+
9+
pub(super) fn check<'cx>(cx: &EarlyContext<'cx>, items: Option<&[MetaItemInner]>, attr: &'cx Attribute) {
10+
if attr.value_span().is_some() {
11+
return;
12+
}
13+
14+
if let Some(items) = items {
15+
for item_inner in items {
16+
if let Some(item) = MetaItemInner::meta_item(item_inner)
17+
&& let MetaItemKind::NameValue(_) = &item.kind
18+
&& item.path == sym::note
19+
{
20+
return;
21+
}
22+
}
23+
}
24+
25+
let mut applicability = Applicability::HasPlaceholders;
26+
let suggestion = items.map_or_else(
27+
|| "#[deprecated(note = /* note */)]".to_owned(),
28+
|items| {
29+
let mut attr_with_fields = String::from("#[deprecated(");
30+
for item in items {
31+
let snippet = snippet_with_applicability(cx, item.span(), "_", &mut applicability);
32+
attr_with_fields.push_str(&snippet);
33+
attr_with_fields.push_str(", ");
34+
}
35+
attr_with_fields.push_str("note = /* note */)]");
36+
attr_with_fields
37+
},
38+
);
39+
40+
span_lint_and_sugg(
41+
cx,
42+
DEPRECATED_ATTRIBUTES_WITHOUT_NOTE,
43+
attr.span,
44+
"`deprecated` attribute without note",
45+
"add a note",
46+
suggestion,
47+
applicability,
48+
);
49+
}

clippy_lints/src/attrs/mod.rs

Lines changed: 27 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;
@@ -105,6 +106,30 @@ declare_clippy_lint! {
105106
"enabling the complete restriction group"
106107
}
107108

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

507532
impl_lint_pass!(Attributes => [
533+
DEPRECATED_ATTRIBUTES_WITHOUT_NOTE,
508534
DEPRECATED_ATTRIBUTES_WITHOUT_SINCE,
509535
INLINE_ALWAYS,
510536
REPR_PACKED_WITHOUT_ABI,
@@ -637,6 +663,7 @@ impl EarlyLintPass for PostExpansionEarlyAttributes {
637663
&& !attr.span.in_external_macro(cx.sess().source_map())
638664
&& !is_from_proc_macro(cx, attr)
639665
{
666+
deprecated_attributes_without_note::check(cx, attr.meta_item_list().as_deref(), attr);
640667
deprecated_attributes_without_since::check(cx, attr.meta_item_list().as_deref(), attr);
641668
}
642669

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

0 commit comments

Comments
 (0)