Skip to content

Commit 6d22f60

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 lints listed in rust-lang#6097 generify `LimitStack` for use in other lints update the `cognitive_complexity` lint to use a `LimitStack<u64>` update existing tests with the msrv attribute
1 parent e298c83 commit 6d22f60

26 files changed

+279
-159
lines changed

clippy_lints/src/cognitive_complexity.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ declare_clippy_lint! {
2727
}
2828

2929
pub struct CognitiveComplexity {
30-
limit: LimitStack,
30+
limit: LimitStack<u64>,
3131
}
3232

3333
impl CognitiveComplexity {
@@ -74,7 +74,7 @@ impl CognitiveComplexity {
7474
rust_cc -= ret_adjust;
7575
}
7676

77-
if rust_cc > self.limit.limit() {
77+
if rust_cc > *self.limit.limit() {
7878
let fn_span = match kind {
7979
FnKind::ItemFn(ident, _, _, _, _) | FnKind::Method(ident, _, _, _) => ident.span,
8080
FnKind::Closure(_) => {

clippy_lints/src/lib.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -920,7 +920,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
920920
&zero_div_zero::ZERO_DIVIDED_BY_ZERO,
921921
]);
922922
// end register lints, do not remove this comment, it’s used in `update_lints`
923-
924923
store.register_late_pass(|| box await_holding_invalid::AwaitHolding);
925924
store.register_late_pass(|| box serde_api::SerdeAPI);
926925
store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new());
@@ -956,7 +955,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
956955
store.register_late_pass(|| box strings::StringAdd);
957956
store.register_late_pass(|| box implicit_return::ImplicitReturn);
958957
store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub);
959-
store.register_late_pass(|| box methods::Methods);
958+
let msrv = conf.msrv.clone();
959+
store.register_late_pass(move || box methods::Methods::new(String::from(msrv.clone())));
960960
store.register_late_pass(|| box map_clone::MapClone);
961961
store.register_late_pass(|| box map_err_ignore::MapErrIgnore);
962962
store.register_late_pass(|| box shadow::Shadow);
@@ -970,7 +970,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
970970
store.register_late_pass(|| box types::Casts);
971971
let type_complexity_threshold = conf.type_complexity_threshold;
972972
store.register_late_pass(move || box types::TypeComplexity::new(type_complexity_threshold));
973-
store.register_late_pass(|| box matches::Matches::default());
973+
let msrv = conf.msrv.clone();
974+
store.register_late_pass(move || box matches::Matches::new(String::from(msrv.clone())));
974975
store.register_late_pass(|| box minmax::MinMaxPass);
975976
store.register_late_pass(|| box open_options::OpenOptions);
976977
store.register_late_pass(|| box zero_div_zero::ZeroDiv);
@@ -1129,7 +1130,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
11291130
store.register_late_pass(|| box if_let_mutex::IfLetMutex);
11301131
store.register_late_pass(|| box mut_mutex_lock::MutMutexLock);
11311132
store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems);
1132-
store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive);
1133+
let msrv = conf.msrv.clone();
1134+
store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(String::from(msrv.clone())));
11331135
store.register_late_pass(|| box manual_async_fn::ManualAsyncFn);
11341136
store.register_early_pass(|| box redundant_field_names::RedundantFieldNames);
11351137
store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero);
@@ -1150,7 +1152,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
11501152
store.register_late_pass(|| box manual_unwrap_or::ManualUnwrapOr);
11511153
store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs);
11521154
store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync);
1153-
store.register_late_pass(|| box manual_strip::ManualStrip);
1155+
let msrv = conf.msrv.clone();
1156+
store.register_late_pass(move || box manual_strip::ManualStrip::new(String::from(msrv.clone())));
11541157
store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem);
11551158
let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::<FxHashSet<_>>();
11561159
store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods));

clippy_lints/src/manual_non_exhaustive.rs

+32-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
use crate::utils::{snippet_opt, span_lint_and_then};
1+
use crate::utils::{snippet_opt, span_lint_and_then, LimitStack};
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::Span;
9+
use semver::Version;
10+
11+
const MANUAL_NON_EXHAUSTIVE_MSRV: &str = "1.40.0";
912

