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:
Suyash458 2020-10-21 18:17:34 +05:30 committed by flip1995
parent f897d27d8b
commit aaa4325045
No known key found for this signature in database
GPG key ID: 1CA0DF2AF59D68A5
20 changed files with 390 additions and 29 deletions

View file

@ -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, its 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));

View file

@ -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]) {

View file

@ -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.

View file

@ -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]

View file

@ -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]);

View file

@ -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 {

View file

@ -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

View file

@ -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]

View file

@ -0,0 +1 @@
msrv = "invalid.version"

View file

@ -0,0 +1,3 @@
#![allow(clippy::redundant_clone)]
fn main() {}

View file

@ -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

View file

@ -0,0 +1 @@
msrv = "1.0.0"

View 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();
}

View file

@ -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

View 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();
}

View file

@ -0,0 +1,4 @@
#![feature(custom_inner_attributes)]
#![clippy::msrv = "invalid.version"]
fn main() {}

View 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

View 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()
}

View file

@ -0,0 +1,4 @@
#![feature(custom_inner_attributes)]
#[clippy::msrv = "invalid.version"]
fn main() {}

View 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