1 use clippy_utils
::diagnostics
::span_lint_and_then
;
2 use clippy_utils
::source
::snippet
;
3 use clippy_utils
::{contains_return, is_res_lang_ctor, path_res, return_ty, visitors::find_all_ret_expressions}
;
4 use if_chain
::if_chain
;
5 use rustc_errors
::Applicability
;
6 use rustc_hir
::intravisit
::FnKind
;
7 use rustc_hir
::LangItem
::{OptionSome, ResultOk}
;
8 use rustc_hir
::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node}
;
9 use rustc_lint
::{LateContext, LateLintPass}
;
11 use rustc_session
::{declare_tool_lint, impl_lint_pass}
;
12 use rustc_span
::symbol
::sym
;
15 declare_clippy_lint
! {
17 /// Checks for private functions that only return `Ok` or `Some`.
19 /// ### Why is this bad?
20 /// It is not meaningful to wrap values when no `None` or `Err` is returned.
22 /// ### Known problems
23 /// There can be false positives if the function signature is designed to
24 /// fit some external requirement.
28 /// fn get_cool_number(a: bool, b: bool) -> Option<i32> {
41 /// fn get_cool_number(a: bool, b: bool) -> i32 {
52 #[clippy::version = "1.50.0"]
53 pub UNNECESSARY_WRAPS
,
55 "functions that only return `Ok` or `Some`"
58 pub struct UnnecessaryWraps
{
59 avoid_breaking_exported_api
: bool
,
62 impl_lint_pass
!(UnnecessaryWraps
=> [UNNECESSARY_WRAPS
]);
64 impl UnnecessaryWraps
{
65 pub fn new(avoid_breaking_exported_api
: bool
) -> Self {
67 avoid_breaking_exported_api
,
72 impl<'tcx
> LateLintPass
<'tcx
> for UnnecessaryWraps
{
75 cx
: &LateContext
<'tcx
>,
76 fn_kind
: FnKind
<'tcx
>,
77 fn_decl
: &FnDecl
<'tcx
>,
82 // Abort if public function/method or closure.
84 FnKind
::ItemFn(..) | FnKind
::Method(..) => {
85 let def_id
= cx
.tcx
.hir().local_def_id(hir_id
);
86 if self.avoid_breaking_exported_api
&& cx
.effective_visibilities
.is_exported(def_id
) {
90 FnKind
::Closure
=> return,
93 // Abort if the method is implementing a trait or of it a trait method.
94 if let Some(Node
::Item(item
)) = cx
.tcx
.hir().find(cx
.tcx
.hir().get_parent_node(hir_id
)) {
97 ItemKind
::Impl(Impl { of_trait: Some(_), .. }
) | ItemKind
::Trait(..)
103 // Get the wrapper and inner types, if can't, abort.
104 let (return_type_label
, lang_item
, inner_type
) = if let ty
::Adt(adt_def
, subst
) = return_ty(cx
, hir_id
).kind() {
105 if cx
.tcx
.is_diagnostic_item(sym
::Option
, adt_def
.did()) {
106 ("Option", OptionSome
, subst
.type_at(0))
107 } else if cx
.tcx
.is_diagnostic_item(sym
::Result
, adt_def
.did()) {
108 ("Result", ResultOk
, subst
.type_at(0))
116 // Check if all return expression respect the following condition and collect them.
117 let mut suggs
= Vec
::new();
118 let can_sugg
= find_all_ret_expressions(cx
, body
.value
, |ret_expr
| {
120 if !ret_expr
.span
.from_expansion();
121 // Check if a function call.
122 if let ExprKind
::Call(func
, [arg
]) = ret_expr
.kind
;
123 if is_res_lang_ctor(cx
, path_res(cx
, func
), lang_item
);
124 // Make sure the function argument does not contain a return expression.
125 if !contains_return(arg
);
130 if inner_type
.is_unit() {
133 snippet(cx
, arg
.span
.source_callsite(), "..").to_string()
144 if can_sugg
&& !suggs
.is_empty() {
145 let (lint_msg
, return_type_sugg_msg
, return_type_sugg
, body_sugg_msg
) = if inner_type
.is_unit() {
147 "this function's return value is unnecessary".to_string(),
148 "remove the return type...".to_string(),
149 snippet(cx
, fn_decl
.output
.span(), "..").to_string(),
150 "...and then remove returned values",
154 format
!("this function's return value is unnecessarily wrapped by `{return_type_label}`"),
155 format
!("remove `{return_type_label}` from the return type..."),
156 inner_type
.to_string(),
157 "...and then change returning expressions",
161 span_lint_and_then(cx
, UNNECESSARY_WRAPS
, span
, lint_msg
.as_str(), |diag
| {
162 diag
.span_suggestion(
163 fn_decl
.output
.span(),
164 return_type_sugg_msg
.as_str(),
166 Applicability
::MaybeIncorrect
,
168 diag
.multipart_suggestion(body_sugg_msg
, suggs
, Applicability
::MaybeIncorrect
);