1013
declare_clippy_lint! {
1114
/// **What it does:** Checks for manual implementations of the non-exhaustive pattern.
@@ -55,10 +58,28 @@ declare_clippy_lint! {
5558
"manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]"
5659
}
5760

58-
declare_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]);
61+
#[derive(Clone)]
62+
pub struct ManualNonExhaustive {
63+
msrv_stack: LimitStack<String>,
64+
}
65+
66+
impl ManualNonExhaustive {
67+
#[must_use]
68+
pub fn new(msrv: String) -> Self {
69+
Self {
70+
msrv_stack: LimitStack::new(msrv),
71+
}
72+
}
73+
}
74+
75+
impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]);
5976

6077
impl EarlyLintPass for ManualNonExhaustive {
6178
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
79+
if Version::parse(&self.msrv_stack.limit()) < Version::parse(MANUAL_NON_EXHAUSTIVE_MSRV) {
80+
return;
81+
}
82+
6283
match &item.kind {
6384
ItemKind::Enum(def, _) => {
6485
check_manual_non_exhaustive_enum(cx, item, &def.variants);
@@ -73,6 +94,14 @@ impl EarlyLintPass for ManualNonExhaustive {
7394
_ => {},
7495
}
7596
}
97+
98+
fn enter_lint_attrs(&mut self, cx: &EarlyContext<'tcx>, attrs: &'tcx [Attribute]) {
99+
self.msrv_stack.push_attrs(cx.sess, attrs, "msrv");
100+
}
101+
102+
fn exit_lint_attrs(&mut self, cx: &EarlyContext<'tcx>, attrs: &'tcx [Attribute]) {
103+
self.msrv_stack.pop_attrs(cx.sess, attrs, "msrv");
104+
}
76105
}
77106

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

clippy_lints/src/manual_strip.rs

+33-5
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
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, higher, match_def_path, multispan_sugg, paths, qpath_res, snippet, span_lint_and_then, LimitStack,
55
};
66

77
use if_chain::if_chain;
8-
use rustc_ast::ast::LitKind;
8+
use rustc_ast::ast::{Attribute, LitKind};
99
use rustc_hir::def::Res;
1010
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
1111
use rustc_hir::BinOpKind;
1212
use rustc_hir::{BorrowKind, Expr, ExprKind};
13-
use rustc_lint::{LateContext, LateLintPass};
13+
use rustc_lint::{LateContext, LateLintPass, LintContext};
1414
use rustc_middle::hir::map::Map;
1515
use rustc_middle::ty;
16-
use rustc_session::{declare_lint_pass, declare_tool_lint};
16+
use rustc_session::{declare_tool_lint, impl_lint_pass};
1717
use rustc_span::source_map::Spanned;
1818
use rustc_span::Span;
19+
use semver::Version;
20+
21+
const MANUAL_STRIP_MSRV: &str = "1.45.0";
1922

2023
declare_clippy_lint! {
2124
/// **What it does:**
@@ -51,7 +54,20 @@ declare_clippy_lint! {
5154
"suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing"
5255
}
5356

54-
declare_lint_pass!(ManualStrip => [MANUAL_STRIP]);
57+
pub struct ManualStrip {
58+
msrv_stack: LimitStack<String>,
59+
}
60+
61+
impl ManualStrip {
62+
#[must_use]
63+
pub fn new(msrv: String) -> Self {
64+
Self {
65+
msrv_stack: LimitStack::new(msrv),
66+
}
67+
}
68+
}
69+
70+
impl_lint_pass!(ManualStrip => [MANUAL_STRIP]);
5571

5672
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5773
enum StripKind {
@@ -61,6 +77,10 @@ enum StripKind {
6177

6278
impl<'tcx> LateLintPass<'tcx> for ManualStrip {
6379
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
80+
if Version::parse(&self.msrv_stack.limit()) < Version::parse(MANUAL_STRIP_MSRV) {
81+
return;
82+
}
83+
6484
if_chain! {
6585
if let Some((cond, then, _)) = higher::if_block(&expr);
6686
if let ExprKind::MethodCall(_, _, [target_arg, pattern], _) = cond.kind;
@@ -114,6 +134,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
114134
}
115135
}
116136
}
137+
138+
fn enter_lint_attrs(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) {
139+
self.msrv_stack.push_attrs(cx.sess(), attrs, "msrv");
140+
}
141+
142+
fn exit_lint_attrs(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) {
143+
self.msrv_stack.pop_attrs(cx.sess(), attrs, "msrv");
144+
}
117145
}
118146

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

clippy_lints/src/matches.rs

+29-4
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ use crate::utils::{
55
expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable,
66
is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, multispan_sugg, remove_blocks, snippet,
77
snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
8-
span_lint_and_then,
8+
span_lint_and_then, LimitStack,
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::Symbol;
26+
use semver::Version;
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_stack: LimitStack<String>,
524526
infallible_destructuring_match_linted: bool,
525527
}
526528

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

558+
const MATCH_LIKE_MATCHES_MACRO_MSRV: &str = "1.42.0";
559+
546560
impl<'tcx> LateLintPass<'tcx> for Matches {
547561
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
548562
if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) {
549563
return;
550564
}
551565

552566
redundant_pattern_match::check(cx, expr);
553-
if !check_match_like_matches(cx, expr) {
554-
lint_match_arms(cx, expr);
567+
568+
if Version::parse(&self.msrv_stack.limit()) >= Version::parse(MATCH_LIKE_MATCHES_MACRO_MSRV) {
569+
if !check_match_like_matches(cx, expr) {
570+
lint_match_arms(cx, expr);
571+
}
555572
}
556573

557574
if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind {
@@ -634,6 +651,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
634651
}
635652
}
636653
}
654+
655+
fn enter_lint_attrs(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) {
656+
self.msrv_stack.push_attrs(cx.sess(), attrs, "msrv");
657+
}
658+
659+
fn exit_lint_attrs(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) {
660+
self.msrv_stack.pop_attrs(cx.sess(), attrs, "msrv");
661+
}
637662
}
638663

