2 is_type_diagnostic_item, match_qpath, paths, return_ty, snippet,
5 use if_chain::if_chain;
6 use rustc_errors::Applicability;
7 use rustc_hir::intravisit::{FnKind, Visitor};
9 use rustc_lint::{LateContext, LateLintPass};
10 use rustc_middle::{hir::map::Map, ty::subst::GenericArgKind};
11 use rustc_session::{declare_lint_pass, declare_tool_lint};
14 declare_clippy_lint! {
15 /// **What it does:** Checks for private functions that only return `Ok` or `Some`.
17 /// **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned.
19 /// **Known problems:** Since this lint changes function type signature, you may need to
20 /// adjust some code at callee side.
25 /// fn get_cool_number(a: bool, b: bool) -> Option<i32> {
38 /// fn get_cool_number(a: bool, b: bool) -> i32 {
51 "functions that only return `Ok` or `Some`"
54 declare_lint_pass!(UnnecessaryWrap => [UNNECESSARY_WRAP]);
56 impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap {
59 cx: &LateContext<'tcx>,
60 fn_kind: FnKind<'tcx>,
61 fn_decl: &FnDecl<'tcx>,
67 if let FnKind::ItemFn(.., visibility, _) = fn_kind;
68 if visibility.node.is_pub();
74 let path = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) {
76 } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) {
82 let mut suggs = Vec::new();
83 let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| {
85 if let ExprKind::Call(ref func, ref args) = ret_expr.kind;
86 if let ExprKind::Path(ref qpath) = func.kind;
87 if match_qpath(qpath, path);
90 suggs.push((ret_expr.span, snippet(cx, args[0].span.source_callsite(), "..").to_string()));
103 "this function returns unnecessarily wrapping data",
105 diag.multipart_suggestion(
106 "factor this out to",
107 suggs.into_iter().chain({
108 let inner_ty = return_ty(cx, hir_id)
110 .skip(1) // skip `std::option::Option` or `std::result::Result`
111 .take(1) // take the first outermost inner type
112 .filter_map(|inner| match inner.unpack() {
113 GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()),
116 inner_ty.map(|inner_ty| (fn_decl.output.span(), inner_ty))
118 Applicability::MachineApplicable,
126 // code below is copied from `bind_instead_of_map`
128 fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir Expr<'hir>, callback: F) -> bool
130 F: FnMut(&'hir Expr<'hir>) -> bool,
132 struct RetFinder<F> {
138 struct WithStmtGuarg<'a, F> {
139 val: &'a mut RetFinder<F>,
143 impl<F> RetFinder<F> {
144 fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> {
145 let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
153 impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
154 type Target = RetFinder<F>;
156 fn deref(&self) -> &Self::Target {
161 impl<F> std::ops::DerefMut for WithStmtGuarg<'_, F> {
162 fn deref_mut(&mut self) -> &mut Self::Target {
167 impl<F> Drop for WithStmtGuarg<'_, F> {
169 self.val.in_stmt = self.prev_in_stmt;
173 impl<'hir, F: FnMut(&'hir Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder<F> {
174 type Map = Map<'hir>;
176 fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
177 intravisit::NestedVisitorMap::None
180 fn visit_stmt(&mut self, stmt: &'hir Stmt<'_>) {
181 intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt)
184 fn visit_expr(&mut self, expr: &'hir Expr<'_>) {
190 ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr),
191 _ => intravisit::walk_expr(self, expr),
195 ExprKind::Match(cond, arms, _) => {
196 self.inside_stmt(true).visit_expr(cond);
198 self.visit_expr(arm.body);
201 ExprKind::Block(..) => intravisit::walk_expr(self, expr),
202 ExprKind::Ret(Some(expr)) => self.visit_expr(expr),
203 _ => self.failed |= !(self.cb)(expr),
209 !contains_try(expr) && {
210 let mut ret_finder = RetFinder {
215 ret_finder.visit_expr(expr);
220 /// returns `true` if expr contains match expr desugared from try
221 fn contains_try(expr: &Expr<'_>) -> bool {
226 impl<'hir> intravisit::Visitor<'hir> for TryFinder {
227 type Map = Map<'hir>;
229 fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
230 intravisit::NestedVisitorMap::None
233 fn visit_expr(&mut self, expr: &'hir Expr<'hir>) {
238 ExprKind::Match(_, _, MatchSource::TryDesugar) => self.found = true,
239 _ => intravisit::walk_expr(self, expr),
244 let mut visitor = TryFinder { found: false };
245 visitor.visit_expr(expr);