use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
-use clippy_utils::ty::{can_partially_move_ty, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
-use clippy_utils::{in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, peel_hir_expr_refs};
+use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
+use clippy_utils::{
+ can_move_expr_to_closure, in_constant, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id,
+ peel_hir_expr_refs,
+};
use rustc_ast::util::parser::PREC_POSTFIX;
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome};
-use rustc_hir::{
- def::Res,
- intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
- Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath,
-};
+use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, MatchSource, Mutability, Pat, PatKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::{
- symbol::{sym, Ident},
- SyntaxContext,
-};
+use rustc_span::{sym, SyntaxContext};
declare_clippy_lint! {
- /// **What it does:** Checks for usages of `match` which could be implemented using `map`
- ///
- /// **Why is this bad?** Using the `map` method is clearer and more concise.
- ///
- /// **Known problems:** None.
+ /// ### What it does
+ /// Checks for usages of `match` which could be implemented using `map`
///
- /// **Example:**
+ /// ### Why is this bad?
+ /// Using the `map` method is clearer and more concise.
///
+ /// ### Example
/// ```rust
/// match Some(0) {
/// Some(x) => Some(x + 1),
None => return,
};
+ // These two lints will go back and forth with each other.
if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit
- && !is_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
+ && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
{
return;
}
+ // `map` won't perform any adjustments.
+ if !cx.typeck_results().expr_adjustments(some_expr).is_empty() {
+ return;
+ }
+
if !can_move_expr_to_closure(cx, some_expr) {
return;
}
scrutinee_str.into()
};
- let body_str = if let PatKind::Binding(annotation, _, some_binding, None) = some_pat.kind {
- match can_pass_as_func(cx, some_binding, some_expr) {
+ let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind {
+ match can_pass_as_func(cx, id, some_expr) {
Some(func) if func.span.ctxt() == some_expr.span.ctxt() => {
snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
},
_ => {
- if match_var(some_expr, some_binding.name)
- && !is_allowed(cx, MATCH_AS_REF, expr.hir_id)
+ if path_to_local_id(some_expr, id)
+ && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id)
&& binding_ref.is_some()
{
return;
}
}
-// Checks if the expression can be moved into a closure as is.
-fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
- struct V<'cx, 'tcx> {
- cx: &'cx LateContext<'tcx>,
- make_closure: bool,
- }
- impl Visitor<'tcx> for V<'_, 'tcx> {
- type Map = ErasedMap<'tcx>;
- fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
- NestedVisitorMap::None
- }
-
- fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
- match e.kind {
- ExprKind::Break(..)
- | ExprKind::Continue(_)
- | ExprKind::Ret(_)
- | ExprKind::Yield(..)
- | ExprKind::InlineAsm(_)
- | ExprKind::LlvmInlineAsm(_) => {
- self.make_closure = false;
- },
- // Accessing a field of a local value can only be done if the type isn't
- // partially moved.
- ExprKind::Field(base_expr, _)
- if matches!(
- base_expr.kind,
- ExprKind::Path(QPath::Resolved(_, Path { res: Res::Local(_), .. }))
- ) && can_partially_move_ty(self.cx, self.cx.typeck_results().expr_ty(base_expr)) =>
- {
- // TODO: check if the local has been partially moved. Assume it has for now.
- self.make_closure = false;
- return;
- }
- _ => (),
- };
- walk_expr(self, e);
- }
- }
-
- let mut v = V { cx, make_closure: true };
- v.visit_expr(expr);
- v.make_closure
-}
-
// Checks whether the expression could be passed as a function, or whether a closure is needed.
// Returns the function to be passed to `map` if it exists.
-fn can_pass_as_func(cx: &LateContext<'tcx>, binding: Ident, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+fn can_pass_as_func(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
match expr.kind {
ExprKind::Call(func, [arg])
- if match_var(arg, binding.name) && cx.typeck_results().expr_adjustments(arg).is_empty() =>
+ if path_to_local_id(arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() =>
{
Some(func)
},