|  | use std::borrow::Cow; | 
|  |  | 
|  | use clippy_config::Conf; | 
|  | use clippy_utils::diagnostics::span_lint_and_then; | 
|  | use clippy_utils::res::{MaybeDef, MaybeQPath}; | 
|  | use clippy_utils::source::snippet; | 
|  | use clippy_utils::visitors::find_all_ret_expressions; | 
|  | use clippy_utils::{contains_return, return_ty}; | 
|  | use rustc_errors::Applicability; | 
|  | use rustc_hir::LangItem::{OptionSome, ResultOk}; | 
|  | use rustc_hir::intravisit::FnKind; | 
|  | use rustc_hir::{Body, ExprKind, FnDecl, Impl, ItemKind, Node}; | 
|  | use rustc_lint::{LateContext, LateLintPass}; | 
|  | use rustc_middle::ty; | 
|  | use rustc_session::impl_lint_pass; | 
|  | use rustc_span::Span; | 
|  | use rustc_span::def_id::LocalDefId; | 
|  | use rustc_span::symbol::sym; | 
|  |  | 
|  | declare_clippy_lint! { | 
|  | /// ### What it does | 
|  | /// Checks for private functions that only return `Ok` or `Some`. | 
|  | /// | 
|  | /// ### Why is this bad? | 
|  | /// It is not meaningful to wrap values when no `None` or `Err` is returned. | 
|  | /// | 
|  | /// ### Known problems | 
|  | /// There can be false positives if the function signature is designed to | 
|  | /// fit some external requirement. | 
|  | /// | 
|  | /// ### Example | 
|  | /// ```no_run | 
|  | /// fn get_cool_number(a: bool, b: bool) -> Option<i32> { | 
|  | ///     if a && b { | 
|  | ///         return Some(50); | 
|  | ///     } | 
|  | ///     if a { | 
|  | ///         Some(0) | 
|  | ///     } else { | 
|  | ///         Some(10) | 
|  | ///     } | 
|  | /// } | 
|  | /// ``` | 
|  | /// Use instead: | 
|  | /// ```no_run | 
|  | /// fn get_cool_number(a: bool, b: bool) -> i32 { | 
|  | ///     if a && b { | 
|  | ///         return 50; | 
|  | ///     } | 
|  | ///     if a { | 
|  | ///         0 | 
|  | ///     } else { | 
|  | ///         10 | 
|  | ///     } | 
|  | /// } | 
|  | /// ``` | 
|  | #[clippy::version = "1.50.0"] | 
|  | pub UNNECESSARY_WRAPS, | 
|  | pedantic, | 
|  | "functions that only return `Ok` or `Some`" | 
|  | } | 
|  |  | 
|  | pub struct UnnecessaryWraps { | 
|  | avoid_breaking_exported_api: bool, | 
|  | } | 
|  |  | 
|  | impl_lint_pass!(UnnecessaryWraps => [UNNECESSARY_WRAPS]); | 
|  |  | 
|  | impl UnnecessaryWraps { | 
|  | pub fn new(conf: &'static Conf) -> Self { | 
|  | Self { | 
|  | avoid_breaking_exported_api: conf.avoid_breaking_exported_api, | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { | 
|  | fn check_fn( | 
|  | &mut self, | 
|  | cx: &LateContext<'tcx>, | 
|  | fn_kind: FnKind<'tcx>, | 
|  | fn_decl: &FnDecl<'tcx>, | 
|  | body: &Body<'tcx>, | 
|  | _span: Span, | 
|  | def_id: LocalDefId, | 
|  | ) { | 
|  | // Abort if public function/method or closure. | 
|  | match fn_kind { | 
|  | FnKind::ItemFn(..) | FnKind::Method(..) => { | 
|  | if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id) { | 
|  | return; | 
|  | } | 
|  | }, | 
|  | FnKind::Closure => return, | 
|  | } | 
|  |  | 
|  | // Abort if the method is implementing a trait or of it a trait method. | 
|  | let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); | 
|  | if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) | 
|  | && matches!( | 
|  | item.kind, | 
|  | ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) | 
|  | ) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Get the wrapper and inner types, if can't, abort. | 
|  | let (return_type_label, lang_item, inner_type) = | 
|  | if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id.expect_owner()).kind() { | 
|  | match cx.tcx.get_diagnostic_name(adt_def.did()) { | 
|  | Some(sym::Option) => ("Option", OptionSome, subst.type_at(0)), | 
|  | Some(sym::Result) => ("Result", ResultOk, subst.type_at(0)), | 
|  | _ => return, | 
|  | } | 
|  | } else { | 
|  | return; | 
|  | }; | 
|  |  | 
|  | // Check if all return expression respect the following condition and collect them. | 
|  | let mut suggs = Vec::new(); | 
|  | let can_sugg = find_all_ret_expressions(cx, body.value, |ret_expr| { | 
|  | if !ret_expr.span.from_expansion() | 
|  | // Check if a function call. | 
|  | && let ExprKind::Call(func, [arg]) = ret_expr.kind | 
|  | && func.res(cx).ctor_parent(cx).is_lang_item(cx, lang_item) | 
|  | // Make sure the function argument does not contain a return expression. | 
|  | && !contains_return(arg) | 
|  | { | 
|  | suggs.push(( | 
|  | ret_expr.span, | 
|  | if inner_type.is_unit() { | 
|  | String::new() | 
|  | } else { | 
|  | snippet(cx, arg.span.source_callsite(), "..").to_string() | 
|  | }, | 
|  | )); | 
|  | true | 
|  | } else { | 
|  | false | 
|  | } | 
|  | }); | 
|  |  | 
|  | if can_sugg && !suggs.is_empty() { | 
|  | let (lint_msg, return_type_sugg_msg, return_type_sugg, body_sugg_msg) = if inner_type.is_unit() { | 
|  | ( | 
|  | "this function's return value is unnecessary".to_string(), | 
|  | "remove the return type...".to_string(), | 
|  | // FIXME: we should instead get the span including the `->` and suggest an | 
|  | // empty string for this case. | 
|  | Cow::Borrowed("()"), | 
|  | Cow::Borrowed("...and then remove returned values"), | 
|  | ) | 
|  | } else { | 
|  | let wrapper = if lang_item == OptionSome { "Some" } else { "Ok" }; | 
|  | ( | 
|  | format!("this function's return value is unnecessarily wrapped by `{return_type_label}`"), | 
|  | format!("remove `{return_type_label}` from the return type..."), | 
|  | Cow::Owned(inner_type.to_string()), | 
|  | Cow::Owned(format!( | 
|  | "...and then remove the surrounding `{wrapper}()` from returning expressions" | 
|  | )), | 
|  | ) | 
|  | }; | 
|  |  | 
|  | span_lint_and_then(cx, UNNECESSARY_WRAPS, cx.tcx.def_span(def_id), lint_msg, |diag| { | 
|  | diag.span_suggestion( | 
|  | fn_decl.output.span(), | 
|  | return_type_sugg_msg, | 
|  | return_type_sugg, | 
|  | Applicability::MaybeIncorrect, | 
|  | ); | 
|  | diag.multipart_suggestion(body_sugg_msg, suggs, Applicability::MaybeIncorrect); | 
|  | }); | 
|  | } | 
|  | } | 
|  | } |