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:
@@ -44,6 +44,7 @@ extern crate rustc_target;
|
|||||||
extern crate rustc_trait_selection;
|
extern crate rustc_trait_selection;
|
||||||
extern crate rustc_typeck;
|
extern crate rustc_typeck;
|
||||||
|
|
||||||
|
use crate::utils::parse_msrv;
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_lint::LintId;
|
use rustc_lint::LintId;
|
||||||
use rustc_session::Session;
|
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,
|
&zero_div_zero::ZERO_DIVIDED_BY_ZERO,
|
||||||
]);
|
]);
|
||||||
// end register lints, do not remove this comment, it’s used in `update_lints`
|
// 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 await_holding_invalid::AwaitHolding);
|
||||||
store.register_late_pass(|| box serde_api::SerdeAPI);
|
store.register_late_pass(|| box serde_api::SerdeAPI);
|
||||||
store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new());
|
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 strings::StringAdd);
|
||||||
store.register_late_pass(|| box implicit_return::ImplicitReturn);
|
store.register_late_pass(|| box implicit_return::ImplicitReturn);
|
||||||
store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub);
|
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_clone::MapClone);
|
||||||
store.register_late_pass(|| box map_err_ignore::MapErrIgnore);
|
store.register_late_pass(|| box map_err_ignore::MapErrIgnore);
|
||||||
store.register_late_pass(|| box shadow::Shadow);
|
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);
|
store.register_late_pass(|| box types::Casts);
|
||||||
let type_complexity_threshold = conf.type_complexity_threshold;
|
let type_complexity_threshold = conf.type_complexity_threshold;
|
||||||
store.register_late_pass(move || box types::TypeComplexity::new(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 minmax::MinMaxPass);
|
||||||
store.register_late_pass(|| box open_options::OpenOptions);
|
store.register_late_pass(|| box open_options::OpenOptions);
|
||||||
store.register_late_pass(|| box zero_div_zero::ZeroDiv);
|
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 if_let_mutex::IfLetMutex);
|
||||||
store.register_late_pass(|| box mut_mutex_lock::MutMutexLock);
|
store.register_late_pass(|| box mut_mutex_lock::MutMutexLock);
|
||||||
store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems);
|
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_late_pass(|| box manual_async_fn::ManualAsyncFn);
|
||||||
store.register_early_pass(|| box redundant_field_names::RedundantFieldNames);
|
store.register_early_pass(|| box redundant_field_names::RedundantFieldNames);
|
||||||
store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero);
|
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 manual_ok_or::ManualOkOr);
|
||||||
store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs);
|
store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs);
|
||||||
store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync);
|
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);
|
store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem);
|
||||||
let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::<FxHashSet<_>>();
|
let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::<FxHashSet<_>>();
|
||||||
store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods));
|
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 if_chain::if_chain;
|
||||||
use rustc_ast::ast::{Attribute, Item, ItemKind, StructField, Variant, VariantData, VisibilityKind};
|
use rustc_ast::ast::{Attribute, Item, ItemKind, StructField, Variant, VariantData, VisibilityKind};
|
||||||
use rustc_attr as attr;
|
use rustc_attr as attr;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
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 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! {
|
declare_clippy_lint! {
|
||||||
/// **What it does:** Checks for manual implementations of the non-exhaustive pattern.
|
/// **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]"
|
"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 {
|
impl EarlyLintPass for ManualNonExhaustive {
|
||||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||||
|
if !meets_msrv(self.msrv.as_ref(), &MANUAL_NON_EXHAUSTIVE_MSRV) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
match &item.kind {
|
match &item.kind {
|
||||||
ItemKind::Enum(def, _) => {
|
ItemKind::Enum(def, _) => {
|
||||||
check_manual_non_exhaustive_enum(cx, item, &def.variants);
|
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]) {
|
fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants: &[Variant]) {
|
||||||
|
|||||||
@@ -1,21 +1,31 @@
|
|||||||
use crate::consts::{constant, Constant};
|
use crate::consts::{constant, Constant};
|
||||||
use crate::utils::usage::mutated_variables;
|
use crate::utils::usage::mutated_variables;
|
||||||
use crate::utils::{
|
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 if_chain::if_chain;
|
||||||
use rustc_ast::ast::LitKind;
|
use rustc_ast::ast::{Attribute, LitKind};
|
||||||
use rustc_hir::def::Res;
|
use rustc_hir::def::Res;
|
||||||
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
||||||
use rustc_hir::BinOpKind;
|
use rustc_hir::BinOpKind;
|
||||||
use rustc_hir::{BorrowKind, Expr, ExprKind};
|
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::hir::map::Map;
|
||||||
use rustc_middle::ty;
|
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::source_map::Spanned;
|
||||||
use rustc_span::Span;
|
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! {
|
declare_clippy_lint! {
|
||||||
/// **What it does:**
|
/// **What it does:**
|
||||||
@@ -51,7 +61,18 @@ declare_clippy_lint! {
|
|||||||
"suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing"
|
"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)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
enum StripKind {
|
enum StripKind {
|
||||||
@@ -61,6 +82,10 @@ enum StripKind {
|
|||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for ManualStrip {
|
impl<'tcx> LateLintPass<'tcx> for ManualStrip {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
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_chain! {
|
||||||
if let Some((cond, then, _)) = higher::if_block(&expr);
|
if let Some((cond, then, _)) = higher::if_block(&expr);
|
||||||
if let ExprKind::MethodCall(_, _, [target_arg, pattern], _) = cond.kind;
|
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.
|
// 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::sugg::Sugg;
|
||||||
use crate::utils::usage::is_unused;
|
use crate::utils::usage::is_unused;
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable,
|
expr_block, get_arg_name, get_inner_attr, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of,
|
||||||
is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, multispan_sugg, remove_blocks, snippet,
|
is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg,
|
||||||
snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
|
remove_blocks, snippet, snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note,
|
||||||
span_lint_and_then,
|
span_lint_and_sugg, span_lint_and_then,
|
||||||
};
|
};
|
||||||
use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash};
|
use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash};
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_ast::ast::LitKind;
|
use rustc_ast::ast::{Attribute, LitKind};
|
||||||
use rustc_data_structures::fx::FxHashMap;
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::def::CtorKind;
|
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_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::source_map::{Span, Spanned};
|
use rustc_span::source_map::{Span, Spanned};
|
||||||
use rustc_span::{sym, Symbol};
|
use rustc_span::{sym, Symbol};
|
||||||
|
use semver::{Version, VersionReq};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
use std::collections::Bound;
|
use std::collections::Bound;
|
||||||
@@ -527,9 +528,20 @@ declare_clippy_lint! {
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Matches {
|
pub struct Matches {
|
||||||
|
msrv: Option<VersionReq>,
|
||||||
infallible_destructuring_match_linted: bool,
|
infallible_destructuring_match_linted: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Matches {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(msrv: Option<VersionReq>) -> Self {
|
||||||
|
Self {
|
||||||
|
msrv,
|
||||||
|
..Matches::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl_lint_pass!(Matches => [
|
impl_lint_pass!(Matches => [
|
||||||
SINGLE_MATCH,
|
SINGLE_MATCH,
|
||||||
MATCH_REF_PATS,
|
MATCH_REF_PATS,
|
||||||
@@ -549,6 +561,14 @@ impl_lint_pass!(Matches => [
|
|||||||
MATCH_SAME_ARMS,
|
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 {
|
impl<'tcx> LateLintPass<'tcx> for Matches {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||||
if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) {
|
if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) {
|
||||||
@@ -556,9 +576,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
|||||||
}
|
}
|
||||||
|
|
||||||
redundant_pattern_match::check(cx, expr);
|
redundant_pattern_match::check(cx, expr);
|
||||||
|
|
||||||
|
if meets_msrv(self.msrv.as_ref(), &MATCH_LIKE_MATCHES_MACRO_MSRV) {
|
||||||
if !check_match_like_matches(cx, expr) {
|
if !check_match_like_matches(cx, expr) {
|
||||||
lint_match_arms(cx, expr);
|
lint_match_arms(cx, expr);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
lint_match_arms(cx, expr);
|
||||||
|
}
|
||||||
|
|
||||||
if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind {
|
if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind {
|
||||||
check_single_match(cx, ex, arms, expr);
|
check_single_match(cx, ex, arms, expr);
|
||||||
@@ -640,6 +665,8 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extract_msrv_attr!(LateContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use std::iter;
|
|||||||
use bind_instead_of_map::BindInsteadOfMap;
|
use bind_instead_of_map::BindInsteadOfMap;
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_ast::ast;
|
use rustc_ast::ast;
|
||||||
|
use rustc_ast::ast::Attribute;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::intravisit::{self, Visitor};
|
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::hir::map::Map;
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_middle::ty::{self, TraitRef, Ty, TyS};
|
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::source_map::Span;
|
||||||
use rustc_span::symbol::{sym, SymbolStr};
|
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::eager_or_lazy::is_lazyness_candidate;
|
||||||
use crate::utils::usage::mutated_variables;
|
use crate::utils::usage::mutated_variables;
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro,
|
contains_ty, get_arg_name, get_inner_attr, get_parent_expr, get_trait_def_id, has_iter_method, higher,
|
||||||
is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath,
|
implements_trait, in_macro, is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment,
|
||||||
match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty,
|
match_def_path, match_qpath, match_trait_method, match_type, match_var, meets_msrv, method_calls,
|
||||||
single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint,
|
method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability,
|
||||||
span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, SpanlessEq,
|
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! {
|
declare_clippy_lint! {
|
||||||
/// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s.
|
/// **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()`"
|
"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,
|
UNWRAP_USED,
|
||||||
EXPECT_USED,
|
EXPECT_USED,
|
||||||
SHOULD_IMPLEMENT_TRAIT,
|
SHOULD_IMPLEMENT_TRAIT,
|
||||||
@@ -1531,8 +1545,12 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||||||
check_pointer_offset(cx, expr, arg_lists[0])
|
check_pointer_offset(cx, expr, arg_lists[0])
|
||||||
},
|
},
|
||||||
["is_file", ..] => lint_filetype_is_file(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_ref"] => {
|
||||||
["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true),
|
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"),
|
["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"),
|
["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"),
|
["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.
|
/// 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
|
/// lint use of `_.as_ref().map(Deref::deref)` for `Option`s
|
||||||
fn lint_option_as_ref_deref<'tcx>(
|
fn lint_option_as_ref_deref<'tcx>(
|
||||||
cx: &LateContext<'tcx>,
|
cx: &LateContext<'tcx>,
|
||||||
@@ -3460,7 +3488,12 @@ fn lint_option_as_ref_deref<'tcx>(
|
|||||||
as_ref_args: &[hir::Expr<'_>],
|
as_ref_args: &[hir::Expr<'_>],
|
||||||
map_args: &[hir::Expr<'_>],
|
map_args: &[hir::Expr<'_>],
|
||||||
is_mut: bool,
|
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 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]);
|
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"),
|
DeprecationStatus::Replaced("cognitive_complexity"),
|
||||||
),
|
),
|
||||||
("dump", DeprecationStatus::None),
|
("dump", DeprecationStatus::None),
|
||||||
|
("msrv", DeprecationStatus::None),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub struct LimitStack {
|
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`,
|
/// Return true if the attributes contain any of `proc_macro`,
|
||||||
/// `proc_macro_derive` or `proc_macro_attribute`, false otherwise
|
/// `proc_macro_derive` or `proc_macro_attribute`, false otherwise
|
||||||
pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool {
|
pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool {
|
||||||
|
|||||||
@@ -106,6 +106,8 @@ macro_rules! define_Conf {
|
|||||||
|
|
||||||
pub use self::helpers::Conf;
|
pub use self::helpers::Conf;
|
||||||
define_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
|
/// 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()),
|
(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
|
/// 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::hir::map::Map;
|
||||||
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
|
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
|
||||||
use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable};
|
use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable};
|
||||||
|
use rustc_session::Session;
|
||||||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||||
use rustc_span::source_map::original_sp;
|
use rustc_span::source_map::original_sp;
|
||||||
use rustc_span::sym as rustc_sym;
|
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_span::{BytePos, Pos, Span, DUMMY_SP};
|
||||||
use rustc_target::abi::Integer;
|
use rustc_target::abi::Integer;
|
||||||
use rustc_trait_selection::traits::query::normalize::AtExt;
|
use rustc_trait_selection::traits::query::normalize::AtExt;
|
||||||
|
use semver::{Version, VersionReq};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::consts::{constant, Constant};
|
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
|
/// Returns `true` if the two spans come from differing expansions (i.e., one is
|
||||||
/// from a macro and one isn't).
|
/// from a macro and one isn't).
|
||||||
#[must_use]
|
#[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
|
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
|
||||||
|
|
||||||
Reference in New Issue
Block a user