use super::{contains_return, BIND_INSTEAD_OF_MAP};
use crate::utils::{
in_macro, match_qpath, match_type, method_calls, multispan_sugg_with_applicability, paths, remove_blocks, snippet,
- snippet_with_macro_callsite, span_lint_and_sugg, span_lint_and_then,
+ snippet_with_macro_callsite, span_lint_and_sugg, span_lint_and_then, visitors::find_all_ret_expressions,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
-use rustc_hir::intravisit::{self, Visitor};
use rustc_lint::LateContext;
-use rustc_middle::hir::map::Map;
use rustc_span::Span;
pub(crate) struct OptionAndThenSome;
+
impl BindInsteadOfMap for OptionAndThenSome {
const TYPE_NAME: &'static str = "Option";
const TYPE_QPATH: &'static [&'static str] = &paths::OPTION;
}
pub(crate) struct ResultAndThenOk;
+
impl BindInsteadOfMap for ResultAndThenOk {
const TYPE_NAME: &'static str = "Result";
const TYPE_QPATH: &'static [&'static str] = &paths::RESULT;
}
pub(crate) struct ResultOrElseErrInfo;
+
impl BindInsteadOfMap for ResultOrElseErrInfo {
const TYPE_NAME: &'static str = "Result";
const TYPE_QPATH: &'static [&'static str] = &paths::RESULT;
}
fn lint_closure_autofixable(
- cx: &LateContext<'_, '_>,
+ cx: &LateContext<'_>,
expr: &hir::Expr<'_>,
args: &[hir::Expr<'_>],
closure_expr: &hir::Expr<'_>,
}
}
- fn lint_closure(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) {
+ fn lint_closure(cx: &LateContext<'_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) -> bool {
let mut suggs = Vec::new();
- let can_sugg = find_all_ret_expressions(cx, closure_expr, |ret_expr| {
+ let can_sugg: bool = find_all_ret_expressions(cx, closure_expr, |ret_expr| {
if_chain! {
if !in_macro(ret_expr.span);
if let hir::ExprKind::Call(ref func_path, ref args) = ret_expr.kind;
)
});
}
+ can_sugg
}
/// Lint use of `_.and_then(|x| Some(y))` for `Option`s
- fn lint(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
- if !match_type(cx, cx.tables().expr_ty(&args[0]), Self::TYPE_QPATH) {
- return;
+ fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) -> bool {
+ if !match_type(cx, cx.typeck_results().expr_ty(&args[0]), Self::TYPE_QPATH) {
+ return false;
}
match args[1].kind {
let closure_body = cx.tcx.hir().body(body_id);
let closure_expr = remove_blocks(&closure_body.value);
- if !Self::lint_closure_autofixable(cx, expr, args, closure_expr, closure_args_span) {
- Self::lint_closure(cx, expr, closure_expr);
+ if Self::lint_closure_autofixable(cx, expr, args, closure_expr, closure_args_span) {
+ true
+ } else {
+ Self::lint_closure(cx, expr, closure_expr)
}
},
// `_.and_then(Some)` case, which is no-op.
snippet(cx, args[0].span, "..").into(),
Applicability::MachineApplicable,
);
+ true
},
- _ => {},
- }
- }
-}
-
-/// returns `true` if expr contains match expr desugared from try
-fn contains_try(expr: &hir::Expr<'_>) -> bool {
- struct TryFinder {
- found: bool,
- }
-
- impl<'hir> intravisit::Visitor<'hir> for TryFinder {
- type Map = Map<'hir>;
-
- fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
- intravisit::NestedVisitorMap::None
- }
-
- fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
- if self.found {
- return;
- }
- match expr.kind {
- hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar) => self.found = true,
- _ => intravisit::walk_expr(self, expr),
- }
- }
- }
-
- let mut visitor = TryFinder { found: false };
- visitor.visit_expr(expr);
- visitor.found
-}
-
-fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_, '_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool
-where
- F: FnMut(&'hir hir::Expr<'hir>) -> bool,
-{
- struct RetFinder<F> {
- in_stmt: bool,
- failed: bool,
- cb: F,
- }
-
- struct WithStmtGuarg<'a, F> {
- val: &'a mut RetFinder<F>,
- prev_in_stmt: bool,
- }
-
- impl<F> RetFinder<F> {
- fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> {
- let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
- WithStmtGuarg {
- val: self,
- prev_in_stmt,
- }
- }
- }
-
- impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
- type Target = RetFinder<F>;
-
- fn deref(&self) -> &Self::Target {
- self.val
- }
- }
-
- impl<F> std::ops::DerefMut for WithStmtGuarg<'_, F> {
- fn deref_mut(&mut self) -> &mut Self::Target {
- self.val
- }
- }
-
- impl<F> Drop for WithStmtGuarg<'_, F> {
- fn drop(&mut self) {
- self.val.in_stmt = self.prev_in_stmt;
- }
- }
-
- impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder<F> {
- type Map = Map<'hir>;
-
- fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
- intravisit::NestedVisitorMap::None
- }
-
- fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) {
- intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt)
+ _ => false,
}
-
- fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) {
- if self.failed {
- return;
- }
- if self.in_stmt {
- match expr.kind {
- hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr),
- _ => intravisit::walk_expr(self, expr),
- }
- } else {
- match expr.kind {
- hir::ExprKind::Match(cond, arms, _) => {
- self.inside_stmt(true).visit_expr(cond);
- for arm in arms {
- self.visit_expr(arm.body);
- }
- },
- hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr),
- hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr),
- _ => self.failed |= !(self.cb)(expr),
- }
- }
- }
- }
-
- !contains_try(expr) && {
- let mut ret_finder = RetFinder {
- in_stmt: false,
- failed: false,
- cb: callback,
- };
- ret_finder.visit_expr(expr);
- !ret_finder.failed
}
}