2 contains_return
, in_macro
, 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}
;
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:** There can be false positives if the function signature is designed to
21 /// fit some external requirement.
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
>,
67 // Abort if public function/method or closure.
69 FnKind
::ItemFn(.., visibility
) | FnKind
::Method(.., Some(visibility
)) => {
70 if visibility
.node
.is_pub() {
74 FnKind
::Closure
=> return,
78 // Abort if the method is implementing a trait or of it a trait method.
79 if let Some(Node
::Item(item
)) = cx
.tcx
.hir().find(cx
.tcx
.hir().get_parent_node(hir_id
)) {
82 ItemKind
::Impl(Impl { of_trait: Some(_), .. }
) | ItemKind
::Trait(..)
88 // Get the wrapper and inner types, if can't, abort.
89 let (return_type_label
, path
, inner_type
) = if let ty
::Adt(adt_def
, subst
) = return_ty(cx
, hir_id
).kind() {
90 if cx
.tcx
.is_diagnostic_item(sym
::option_type
, adt_def
.did
) {
91 ("Option", &paths
::OPTION_SOME
, subst
.type_at(0))
92 } else if cx
.tcx
.is_diagnostic_item(sym
::result_type
, adt_def
.did
) {
93 ("Result", &paths
::RESULT_OK
, subst
.type_at(0))
101 // Check if all return expression respect the following condition and collect them.
102 let mut suggs
= Vec
::new();
103 let can_sugg
= find_all_ret_expressions(cx
, &body
.value
, |ret_expr
| {
105 if !in_macro(ret_expr
.span
);
106 // Check if a function call.
107 if let ExprKind
::Call(ref func
, ref args
) = ret_expr
.kind
;
108 // Get the Path of the function call.
109 if let ExprKind
::Path(ref qpath
) = func
.kind
;
110 // Check if OPTION_SOME or RESULT_OK, depending on return type.
111 if match_qpath(qpath
, path
);
113 // Make sure the function argument does not contain a return expression.
114 if !contains_return(&args
[0]);
119 if inner_type
.is_unit() {
122 snippet(cx
, args
[0].span
.source_callsite(), "..").to_string()
133 if can_sugg
&& !suggs
.is_empty() {
134 let (lint_msg
, return_type_sugg_msg
, return_type_sugg
, body_sugg_msg
) = if inner_type
.is_unit() {
136 "this function's return value is unnecessary".to_string(),
137 "remove the return type...".to_string(),
138 snippet(cx
, fn_decl
.output
.span(), "..").to_string(),
139 "...and then remove returned values",
144 "this function's return value is unnecessarily wrapped by `{}`",
147 format
!("remove `{}` from the return type...", return_type_label
),
148 inner_type
.to_string(),
149 "...and then change returning expressions",
153 span_lint_and_then(cx
, UNNECESSARY_WRAPS
, span
, lint_msg
.as_str(), |diag
| {
154 diag
.span_suggestion(
155 fn_decl
.output
.span(),
156 return_type_sugg_msg
.as_str(),
158 Applicability
::MaybeIncorrect
,
160 diag
.multipart_suggestion(body_sugg_msg
, suggs
, Applicability
::MaybeIncorrect
);