diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7e8cbd00c..866dae110 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -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::>(); store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index a1450b0d5..4762ba16a 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -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, +} + +impl ManualNonExhaustive { + #[must_use] + pub fn new(msrv: Option) -> 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]) { diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 4afb0ab3b..446641ca1 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -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, +} + +impl ManualStrip { + #[must_use] + pub fn new(msrv: Option) -> 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. diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index af59917e8..5c38abbd9 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -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, infallible_destructuring_match_linted: bool, } +impl Matches { + #[must_use] + pub fn new(msrv: Option) -> 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] diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fa1744043..6b4789860 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -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, +} + +impl Methods { + #[must_use] + pub fn new(msrv: Option) -> 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]); diff --git a/clippy_lints/src/utils/attrs.rs b/clippy_lints/src/utils/attrs.rs index e6d41341a..aed6ee5dc 100644 --- a/clippy_lints/src/utils/attrs.rs +++ b/clippy_lints/src/utils/attrs.rs @@ -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(sess: &Session, attrs: &[ast::Attribute], name: &' } } +pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'static str) -> Option { + 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 { diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 0ac8fff69..fc6304118 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -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, 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, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index e9c71e23a..f8e885120 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -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) -> Option { + 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] diff --git a/tests/ui-toml/invalid_min_rust_version/clippy.toml b/tests/ui-toml/invalid_min_rust_version/clippy.toml new file mode 100644 index 000000000..088b12b2d --- /dev/null +++ b/tests/ui-toml/invalid_min_rust_version/clippy.toml @@ -0,0 +1 @@ +msrv = "invalid.version" diff --git a/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs b/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs new file mode 100644 index 000000000..2ebf28645 --- /dev/null +++ b/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs @@ -0,0 +1,3 @@ +#![allow(clippy::redundant_clone)] + +fn main() {} diff --git a/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr b/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr new file mode 100644 index 000000000..e9d8fd2e0 --- /dev/null +++ b/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr @@ -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 + diff --git a/tests/ui-toml/min_rust_version/clippy.toml b/tests/ui-toml/min_rust_version/clippy.toml new file mode 100644 index 000000000..8e17d8074 --- /dev/null +++ b/tests/ui-toml/min_rust_version/clippy.toml @@ -0,0 +1 @@ +msrv = "1.0.0" diff --git a/tests/ui-toml/min_rust_version/min_rust_version.rs b/tests/ui-toml/min_rust_version/min_rust_version.rs new file mode 100644 index 000000000..bc41efa42 --- /dev/null +++ b/tests/ui-toml/min_rust_version/min_rust_version.rs @@ -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(); +} diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index a58e7e918..af3d9ecf6 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -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 diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs new file mode 100644 index 000000000..8ed483a3a --- /dev/null +++ b/tests/ui/min_rust_version_attr.rs @@ -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(); +} diff --git a/tests/ui/min_rust_version_invalid_attr.rs b/tests/ui/min_rust_version_invalid_attr.rs new file mode 100644 index 000000000..f20841891 --- /dev/null +++ b/tests/ui/min_rust_version_invalid_attr.rs @@ -0,0 +1,4 @@ +#![feature(custom_inner_attributes)] +#![clippy::msrv = "invalid.version"] + +fn main() {} diff --git a/tests/ui/min_rust_version_invalid_attr.stderr b/tests/ui/min_rust_version_invalid_attr.stderr new file mode 100644 index 000000000..6ff88ca56 --- /dev/null +++ b/tests/ui/min_rust_version_invalid_attr.stderr @@ -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 + diff --git a/tests/ui/min_rust_version_no_patch.rs b/tests/ui/min_rust_version_no_patch.rs new file mode 100644 index 000000000..515fe8f95 --- /dev/null +++ b/tests/ui/min_rust_version_no_patch.rs @@ -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() +} diff --git a/tests/ui/min_rust_version_outer_attr.rs b/tests/ui/min_rust_version_outer_attr.rs new file mode 100644 index 000000000..551948bd7 --- /dev/null +++ b/tests/ui/min_rust_version_outer_attr.rs @@ -0,0 +1,4 @@ +#![feature(custom_inner_attributes)] + +#[clippy::msrv = "invalid.version"] +fn main() {} diff --git a/tests/ui/min_rust_version_outer_attr.stderr b/tests/ui/min_rust_version_outer_attr.stderr new file mode 100644 index 000000000..579ee7a87 --- /dev/null +++ b/tests/ui/min_rust_version_outer_attr.stderr @@ -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 +