Skip to content

Commit 5e0afee

Browse files
committed
Auto merge of rust-lang#12394 - CBSpeir:issue-12390-refactor, r=Alexendoo
Refactor lints in clippy_lints::attrs into separate submodules/files This pull request contains the changes requested in issue rust-lang#12390. changelog: none
2 parents 346b094 + 1580af6 commit 5e0afee

16 files changed

+1361
-1265
lines changed

Diff for: clippy_lints/src/attrs.rs

-1,265
This file was deleted.
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use super::{Attribute, ALLOW_ATTRIBUTES_WITHOUT_REASON};
2+
use clippy_utils::diagnostics::span_lint_and_help;
3+
use clippy_utils::is_from_proc_macro;
4+
use rustc_ast::{MetaItemKind, NestedMetaItem};
5+
use rustc_lint::{LateContext, LintContext};
6+
use rustc_middle::lint::in_external_macro;
7+
use rustc_span::sym;
8+
use rustc_span::symbol::Symbol;
9+
10+
pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[NestedMetaItem], attr: &'cx Attribute) {
11+
// Check for the feature
12+
if !cx.tcx.features().lint_reasons {
13+
return;
14+
}
15+
16+
// Check if the reason is present
17+
if let Some(item) = items.last().and_then(NestedMetaItem::meta_item)
18+
&& let MetaItemKind::NameValue(_) = &item.kind
19+
&& item.path == sym::reason
20+
{
21+
return;
22+
}
23+
24+
// Check if the attribute is in an external macro and therefore out of the developer's control
25+
if in_external_macro(cx.sess(), attr.span) || is_from_proc_macro(cx, &attr) {
26+
return;
27+
}
28+
29+
span_lint_and_help(
30+
cx,
31+
ALLOW_ATTRIBUTES_WITHOUT_REASON,
32+
attr.span,
33+
&format!("`{}` attribute without specifying a reason", name.as_str()),
34+
None,
35+
"try adding a reason at the end with `, reason = \"..\"`",
36+
);
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use super::utils::extract_clippy_lint;
2+
use super::BLANKET_CLIPPY_RESTRICTION_LINTS;
3+
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
4+
use rustc_ast::NestedMetaItem;
5+
use rustc_lint::{LateContext, Level, LintContext};
6+
use rustc_span::symbol::Symbol;
7+
use rustc_span::{sym, DUMMY_SP};
8+
9+
pub(super) fn check(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem]) {
10+
for lint in items {
11+
if let Some(lint_name) = extract_clippy_lint(lint) {
12+
if lint_name.as_str() == "restriction" && name != sym::allow {
13+
span_lint_and_help(
14+
cx,
15+
BLANKET_CLIPPY_RESTRICTION_LINTS,
16+
lint.span(),
17+
"`clippy::restriction` is not meant to be enabled as a group",
18+
None,
19+
"enable the restriction lints you need individually",
20+
);
21+
}
22+
}
23+
}
24+
}
25+
26+
pub(super) fn check_command_line(cx: &LateContext<'_>) {
27+
for (name, level) in &cx.sess().opts.lint_opts {
28+
if name == "clippy::restriction" && *level > Level::Allow {
29+
span_lint_and_then(
30+
cx,
31+
BLANKET_CLIPPY_RESTRICTION_LINTS,
32+
DUMMY_SP,
33+
"`clippy::restriction` is not meant to be enabled as a group",
34+
|diag| {
35+
diag.note(format!(
36+
"because of the command line `--{} clippy::restriction`",
37+
level.as_str()
38+
));
39+
diag.help("enable the restriction lints you need individually");
40+
},
41+
);
42+
}
43+
}
44+
}