639664
#[rustfmt::skip]

clippy_lints/src/methods/mod.rs

+38-5
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,9 +21,10 @@ 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};
27+
use semver::Version;
2628

2729
use crate::consts::{constant, Constant};
2830
use crate::utils::eager_or_lazy::is_lazyness_candidate;
@@ -32,7 +34,8 @@ use crate::utils::{
3234
is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath,
3335
match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty,
3436
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,
37+
span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth,
38+
LimitStack, SpanlessEq,
3639
};
3740

3841
declare_clippy_lint! {
@@ -1369,7 +1372,20 @@ declare_clippy_lint! {
13691372
"using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
13701373
}
13711374

1372-
declare_lint_pass!(Methods => [
1375+
pub struct Methods {
1376+
msrv_stack: LimitStack<String>,
1377+
}
1378+
1379+
impl Methods {
1380+
#[must_use]
1381+
pub fn new(msrv: String) -> Self {
1382+
Self {
1383+
msrv_stack: LimitStack::new(msrv.clone()),
1384+
}
1385+
}
1386+
}
1387+
1388+
impl_lint_pass!(Methods => [
13731389
UNWRAP_USED,
13741390
EXPECT_USED,
13751391
SHOULD_IMPLEMENT_TRAIT,
@@ -1495,8 +1511,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
14951511
check_pointer_offset(cx, expr, arg_lists[0])
14961512
},
14971513
["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]),
1498-
["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false),
1499-
["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true),
1514+
["map", "as_ref"] => {
1515+
lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false, &self.msrv_stack)
1516+
},
1517+
["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true, &self.msrv_stack),
15001518
["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"),
15011519
["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"),
15021520
["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"),
@@ -1693,6 +1711,14 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
16931711
}
16941712
}
16951713
}
1714+
1715+
fn enter_lint_attrs(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) {
1716+
self.msrv_stack.push_attrs(cx.sess(), attrs, "msrv");
1717+
}
1718+
1719+
fn exit_lint_attrs(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) {
1720+
self.msrv_stack.pop_attrs(cx.sess(), attrs, "msrv");
1721+
}
16961722
}
16971723

16981724
/// Checks for the `OR_FUN_CALL` lint.
@@ -3364,14 +3390,21 @@ fn lint_suspicious_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
33643390
);
33653391
}
33663392

3393+
const OPTION_AS_REF_DEREF_MSRV: &str = "1.40.0";
3394+
33673395
/// lint use of `_.as_ref().map(Deref::deref)` for `Option`s
33683396
fn lint_option_as_ref_deref<'tcx>(
33693397
cx: &LateContext<'tcx>,
33703398
expr: &hir::Expr<'_>,
33713399
as_ref_args: &[hir::Expr<'_>],
33723400
map_args: &[hir::Expr<'_>],
33733401
is_mut: bool,
3402+
msrv_stack: &LimitStack<String>,
33743403
) {
3404+
if Version::parse(&msrv_stack.limit()) < Version::parse(OPTION_AS_REF_DEREF_MSRV) {
3405+
return;
3406+
}
3407+
33753408
let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not);
33763409

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

0 commit comments

Comments
 (0)