mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-23 13:13:34 +00:00
add support for minimum supported rust version.
add configuration option for minimum supported rust version add msrv attribute to some lints listed in #6097 add tests
This commit is contained in:
parent
f897d27d8b
commit
aaa4325045
20 changed files with 390 additions and 29 deletions
|
@ -44,6 +44,7 @@ extern crate rustc_target;
|
|||
extern crate rustc_trait_selection;
|
||||
extern crate rustc_typeck;
|
||||
|
||||
use crate::utils::parse_msrv;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_lint::LintId;
|
||||
use rustc_session::Session;
|
||||
|
@ -933,7 +934,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
&zero_div_zero::ZERO_DIVIDED_BY_ZERO,
|
||||
]);
|
||||
// end register lints, do not remove this comment, it’s used in `update_lints`
|
||||
|
||||
store.register_late_pass(|| box await_holding_invalid::AwaitHolding);
|
||||
store.register_late_pass(|| box serde_api::SerdeAPI);
|
||||
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:
|
|||
store.register_late_pass(|| box strings::StringAdd);
|
||||
store.register_late_pass(|| box implicit_return::ImplicitReturn);
|
||||
store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub);
|
||||
store.register_late_pass(|| box methods::Methods);
|
||||
|
||||
let parsed_msrv = conf.msrv.as_ref().and_then(|s| {
|
||||
parse_msrv(s, None, None).or_else(|| {
|
||||
sess.err(&format!("error reading Clippy's configuration file. `{}` is not a valid Rust version", s));
|
||||
None
|
||||
})
|
||||
});
|
||||
|
||||
let msrv = parsed_msrv.clone();
|
||||
store.register_late_pass(move || box methods::Methods::new(msrv.clone()));
|
||||
let msrv = parsed_msrv.clone();
|
||||
store.register_late_pass(move || box matches::Matches::new(msrv.clone()));
|
||||
let msrv = parsed_msrv.clone();
|
||||
store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv.clone()));
|
||||
let msrv = parsed_msrv;
|
||||
store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv.clone()));
|
||||
|
||||
store.register_late_pass(|| box map_clone::MapClone);
|
||||
store.register_late_pass(|| box map_err_ignore::MapErrIgnore);
|
||||
store.register_late_pass(|| box shadow::Shadow);
|
||||
|
@ -983,7 +999,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| box types::Casts);
|
||||
let type_complexity_threshold = conf.type_complexity_threshold;
|
||||
store.register_late_pass(move || box types::TypeComplexity::new(type_complexity_threshold));
|
||||
store.register_late_pass(|| box matches::Matches::default());
|
||||
store.register_late_pass(|| box minmax::MinMaxPass);
|
||||
store.register_late_pass(|| box open_options::OpenOptions);
|
||||
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:
|
|||
store.register_late_pass(|| box if_let_mutex::IfLetMutex);
|
||||
store.register_late_pass(|| box mut_mutex_lock::MutMutexLock);
|
||||
store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems);
|
||||
store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive);
|
||||
store.register_late_pass(|| box manual_async_fn::ManualAsyncFn);
|
||||
store.register_early_pass(|| box redundant_field_names::RedundantFieldNames);
|
||||
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:
|
|||
store.register_late_pass(|| box manual_ok_or::ManualOkOr);
|
||||
store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs);
|
||||
store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync);
|
||||
store.register_late_pass(|| box manual_strip::ManualStrip);
|
||||
store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem);
|
||||
let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::<FxHashSet<_>>();
|
||||
store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods));
|
||||
|
|
|
@ -1,11 +1,20 @@
|
|||
use crate::utils::{snippet_opt, span_lint_and_then};
|
||||
use crate::utils::{get_inner_attr, meets_msrv, snippet_opt, span_lint_and_then};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{Attribute, Item, ItemKind, StructField, Variant, VariantData, VisibilityKind};
|
||||
use rustc_attr as attr;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::{sym, Span};
|
||||
use semver::{Version, VersionReq};
|
||||
|
||||
const MANUAL_NON_EXHAUSTIVE_MSRV: Version = Version {
|
||||
major: 1,
|
||||
minor: 40,
|
||||
patch: 0,
|
||||
pre: Vec::new(),
|
||||
build: Vec::new(),
|
||||
};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for manual implementations of the non-exhaustive pattern.
|
||||
|
@ -55,10 +64,26 @@ declare_clippy_lint! {
|
|||
"manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]);
|
||||
#[derive(Clone)]
|
||||
pub struct ManualNonExhaustive {
|
||||
msrv: Option<VersionReq>,
|
||||
}
|
||||
|
||||
impl ManualNonExhaustive {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<VersionReq>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]);
|
||||
|
||||
impl EarlyLintPass for ManualNonExhaustive {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &MANUAL_NON_EXHAUSTIVE_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
match &item.kind {
|
||||
ItemKind::Enum(def, _) => {
|
||||
check_manual_non_exhaustive_enum(cx, item, &def.variants);
|
||||
|
@ -73,6 +98,8 @@ impl EarlyLintPass for ManualNonExhaustive {
|
|||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(EarlyContext);
|
||||
}
|
||||
|
||||
fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants: &[Variant]) {
|
||||
|
|
|
@ -1,21 +1,31 @@
|
|||
use crate::consts::{constant, Constant};
|
||||
use crate::utils::usage::mutated_variables;
|
||||
use crate::utils::{
|
||||
eq_expr_value, higher, match_def_path, multispan_sugg, paths, qpath_res, snippet, span_lint_and_then,
|
||||
eq_expr_value, get_inner_attr, higher, match_def_path, meets_msrv, multispan_sugg, paths, qpath_res, snippet,
|
||||
span_lint_and_then,
|
||||
};
|
||||
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_ast::ast::{Attribute, LitKind};
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::BinOpKind;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::Span;
|
||||
use semver::{Version, VersionReq};
|
||||
|
||||
const MANUAL_STRIP_MSRV: Version = Version {
|
||||
major: 1,
|
||||
minor: 45,
|
||||
patch: 0,
|
||||
pre: Vec::new(),
|
||||
build: Vec::new(),
|
||||
};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
|
@ -51,7 +61,18 @@ declare_clippy_lint! {
|
|||
"suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ManualStrip => [MANUAL_STRIP]);
|
||||
pub struct ManualStrip {
|
||||
msrv: Option<VersionReq>,
|
||||
}
|
||||
|
||||
impl ManualStrip {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<VersionReq>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ManualStrip => [MANUAL_STRIP]);
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
enum StripKind {
|
||||
|
@ -61,6 +82,10 @@ enum StripKind {
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualStrip {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &MANUAL_STRIP_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let Some((cond, then, _)) = higher::if_block(&expr);
|
||||
if let ExprKind::MethodCall(_, _, [target_arg, pattern], _) = cond.kind;
|
||||
|
@ -114,6 +139,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
// Returns `Some(arg)` if `expr` matches `arg.len()` and `None` otherwise.
|
||||
|
|
|
@ -2,14 +2,14 @@ use crate::consts::{constant, miri_to_const, Constant};
|
|||
use crate::utils::sugg::Sugg;
|
||||
use crate::utils::usage::is_unused;
|
||||
use crate::utils::{
|
||||
expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable,
|
||||
is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, multispan_sugg, remove_blocks, snippet,
|
||||
snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
|
||||
span_lint_and_then,
|
||||
expr_block, get_arg_name, get_inner_attr, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of,
|
||||
is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg,
|
||||
remove_blocks, snippet, snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note,
|
||||
span_lint_and_sugg, span_lint_and_then,
|
||||
};
|
||||
use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_ast::ast::{Attribute, LitKind};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::CtorKind;
|
||||
|
@ -23,6 +23,7 @@ use rustc_middle::ty::{self, Ty, TyS};
|
|||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::{Span, Spanned};
|
||||
use rustc_span::{sym, Symbol};
|
||||
use semver::{Version, VersionReq};
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::Bound;
|
||||
|
@ -527,9 +528,20 @@ declare_clippy_lint! {
|
|||
|
||||
#[derive(Default)]
|
||||
pub struct Matches {
|
||||
msrv: Option<VersionReq>,
|
||||
infallible_destructuring_match_linted: bool,
|
||||
}
|
||||
|
||||
impl Matches {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<VersionReq>) -> Self {
|
||||
Self {
|
||||
msrv,
|
||||
..Matches::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(Matches => [
|
||||
SINGLE_MATCH,
|
||||
MATCH_REF_PATS,
|
||||
|
@ -549,6 +561,14 @@ impl_lint_pass!(Matches => [
|
|||
MATCH_SAME_ARMS,
|
||||
]);
|
||||
|
||||
const MATCH_LIKE_MATCHES_MACRO_MSRV: Version = Version {
|
||||
major: 1,
|
||||
minor: 42,
|
||||
patch: 0,
|
||||
pre: Vec::new(),
|
||||
build: Vec::new(),
|
||||
};
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Matches {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) {
|
||||
|
@ -556,7 +576,12 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
|||
}
|
||||
|
||||
redundant_pattern_match::check(cx, expr);
|
||||
if !check_match_like_matches(cx, expr) {
|
||||
|
||||
if meets_msrv(self.msrv.as_ref(), &MATCH_LIKE_MATCHES_MACRO_MSRV) {
|
||||
if !check_match_like_matches(cx, expr) {
|
||||
lint_match_arms(cx, expr);
|
||||
}
|
||||
} else {
|
||||
lint_match_arms(cx, expr);
|
||||
}
|
||||
|
||||
|
@ -640,6 +665,8 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
|
|
|
@ -12,6 +12,7 @@ use std::iter;
|
|||
use bind_instead_of_map::BindInsteadOfMap;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast;
|
||||
use rustc_ast::ast::Attribute;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
|
@ -20,7 +21,7 @@ use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
|
|||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, TraitRef, Ty, TyS};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::{sym, SymbolStr};
|
||||
|
||||
|
@ -28,12 +29,14 @@ use crate::consts::{constant, Constant};
|
|||
use crate::utils::eager_or_lazy::is_lazyness_candidate;
|
||||
use crate::utils::usage::mutated_variables;
|
||||
use crate::utils::{
|
||||
contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro,
|
||||
is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath,
|
||||
match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty,
|
||||
single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint,
|
||||
span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, SpanlessEq,
|
||||
contains_ty, get_arg_name, get_inner_attr, get_parent_expr, get_trait_def_id, has_iter_method, higher,
|
||||
implements_trait, in_macro, is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment,
|
||||
match_def_path, match_qpath, match_trait_method, match_type, match_var, meets_msrv, method_calls,
|
||||
method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability,
|
||||
snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg,
|
||||
walk_ptrs_ty_depth, SpanlessEq,
|
||||
};
|
||||
use semver::{Version, VersionReq};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s.
|
||||
|
@ -1404,7 +1407,18 @@ declare_clippy_lint! {
|
|||
"use `.collect()` instead of `::from_iter()`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Methods => [
|
||||
pub struct Methods {
|
||||
msrv: Option<VersionReq>,
|
||||
}
|
||||
|
||||
impl Methods {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<VersionReq>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(Methods => [
|
||||
UNWRAP_USED,
|
||||
EXPECT_USED,
|
||||
SHOULD_IMPLEMENT_TRAIT,
|
||||
|
@ -1531,8 +1545,12 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
check_pointer_offset(cx, expr, arg_lists[0])
|
||||
},
|
||||
["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]),
|
||||
["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false),
|
||||
["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true),
|
||||
["map", "as_ref"] => {
|
||||
lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false, self.msrv.as_ref())
|
||||
},
|
||||
["map", "as_mut"] => {
|
||||
lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true, self.msrv.as_ref())
|
||||
},
|
||||
["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"),
|
||||
["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"),
|
||||
["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"),
|
||||
|
@ -1738,6 +1756,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
/// Checks for the `OR_FUN_CALL` lint.
|
||||
|
@ -3453,6 +3473,14 @@ fn lint_suspicious_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
|
|||
);
|
||||
}
|
||||
|
||||
const OPTION_AS_REF_DEREF_MSRV: Version = Version {
|
||||
major: 1,
|
||||
minor: 40,
|
||||
patch: 0,
|
||||
pre: Vec::new(),
|
||||
build: Vec::new(),
|
||||
};
|
||||
|
||||
/// lint use of `_.as_ref().map(Deref::deref)` for `Option`s
|
||||
fn lint_option_as_ref_deref<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
|
@ -3460,7 +3488,12 @@ fn lint_option_as_ref_deref<'tcx>(
|
|||
as_ref_args: &[hir::Expr<'_>],
|
||||
map_args: &[hir::Expr<'_>],
|
||||
is_mut: bool,
|
||||
msrv: Option<&VersionReq>,
|
||||
) {
|
||||
if !meets_msrv(msrv, &OPTION_AS_REF_DEREF_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not);
|
||||
|
||||
let option_ty = cx.typeck_results().expr_ty(&as_ref_args[0]);
|
||||
|
|
|
@ -21,6 +21,7 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[
|
|||
DeprecationStatus::Replaced("cognitive_complexity"),
|
||||
),
|
||||
("dump", DeprecationStatus::None),
|
||||
("msrv", DeprecationStatus::None),
|
||||
];
|
||||
|
||||
pub struct LimitStack {
|
||||
|
@ -123,6 +124,22 @@ fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'static str) -> Option<ast::Attribute> {
|
||||
let mut unique_attr = None;
|
||||
for attr in get_attr(sess, attrs, name) {
|
||||
match attr.style {
|
||||
ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()),
|
||||
ast::AttrStyle::Inner => {
|
||||
sess.span_err(attr.span, &format!("`{}` is defined multiple times", name));
|
||||
},
|
||||
ast::AttrStyle::Outer => {
|
||||
sess.span_err(attr.span, &format!("`{}` cannot be an outer attribute", name));
|
||||
},
|
||||
}
|
||||
}
|
||||
unique_attr
|
||||
}
|
||||
|
||||
/// Return true if the attributes contain any of `proc_macro`,
|
||||
/// `proc_macro_derive` or `proc_macro_attribute`, false otherwise
|
||||
pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool {
|
||||
|
|
|
@ -106,6 +106,8 @@ macro_rules! define_Conf {
|
|||
|
||||
pub use self::helpers::Conf;
|
||||
define_Conf! {
|
||||
/// Lint: MANUAL_NON_EXHAUSTIVE, MANUAL_STRIP, OPTION_AS_REF_DEREF, MATCH_LIKE_MATCHES_MACRO. The minimum rust version that the project supports
|
||||
(msrv, "msrv": Option<String>, None),
|
||||
/// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses
|
||||
(blacklisted_names, "blacklisted_names": Vec<String>, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
|
||||
/// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have
|
||||
|
|
|
@ -51,6 +51,7 @@ use rustc_lint::{LateContext, Level, Lint, LintContext};
|
|||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
|
||||
use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||
use rustc_span::source_map::original_sp;
|
||||
use rustc_span::sym as rustc_sym;
|
||||
|
@ -58,10 +59,58 @@ use rustc_span::symbol::{self, kw, Symbol};
|
|||
use rustc_span::{BytePos, Pos, Span, DUMMY_SP};
|
||||
use rustc_target::abi::Integer;
|
||||
use rustc_trait_selection::traits::query::normalize::AtExt;
|
||||
use semver::{Version, VersionReq};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::consts::{constant, Constant};
|
||||
|
||||
pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<VersionReq> {
|
||||
if let Ok(version) = VersionReq::parse(msrv) {
|
||||
return Some(version);
|
||||
} else if let Some(sess) = sess {
|
||||
if let Some(span) = span {
|
||||
sess.span_err(span, &format!("`{}` is not a valid Rust version", msrv));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn meets_msrv(msrv: Option<&VersionReq>, lint_msrv: &Version) -> bool {
|
||||
msrv.map_or(true, |msrv| !msrv.matches(lint_msrv))
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! extract_msrv_attr {
|
||||
(LateContext) => {
|
||||
fn enter_lint_attrs(&mut self, cx: &rustc_lint::LateContext<'tcx>, attrs: &'tcx [Attribute]) {
|
||||
match get_inner_attr(cx.sess(), attrs, "msrv") {
|
||||
Some(msrv_attr) => {
|
||||
if let Some(msrv) = msrv_attr.value_str() {
|
||||
self.msrv = crate::utils::parse_msrv(&msrv.to_string(), Some(cx.sess()), Some(msrv_attr.span));
|
||||
} else {
|
||||
cx.sess().span_err(msrv_attr.span, "bad clippy attribute");
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
};
|
||||
(EarlyContext) => {
|
||||
fn enter_lint_attrs(&mut self, cx: &rustc_lint::EarlyContext<'tcx>, attrs: &'tcx [Attribute]) {
|
||||
match get_inner_attr(cx.sess, attrs, "msrv") {
|
||||
Some(msrv_attr) => {
|
||||
if let Some(msrv) = msrv_attr.value_str() {
|
||||
self.msrv = crate::utils::parse_msrv(&msrv.to_string(), Some(cx.sess), Some(msrv_attr.span));
|
||||
} else {
|
||||
cx.sess.span_err(msrv_attr.span, "bad clippy attribute");
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns `true` if the two spans come from differing expansions (i.e., one is
|
||||
/// from a macro and one isn't).
|
||||
#[must_use]
|
||||
|
|
1
tests/ui-toml/invalid_min_rust_version/clippy.toml
Normal file
1
tests/ui-toml/invalid_min_rust_version/clippy.toml
Normal file
|
@ -0,0 +1 @@
|
|||
msrv = "invalid.version"
|
|
@ -0,0 +1,3 @@
|
|||
#![allow(clippy::redundant_clone)]
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,4 @@
|
|||
error: error reading Clippy's configuration file. `invalid.version` is not a valid Rust version
|
||||
|
||||
error: aborting due to previous error
|
||||
|
1
tests/ui-toml/min_rust_version/clippy.toml
Normal file
1
tests/ui-toml/min_rust_version/clippy.toml
Normal file
|
@ -0,0 +1 @@
|
|||
msrv = "1.0.0"
|
68
tests/ui-toml/min_rust_version/min_rust_version.rs
Normal file
68
tests/ui-toml/min_rust_version/min_rust_version.rs
Normal file
|
@ -0,0 +1,68 @@
|
|||
#![allow(clippy::redundant_clone)]
|
||||
#![warn(clippy::manual_non_exhaustive)]
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
mod enums {
|
||||
enum E {
|
||||
A,
|
||||
B,
|
||||
#[doc(hidden)]
|
||||
_C,
|
||||
}
|
||||
|
||||
// user forgot to remove the marker
|
||||
#[non_exhaustive]
|
||||
enum Ep {
|
||||
A,
|
||||
B,
|
||||
#[doc(hidden)]
|
||||
_C,
|
||||
}
|
||||
}
|
||||
|
||||
fn option_as_ref_deref() {
|
||||
let mut opt = Some(String::from("123"));
|
||||
|
||||
let _ = opt.as_ref().map(String::as_str);
|
||||
let _ = opt.as_ref().map(|x| x.as_str());
|
||||
let _ = opt.as_mut().map(String::as_mut_str);
|
||||
let _ = opt.as_mut().map(|x| x.as_mut_str());
|
||||
}
|
||||
|
||||
fn match_like_matches() {
|
||||
let _y = match Some(5) {
|
||||
Some(0) => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
fn match_same_arms() {
|
||||
match (1, 2, 3) {
|
||||
(1, .., 3) => 42,
|
||||
(.., 3) => 42, //~ ERROR match arms have same body
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
|
||||
fn match_same_arms2() {
|
||||
let _ = match Some(42) {
|
||||
Some(_) => 24,
|
||||
None => 24, //~ ERROR match arms have same body
|
||||
};
|
||||
}
|
||||
|
||||
fn manual_strip_msrv() {
|
||||
let s = "hello, world!";
|
||||
if s.starts_with("hello, ") {
|
||||
assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
option_as_ref_deref();
|
||||
match_like_matches();
|
||||
match_same_arms();
|
||||
match_same_arms2();
|
||||
manual_strip_msrv();
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `third-party` at line 5 column 1
|
||||
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `third-party` at line 5 column 1
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
51
tests/ui/min_rust_version_attr.rs
Normal file
51
tests/ui/min_rust_version_attr.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
#![allow(clippy::redundant_clone)]
|
||||
#![feature(custom_inner_attributes)]
|
||||
#![clippy::msrv = "1.0.0"]
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
fn option_as_ref_deref() {
|
||||
let mut opt = Some(String::from("123"));
|
||||
|
||||
let _ = opt.as_ref().map(String::as_str);
|
||||
let _ = opt.as_ref().map(|x| x.as_str());
|
||||
let _ = opt.as_mut().map(String::as_mut_str);
|
||||
let _ = opt.as_mut().map(|x| x.as_mut_str());
|
||||
}
|
||||
|
||||
fn match_like_matches() {
|
||||
let _y = match Some(5) {
|
||||
Some(0) => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
fn match_same_arms() {
|
||||
match (1, 2, 3) {
|
||||
(1, .., 3) => 42,
|
||||
(.., 3) => 42, //~ ERROR match arms have same body
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
|
||||
fn match_same_arms2() {
|
||||
let _ = match Some(42) {
|
||||
Some(_) => 24,
|
||||
None => 24, //~ ERROR match arms have same body
|
||||
};
|
||||
}
|
||||
|
||||
fn manual_strip_msrv() {
|
||||
let s = "hello, world!";
|
||||
if s.starts_with("hello, ") {
|
||||
assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
option_as_ref_deref();
|
||||
match_like_matches();
|
||||
match_same_arms();
|
||||
match_same_arms2();
|
||||
manual_strip_msrv();
|
||||
}
|
4
tests/ui/min_rust_version_invalid_attr.rs
Normal file
4
tests/ui/min_rust_version_invalid_attr.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
#![feature(custom_inner_attributes)]
|
||||
#![clippy::msrv = "invalid.version"]
|
||||
|
||||
fn main() {}
|
8
tests/ui/min_rust_version_invalid_attr.stderr
Normal file
8
tests/ui/min_rust_version_invalid_attr.stderr
Normal file
|
@ -0,0 +1,8 @@
|
|||
error: `invalid.version` is not a valid Rust version
|
||||
--> $DIR/min_rust_version_invalid_attr.rs:2:1
|
||||
|
|
||||
LL | #![clippy::msrv = "invalid.version"]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
14
tests/ui/min_rust_version_no_patch.rs
Normal file
14
tests/ui/min_rust_version_no_patch.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
#![allow(clippy::redundant_clone)]
|
||||
#![feature(custom_inner_attributes)]
|
||||
#![clippy::msrv = "^1.0"]
|
||||
|
||||
fn manual_strip_msrv() {
|
||||
let s = "hello, world!";
|
||||
if s.starts_with("hello, ") {
|
||||
assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
manual_strip_msrv()
|
||||
}
|
4
tests/ui/min_rust_version_outer_attr.rs
Normal file
4
tests/ui/min_rust_version_outer_attr.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
#![feature(custom_inner_attributes)]
|
||||
|
||||
#[clippy::msrv = "invalid.version"]
|
||||
fn main() {}
|
8
tests/ui/min_rust_version_outer_attr.stderr
Normal file
8
tests/ui/min_rust_version_outer_attr.stderr
Normal file
|
@ -0,0 +1,8 @@
|
|||
error: `msrv` cannot be an outer attribute
|
||||
--> $DIR/min_rust_version_outer_attr.rs:3:1
|
||||
|
|
||||
LL | #[clippy::msrv = "invalid.version"]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
Loading…
Reference in a new issue