Diff for: clippy_lints/src/attrs/deprecated_cfg_attr.rs

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
use super::{unnecessary_clippy_cfg, Attribute, DEPRECATED_CFG_ATTR, DEPRECATED_CLIPPY_CFG_ATTR};
2+
use clippy_config::msrvs::{self, Msrv};
3+
use clippy_utils::diagnostics::span_lint_and_sugg;
4+
use rustc_ast::AttrStyle;
5+
use rustc_errors::Applicability;
6+
use rustc_lint::EarlyContext;
7+
use rustc_span::sym;
8+
9+
pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msrv) {
10+
// check cfg_attr
11+
if attr.has_name(sym::cfg_attr)
12+
&& let Some(items) = attr.meta_item_list()
13+
&& items.len() == 2
14+
&& let Some(feature_item) = items[0].meta_item()
15+
{
16+
// check for `rustfmt`
17+
if feature_item.has_name(sym::rustfmt)
18+
&& msrv.meets(msrvs::TOOL_ATTRIBUTES)
19+
// check for `rustfmt_skip` and `rustfmt::skip`
20+
&& let Some(skip_item) = &items[1].meta_item()
21+
&& (skip_item.has_name(sym!(rustfmt_skip))
22+
|| skip_item
23+
.path
24+
.segments
25+
.last()
26+
.expect("empty path in attribute")
27+
.ident
28+
.name
29+
== sym::skip)
30+
// Only lint outer attributes, because custom inner attributes are unstable
31+
// Tracking issue: https://github.com/rust-lang/rust/issues/54726
32+
&& attr.style == AttrStyle::Outer
33+
{
34+
span_lint_and_sugg(
35+
cx,
36+
DEPRECATED_CFG_ATTR,
37+
attr.span,
38+
"`cfg_attr` is deprecated for rustfmt and got replaced by tool attributes",
39+
"use",
40+
"#[rustfmt::skip]".to_string(),
41+
Applicability::MachineApplicable,
42+
);
43+
} else {
44+
check_deprecated_cfg_recursively(cx, feature_item);
45+
if let Some(behind_cfg_attr) = items[1].meta_item() {
46+
unnecessary_clippy_cfg::check(cx, feature_item, behind_cfg_attr, attr);
47+
}
48+
}
49+
}
50+
}
51+
52+
pub(super) fn check_clippy(cx: &EarlyContext<'_>, attr: &Attribute) {
53+
if attr.has_name(sym::cfg)
54+
&& let Some(list) = attr.meta_item_list()
55+
{
56+
for item in list.iter().filter_map(|item| item.meta_item()) {
57+
check_deprecated_cfg_recursively(cx, item);
58+
}
59+
}
60+
}
61+
62+
fn check_deprecated_cfg_recursively(cx: &EarlyContext<'_>, attr: &rustc_ast::MetaItem) {
63+
if let Some(ident) = attr.ident() {
64+
if ["any", "all", "not"].contains(&ident.name.as_str()) {
65+
let Some(list) = attr.meta_item_list() else { return };
66+
for item in list.iter().filter_map(|item| item.meta_item()) {
67+
check_deprecated_cfg_recursively(cx, item);
68+
}
69+
} else {
70+
check_cargo_clippy_attr(cx, attr);
71+
}
72+
}
73+
}
74+
75+
fn check_cargo_clippy_attr(cx: &EarlyContext<'_>, item: &rustc_ast::MetaItem) {
76+
if item.has_name(sym::feature) && item.value_str().is_some_and(|v| v.as_str() == "cargo-clippy") {
77+
span_lint_and_sugg(
78+
cx,
79+
DEPRECATED_CLIPPY_CFG_ATTR,
80+
item.span,
81+
"`feature = \"cargo-clippy\"` was replaced by `clippy`",
82+
"replace with",
83+
"clippy".to_string(),
84+
Applicability::MachineApplicable,
85+
);
86+
}
87+
}

Diff for: clippy_lints/src/attrs/deprecated_semver.rs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
use super::DEPRECATED_SEMVER;
2+
use clippy_utils::diagnostics::span_lint;
3+
use rustc_ast::{LitKind, MetaItemLit};
4+
use rustc_lint::LateContext;
5+
use rustc_span::Span;
6+
use semver::Version;
7+
8+
pub(super) fn check(cx: &LateContext<'_>, span: Span, lit: &MetaItemLit) {
9+
if let LitKind::Str(is, _) = lit.kind {
10+
if is.as_str() == "TBD" || Version::parse(is.as_str()).is_ok() {
11+
return;
12+
}
13+
}
14+
span_lint(
15+
cx,
16+
DEPRECATED_SEMVER,
17+
span,
18+
"the since field must contain a semver-compliant version",
19+
);
20+
}

