Skip to content

Commit b86b020

Browse files
bors[bot]flip1995Michael Wrightphansch
committed
3161: New lint: Unknown clippy lints r=phansch a=flip1995 This is the Clippy version of the `rustc` lint `unknown_lints`. The behavior of this lint is pretty much the same. Before this is merged a small change in the compiler needs to be done: `CheckLintNameResult` needs to be public. See rust-lang/rust#54106 3387: Replace big if/else expression with match r=flip1995 a=mikerite 3388: RIIR update lints: Generate deprecated lints r=phansch a=phansch The update script now also generates the 'register_removed' section in `clippy_lints/src/lib.rs`. Also, instead of using `let mut store ...`, I added a new identifier line so that the replacement will continue to work in case `let mut store ...` ever changes. cc #2882 Co-authored-by: flip1995 <[email protected]> Co-authored-by: flip1995 <[email protected]> Co-authored-by: Michael Wright <[email protected]> Co-authored-by: Philipp Hansch <[email protected]>
4 parents 5172271 + 11fea50 + 0a41dfd + 7e02721 commit b86b020

File tree

11 files changed

+229
-69
lines changed

11 files changed

+229
-69
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,7 @@ All notable changes to this project will be documented in this file.
866866
[`unimplemented`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#unimplemented
867867
[`unit_arg`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#unit_arg
868868
[`unit_cmp`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#unit_cmp
869+
[`unknown_clippy_lints`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#unknown_clippy_lints
869870
[`unnecessary_cast`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#unnecessary_cast
870871
[`unnecessary_filter_map`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#unnecessary_filter_map
871872
[`unnecessary_fold`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#unnecessary_fold

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ We are currently in the process of discussing Clippy 1.0 via the RFC process in
99

1010
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
1111

12-
[There are 283 lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html)
12+
[There are 284 lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html)
1313

1414
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1515

clippy_dev/src/lib.rs

+38-3
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,34 @@ impl Lint {
7272
}
7373
}
7474

75+
/// Generates the list of lint links at the bottom of the README
7576
pub fn gen_changelog_lint_list(lints: Vec<Lint>) -> Vec<String> {
7677
let mut lint_list_sorted: Vec<Lint> = lints;
7778
lint_list_sorted.sort_by_key(|l| l.name.clone());
7879
lint_list_sorted
7980
.iter()
80-
.filter(|l| !l.is_internal())
81-
.map(|l| {
82-
format!("[`{}`]: {}#{}", l.name, DOCS_LINK.clone(), l.name)
81+
.filter_map(|l| {
82+
if l.is_internal() {
83+
None
84+
} else {
85+
Some(format!("[`{}`]: {}#{}", l.name, DOCS_LINK.clone(), l.name))
86+
}
87+
}).collect()
88+
}
89+
90+
/// Generates the `register_removed` code in `./clippy_lints/src/lib.rs`.
91+
pub fn gen_deprecated(lints: &[Lint]) -> Vec<String> {
92+
lints.iter()
93+
.filter_map(|l| {
94+
l.clone().deprecation.and_then(|depr_text| {
95+
Some(
96+
format!(
97+
" store.register_removed(\n \"{}\",\n \"{}\",\n );",
98+
l.name,
99+
depr_text
100+
)
101+
)
102+
})
83103
})
84104
.collect()
85105
}
@@ -321,3 +341,18 @@ fn test_gen_changelog_lint_list() {
321341
];
322342
assert_eq!(expected, gen_changelog_lint_list(lints));
323343
}
344+
345+
#[test]
346+
fn test_gen_deprecated() {
347+
let lints = vec![
348+
Lint::new("should_assert_eq", "group1", "abc", Some("has been superseeded by should_assert_eq2"), "module_name"),
349+
Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")
350+
];
351+
let expected: Vec<String> = vec![
352+
r#" store.register_removed(
353+
"should_assert_eq",
354+
"has been superseeded by should_assert_eq2",
355+
);"#.to_string()
356+
];
357+
assert_eq!(expected, gen_deprecated(&lints));
358+
}

clippy_dev/src/main.rs

+8
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,12 @@ fn update_lints() {
8282
false,
8383
|| { gen_changelog_lint_list(lint_list.clone()) }
8484
);
85+
86+
replace_region_in_file(
87+
"../clippy_lints/src/lib.rs",
88+
"begin deprecated lints",
89+
"end deprecated lints",
90+
false,
91+
|| { gen_deprecated(&lint_list) }
92+
);
8593
}

clippy_lints/src/attrs.rs

+85-7
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,20 @@
1212
1313
use crate::reexport::*;
1414
use crate::utils::{
15-
in_macro, last_line_of_span, match_def_path, opt_def_id, paths, snippet_opt, span_lint, span_lint_and_then,
16-
without_block_comments,
15+
in_macro, last_line_of_span, match_def_path, opt_def_id, paths, snippet_opt, span_lint,
16+
span_lint_and_then, without_block_comments,
1717
};
18-
use crate::rustc::hir::*;
19-
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
20-
use crate::rustc::{declare_tool_lint, lint_array};
2118
use if_chain::if_chain;
19+
use crate::rustc::hir::*;
20+
use crate::rustc::lint::{
21+
CheckLintNameResult, LateContext, LateLintPass, LintArray, LintContext, LintPass,
22+
};
2223
use crate::rustc::ty::{self, TyCtxt};
24+
use crate::rustc::{declare_tool_lint, lint_array};
2325
use semver::Version;
24-
use crate::syntax::ast::{AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem, NestedMetaItemKind};
26+
use crate::syntax::ast::{
27+
AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem, NestedMetaItemKind,
28+
};
2529
use crate::syntax::source_map::Span;
2630
use crate::rustc_errors::Applicability;
2731

@@ -138,6 +142,33 @@ declare_clippy_lint! {
138142
"empty line after outer attribute"
139143
}
140144

145+
/// **What it does:** Checks for `allow`/`warn`/`deny`/`forbid` attributes with scoped clippy
146+
/// lints and if those lints exist in clippy. If there is a uppercase letter in the lint name
147+
/// (not the tool name) and a lowercase version of this lint exists, it will suggest to lowercase
148+
/// the lint name.
149+
///
150+
/// **Why is this bad?** A lint attribute with a mistyped lint name won't have an effect.
151+
///
152+
/// **Known problems:** None.
153+
///
154+
/// **Example:**
155+
/// Bad:
156+
/// ```rust
157+
/// #![warn(if_not_els)]
158+
/// #![deny(clippy::All)]
159+
/// ```
160+
///
161+
/// Good:
162+
/// ```rust
163+
/// #![warn(if_not_else)]
164+
/// #![deny(clippy::all)]
165+
/// ```
166+
declare_clippy_lint! {
167+
pub UNKNOWN_CLIPPY_LINTS,
168+
style,
169+
"unknown_lints for scoped Clippy lints"
170+
}
171+
141172
#[derive(Copy, Clone)]
142173
pub struct AttrPass;
143174

@@ -147,14 +178,21 @@ impl LintPass for AttrPass {
147178
INLINE_ALWAYS,
148179
DEPRECATED_SEMVER,
149180
USELESS_ATTRIBUTE,
150-
EMPTY_LINE_AFTER_OUTER_ATTR
181+
EMPTY_LINE_AFTER_OUTER_ATTR,
182+
UNKNOWN_CLIPPY_LINTS,
151183
)
152184
}
153185
}
154186

155187
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AttrPass {
156188
fn check_attribute(&mut self, cx: &LateContext<'a, 'tcx>, attr: &'tcx Attribute) {
157189
if let Some(ref items) = attr.meta_item_list() {
190+
match &*attr.name().as_str() {
191+
"allow" | "warn" | "deny" | "forbid" => {
192+
check_clippy_lint_names(cx, items);
193+
}
194+
_ => {}
195+
}
158196
if items.is_empty() || attr.name() != "deprecated" {
159197
return;
160198
}
@@ -247,6 +285,46 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AttrPass {
247285
}
248286
}
249287

288+
fn check_clippy_lint_names(cx: &LateContext<'_, '_>, items: &[NestedMetaItem]) {
289+
let lint_store = cx.lints();
290+
for lint in items {
291+
if_chain! {
292+
if let Some(word) = lint.word();
293+
if let Some(tool_name) = word.is_scoped();
294+
if tool_name.as_str() == "clippy";
295+
let name = word.name();
296+
if let CheckLintNameResult::Tool(Err((None, _))) = lint_store.check_lint_name(
297+
&name.as_str(),
298+
Some(tool_name.as_str()),
299+
);
300+
then {
301+
span_lint_and_then(
302+
cx,
303+
UNKNOWN_CLIPPY_LINTS,
304+
lint.span,
305+
&format!("unknown clippy lint: clippy::{}", name),
306+
|db| {
307+
if name.as_str().chars().any(|c| c.is_uppercase()) {
308+
let name_lower = name.as_str().to_lowercase().to_string();
309+
match lint_store.check_lint_name(
310+
&name_lower,
311+
Some(tool_name.as_str())
312+
) {
313+
CheckLintNameResult::NoLint => {}
314+
_ => {
315+
db.span_suggestion(lint.span,
316+
"lowercase the lint name",
317+
name_lower);
318+
}
319+
}
320+
}
321+
}
322+
);
323+
}
324+
};
325+
}
326+
}
327+
250328
fn is_relevant_item(tcx: TyCtxt<'_, '_, '_>, item: &Item) -> bool {
251329
if let ItemKind::Fn(_, _, _, eid) = item.node {
252330
is_relevant_expr(tcx, tcx.body_tables(eid), &tcx.hir.body(eid).value)

clippy_lints/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ pub fn read_conf(reg: &rustc_plugin::Registry<'_>) -> Conf {
271271
#[rustfmt::skip]
272272
pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
273273
let mut store = reg.sess.lint_store.borrow_mut();
274+
// begin deprecated lints, do not remove this comment, it’s used in `update_lints`
274275
store.register_removed(
275276
"should_assert_eq",
276277
"`assert!()` will be more flexible with RFC 2011",
@@ -532,6 +533,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
532533
assign_ops::ASSIGN_OP_PATTERN,
533534
assign_ops::MISREFACTORED_ASSIGN_OP,
534535
attrs::DEPRECATED_SEMVER,
536+
attrs::UNKNOWN_CLIPPY_LINTS,
535537
attrs::USELESS_ATTRIBUTE,
536538
bit_mask::BAD_BIT_MASK,
537539
bit_mask::INEFFECTIVE_BIT_MASK,
@@ -748,6 +750,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
748750

749751
reg.register_lint_group("clippy::style", Some("clippy_style"), vec![
750752
assign_ops::ASSIGN_OP_PATTERN,
753+
attrs::UNKNOWN_CLIPPY_LINTS,
751754
bit_mask::VERBOSE_BIT_MASK,
752755
blacklisted_name::BLACKLISTED_NAME,
753756
block_in_if_condition::BLOCK_IN_IF_CONDITION_EXPR,

clippy_lints/src/methods/mod.rs

+36-56
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@ use crate::rustc::{declare_tool_lint, lint_array};
1616
use crate::rustc_errors::Applicability;
1717
use crate::syntax::ast;
1818
use crate::syntax::source_map::{BytePos, Span};
19+
use crate::syntax::symbol::LocalInternedString;
1920
use crate::utils::paths;
2021
use crate::utils::sugg;
2122
use crate::utils::{
2223
get_arg_name, get_trait_def_id, implements_trait, in_macro, is_copy, is_expn_of, is_self, is_self_ty,
2324
iter_input_pats, last_path_segment, match_def_path, match_path, match_qpath, match_trait_method, match_type,
24-
match_var, method_chain_args, remove_blocks, return_ty, same_tys, single_segment_path, snippet, snippet_with_macro_callsite, span_lint,
25+
match_var, method_calls, method_chain_args, remove_blocks, return_ty, same_tys, single_segment_path, snippet, snippet_with_macro_callsite, span_lint,
2526
span_lint_and_sugg, span_lint_and_then, span_note_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq,
2627
};
2728
use if_chain::if_chain;
@@ -790,63 +791,42 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
790791
return;
791792
}
792793

794+
let (method_names, arg_lists) = method_calls(expr, 2);
795+
let method_names: Vec<LocalInternedString> = method_names.iter().map(|s| s.as_str()).collect();
796+
let method_names: Vec<&str> = method_names.iter().map(|s| s.as_ref()).collect();
797+
798+
match method_names.as_slice() {
799+
["unwrap", "get"] => lint_get_unwrap(cx, expr, arg_lists[1], false),
800+
["unwrap", "get_mut"] => lint_get_unwrap(cx, expr, arg_lists[1], true),
801+
["unwrap", ..] => lint_unwrap(cx, expr, arg_lists[0]),
802+
["expect", "ok"] => lint_ok_expect(cx, expr, arg_lists[1]),
803+
["unwrap_or", "map"] => lint_map_unwrap_or(cx, expr, arg_lists[1], arg_lists[0]),
804+
["unwrap_or_else", "map"] => lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]),
805+
["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]),
806+
["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]),
807+
["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]),
808+
["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]),
809+
["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
810+
["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
811+
["flatten", "map"] => lint_map_flatten(cx, expr, arg_lists[1]),
812+
["is_some", "find"] => lint_search_is_some(cx, expr, "find", arg_lists[1], arg_lists[0]),
813+
["is_some", "position"] => lint_search_is_some(cx, expr, "position", arg_lists[1], arg_lists[0]),
814+
["is_some", "rposition"] => lint_search_is_some(cx, expr, "rposition", arg_lists[1], arg_lists[0]),
815+
["extend", ..] => lint_extend(cx, expr, arg_lists[0]),
816+
["as_ptr", "unwrap"] => lint_cstring_as_ptr(cx, expr, &arg_lists[1][0], &arg_lists[0][0]),
817+
["nth", "iter"] => lint_iter_nth(cx, expr, arg_lists[1], false),
818+
["nth", "iter_mut"] => lint_iter_nth(cx, expr, arg_lists[1], true),
819+
["next", "skip"] => lint_iter_skip_next(cx, expr),
820+
["collect", "cloned"] => lint_iter_cloned_collect(cx, expr, arg_lists[1]),
821+
["as_ref", ..] => lint_asref(cx, expr, "as_ref", arg_lists[0]),
822+
["as_mut", ..] => lint_asref(cx, expr, "as_mut", arg_lists[0]),
823+
["fold", ..] => lint_unnecessary_fold(cx, expr, arg_lists[0]),
824+
["filter_map", ..] => unnecessary_filter_map::lint(cx, expr, arg_lists[0]),
825+
_ => {}
826+
}
827+
793828
match expr.node {
794829
hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args) => {
795-
// Chain calls
796-
// GET_UNWRAP needs to be checked before general `UNWRAP` lints
797-
if let Some(arglists) = method_chain_args(expr, &["get", "unwrap"]) {
798-
lint_get_unwrap(cx, expr, arglists[0], false);
799-
} else if let Some(arglists) = method_chain_args(expr, &["get_mut", "unwrap"]) {
800-
lint_get_unwrap(cx, expr, arglists[0], true);
801-
} else if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
802-
lint_unwrap(cx, expr, arglists[0]);
803-
} else if let Some(arglists) = method_chain_args(expr, &["ok", "expect"]) {
804-
lint_ok_expect(cx, expr, arglists[0]);
805-
} else if let Some(arglists) = method_chain_args(expr, &["map", "unwrap_or"]) {
806-
lint_map_unwrap_or(cx, expr, arglists[0], arglists[1]);
807-
} else if let Some(arglists) = method_chain_args(expr, &["map", "unwrap_or_else"]) {
808-
lint_map_unwrap_or_else(cx, expr, arglists[0], arglists[1]);
809-
} else if let Some(arglists) = method_chain_args(expr, &["map_or"]) {
810-
lint_map_or_none(cx, expr, arglists[0]);
811-
} else if let Some(arglists) = method_chain_args(expr, &["filter", "next"]) {
812-
lint_filter_next(cx, expr, arglists[0]);
813-
} else if let Some(arglists) = method_chain_args(expr, &["filter", "map"]) {
814-
lint_filter_map(cx, expr, arglists[0], arglists[1]);
815-
} else if let Some(arglists) = method_chain_args(expr, &["filter_map", "map"]) {
816-
lint_filter_map_map(cx, expr, arglists[0], arglists[1]);
817-
} else if let Some(arglists) = method_chain_args(expr, &["filter", "flat_map"]) {
818-
lint_filter_flat_map(cx, expr, arglists[0], arglists[1]);
819-
} else if let Some(arglists) = method_chain_args(expr, &["filter_map", "flat_map"]) {
820-
lint_filter_map_flat_map(cx, expr, arglists[0], arglists[1]);
821-
} else if let Some(arglists) = method_chain_args(expr, &["map", "flatten"]) {
822-
lint_map_flatten(cx, expr, arglists[0]);
823-
} else if let Some(arglists) = method_chain_args(expr, &["find", "is_some"]) {
824-
lint_search_is_some(cx, expr, "find", arglists[0], arglists[1]);
825-
} else if let Some(arglists) = method_chain_args(expr, &["position", "is_some"]) {
826-
lint_search_is_some(cx, expr, "position", arglists[0], arglists[1]);
827-
} else if let Some(arglists) = method_chain_args(expr, &["rposition", "is_some"]) {
828-
lint_search_is_some(cx, expr, "rposition", arglists[0], arglists[1]);
829-
} else if let Some(arglists) = method_chain_args(expr, &["extend"]) {
830-
lint_extend(cx, expr, arglists[0]);
831-
} else if let Some(arglists) = method_chain_args(expr, &["unwrap", "as_ptr"]) {
832-
lint_cstring_as_ptr(cx, expr, &arglists[0][0], &arglists[1][0]);
833-
} else if let Some(arglists) = method_chain_args(expr, &["iter", "nth"]) {
834-
lint_iter_nth(cx, expr, arglists[0], false);
835-
} else if let Some(arglists) = method_chain_args(expr, &["iter_mut", "nth"]) {
836-
lint_iter_nth(cx, expr, arglists[0], true);
837-
} else if method_chain_args(expr, &["skip", "next"]).is_some() {
838-
lint_iter_skip_next(cx, expr);
839-
} else if let Some(arglists) = method_chain_args(expr, &["cloned", "collect"]) {
840-
lint_iter_cloned_collect(cx, expr, arglists[0]);
841-
} else if let Some(arglists) = method_chain_args(expr, &["as_ref"]) {
842-
lint_asref(cx, expr, "as_ref", arglists[0]);
843-
} else if let Some(arglists) = method_chain_args(expr, &["as_mut"]) {
844-
lint_asref(cx, expr, "as_mut", arglists[0]);
845-
} else if let Some(arglists) = method_chain_args(expr, &["fold"]) {
846-
lint_unnecessary_fold(cx, expr, arglists[0]);
847-
} else if let Some(arglists) = method_chain_args(expr, &["filter_map"]) {
848-
unnecessary_filter_map::lint(cx, expr, arglists[0]);
849-
}
850830

851831
lint_or_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);
852832
lint_expect_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);

clippy_lints/src/utils/mod.rs

+24-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use crate::syntax::ast::{self, LitKind};
3131
use crate::syntax::attr;
3232
use crate::syntax::source_map::{Span, DUMMY_SP};
3333
use crate::syntax::errors::DiagnosticBuilder;
34-
use crate::syntax::symbol::keywords;
34+
use crate::syntax::symbol::{keywords, Symbol};
3535

3636
pub mod camel_case;
3737

@@ -274,6 +274,29 @@ pub fn resolve_node(cx: &LateContext<'_, '_>, qpath: &QPath, id: HirId) -> def::
274274
cx.tables.qpath_def(qpath, id)
275275
}
276276

277+
/// Return the method names and argument list of nested method call expressions that make up
278+
/// `expr`.
279+
pub fn method_calls<'a>(expr: &'a Expr, max_depth: usize) -> (Vec<Symbol>, Vec<&'a [Expr]>) {
280+
let mut method_names = Vec::with_capacity(max_depth);
281+
let mut arg_lists = Vec::with_capacity(max_depth);
282+
283+
let mut current = expr;
284+
for _ in 0..max_depth {
285+
if let ExprKind::MethodCall(path, _, args) = &current.node {
286+
if args.iter().any(|e| in_macro(e.span)) {
287+
break;
288+
}
289+
method_names.push(path.ident.name);
290+
arg_lists.push(&**args);
291+
current = &args[0];
292+
} else {
293+
break;
294+
}
295+
}
296+
297+
(method_names, arg_lists)
298+
}
299+
277300
/// Match an `Expr` against a chain of methods, and return the matched `Expr`s.
278301
///
279302
/// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`,

0 commit comments

Comments
 (0)