Skip to content

Commit aaa4325

Browse files
suyashb95flip1995
authored andcommitted
add support for minimum supported rust version.
add configuration option for minimum supported rust version add msrv attribute to some lints listed in rust-lang#6097 add tests
1 parent f897d27 commit aaa4325

20 files changed

+390
-29
lines changed

clippy_lints/src/lib.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ extern crate rustc_target;
4444
extern crate rustc_trait_selection;
4545
extern crate rustc_typeck;
4646

47+
use crate::utils::parse_msrv;
4748
use rustc_data_structures::fx::FxHashSet;
4849
use rustc_lint::LintId;
4950
use rustc_session::Session;
@@ -933,7 +934,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
933934
&zero_div_zero::ZERO_DIVIDED_BY_ZERO,
934935
]);
935936
// end register lints, do not remove this comment, it’s used in `update_lints`
936-
937937
store.register_late_pass(|| box await_holding_invalid::AwaitHolding);
938938
store.register_late_pass(|| box serde_api::SerdeAPI);
939939
store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new());
@@ -969,7 +969,23 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
969969
store.register_late_pass(|| box strings::StringAdd);
970970
store.register_late_pass(|| box implicit_return::ImplicitReturn);
971971
store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub);
972-
store.register_late_pass(|| box methods::Methods);
972+
973+
let parsed_msrv = conf.msrv.as_ref().and_then(|s| {
974+
parse_msrv(s, None, None).or_else(|| {
975+
sess.err(&format!("error reading Clippy's configuration file. `{}` is not a valid Rust version", s));
976+
None
977+
})
978+
});
979+
980+
let msrv = parsed_msrv.clone();
981+
store.register_late_pass(move || box methods::Methods::new(msrv.clone()));
982+
let msrv = parsed_msrv.clone();
983+
store.register_late_pass(move || box matches::Matches::new(msrv.clone()));
984+
let msrv = parsed_msrv.clone();
985+
store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv.clone()));
986+
let msrv = parsed_msrv;
987+
store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv.clone()));
988+
973989
store.register_late_pass(|| box map_clone::MapClone);
974990
store.register_late_pass(|| box map_err_ignore::MapErrIgnore);
975991
store.register_late_pass(|| box shadow::Shadow);
@@ -983,7 +999,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
983999
store.register_late_pass(|| box types::Casts);
9841000
let type_complexity_threshold = conf.type_complexity_threshold;
9851001
store.register_late_pass(move || box types::TypeComplexity::new(type_complexity_threshold));
986-
store.register_late_pass(|| box matches::Matches::default());
9871002
store.register_late_pass(|| box minmax::MinMaxPass);
9881003
store.register_late_pass(|| box open_options::OpenOptions);
9891004
store.register_late_pass(|| box zero_div_zero::ZeroDiv);
@@ -1144,7 +1159,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
11441159
store.register_late_pass(|| box if_let_mutex::IfLetMutex);
11451160
store.register_late_pass(|| box mut_mutex_lock::MutMutexLock);
11461161
store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems);
1147-
store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive);
11481162
store.register_late_pass(|| box manual_async_fn::ManualAsyncFn);
11491163
store.register_early_pass(|| box redundant_field_names::RedundantFieldNames);
11501164
store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero);
@@ -1166,7 +1180,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
11661180
store.register_late_pass(|| box manual_ok_or::ManualOkOr);
11671181
store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs);
11681182
store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync);
1169-
store.register_late_pass(|| box manual_strip::ManualStrip);
11701183
store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem);
11711184
let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::<FxHashSet<_>>();
11721185
store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods));

clippy_lints/src/manual_non_exhaustive.rs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
1-
use crate::utils::{snippet_opt, span_lint_and_then};
1+
use crate::utils::{get_inner_attr, meets_msrv, snippet_opt, span_lint_and_then};
22
use if_chain::if_chain;
33
use rustc_ast::ast::{Attribute, Item, ItemKind, StructField, Variant, VariantData, VisibilityKind};
44
use rustc_attr as attr;
55
use rustc_errors::Applicability;
66
use rustc_lint::{EarlyContext, EarlyLintPass};
7-
use rustc_session::{declare_lint_pass, declare_tool_lint};
7+
use rustc_session::{declare_tool_lint, impl_lint_pass};
88
use rustc_span::{sym, Span};
9+
use semver::{Version, VersionReq};
10+
11+
const MANUAL_NON_EXHAUSTIVE_MSRV: Version = Version {
12+
major: 1,
13+
minor: 40,
14+
patch: 0,
15+
pre: Vec::new(),
16+
build: Vec::new(),
17+
};
918

