]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
New upstream version 1.61.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / unnecessary_wraps.rs
CommitLineData
cdc7bbd5
XL
1use clippy_utils::diagnostics::span_lint_and_then;
2use clippy_utils::source::snippet;
a2a8927a 3use clippy_utils::{contains_return, is_lang_ctor, return_ty, visitors::find_all_ret_expressions};
f20569fa
XL
4use if_chain::if_chain;
5use rustc_errors::Applicability;
6use rustc_hir::intravisit::FnKind;
cdc7bbd5 7use rustc_hir::LangItem::{OptionSome, ResultOk};
f20569fa
XL
8use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node};
9use rustc_lint::{LateContext, LateLintPass};
10use rustc_middle::ty;
17df50a5 11use rustc_session::{declare_tool_lint, impl_lint_pass};
f20569fa
XL
12use rustc_span::symbol::sym;
13use rustc_span::Span;
14
15declare_clippy_lint! {
94222f64
XL
16 /// ### What it does
17 /// Checks for private functions that only return `Ok` or `Some`.
f20569fa 18 ///
94222f64
XL
19 /// ### Why is this bad?
20 /// It is not meaningful to wrap values when no `None` or `Err` is returned.
f20569fa 21 ///
94222f64
XL
22 /// ### Known problems
23 /// There can be false positives if the function signature is designed to
f20569fa
XL
24 /// fit some external requirement.
25 ///
94222f64 26 /// ### Example
f20569fa
XL
27 /// ```rust
28 /// fn get_cool_number(a: bool, b: bool) -> Option<i32> {
29 /// if a && b {
30 /// return Some(50);
31 /// }
32 /// if a {
33 /// Some(0)
34 /// } else {
35 /// Some(10)
36 /// }
37 /// }
38 /// ```
39 /// Use instead:
40 /// ```rust
41 /// fn get_cool_number(a: bool, b: bool) -> i32 {
42 /// if a && b {
43 /// return 50;
44 /// }
45 /// if a {
46 /// 0
47 /// } else {
48 /// 10
49 /// }
50 /// }
51 /// ```
a2a8927a 52 #[clippy::version = "1.50.0"]
f20569fa
XL
53 pub UNNECESSARY_WRAPS,
54 pedantic,
55 "functions that only return `Ok` or `Some`"
56}
57
17df50a5
XL
58pub struct UnnecessaryWraps {
59 avoid_breaking_exported_api: bool,
60}
61
62impl_lint_pass!(UnnecessaryWraps => [UNNECESSARY_WRAPS]);
63
64impl UnnecessaryWraps {
65 pub fn new(avoid_breaking_exported_api: bool) -> Self {
66 Self {
67 avoid_breaking_exported_api,
68 }
69 }
70}
f20569fa
XL
71
72impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
73 fn check_fn(
74 &mut self,
75 cx: &LateContext<'tcx>,
76 fn_kind: FnKind<'tcx>,
77 fn_decl: &FnDecl<'tcx>,
78 body: &Body<'tcx>,
79 span: Span,
80 hir_id: HirId,
81 ) {
82 // Abort if public function/method or closure.
83 match fn_kind {
17df50a5 84 FnKind::ItemFn(..) | FnKind::Method(..) => {
94222f64
XL
85 let def_id = cx.tcx.hir().local_def_id(hir_id);
86 if self.avoid_breaking_exported_api && cx.access_levels.is_exported(def_id) {
f20569fa
XL
87 return;
88 }
89 },
90 FnKind::Closure => return,
f20569fa
XL
91 }
92
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)) {
95 if matches!(
96 item.kind,
97 ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..)
98 ) {
99 return;
100 }
101 }
102
103 // Get the wrapper and inner types, if can't, abort.
cdc7bbd5 104 let (return_type_label, lang_item, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id).kind() {
ee023bcb 105 if cx.tcx.is_diagnostic_item(sym::Option, adt_def.did()) {
cdc7bbd5 106 ("Option", OptionSome, subst.type_at(0))
ee023bcb 107 } else if cx.tcx.is_diagnostic_item(sym::Result, adt_def.did()) {
cdc7bbd5 108 ("Result", ResultOk, subst.type_at(0))
f20569fa
XL
109 } else {
110 return;
111 }
112 } else {
113 return;
114 };
115
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| {
119 if_chain! {
a2a8927a 120 if !ret_expr.span.from_expansion();
f20569fa 121 // Check if a function call.
cdc7bbd5 122 if let ExprKind::Call(func, [arg]) = ret_expr.kind;
f20569fa 123 // Check if OPTION_SOME or RESULT_OK, depending on return type.
cdc7bbd5
XL
124 if let ExprKind::Path(qpath) = &func.kind;
125 if is_lang_ctor(cx, qpath, lang_item);
f20569fa 126 // Make sure the function argument does not contain a return expression.
cdc7bbd5 127 if !contains_return(arg);
f20569fa
XL
128 then {
129 suggs.push(
130 (
131 ret_expr.span,
132 if inner_type.is_unit() {
133 "".to_string()
134 } else {
cdc7bbd5 135 snippet(cx, arg.span.source_callsite(), "..").to_string()
f20569fa
XL
136 }
137 )
138 );
139 true
140 } else {
141 false
142 }
143 }
144 });
145
146 if can_sugg && !suggs.is_empty() {
147 let (lint_msg, return_type_sugg_msg, return_type_sugg, body_sugg_msg) = if inner_type.is_unit() {
148 (
149 "this function's return value is unnecessary".to_string(),
150 "remove the return type...".to_string(),
151 snippet(cx, fn_decl.output.span(), "..").to_string(),
152 "...and then remove returned values",
153 )
154 } else {
155 (
156 format!(
157 "this function's return value is unnecessarily wrapped by `{}`",
158 return_type_label
159 ),
160 format!("remove `{}` from the return type...", return_type_label),
161 inner_type.to_string(),
162 "...and then change returning expressions",
163 )
164 };
165
166 span_lint_and_then(cx, UNNECESSARY_WRAPS, span, lint_msg.as_str(), |diag| {
167 diag.span_suggestion(
168 fn_decl.output.span(),
169 return_type_sugg_msg.as_str(),
170 return_type_sugg,
171 Applicability::MaybeIncorrect,
172 );
173 diag.multipart_suggestion(body_sugg_msg, suggs, Applicability::MaybeIncorrect);
174 });
175 }
176 }
177}