Diff for: clippy_lints/src/attrs/empty_line_after.rs

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use super::{EMPTY_LINE_AFTER_DOC_COMMENTS, EMPTY_LINE_AFTER_OUTER_ATTR};
2+
use clippy_utils::diagnostics::span_lint;
3+
use clippy_utils::source::{is_present_in_source, snippet_opt, without_block_comments};
4+
use rustc_ast::{AttrKind, AttrStyle};
5+
use rustc_lint::EarlyContext;
6+
use rustc_span::Span;
7+
8+
/// Check for empty lines after outer attributes.
9+
///
10+
/// Attributes and documentation comments are both considered outer attributes
11+
/// by the AST. However, the average user likely considers them to be different.
12+
/// Checking for empty lines after each of these attributes is split into two different
13+
/// lints but can share the same logic.
14+
pub(super) fn check(cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
15+
let mut iter = item.attrs.iter().peekable();
16+
while let Some(attr) = iter.next() {
17+
if (matches!(attr.kind, AttrKind::Normal(..)) || matches!(attr.kind, AttrKind::DocComment(..)))
18+
&& attr.style == AttrStyle::Outer
19+
&& is_present_in_source(cx, attr.span)
20+
{
21+
let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt(), item.span.parent());
22+
let end_of_attr_to_next_attr_or_item = Span::new(
23+
attr.span.hi(),
24+
iter.peek().map_or(item.span.lo(), |next_attr| next_attr.span.lo()),
25+
item.span.ctxt(),
26+
item.span.parent(),
27+
);
28+
29+
if let Some(snippet) = snippet_opt(cx, end_of_attr_to_next_attr_or_item) {
30+
let lines = snippet.split('\n').collect::<Vec<_>>();
31+
let lines = without_block_comments(lines);
32+
33+
if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 {
34+
let (lint_msg, lint_type) = match attr.kind {
35+
AttrKind::DocComment(..) => (
36+
"found an empty line after a doc comment. \
37+
Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`?",
38+
EMPTY_LINE_AFTER_DOC_COMMENTS,
39+
),
40+
AttrKind::Normal(..) => (
41+
"found an empty line after an outer attribute. \
42+
Perhaps you forgot to add a `!` to make it an inner attribute?",
43+
EMPTY_LINE_AFTER_OUTER_ATTR,
44+
),
45+
};
46+
47+
span_lint(cx, lint_type, begin_of_attr_to_item, lint_msg);
48+
}
49+
}
50+
}
51+
}
52+
}

Diff for: clippy_lints/src/attrs/inline_always.rs

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use super::utils::is_word;
2+
use super::INLINE_ALWAYS;
3+
use clippy_utils::diagnostics::span_lint;
4+
use rustc_ast::Attribute;
5+
use rustc_lint::LateContext;
6+
use rustc_span::symbol::Symbol;
7+
use rustc_span::{sym, Span};
8+
9+
pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) {
10+
if span.from_expansion() {
11+
return;
12+
}
13+
14+
for attr in attrs {
15+
if let Some(values) = attr.meta_item_list() {
16+
if values.len() != 1 || !attr.has_name(sym::inline) {
17+
continue;
18+
}
19+
if is_word(&values[0], sym::always) {
20+
span_lint(
21+
cx,
22+
INLINE_ALWAYS,
23+
attr.span,
24+
&format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"),
25+
);
26+
}
27+
}
28+
}
29+
}

Diff for: clippy_lints/src/attrs/maybe_misused_cfg.rs

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
use super::{Attribute, MAYBE_MISUSED_CFG};
2+
use clippy_utils::diagnostics::span_lint_and_sugg;
3+
use rustc_ast::{MetaItemKind, NestedMetaItem};
4+
use rustc_errors::Applicability;
5+
use rustc_lint::EarlyContext;
6+
use rustc_span::sym;
7+
8+
pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) {
9+
if attr.has_name(sym::cfg)
10+
&& let Some(items) = attr.meta_item_list()
11+
{
12+
check_nested_misused_cfg(cx, &items);
13+
}
14+
}
15+
16+
fn check_nested_misused_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
17+
for item in items {
18+
if let NestedMetaItem::MetaItem(meta) = item {
19+
if let Some(ident) = meta.ident()
20+
&& ident.name.as_str() == "features"
21+
&& let Some(val) = meta.value_str()
22+
{
23+
span_lint_and_sugg(
24+
cx,
25+
MAYBE_MISUSED_CFG,
26+
meta.span,
27+
"'feature' may be misspelled as 'features'",
28+
"did you mean",
29+
format!("feature = \"{val}\""),
30+
Applicability::MaybeIncorrect,
31+
);
32+
}
33+
if let MetaItemKind::List(list) = &meta.kind {
34+
check_nested_misused_cfg(cx, list);
35+
// If this is not a list, then we check for `cfg(test)`.
36+
} else if let Some(ident) = meta.ident()
37+
&& matches!(ident.name.as_str(), "tests" | "Test")
38+
{
39+
span_lint_and_sugg(
40+
cx,
41+
MAYBE_MISUSED_CFG,
42+
meta.span,
43+
&format!("'test' may be misspelled as '{}'", ident.name.as_str()),
44+
"did you mean",
45+
"test".to_string(),
46+
Applicability::MaybeIncorrect,
47+
);
48+
}
49+
}
50+
}
51+
}

0 commit comments

Comments
 (0)