use std::borrow::Cow;
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::meets_msrv;
use clippy_utils::sugg::Sugg;
+use clippy_utils::{meets_msrv, msrvs};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Mutability, TyKind};
use super::PTR_AS_PTR;
-const PTR_AS_PTR_MSRV: RustcVersion = RustcVersion::new(1, 38, 0);
-
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: &Option<RustcVersion>) {
- if !meets_msrv(msrv.as_ref(), &PTR_AS_PTR_MSRV) {
+ if !meets_msrv(msrv.as_ref(), &msrvs::POINTER_CAST) {
return;
}
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{meets_msrv, SpanlessEq};
+use clippy_utils::{meets_msrv, msrvs, SpanlessEq};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
-const CHECKED_CONVERSIONS_MSRV: RustcVersion = RustcVersion::new(1, 34, 0);
-
declare_clippy_lint! {
/// **What it does:** Checks for explicit bounds checking when casting.
///
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) {
+ if !meets_msrv(self.msrv.as_ref(), &msrvs::TRY_FROM) {
return;
}
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::paths::INTO;
-use clippy_utils::{match_def_path, meets_msrv};
+use clippy_utils::{match_def_path, meets_msrv, msrvs};
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
-const FROM_OVER_INTO_MSRV: RustcVersion = RustcVersion::new(1, 41, 0);
-
declare_clippy_lint! {
/// **What it does:** Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead.
///
impl LateLintPass<'_> for FromOverInto {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
- if !meets_msrv(self.msrv.as_ref(), &FROM_OVER_INTO_MSRV) {
+ if !meets_msrv(self.msrv.as_ref(), &msrvs::RE_REBALANCING_COHERENCE) {
return;
}
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::{is_else_clause, is_lang_ctor, meets_msrv};
+use clippy_utils::{is_else_clause, is_lang_ctor, meets_msrv, msrvs};
use if_chain::if_chain;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{Expr, ExprKind};
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
-const IF_THEN_SOME_ELSE_NONE_MSRV: RustcVersion = RustcVersion::new(1, 50, 0);
-
declare_clippy_lint! {
/// **What it does:** Checks for if-else that could be written to `bool::then`.
///
impl LateLintPass<'_> for IfThenSomeElseNone {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
- if !meets_msrv(self.msrv.as_ref(), &IF_THEN_SOME_ELSE_NONE_MSRV) {
+ if !meets_msrv(self.msrv.as_ref(), &msrvs::BOOL_THEN) {
return;
}
store.register_late_pass(move || box from_over_into::FromOverInto::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(move || box needless_question_mark::NeedlessQuestionMark::new(msrv));
+ store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark);
store.register_late_pass(move || box casts::Casts::new(msrv));
store.register_early_pass(move || box unnested_or_patterns::UnnestedOrPatterns::new(msrv));
use clippy_utils::attrs::is_doc_hidden;
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::meets_msrv;
use clippy_utils::source::snippet_opt;
+use clippy_utils::{meets_msrv, msrvs};
use if_chain::if_chain;
use rustc_ast::ast::{FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind};
use rustc_errors::Applicability;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{sym, Span};
-const MANUAL_NON_EXHAUSTIVE_MSRV: RustcVersion = RustcVersion::new(1, 40, 0);
-
declare_clippy_lint! {
/// **What it does:** Checks for manual implementations of the non-exhaustive pattern.
///
impl EarlyLintPass for ManualNonExhaustive {
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
- if !meets_msrv(self.msrv.as_ref(), &MANUAL_NON_EXHAUSTIVE_MSRV) {
+ if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) {
return;
}
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
use clippy_utils::source::snippet;
use clippy_utils::usage::mutated_variables;
-use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, paths};
+use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, msrvs, paths};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_hir::def::Res;
use rustc_span::source_map::Spanned;
use rustc_span::Span;
-const MANUAL_STRIP_MSRV: RustcVersion = RustcVersion::new(1, 45, 0);
-
declare_clippy_lint! {
/// **What it does:**
/// Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using
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) {
+ if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) {
return;
}
then {
let reindented_or_body =
reindent_multiline(or_body_snippet.into(), true, Some(indent));
+
+ let suggestion = if scrutinee.span.from_expansion() {
+ // we don't want parenthesis around macro, e.g. `(some_macro!()).unwrap_or(0)`
+ sugg::Sugg::hir_with_macro_callsite(cx, scrutinee, "..")
+ }
+ else {
+ sugg::Sugg::hir(cx, scrutinee, "..").maybe_par()
+ };
+
span_lint_and_sugg(
cx,
MANUAL_UNWRAP_OR, expr.span,
"replace with",
format!(
"{}.unwrap_or({})",
- sugg::Sugg::hir(cx, scrutinee, "..").maybe_par(),
+ suggestion,
reindented_or_body,
),
Applicability::MachineApplicable,
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
use clippy_utils::visitors::LocalUsedVisitor;
use clippy_utils::{
- get_parent_expr, in_macro, is_allowed, is_expn_of, is_lang_ctor, is_refutable, is_wild, meets_msrv, path_to_local,
- path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks, strip_pat_refs,
+ get_parent_expr, in_macro, is_allowed, is_expn_of, is_lang_ctor, is_refutable, is_wild, meets_msrv, msrvs,
+ path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks,
+ strip_pat_refs,
};
use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
use if_chain::if_chain;
MATCH_SAME_ARMS,
]);
-const MATCH_LIKE_MATCHES_MACRO_MSRV: RustcVersion = RustcVersion::new(1, 42, 0);
-
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) {
redundant_pattern_match::check(cx, expr);
- if meets_msrv(self.msrv.as_ref(), &MATCH_LIKE_MATCHES_MACRO_MSRV) {
+ if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) {
if !check_match_like_matches(cx, expr) {
lint_match_arms(cx, expr);
}
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet, snippet_with_applicability};
-use clippy_utils::{in_macro, is_diag_trait_item, is_lang_ctor, match_def_path, meets_msrv, paths};
+use clippy_utils::{in_macro, is_diag_trait_item, is_lang_ctor, match_def_path, meets_msrv, msrvs, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
}
}
-const MEM_REPLACE_WITH_DEFAULT_MSRV: RustcVersion = RustcVersion::new(1, 40, 0);
-
pub struct MemReplace {
msrv: Option<RustcVersion>,
}
then {
check_replace_option_with_none(cx, src, dest, expr.span);
check_replace_with_uninit(cx, src, dest, expr.span);
- if meets_msrv(self.msrv.as_ref(), &MEM_REPLACE_WITH_DEFAULT_MSRV) {
+ if meets_msrv(self.msrv.as_ref(), &msrvs::MEM_TAKE) {
check_replace_with_default(cx, src, dest, expr.span);
}
}
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::ty::{get_iterator_item_ty, is_copy};
-use clippy_utils::{is_trait_method, meets_msrv};
+use clippy_utils::{is_trait_method, meets_msrv, msrvs};
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use super::CLONED_INSTEAD_OF_COPIED;
-const ITERATOR_COPIED_MSRV: RustcVersion = RustcVersion::new(1, 36, 0);
-const OPTION_COPIED_MSRV: RustcVersion = RustcVersion::new(1, 35, 0);
-
pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option<&RustcVersion>) {
let recv_ty = cx.typeck_results().expr_ty_adjusted(recv);
let inner_ty = match recv_ty.kind() {
// `Option<T>` -> `T`
ty::Adt(adt, subst)
- if cx.tcx.is_diagnostic_item(sym::option_type, adt.did) && meets_msrv(msrv, &OPTION_COPIED_MSRV) =>
+ if cx.tcx.is_diagnostic_item(sym::option_type, adt.did) && meets_msrv(msrv, &msrvs::OPTION_COPIED) =>
{
subst.type_at(0)
},
- _ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, &ITERATOR_COPIED_MSRV) => {
+ _ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, &msrvs::ITERATOR_COPIED) => {
match get_iterator_item_ty(cx, recv_ty) {
// <T as Iterator>::Item
Some(ty) => ty,
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
use clippy_utils::source::snippet;
-use clippy_utils::{is_trait_method, meets_msrv};
+use clippy_utils::{is_trait_method, meets_msrv, msrvs};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use super::FILTER_MAP_NEXT;
-const FILTER_MAP_NEXT_MSRV: RustcVersion = RustcVersion::new(1, 30, 0);
-
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'_>,
msrv: Option<&RustcVersion>,
) {
if is_trait_method(cx, expr, sym::Iterator) {
- if !meets_msrv(msrv, &FILTER_MAP_NEXT_MSRV) {
+ if !meets_msrv(msrv, &msrvs::ITERATOR_FIND_MAP) {
return;
}
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
-use clippy_utils::meets_msrv;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::usage::mutated_variables;
+use clippy_utils::{meets_msrv, msrvs};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use super::MAP_UNWRAP_OR;
-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
pub(super) fn check<'tcx>(
unwrap_arg: &'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(recv), sym::option_type);
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::result_type);
+ if is_result && !meets_msrv(msrv, &msrvs::RESULT_MAP_OR_ELSE) {
+ return false;
+ }
+
if is_option || is_result {
// Don't make a suggestion that may fail to compile due to mutably borrowing
// the same variable twice.
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{match_def_path, meets_msrv, path_to_local_id, paths, remove_blocks};
+use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, remove_blocks};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use super::OPTION_AS_REF_DEREF;
-const OPTION_AS_REF_DEREF_MSRV: RustcVersion = RustcVersion::new(1, 40, 0);
-
/// lint use of `_.as_ref().map(Deref::deref)` for `Option`s
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
is_mut: bool,
msrv: Option<&RustcVersion>,
) {
- if !meets_msrv(msrv, &OPTION_AS_REF_DEREF_MSRV) {
+ if !meets_msrv(msrv, &msrvs::OPTION_AS_DEREF) {
return;
}
use clippy_utils::diagnostics::span_lint;
use clippy_utils::qualify_min_const_fn::is_min_const_fn;
use clippy_utils::ty::has_drop;
-use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, trait_ref_of_method};
+use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, msrvs, trait_ref_of_method};
use rustc_hir as hir;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId};
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:**
///
span: Span,
hir_id: HirId,
) {
- if !meets_msrv(self.msrv.as_ref(), &MISSING_CONST_FOR_FN_MSRV) {
+ if !meets_msrv(self.msrv.as_ref(), &msrvs::CONST_IF_MATCH) {
return;
}
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_lang_ctor;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{differing_macro_contexts, meets_msrv};
+use clippy_utils::{differing_macro_contexts, is_lang_ctor};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionSome, ResultOk};
use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_semver::RustcVersion;
-use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! {
"Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result<T, E>`."
}
-const NEEDLESS_QUESTION_MARK_RESULT_MSRV: RustcVersion = RustcVersion::new(1, 13, 0);
-const NEEDLESS_QUESTION_MARK_OPTION_MSRV: RustcVersion = RustcVersion::new(1, 22, 0);
-
-pub struct NeedlessQuestionMark {
- msrv: Option<RustcVersion>,
-}
-
-impl NeedlessQuestionMark {
- #[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
- Self { msrv }
- }
-}
-
-impl_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]);
+declare_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]);
#[derive(Debug)]
enum SomeOkCall<'a> {
_ => return,
};
- if let Some(ok_some_call) = is_some_or_ok_call(self, cx, e) {
+ if let Some(ok_some_call) = is_some_or_ok_call(cx, e) {
emit_lint(cx, &ok_some_call);
}
}
if_chain! {
if let Some(expr) = expr_opt;
- if let Some(ok_some_call) = is_some_or_ok_call(self, cx, expr);
+ if let Some(ok_some_call) = is_some_or_ok_call(cx, expr);
then {
emit_lint(cx, &ok_some_call);
}
};
}
-
- extract_msrv_attr!(LateContext);
}
fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) {
);
}
-fn is_some_or_ok_call<'a>(
- nqml: &NeedlessQuestionMark,
- cx: &'a LateContext<'_>,
- expr: &'a Expr<'_>,
-) -> Option<SomeOkCall<'a>> {
+fn is_some_or_ok_call<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option<SomeOkCall<'a>> {
if_chain! {
// Check outer expression matches CALL_IDENT(ARGUMENT) format
if let ExprKind::Call(path, args) = &expr.kind;
let inner_is_some = is_type_diagnostic_item(cx, inner_ty, sym::option_type);
// Check for Option MSRV
- let meets_option_msrv = meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV);
- if outer_is_some && inner_is_some && meets_option_msrv {
+ if outer_is_some && inner_is_some {
return Some(SomeOkCall::SomeCall(expr, inner_expr));
}
let does_not_call_from = !has_implicit_error_from(cx, expr, inner_expr);
// Must meet Result MSRV
- let meets_result_msrv = meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_RESULT_MSRV);
- if outer_is_result && inner_is_result && does_not_call_from && meets_result_msrv {
+ if outer_is_result && inner_is_result && does_not_call_from {
return Some(SomeOkCall::OkCall(expr, inner_expr));
}
}
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
use clippy_utils::sugg::Sugg;
-use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path};
+use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, single_segment_path};
use clippy_utils::{higher, SpanlessEq};
use if_chain::if_chain;
use rustc_ast::ast::RangeLimits;
"manually reimplementing {`Range`, `RangeInclusive`}`::contains`"
}
-const MANUAL_RANGE_CONTAINS_MSRV: RustcVersion = RustcVersion::new(1, 35, 0);
-
pub struct Ranges {
msrv: Option<RustcVersion>,
}
check_range_zip_with_len(cx, path, args, expr.span);
},
ExprKind::Binary(ref op, l, r) => {
- if meets_msrv(self.msrv.as_ref(), &MANUAL_RANGE_CONTAINS_MSRV) {
+ if meets_msrv(self.msrv.as_ref(), &msrvs::RANGE_CONTAINS) {
check_possible_range_contains(cx, op.node, l, r, expr);
}
},
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::meets_msrv;
+use clippy_utils::{meets_msrv, msrvs};
use rustc_ast::ast::{Expr, ExprKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
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
/// could be used.
impl EarlyLintPass for RedundantFieldNames {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
- if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_FIELD_NAMES_MSRV) {
+ if !meets_msrv(self.msrv.as_ref(), &msrvs::FIELD_INIT_SHORTHAND) {
return;
}
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::meets_msrv;
use clippy_utils::source::snippet;
+use clippy_utils::{meets_msrv, msrvs};
use rustc_ast::ast::{Item, ItemKind, Ty, TyKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
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.
///
impl EarlyLintPass for RedundantStaticLifetimes {
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
- if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_STATIC_LIFETIMES_MSRV) {
+ if !meets_msrv(self.msrv.as_ref(), &msrvs::STATIC_IN_CONST) {
return;
}
#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
+use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path};
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::over;
-use clippy_utils::{
- ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path},
- meets_msrv,
-};
+use clippy_utils::{meets_msrv, msrvs, over};
use rustc_ast::mut_visit::*;
use rustc_ast::ptr::P;
use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
"unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`"
}
-const UNNESTED_OR_PATTERNS_MSRV: RustcVersion = RustcVersion::new(1, 53, 0);
-
#[derive(Clone, Copy)]
pub struct UnnestedOrPatterns {
msrv: Option<RustcVersion>,
impl EarlyLintPass for UnnestedOrPatterns {
fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) {
- if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) {
+ if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
lint_unnested_or_patterns(cx, &a.pat);
}
}
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
- if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) {
+ if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
if let ast::ExprKind::Let(pat, _) = &e.kind {
lint_unnested_or_patterns(cx, pat);
}
}
fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) {
- if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) {
+ if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
lint_unnested_or_patterns(cx, &p.pat);
}
}
fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) {
- if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) {
+ if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
lint_unnested_or_patterns(cx, &l.pat);
}
}
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
-use clippy_utils::{in_macro, meets_msrv};
+use clippy_utils::{in_macro, meets_msrv, msrvs};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
stack: Vec<StackItem>,
}
-const USE_SELF_MSRV: RustcVersion = RustcVersion::new(1, 37, 0);
-
impl UseSelf {
#[must_use]
pub fn new(msrv: Option<RustcVersion>) -> Self {
}
fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
- if in_macro(hir_ty.span) | in_impl(cx, hir_ty) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) {
+ if in_macro(hir_ty.span)
+ || in_impl(cx, hir_ty)
+ || !meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS)
+ {
return;
}
}
}
- if in_macro(expr.span) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) {
+ if in_macro(expr.span) || !meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS) {
return;
}
pub use self::helpers::Conf;
define_Conf! {
- /// Lint: CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, NEEDLESS_QUESTION_MARK, PTR_AS_PTR. The minimum rust version that the project supports
+ /// Lint: CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR. 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()),
pub mod eager_or_lazy;
pub mod higher;
mod hir_utils;
+pub mod msrvs;
pub mod numeric_literal;
pub mod paths;
pub mod ptr;
--- /dev/null
+use rustc_semver::RustcVersion;
+
+macro_rules! msrv_aliases {
+ ($($major:literal,$minor:literal,$patch:literal {
+ $($name:ident),* $(,)?
+ })*) => {
+ $($(
+ pub const $name: RustcVersion = RustcVersion::new($major, $minor, $patch);
+ )*)*
+ };
+}
+
+// names may refer to stabilized feature flags or library items
+msrv_aliases! {
+ 1,53,0 { OR_PATTERNS }
+ 1,50,0 { BOOL_THEN }
+ 1,46,0 { CONST_IF_MATCH }
+ 1,45,0 { STR_STRIP_PREFIX }
+ 1,42,0 { MATCHES_MACRO }
+ 1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE }
+ 1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF }
+ 1,38,0 { POINTER_CAST }
+ 1,37,0 { TYPE_ALIAS_ENUM_VARIANTS }
+ 1,36,0 { ITERATOR_COPIED }
+ 1,35,0 { OPTION_COPIED, RANGE_CONTAINS }
+ 1,34,0 { TRY_FROM }
+ 1,30,0 { ITERATOR_FIND_MAP }
+ 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST }
+}
## 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,
+Sometimes a lint makes suggestions that require a certain version of Rust. For example, the `manual_strip` lint suggests
+using `str::strip_prefix` and `str::strip_suffix` which is only available after Rust 1.45. In such cases, you need to
+ensure that the MSRV configured for the project is >= the MSRV of the required Rust feature. If multiple features are
+required, just use the one with a lower MSRV.
+
+First, add an MSRV alias for the required feature in [`clippy_utils::msrvs`](/clippy_utils/src/msrvs.rs). This can be
+accessed later as `msrvs::STR_STRIP_PREFIX`, for example.
```rust
-const MANUAL_STRIP_MSRV: RustcVersion = RustcVersion::new(1, 45, 0);
+msrv_aliases! {
+ ..
+ 1,45,0 { STR_STRIP_PREFIX }
+}
```
-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`
+In order to access the project-configured MSRV, you need to have an `msrv` field in the LintPass struct, and a
+constructor to initialize the field. The `msrv` value is passed to the constructor in `clippy_lints/lib.rs`.
```rust
pub struct ManualStrip {
}
```
-The project's MSRV can then be matched against the lint's `msrv` in the LintPass
+The project's MSRV can then be matched against the feature MSRV in the LintPass
using the `meets_msrv` utility function.
``` rust
-if !meets_msrv(self.msrv.as_ref(), &MANUAL_STRIP_MSRV) {
+if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) {
return;
}
```
};
}
+mod issue6965 {
+ macro_rules! some_macro {
+ () => {
+ if 1 > 2 { Some(1) } else { None }
+ };
+ }
+
+ fn test() {
+ let _ = some_macro!().unwrap_or(0);
+ }
+}
+
fn main() {}
};
}
+mod issue6965 {
+ macro_rules! some_macro {
+ () => {
+ if 1 > 2 { Some(1) } else { None }
+ };
+ }
+
+ fn test() {
+ let _ = match some_macro!() {
+ Some(val) => val,
+ None => 0,
+ };
+ }
+}
+
fn main() {}
LL | | };
| |_____^ help: replace with: `Ok::<&str, &str>("Bob").unwrap_or("Alice")`
-error: aborting due to 13 previous errors
+error: this pattern reimplements `Option::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:201:17
+ |
+LL | let _ = match some_macro!() {
+ | _________________^
+LL | | Some(val) => val,
+LL | | None => 0,
+LL | | };
+ | |_________^ help: replace with: `some_macro!().unwrap_or(0)`
+
+error: aborting due to 14 previous errors
fn main() {}
-mod question_mark_none {
- #![clippy::msrv = "1.12.0"]
- fn needless_question_mark_option() -> Option<usize> {
- struct TO {
- magic: Option<usize>,
- }
- let to = TO { magic: None };
- Some(to.magic?) // should not be triggered
- }
-
- fn needless_question_mark_result() -> Result<usize, bool> {
- struct TO {
- magic: Result<usize, bool>,
- }
- let to = TO { magic: Ok(1_usize) };
- Ok(to.magic?) // should not be triggered
- }
-
- fn main() {
- needless_question_mark_option();
- needless_question_mark_result();
- }
-}
-
-mod question_mark_result {
- #![clippy::msrv = "1.21.0"]
- fn needless_question_mark_option() -> Option<usize> {
- struct TO {
- magic: Option<usize>,
- }
- let to = TO { magic: None };
- Some(to.magic?) // should not be triggered
- }
-
- fn needless_question_mark_result() -> Result<usize, bool> {
- struct TO {
- magic: Result<usize, bool>,
- }
- let to = TO { magic: Ok(1_usize) };
- to.magic // should be triggered
- }
-
- fn main() {
- needless_question_mark_option();
- needless_question_mark_result();
- }
-}
-
-mod question_mark_both {
- #![clippy::msrv = "1.22.0"]
- fn needless_question_mark_option() -> Option<usize> {
- struct TO {
- magic: Option<usize>,
- }
- let to = TO { magic: None };
- to.magic // should be triggered
- }
-
- fn needless_question_mark_result() -> Result<usize, bool> {
- struct TO {
- magic: Result<usize, bool>,
- }
- let to = TO { magic: Ok(1_usize) };
- to.magic // should be triggered
- }
-
- fn main() {
- needless_question_mark_option();
- needless_question_mark_result();
- }
-}
-
// #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use,
// the suggestion fails to apply; do not lint
macro_rules! some_in_macro {
fn main() {}
-mod question_mark_none {
- #![clippy::msrv = "1.12.0"]
- fn needless_question_mark_option() -> Option<usize> {
- struct TO {
- magic: Option<usize>,
- }
- let to = TO { magic: None };
- Some(to.magic?) // should not be triggered
- }
-
- fn needless_question_mark_result() -> Result<usize, bool> {
- struct TO {
- magic: Result<usize, bool>,
- }
- let to = TO { magic: Ok(1_usize) };
- Ok(to.magic?) // should not be triggered
- }
-
- fn main() {
- needless_question_mark_option();
- needless_question_mark_result();
- }
-}
-
-mod question_mark_result {
- #![clippy::msrv = "1.21.0"]
- fn needless_question_mark_option() -> Option<usize> {
- struct TO {
- magic: Option<usize>,
- }
- let to = TO { magic: None };
- Some(to.magic?) // should not be triggered
- }
-
- fn needless_question_mark_result() -> Result<usize, bool> {
- struct TO {
- magic: Result<usize, bool>,
- }
- let to = TO { magic: Ok(1_usize) };
- Ok(to.magic?) // should be triggered
- }
-
- fn main() {
- needless_question_mark_option();
- needless_question_mark_result();
- }
-}
-
-mod question_mark_both {
- #![clippy::msrv = "1.22.0"]
- fn needless_question_mark_option() -> Option<usize> {
- struct TO {
- magic: Option<usize>,
- }
- let to = TO { magic: None };
- Some(to.magic?) // should be triggered
- }
-
- fn needless_question_mark_result() -> Result<usize, bool> {
- struct TO {
- magic: Result<usize, bool>,
- }
- let to = TO { magic: Ok(1_usize) };
- Ok(to.magic?) // should be triggered
- }
-
- fn main() {
- needless_question_mark_option();
- needless_question_mark_result();
- }
-}
-
// #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use,
// the suggestion fails to apply; do not lint
macro_rules! some_in_macro {
| ^^^^^^^^^^^^ help: try: `t.magic`
error: question mark operator is useless here
- --> $DIR/needless_question_mark.rs:138:9
- |
-LL | Ok(to.magic?) // should be triggered
- | ^^^^^^^^^^^^^ help: try: `to.magic`
-
-error: question mark operator is useless here
- --> $DIR/needless_question_mark.rs:154:9
- |
-LL | Some(to.magic?) // should be triggered
- | ^^^^^^^^^^^^^^^ help: try: `to.magic`
-
-error: question mark operator is useless here
- --> $DIR/needless_question_mark.rs:162:9
- |
-LL | Ok(to.magic?) // should be triggered
- | ^^^^^^^^^^^^^ help: try: `to.magic`
-
-error: question mark operator is useless here
- --> $DIR/needless_question_mark.rs:187:27
+ --> $DIR/needless_question_mark.rs:115:27
|
LL | || -> Option<_> { Some(Some($expr)?) }()
| ^^^^^^^^^^^^^^^^^^ help: try: `Some($expr)`
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
-error: aborting due to 15 previous errors
+error: aborting due to 12 previous errors