1
1
use crate :: utils:: {
2
- contains_return, in_macro, is_type_diagnostic_item , match_qpath, paths, return_ty, snippet, span_lint_and_then,
2
+ contains_return, in_macro, match_qpath, paths, return_ty, snippet, span_lint_and_then,
3
3
visitors:: find_all_ret_expressions,
4
4
} ;
5
5
use if_chain:: if_chain;
6
6
use rustc_errors:: Applicability ;
7
7
use rustc_hir:: intravisit:: FnKind ;
8
8
use rustc_hir:: { Body , ExprKind , FnDecl , HirId , Impl , ItemKind , Node } ;
9
9
use rustc_lint:: { LateContext , LateLintPass } ;
10
- use rustc_middle:: ty:: subst :: GenericArgKind ;
10
+ use rustc_middle:: ty;
11
11
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
12
12
use rustc_span:: symbol:: sym;
13
13
use rustc_span:: Span ;
@@ -64,6 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
64
64
span : Span ,
65
65
hir_id : HirId ,
66
66
) {
67
+ // Abort if public function/method or closure.
67
68
match fn_kind {
68
69
FnKind :: ItemFn ( .., visibility, _) | FnKind :: Method ( .., Some ( visibility) , _) => {
69
70
if visibility. node . is_pub ( ) {
@@ -74,6 +75,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
74
75
_ => ( ) ,
75
76
}
76
77
78
+ // Abort if the method is implementing a trait or of it a trait method.
77
79
if let Some ( Node :: Item ( item) ) = cx. tcx . hir ( ) . find ( cx. tcx . hir ( ) . get_parent_node ( hir_id) ) {
78
80
if matches ! (
79
81
item. kind,
@@ -83,25 +85,44 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
83
85
}
84
86
}
85
87
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 )
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 ) )
94
+ } else {
95
+ return ;
96
+ }
90
97
} else {
91
98
return ;
92
99
} ;
93
100
101
+ // Check if all return expression respect the following condition and collect them.
94
102
let mut suggs = Vec :: new ( ) ;
95
103
let can_sugg = find_all_ret_expressions ( cx, & body. value , |ret_expr| {
96
104
if_chain ! {
97
105
if !in_macro( ret_expr. span) ;
106
+ // Check if a function call.
98
107
if let ExprKind :: Call ( ref func, ref args) = ret_expr. kind;
108
+ // Get the Path of the function call.
99
109
if let ExprKind :: Path ( ref qpath) = func. kind;
110
+ // Check if OPTION_SOME or RESULT_OK, depending on return type.
100
111
if match_qpath( qpath, path) ;
101
112
if args. len( ) == 1 ;
113
+ // Make sure the function argument does not contain a return expression.
102
114
if !contains_return( & args[ 0 ] ) ;
103
115
then {
104
- suggs. push( ( ret_expr. span, snippet( cx, args[ 0 ] . span. source_callsite( ) , ".." ) . to_string( ) ) ) ;
116
+ suggs. push(
117
+ (
118
+ ret_expr. span,
119
+ if inner_type. is_unit( ) {
120
+ "" . to_string( )
121
+ } else {
122
+ snippet( cx, args[ 0 ] . span. source_callsite( ) , ".." ) . to_string( )
123
+ }
124
+ )
125
+ ) ;
105
126
true
106
127
} else {
107
128
false
@@ -110,39 +131,34 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
110
131
} ) ;
111
132
112
133
if can_sugg && !suggs. is_empty ( ) {
113
- span_lint_and_then (
114
- cx,
115
- UNNECESSARY_WRAPS ,
116
- span,
117
- format ! (
118
- "this function's return value is unnecessarily wrapped by `{}`" ,
119
- return_type
134
+ let ( lint_msg, return_type_sugg_msg, return_type_sugg, body_sugg_msg) = if inner_type. is_unit ( ) {
135
+ (
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" ,
120
140
)
121
- . as_str ( ) ,
122
- |diag| {
123
- let inner_ty = return_ty ( cx, hir_id)
124
- . walk ( )
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 ( ) ) ,
129
- _ => None ,
130
- } ) ;
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 ( ) ,
135
- inner_ty,
136
- Applicability :: MaybeIncorrect ,
137
- ) ;
138
- } ) ;
139
- diag. multipart_suggestion (
140
- "...and change the returning expressions" ,
141
- suggs,
142
- Applicability :: MaybeIncorrect ,
143
- ) ;
144
- } ,
145
- ) ;
141
+ } else {
142
+ (
143
+ format ! (
144
+ "this function's return value is unnecessarily wrapped by `{}`" ,
145
+ return_type_label
146
+ ) ,
147
+ format ! ( "remove `{}` from the return type..." , return_type_label) ,
148
+ inner_type. to_string ( ) ,
149
+ "...and then change returning expressions" ,
150
+ )
151
+ } ;
152
+
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 ( ) ,
157
+ return_type_sugg,
158
+ Applicability :: MaybeIncorrect ,
159
+ ) ;
160
+ diag. multipart_suggestion ( body_sugg_msg, suggs, Applicability :: MaybeIncorrect ) ;
161
+ } ) ;
146
162
}
147
163
}
148
164
}
0 commit comments