Auto merge of #6424 - Suyash458:master, r=flip1995
Add MSRV to more lints specified in #6097 add MSRV to more lints specified in #6097 add instructions for adding msrv in other lints update tests - [x] `redundant_field_names` requires Rust 1.17 due to suggest feature stablized in that version. - [x] `redundant_static_lifetimes` requires Rust 1.17 due to suggest feature stablized in that version. - [x] `filter_map_next` requires Rust 1.30 due to suggest `Iterator::find_map`. - [x] `checked_conversions` requires Rust 1.34 due to suggest `TryFrom`. - [x] `match_like_matches_macro` requires Rust 1.42 due to suggest `matches!`. Addressed in #6201 - [x] `manual_strip` requires Rust 1.45 due to suggest `str::{strip_prefix, strip_suffix}`. Addressed in #6201 - [x] `option_as_ref_deref` requires Rust 1.40 due to suggest `Option::{as_deref, as_deref_mut}`. Addressed in #6201 - [x] `manual_non_exhaustive` requires Rust 1.40 due to suggest `#[non_exhaustive]`. Addressed in #6201 - [x] `manual_range_contains` requires Rust 1.35 due to suggest `Range*::contains`. - [x] `use_self` requires Rust 1.37 due to suggest `Self::Variant on enum`. - [x] `mem_replace_with_default` requires Rust 1.40 due to suggest `mem::take`. - [x] `map_unwrap_or` requires Rust 1.41 due to suggest `Result::{map_or, map_or_else}`. - [x] `missing_const_for_fn` requires Rust 1.46 due to `match/if/loop in const fn` needs that version. changelog: Add MSRV config to more lints. ^This is now the complete list, AFAWK
This commit is contained in:
commit
27fd6ed581
@ -6,9 +6,12 @@
|
||||
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
use crate::utils::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq};
|
||||
use crate::utils::{meets_msrv, snippet_with_applicability, span_lint_and_sugg, SpanlessEq};
|
||||
|
||||
const CHECKED_CONVERSIONS_MSRV: RustcVersion = RustcVersion::new(1, 34, 0);
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for explicit bounds checking when casting.
|
||||
@ -39,10 +42,25 @@
|
||||
"`try_from` could replace manual bounds checking when casting"
|
||||
}
|
||||
|
||||
declare_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
|
||||
pub struct CheckedConversions {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl CheckedConversions {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &CHECKED_CONVERSIONS_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
let result = if_chain! {
|
||||
if !in_external_macro(cx.sess(), item.span);
|
||||
if let ExprKind::Binary(op, ref left, ref right) = &item.kind;
|
||||
@ -74,6 +92,8 @@ fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
/// Searches for a single check from unsigned to _ is done
|
||||
|
@ -1005,6 +1005,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(move || box matches::Matches::new(msrv));
|
||||
store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv));
|
||||
store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv));
|
||||
store.register_early_pass(move || box redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv));
|
||||
store.register_early_pass(move || box redundant_field_names::RedundantFieldNames::new(msrv));
|
||||
store.register_late_pass(move || box checked_conversions::CheckedConversions::new(msrv));
|
||||
store.register_late_pass(move || box mem_replace::MemReplace::new(msrv));
|
||||
store.register_late_pass(move || box ranges::Ranges::new(msrv));
|
||||
store.register_late_pass(move || box use_self::UseSelf::new(msrv));
|
||||
store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv));
|
||||
|
||||
store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount);
|
||||
store.register_late_pass(|| box map_clone::MapClone);
|
||||
store.register_late_pass(|| box map_err_ignore::MapErrIgnore);
|
||||
@ -1015,7 +1023,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box main_recursion::MainRecursion::default());
|
||||
store.register_late_pass(|| box lifetimes::Lifetimes);
|
||||
store.register_late_pass(|| box entry::HashMapPass);
|
||||
store.register_late_pass(|| box ranges::Ranges);
|
||||
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));
|
||||
@ -1060,7 +1067,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box neg_multiply::NegMultiply);
|
||||
store.register_late_pass(|| box mem_discriminant::MemDiscriminant);
|
||||
store.register_late_pass(|| box mem_forget::MemForget);
|
||||
store.register_late_pass(|| box mem_replace::MemReplace);
|
||||
store.register_late_pass(|| box arithmetic::Arithmetic::default());
|
||||
store.register_late_pass(|| box assign_ops::AssignOps);
|
||||
store.register_late_pass(|| box let_if_seq::LetIfSeq);
|
||||
@ -1082,7 +1088,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(move || box pass_by_ref_or_value);
|
||||
store.register_late_pass(|| box ref_option_ref::RefOptionRef);
|
||||
store.register_late_pass(|| box try_err::TryErr);
|
||||
store.register_late_pass(|| box use_self::UseSelf);
|
||||
store.register_late_pass(|| box bytecount::ByteCount);
|
||||
store.register_late_pass(|| box infinite_iter::InfiniteIter);
|
||||
store.register_late_pass(|| box inline_fn_without_body::InlineFnWithoutBody);
|
||||
@ -1108,10 +1113,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box unnecessary_wraps::UnnecessaryWraps);
|
||||
store.register_late_pass(|| box types::RefToMut);
|
||||
store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants);
|
||||
store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn);
|
||||
store.register_late_pass(|| box transmuting_null::TransmutingNull);
|
||||
store.register_late_pass(|| box path_buf_push_overwrite::PathBufPushOverwrite);
|
||||
store.register_late_pass(|| box checked_conversions::CheckedConversions);
|
||||
store.register_late_pass(|| box integer_division::IntegerDivision);
|
||||
store.register_late_pass(|| box inherent_to_string::InherentToString);
|
||||
let max_trait_bounds = conf.max_trait_bounds;
|
||||
@ -1140,7 +1143,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_early_pass(|| box redundant_else::RedundantElse);
|
||||
store.register_late_pass(|| box create_dir::CreateDir);
|
||||
store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType);
|
||||
store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes);
|
||||
store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata);
|
||||
store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions);
|
||||
store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies);
|
||||
@ -1180,7 +1182,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box mut_mutex_lock::MutMutexLock);
|
||||
store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems);
|
||||
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);
|
||||
store.register_late_pass(|| box panic_in_result_fn::PanicInResultFn);
|
||||
let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
|
||||
|
@ -1,13 +1,14 @@
|
||||
use crate::utils::{
|
||||
in_macro, match_def_path, match_qpath, paths, snippet, snippet_with_applicability, span_lint_and_help,
|
||||
in_macro, match_def_path, match_qpath, meets_msrv, paths, snippet, snippet_with_applicability, span_lint_and_help,
|
||||
span_lint_and_sugg, span_lint_and_then,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
@ -94,7 +95,7 @@
|
||||
"replacing a value of type `T` with `T::default()` instead of using `std::mem::take`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(MemReplace =>
|
||||
impl_lint_pass!(MemReplace =>
|
||||
[MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]);
|
||||
|
||||
fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
|
||||
@ -224,6 +225,19 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<
|
||||
}
|
||||
}
|
||||
|
||||
const MEM_REPLACE_WITH_DEFAULT_MSRV: RustcVersion = RustcVersion::new(1, 40, 0);
|
||||
|
||||
pub struct MemReplace {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl MemReplace {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MemReplace {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
@ -236,8 +250,11 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
then {
|
||||
check_replace_option_with_none(cx, src, dest, expr.span);
|
||||
check_replace_with_uninit(cx, src, dest, expr.span);
|
||||
check_replace_with_default(cx, src, dest, expr.span);
|
||||
if meets_msrv(self.msrv.as_ref(), &MEM_REPLACE_WITH_DEFAULT_MSRV) {
|
||||
check_replace_with_default(cx, src, dest, expr.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
@ -1487,7 +1487,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
["expect", ..] => lint_expect(cx, expr, arg_lists[0]),
|
||||
["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]),
|
||||
["unwrap_or_else", "map"] => {
|
||||
if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]) {
|
||||
if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0], self.msrv.as_ref()) {
|
||||
unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or");
|
||||
}
|
||||
},
|
||||
@ -1509,7 +1509,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
["next", "iter"] => lint_iter_next(cx, expr, arg_lists[1]),
|
||||
["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1]),
|
||||
["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1], self.msrv.as_ref()),
|
||||
["map", "find"] => lint_find_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
@ -2733,6 +2733,8 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
|
||||
}
|
||||
}
|
||||
|
||||
const MAP_UNWRAP_OR_MSRV: RustcVersion = RustcVersion::new(1, 41, 0);
|
||||
|
||||
/// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
|
||||
/// Return true if lint triggered
|
||||
fn lint_map_unwrap_or_else<'tcx>(
|
||||
@ -2740,7 +2742,11 @@ fn lint_map_unwrap_or_else<'tcx>(
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
map_args: &'tcx [hir::Expr<'_>],
|
||||
unwrap_args: &'tcx [hir::Expr<'_>],
|
||||
msrv: Option<&RustcVersion>,
|
||||
) -> bool {
|
||||
if !meets_msrv(msrv, &MAP_UNWRAP_OR_MSRV) {
|
||||
return false;
|
||||
}
|
||||
// lint if the caller of `map()` is an `Option`
|
||||
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type);
|
||||
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::result_type);
|
||||
@ -2923,9 +2929,20 @@ fn lint_filter_map<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
const FILTER_MAP_NEXT_MSRV: RustcVersion = RustcVersion::new(1, 30, 0);
|
||||
|
||||
/// lint use of `filter_map().next()` for `Iterators`
|
||||
fn lint_filter_map_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, filter_args: &'tcx [hir::Expr<'_>]) {
|
||||
fn lint_filter_map_next<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
filter_args: &'tcx [hir::Expr<'_>],
|
||||
msrv: Option<&RustcVersion>,
|
||||
) {
|
||||
if match_trait_method(cx, expr, &paths::ITERATOR) {
|
||||
if !meets_msrv(msrv, &FILTER_MAP_NEXT_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
let msg = "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling \
|
||||
`.find_map(..)` instead.";
|
||||
let filter_snippet = snippet(cx, filter_args[1].span, "..");
|
||||
|
@ -1,14 +1,19 @@
|
||||
use crate::utils::qualify_min_const_fn::is_min_const_fn;
|
||||
use crate::utils::{fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method};
|
||||
use crate::utils::{
|
||||
fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, meets_msrv, span_lint, trait_ref_of_method,
|
||||
};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::Span;
|
||||
use rustc_typeck::hir_ty_to_ty;
|
||||
|
||||
const MISSING_CONST_FOR_FN_MSRV: RustcVersion = RustcVersion::new(1, 37, 0);
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
///
|
||||
@ -69,7 +74,18 @@
|
||||
"Lint functions definitions that could be made `const fn`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]);
|
||||
impl_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]);
|
||||
|
||||
pub struct MissingConstForFn {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl MissingConstForFn {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
||||
fn check_fn(
|
||||
@ -81,6 +97,10 @@ fn check_fn(
|
||||
span: Span,
|
||||
hir_id: HirId,
|
||||
) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &MISSING_CONST_FOR_FN_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
let def_id = cx.tcx.hir().local_def_id(hir_id);
|
||||
|
||||
if in_external_macro(cx.tcx.sess, span) || is_entrypoint_fn(cx, def_id.to_def_id()) {
|
||||
@ -126,6 +146,7 @@ fn check_fn(
|
||||
span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`");
|
||||
}
|
||||
}
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
/// Returns true if any of the method parameters is a type that implements `Drop`. The method
|
||||
|
@ -3,9 +3,10 @@
|
||||
use rustc_ast::ast::RangeLimits;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::{Span, Spanned};
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::Ident;
|
||||
@ -13,8 +14,8 @@
|
||||
|
||||
use crate::utils::sugg::Sugg;
|
||||
use crate::utils::{
|
||||
get_parent_expr, is_integer_const, single_segment_path, snippet, snippet_opt, snippet_with_applicability,
|
||||
span_lint, span_lint_and_sugg, span_lint_and_then,
|
||||
get_parent_expr, is_integer_const, meets_msrv, single_segment_path, snippet, snippet_opt,
|
||||
snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then,
|
||||
};
|
||||
use crate::utils::{higher, SpanlessEq};
|
||||
|
||||
@ -160,7 +161,20 @@
|
||||
"manually reimplementing {`Range`, `RangeInclusive`}`::contains`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Ranges => [
|
||||
const MANUAL_RANGE_CONTAINS_MSRV: RustcVersion = RustcVersion::new(1, 35, 0);
|
||||
|
||||
pub struct Ranges {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl Ranges {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(Ranges => [
|
||||
RANGE_ZIP_WITH_LEN,
|
||||
RANGE_PLUS_ONE,
|
||||
RANGE_MINUS_ONE,
|
||||
@ -175,7 +189,9 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
check_range_zip_with_len(cx, path, args, expr.span);
|
||||
},
|
||||
ExprKind::Binary(ref op, ref l, ref r) => {
|
||||
check_possible_range_contains(cx, op.node, l, r, expr.span);
|
||||
if meets_msrv(self.msrv.as_ref(), &MANUAL_RANGE_CONTAINS_MSRV) {
|
||||
check_possible_range_contains(cx, op.node, l, r, expr.span);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
@ -184,6 +200,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
check_inclusive_range_minus_one(cx, expr);
|
||||
check_reversed_empty_range(cx, expr);
|
||||
}
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, span: Span) {
|
||||
|
@ -1,9 +1,12 @@
|
||||
use crate::utils::span_lint_and_sugg;
|
||||
use crate::utils::{meets_msrv, span_lint_and_sugg};
|
||||
use rustc_ast::ast::{Expr, ExprKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
const REDUNDANT_FIELD_NAMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0);
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for fields in struct literals where shorthands
|
||||
@ -33,10 +36,25 @@
|
||||
"checks for fields in struct literals where shorthands could be used"
|
||||
}
|
||||
|
||||
declare_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]);
|
||||
pub struct RedundantFieldNames {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl RedundantFieldNames {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]);
|
||||
|
||||
impl EarlyLintPass for RedundantFieldNames {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_FIELD_NAMES_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
if in_external_macro(cx.sess, expr.span) {
|
||||
return;
|
||||
}
|
||||
@ -64,4 +82,5 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
}
|
||||
}
|
||||
}
|
||||
extract_msrv_attr!(EarlyContext);
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
use crate::utils::{snippet, span_lint_and_then};
|
||||
use crate::utils::{meets_msrv, snippet, span_lint_and_then};
|
||||
use rustc_ast::ast::{Item, ItemKind, Ty, TyKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
const REDUNDANT_STATIC_LIFETIMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0);
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for constants and statics with an explicit `'static` lifetime.
|
||||
@ -29,7 +32,18 @@
|
||||
"Using explicit `'static` lifetime for constants or statics when elision rules would allow omitting them."
|
||||
}
|
||||
|
||||
declare_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]);
|
||||
pub struct RedundantStaticLifetimes {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl RedundantStaticLifetimes {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]);
|
||||
|
||||
impl RedundantStaticLifetimes {
|
||||
// Recursively visit types
|
||||
@ -84,6 +98,10 @@ fn visit_type(&mut self, ty: &Ty, cx: &EarlyContext<'_>, reason: &str) {
|
||||
|
||||
impl EarlyLintPass for RedundantStaticLifetimes {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_STATIC_LIFETIMES_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
if !item.span.from_expansion() {
|
||||
if let ItemKind::Const(_, ref var_type, _) = item.kind {
|
||||
self.visit_type(var_type, cx, "constants have by default a `'static` lifetime");
|
||||
@ -96,4 +114,6 @@ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(EarlyContext);
|
||||
}
|
||||
|
@ -12,11 +12,12 @@
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::{DefIdTree, Ty};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_typeck::hir_ty_to_ty;
|
||||
|
||||
use crate::utils::{differing_macro_contexts, span_lint_and_sugg};
|
||||
use crate::utils::{differing_macro_contexts, meets_msrv, span_lint_and_sugg};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for unnecessary repetition of structure name when a
|
||||
@ -53,7 +54,7 @@
|
||||
"unnecessary structure name repetition whereas `Self` is applicable"
|
||||
}
|
||||
|
||||
declare_lint_pass!(UseSelf => [USE_SELF]);
|
||||
impl_lint_pass!(UseSelf => [USE_SELF]);
|
||||
|
||||
const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element";
|
||||
|
||||
@ -157,8 +158,25 @@ fn check_trait_method_impl_decl<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
const USE_SELF_MSRV: RustcVersion = RustcVersion::new(1, 37, 0);
|
||||
|
||||
pub struct UseSelf {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl UseSelf {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for UseSelf {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
if in_external_macro(cx.sess(), item.span) {
|
||||
return;
|
||||
}
|
||||
@ -204,6 +222,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
}
|
||||
}
|
||||
}
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
struct UseSelfVisitor<'a, 'tcx> {
|
||||
|
@ -226,13 +226,13 @@ store.register_early_pass(|| box foo_functions::FooFunctions);
|
||||
```
|
||||
|
||||
As one may expect, there is a corresponding `register_late_pass` method
|
||||
available as well. Without a call to one of `register_early_pass` or
|
||||
available as well. Without a call to one of `register_early_pass` or
|
||||
`register_late_pass`, the lint pass in question will not be run.
|
||||
|
||||
One reason that `cargo dev` does not automate this step is that multiple lints
|
||||
One reason that `cargo dev` does not automate this step is that multiple lints
|
||||
can use the same lint pass, so registering the lint pass may already be done
|
||||
when adding a new lint. Another reason that this step is not automated is that
|
||||
the order that the passes are registered determines the order the passes
|
||||
the order that the passes are registered determines the order the passes
|
||||
actually run, which in turn affects the order that any emitted lints are output
|
||||
in.
|
||||
|
||||
@ -380,6 +380,57 @@ pass.
|
||||
[`FnKind::Fn`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html#variant.Fn
|
||||
[ident]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Ident.html
|
||||
|
||||
## Specifying the lint's minimum supported Rust version (msrv)
|
||||
|
||||
Projects supporting older versions of Rust would need to disable a lint if it targets features
|
||||
present in later versions. Support for this can be added by specifying an msrv in your lint like so,
|
||||
|
||||
```rust
|
||||
const MANUAL_STRIP_MSRV: RustcVersion = RustcVersion::new(1, 45, 0);
|
||||
```
|
||||
|
||||
The project's msrv will also have to be an attribute in the lint so you'll have to add a struct
|
||||
and constructor for your lint. The project's msrv needs to be passed when the lint is registered
|
||||
in `lib.rs`
|
||||
|
||||
```rust
|
||||
pub struct ManualStrip {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl ManualStrip {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The project's msrv can then be matched against the lint's msrv in the LintPass using the `meets_msrv` utility
|
||||
function.
|
||||
|
||||
``` rust
|
||||
if !meets_msrv(self.msrv.as_ref(), &MANUAL_STRIP_MSRV) {
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
The project's msrv can also be specified as an inner attribute, which overrides the value from
|
||||
`clippy.toml`. This can be accounted for using the `extract_msrv_attr!(LintContext)` macro and passing
|
||||
LateContext/EarlyContext.
|
||||
|
||||
```rust
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualStrip {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
...
|
||||
}
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
```
|
||||
|
||||
Once the msrv is added to the lint, a relevant test case should be added to `tests/ui/min_rust_version_attr.rs`
|
||||
which verifies that the lint isn't emitted if the project's msrv is lower.
|
||||
|
||||
## Author lint
|
||||
|
||||
If you have trouble implementing your lint, there is also the internal `author`
|
||||
|
@ -2,7 +2,7 @@
|
||||
#![feature(custom_inner_attributes)]
|
||||
#![clippy::msrv = "1.0.0"]
|
||||
|
||||
use std::ops::Deref;
|
||||
use std::ops::{Deref, RangeFrom};
|
||||
|
||||
fn option_as_ref_deref() {
|
||||
let mut opt = Some(String::from("123"));
|
||||
@ -42,12 +42,94 @@ pub fn manual_strip_msrv() {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn redundant_fieldnames() {
|
||||
let start = 0;
|
||||
let _ = RangeFrom { start: start };
|
||||
}
|
||||
|
||||
pub fn redundant_static_lifetime() {
|
||||
const VAR_ONE: &'static str = "Test constant #1";
|
||||
}
|
||||
|
||||
pub fn checked_conversion() {
|
||||
let value: i64 = 42;
|
||||
let _ = value <= (u32::max_value() as i64) && value >= 0;
|
||||
let _ = value <= (u32::MAX as i64) && value >= 0;
|
||||
}
|
||||
|
||||
pub fn filter_map_next() {
|
||||
let a = ["1", "lol", "3", "NaN", "5"];
|
||||
|
||||
#[rustfmt::skip]
|
||||
let _: Option<u32> = vec![1, 2, 3, 4, 5, 6]
|
||||
.into_iter()
|
||||
.filter_map(|x| {
|
||||
if x == 2 {
|
||||
Some(x * 2)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.next();
|
||||
}
|
||||
|
||||
#[allow(clippy::no_effect)]
|
||||
#[allow(clippy::short_circuit_statement)]
|
||||
#[allow(clippy::unnecessary_operation)]
|
||||
pub fn manual_range_contains() {
|
||||
let x = 5;
|
||||
x >= 8 && x < 12;
|
||||
}
|
||||
|
||||
pub fn use_self() {
|
||||
struct Foo {}
|
||||
|
||||
impl Foo {
|
||||
fn new() -> Foo {
|
||||
Foo {}
|
||||
}
|
||||
fn test() -> Foo {
|
||||
Foo::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn replace_with_default() {
|
||||
let mut s = String::from("foo");
|
||||
let _ = std::mem::replace(&mut s, String::default());
|
||||
}
|
||||
|
||||
fn map_unwrap_or() {
|
||||
let opt = Some(1);
|
||||
|
||||
// Check for `option.map(_).unwrap_or(_)` use.
|
||||
// Single line case.
|
||||
let _ = opt
|
||||
.map(|x| x + 1)
|
||||
// Should lint even though this call is on a separate line.
|
||||
.unwrap_or(0);
|
||||
}
|
||||
|
||||
// Could be const
|
||||
fn missing_const_for_fn() -> i32 {
|
||||
1
|
||||
}
|
||||
|
||||
fn main() {
|
||||
filter_map_next();
|
||||
checked_conversion();
|
||||
redundant_fieldnames();
|
||||
redundant_static_lifetime();
|
||||
option_as_ref_deref();
|
||||
match_like_matches();
|
||||
match_same_arms();
|
||||
match_same_arms2();
|
||||
manual_strip_msrv();
|
||||
manual_range_contains();
|
||||
use_self();
|
||||
replace_with_default();
|
||||
map_unwrap_or();
|
||||
missing_const_for_fn();
|
||||
}
|
||||
|
||||
mod meets_msrv {
|
||||
|
@ -1,12 +1,12 @@
|
||||
error: stripping a prefix manually
|
||||
--> $DIR/min_rust_version_attr.rs:60:24
|
||||
--> $DIR/min_rust_version_attr.rs:142:24
|
||||
|
|
||||
LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::manual-strip` implied by `-D warnings`
|
||||
note: the prefix was tested here
|
||||
--> $DIR/min_rust_version_attr.rs:59:9
|
||||
--> $DIR/min_rust_version_attr.rs:141:9
|
||||
|
|
||||
LL | if s.starts_with("hello, ") {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -17,13 +17,13 @@ LL | assert_eq!(<stripped>.to_uppercase(), "WORLD!");
|
||||
|
|
||||
|
||||
error: stripping a prefix manually
|
||||
--> $DIR/min_rust_version_attr.rs:72:24
|
||||
--> $DIR/min_rust_version_attr.rs:154:24
|
||||
|
|
||||
LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: the prefix was tested here
|
||||
--> $DIR/min_rust_version_attr.rs:71:9
|
||||
--> $DIR/min_rust_version_attr.rs:153:9
|
||||
|
|
||||
LL | if s.starts_with("hello, ") {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
Loading…
Reference in New Issue
Block a user