11use 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,
33 visitors:: find_all_ret_expressions,
44} ;
55use if_chain:: if_chain;
66use rustc_errors:: Applicability ;
77use rustc_hir:: intravisit:: FnKind ;
88use rustc_hir:: { Body , ExprKind , FnDecl , HirId , Impl , ItemKind , Node } ;
99use rustc_lint:: { LateContext , LateLintPass } ;
10- use rustc_middle:: ty:: subst :: GenericArgKind ;
10+ use rustc_middle:: ty;
1111use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
1212use rustc_span:: symbol:: sym;
1313use rustc_span:: Span ;
@@ -64,6 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
6464 span : Span ,
6565 hir_id : HirId ,
6666 ) {
67+ // Abort if public function/method or closure.
6768 match fn_kind {
6869 FnKind :: ItemFn ( .., visibility, _) | FnKind :: Method ( .., Some ( visibility) , _) => {
6970 if visibility. node . is_pub ( ) {
@@ -74,6 +75,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
7475 _ => ( ) ,
7576 }
7677
78+ // Abort if the method is implementing a trait or of it a trait method.
7779 if let Some ( Node :: Item ( item) ) = cx. tcx . hir ( ) . find ( cx. tcx . hir ( ) . get_parent_node ( hir_id) ) {
7880 if matches ! (
7981 item. kind,
@@ -83,25 +85,44 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
8385 }
8486 }
8587
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+ }
9097 } else {
9198 return ;
9299 } ;
93100
101+ // Check if all return expression respect the following condition and collect them.
94102 let mut suggs = Vec :: new ( ) ;
95103 let can_sugg = find_all_ret_expressions ( cx, & body. value , |ret_expr| {
96104 if_chain ! {
97105 if !in_macro( ret_expr. span) ;
106+ // Check if a function call.
98107 if let ExprKind :: Call ( ref func, ref args) = ret_expr. kind;
108+ // Get the Path of the function call.
99109 if let ExprKind :: Path ( ref qpath) = func. kind;
110+ // Check if OPTION_SOME or RESULT_OK, depending on return type.
100111 if match_qpath( qpath, path) ;
101112 if args. len( ) == 1 ;
113+ // Make sure the function argument does not contain a return expression.
102114 if !contains_return( & args[ 0 ] ) ;
103115 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+ ) ;
105126 true
106127 } else {
107128 false
@@ -110,39 +131,34 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
110131 } ) ;
111132
112133 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" ,
120140 )
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+ } ) ;
146162 }
147163 }
148164}
0 commit comments