2 contains_return, in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then,
3 visitors::find_all_ret_expressions,
5 use if_chain::if_chain;
6 use rustc_errors::Applicability;
7 use rustc_hir::intravisit::FnKind;
8 use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node};
9 use rustc_lint::{LateContext, LateLintPass};
10 use rustc_middle::ty::subst::GenericArgKind;
11 use rustc_session::{declare_lint_pass, declare_tool_lint};
12 use rustc_span::symbol::sym;
15 declare_clippy_lint! {
16 /// **What it does:** Checks for private functions that only return `Ok` or `Some`.
18 /// **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned.
20 /// **Known problems:** Since this lint changes function type signature, you may need to
21 /// adjust some code at callee side.
26 /// fn get_cool_number(a: bool, b: bool) -> Option<i32> {
39 /// fn get_cool_number(a: bool, b: bool) -> i32 {
50 pub UNNECESSARY_WRAPS,
52 "functions that only return `Ok` or `Some`"
55 declare_lint_pass!(UnnecessaryWraps => [UNNECESSARY_WRAPS]);
57 impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
60 cx: &LateContext<'tcx>,
61 fn_kind: FnKind<'tcx>,
62 fn_decl: &FnDecl<'tcx>,
68 FnKind::ItemFn(.., visibility, _) | FnKind::Method(.., Some(visibility), _) => {
69 if visibility.node.is_pub() {
73 FnKind::Closure(..) => return,
77 if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
80 ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..)
86 let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::option_type) {
87 ("Option", &paths::OPTION_SOME)
88 } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type) {
89 ("Result", &paths::RESULT_OK)
94 let mut suggs = Vec::new();
95 let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| {
97 if !in_macro(ret_expr.span);
98 if let ExprKind::Call(ref func, ref args) = ret_expr.kind;
99 if let ExprKind::Path(ref qpath) = func.kind;
100 if match_qpath(qpath, path);
102 if !contains_return(&args[0]);
104 suggs.push((ret_expr.span, snippet(cx, args[0].span.source_callsite(), "..").to_string()));
112 if can_sugg && !suggs.is_empty() {
118 "this function's return value is unnecessarily wrapped by `{}`",
123 let inner_ty = return_ty(cx, hir_id)
125 .skip(1) // skip `std::option::Option` or `std::result::Result`
126 .take(1) // take the first outermost inner type
127 .filter_map(|inner| match inner.unpack() {
128 GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()),
131 inner_ty.for_each(|inner_ty| {
132 diag.span_suggestion(
133 fn_decl.output.span(),
134 format!("remove `{}` from the return type...", return_type).as_str(),
136 Applicability::MaybeIncorrect,
139 diag.multipart_suggestion(
140 "...and change the returning expressions",
142 Applicability::MaybeIncorrect,