Skip to content

Commit 5d9dbd3

Browse files
committed
[WIP] add support for minimum supported rust version. fixes rust-lang#6097
add configuration option for minimum supported rust version add msrv attribute to some lints listed in rust-lang#6097 add tests
1 parent 5b40ce3 commit 5d9dbd3

20 files changed

+387
-29
lines changed

clippy_lints/src/lib.rs

+18-5
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.clone();
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

+30-3
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

+32-5
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

+33-6
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;
@@ -521,9 +522,20 @@ declare_clippy_lint! {
521522

522523
#[derive(Default)]
523524
pub struct Matches {
525+
msrv: Option<VersionReq>,
524526
infallible_destructuring_match_linted: bool,
525527
}
526528

529+
impl Matches {
530+
#[must_use]
531+
pub fn new(msrv: Option<VersionReq>) -> Self {
532+
Self {
533+
msrv,
534+
..Default::default()
535+
}
536+
}
537+
}
538+
527539
impl_lint_pass!(Matches => [
528540
SINGLE_MATCH,
529541
MATCH_REF_PATS,
@@ -543,14 +555,27 @@ impl_lint_pass!(Matches => [
543555
MATCH_SAME_ARMS,
544556
]);
545557

558+
const MATCH_LIKE_MATCHES_MACRO_MSRV: Version = Version {
559+
major: 1,
560+
minor: 42,
561+
patch: 0,
562+
pre: Vec::new(),
563+
build: Vec::new(),
564+
};
565+
546566
impl<'tcx> LateLintPass<'tcx> for Matches {
547567
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
548568
if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) {
549569
return;
550570
}
551571

552572
redundant_pattern_match::check(cx, expr);
553-
if !check_match_like_matches(cx, expr) {
573+
574+
if meets_msrv(self.msrv.as_ref(), &MATCH_LIKE_MATCHES_MACRO_MSRV) {
575+
if !check_match_like_matches(cx, expr) {
576+
lint_match_arms(cx, expr);
577+
}
578+
} else {
554579
lint_match_arms(cx, expr);
555580
}
556581

@@ -634,6 +659,8 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
634659
}
635660
}
636661
}
662+
663+
extract_msrv_attr!(LateContext);
637664
}
638665

639666
#[rustfmt::skip]

clippy_lints/src/methods/mod.rs

+42-9
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)