1019
declare_clippy_lint! {
1120
/// **What it does:** Checks for manual implementations of the non-exhaustive pattern.
@@ -55,10 +64,26 @@ declare_clippy_lint! {
5564
"manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]"
5665
}
5766

58-
declare_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]);
67+
#[derive(Clone)]
68+
pub struct ManualNonExhaustive {
69+
msrv: Option<VersionReq>,
70+
}
71+
72+
impl ManualNonExhaustive {
73+
#[must_use]
74+
pub fn new(msrv: Option<VersionReq>) -> Self {
75+
Self { msrv }
76+
}
77+
}
78+
79+
impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]);
5980

6081
impl EarlyLintPass for ManualNonExhaustive {
6182
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
83+
if !meets_msrv(self.msrv.as_ref(), &MANUAL_NON_EXHAUSTIVE_MSRV) {
84+
return;
85+
}
86+
6287
match &item.kind {
6388
ItemKind::Enum(def, _) => {
6489
check_manual_non_exhaustive_enum(cx, item, &def.variants);
@@ -73,6 +98,8 @@ impl EarlyLintPass for ManualNonExhaustive {
7398
_ => {},
7499
}
75100
}
101+
102+
extract_msrv_attr!(EarlyContext);
76103
}
77104

78105
fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants: &[Variant]) {

clippy_lints/src/manual_strip.rs

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,31 @@
11
use crate::consts::{constant, Constant};
22
use crate::utils::usage::mutated_variables;
33
use crate::utils::{
4-
eq_expr_value, higher, match_def_path, multispan_sugg, paths, qpath_res, snippet, span_lint_and_then,
4+
eq_expr_value, get_inner_attr, higher, match_def_path, meets_msrv, multispan_sugg, paths, qpath_res, snippet,
5+
span_lint_and_then,
56
};
67

78
use if_chain::if_chain;
8-
use rustc_ast::ast::LitKind;
9+
use rustc_ast::ast::{Attribute, LitKind};
910
use rustc_hir::def::Res;
1011
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
1112
use rustc_hir::BinOpKind;
1213
use rustc_hir::{BorrowKind, Expr, ExprKind};
13-
use rustc_lint::{LateContext, LateLintPass};
14+
use rustc_lint::{LateContext, LateLintPass, LintContext};
1415
use rustc_middle::hir::map::Map;
1516
use rustc_middle::ty;
16-
use rustc_session::{declare_lint_pass, declare_tool_lint};
17+
use rustc_session::{declare_tool_lint, impl_lint_pass};
1718
use rustc_span::source_map::Spanned;
1819
use rustc_span::Span;
20+
use semver::{Version, VersionReq};
21+
22+
const MANUAL_STRIP_MSRV: Version = Version {
23+
major: 1,
24+
minor: 45,
25+
patch: 0,
26+
pre: Vec::new(),
27+
build: Vec::new(),
28+
};
1929

2030
declare_clippy_lint! {
2131
/// **What it does:**
@@ -51,7 +61,18 @@ declare_clippy_lint! {
5161
"suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing"
5262
}
5363

54-
declare_lint_pass!(ManualStrip => [MANUAL_STRIP]);
64+
pub struct ManualStrip {
65+
msrv: Option<VersionReq>,
66+
}
67+
68+
impl ManualStrip {
69+
#[must_use]
70+
pub fn new(msrv: Option<VersionReq>) -> Self {
71+
Self { msrv }
72+
}
73+
}
74+
75+
impl_lint_pass!(ManualStrip => [MANUAL_STRIP]);
5576

5677
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5778
enum StripKind {
@@ -61,6 +82,10 @@ enum StripKind {
6182

6283
impl<'tcx> LateLintPass<'tcx> for ManualStrip {
6384
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
85+
if !meets_msrv(self.msrv.as_ref(), &MANUAL_STRIP_MSRV) {
86+
return;
87+
}
88+
6489
if_chain! {
6590
if let Some((cond, then, _)) = higher::if_block(&expr);
6691
if let ExprKind::MethodCall(_, _, [target_arg, pattern], _) = cond.kind;
@@ -114,6 +139,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
114139
}
115140
}
116141
}
142+
143+
extract_msrv_attr!(LateContext);
117144
}
118145

119146
// Returns `Some(arg)` if `expr` matches `arg.len()` and `None` otherwise.

clippy_lints/src/matches.rs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ use crate::consts::{constant, miri_to_const, Constant};
22
use crate::utils::sugg::Sugg;
33
use crate::utils::usage::is_unused;
44
use crate::utils::{
5-
expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable,
6-
is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, multispan_sugg, remove_blocks, snippet,
7-
snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
8-
span_lint_and_then,
5+
expr_block, get_arg_name, get_inner_attr, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of,
6+
is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg,
7+
remove_blocks, snippet, snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note,
8+
span_lint_and_sugg, span_lint_and_then,
99
};
1010
use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash};
1111
use if_chain::if_chain;
12-
use rustc_ast::ast::LitKind;
12+
use rustc_ast::ast::{Attribute, LitKind};
1313
use rustc_data_structures::fx::FxHashMap;
1414
use rustc_errors::Applicability;
1515
use rustc_hir::def::CtorKind;
@@ -23,6 +23,7 @@ use rustc_middle::ty::{self, Ty, TyS};
2323
use rustc_session::{declare_tool_lint, impl_lint_pass};
2424
use rustc_span::source_map::{Span, Spanned};
2525
use rustc_span::{sym, Symbol};
26+
use semver::{Version, VersionReq};
2627
use std::cmp::Ordering;
2728
use std::collections::hash_map::Entry;
2829
use std::collections::Bound;
@@ -527,9 +528,20 @@ declare_clippy_lint! {
527528

528529
#[derive(Default)]
529530
pub struct Matches {
531+
msrv: Option<VersionReq>,
530532
infallible_destructuring_match_linted: bool,
531533
}
532534

535+
impl Matches {
536+
#[must_use]
537+
pub fn new(msrv: Option<VersionReq>) -> Self {
538+
Self {
539+
msrv,
540+
..Matches::default()
541+
}
542+
}
543+
}
544+
533545
impl_lint_pass!(Matches => [
534546
SINGLE_MATCH,
535547
MATCH_REF_PATS,
@@ -549,14 +561,27 @@ impl_lint_pass!(Matches => [
549561
MATCH_SAME_ARMS,
550562
]);
551563

564+
const MATCH_LIKE_MATCHES_MACRO_MSRV: Version = Version {
565+
major: 1,
566+
minor: 42,
567+
patch: 0,
568+
pre: Vec::new(),
569+
build: Vec::new(),
570+
};
571+
552572
impl<'tcx> LateLintPass<'tcx> for Matches {
553573
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
554574
if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) {
555575
return;
556576
}
557577

558578
redundant_pattern_match::check(cx, expr);
559-
if !check_match_like_matches(cx, expr) {
579+
580+
if meets_msrv(self.msrv.as_ref(), &MATCH_LIKE_MATCHES_MACRO_MSRV) {
581+
if !check_match_like_matches(cx, expr) {
582+
lint_match_arms(cx, expr);
583+
}
584+
} else {
560585
lint_match_arms(cx, expr);
561586
}
562587

@@ -640,6 +665,8 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
640665
}
641666
}
642667
}
668+
669+
extract_msrv_attr!(LateContext);
643670
}
644671

645672
#[rustfmt::skip]

clippy_lints/src/methods/mod.rs

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use std::iter;
1212
use bind_instead_of_map::BindInsteadOfMap;
1313
use if_chain::if_chain;
1414
use rustc_ast::ast;
15+
use rustc_ast::ast::Attribute;
1516
use rustc_errors::Applicability;
1617
use rustc_hir as hir;
1718
use rustc_hir::intravisit::{self, Visitor};
@@ -20,20 +21,22 @@ use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
2021
use rustc_middle::hir::map::Map;
2122
use rustc_middle::lint::in_external_macro;
2223
use rustc_middle::ty::{self, TraitRef, Ty, TyS};
23-
use rustc_session::{declare_lint_pass, declare_tool_lint};
24+
use rustc_session::{declare_tool_lint, impl_lint_pass};
2425
use rustc_span::source_map::Span;
2526
use rustc_span::symbol::{sym, SymbolStr};
2627

2728
use crate::consts::{constant, Constant};
2829
use crate::utils::eager_or_lazy::is_lazyness_candidate;
2930
use crate::utils::usage::mutated_variables;
3031
use crate::utils::{
31-
contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro,
32-
is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath,
33-
match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty,
34-
single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint,
35-
span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, SpanlessEq,
32+
contains_ty, get_arg_name, get_inner_attr, get_parent_expr, get_trait_def_id, has_iter_method, higher,
33+
implements_trait, in_macro, is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment,
34+
match_def_path, match_qpath, match_trait_method, match_type, match_var, meets_msrv, method_calls,
35+
method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability,
36+
snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg,
37+
walk_ptrs_ty_depth, SpanlessEq,
3638
};
39+
use semver::{Version, VersionReq};
3740

3841
declare_clippy_lint! {
3942
/// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s.
@@ -1404,7 +1407,18 @@ declare_clippy_lint! {
14041407
"use `.collect()` instead of `::from_iter()`"
14051408
}
14061409

1407-
declare_lint_pass!(Methods => [
1410+
pub struct Methods {
1411+
msrv: Option<VersionReq>,
1412+
}
1413+
1414+
impl Methods {
1415+
#[must_use]
1416+
pub fn new(msrv: Option<VersionReq>) -> Self {
1417+
Self { msrv }
1418+
}
1419+
}
1420+
1421+
impl_lint_pass!(Methods => [
14081422
UNWRAP_USED,
14091423
EXPECT_USED,
14101424
SHOULD_IMPLEMENT_TRAIT,
@@ -1531,8 +1545,12 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
15311545
check_pointer_offset(cx, expr, arg_lists[0])
15321546
},
15331547
["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]),
1534-
["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false),
1535-
["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true),
1548+
["map", "as_ref"] => {
1549+
lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false, self.msrv.as_ref())
1550+
},
1551+
["map", "as_mut"] => {
1552+
lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true, self.msrv.as_ref())
1553+
},
15361554
["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"),
15371555
["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"),
15381556
["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"),
@@ -1738,6 +1756,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
17381756
}
17391757
}
17401758
}
1759+
1760+
extract_msrv_attr!(LateContext);
17411761
}
17421762

17431763
/// Checks for the `OR_FUN_CALL` lint.
@@ -3453,14 +3473,27 @@ fn lint_suspicious_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
34533473
);
34543474
}
34553475

3476+
const OPTION_AS_REF_DEREF_MSRV: Version = Version {
3477+
major: 1,
3478+
minor: 40,
3479+
patch: 0,
3480+
pre: Vec::new(),
3481+
build: Vec::new(),
3482+
};
3483+
34563484
/// lint use of `_.as_ref().map(Deref::deref)` for `Option`s
34573485
fn lint_option_as_ref_deref<'tcx>(
34583486
cx: &LateContext<'tcx>,
34593487
expr: &hir::Expr<'_>,
34603488
as_ref_args: &[hir::Expr<'_>],
34613489
map_args: &[hir::Expr<'_>],
34623490
is_mut: bool,
3491+
msrv: Option<&VersionReq>,
34633492
) {
3493+
if !meets_msrv(msrv, &OPTION_AS_REF_DEREF_MSRV) {
3494+
return;
3495+
}
3496+
34643497
let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not);
34653498

34663499
let option_ty = cx.typeck_results().expr_ty(&as_ref_args[0]);

0 commit comments

Comments
 (0)