1
- use crate :: utils:: { in_macro_or_desugar, is_expn_of, snippet_opt, span_lint_and_then} ;
2
- use rustc:: hir:: { intravisit:: FnKind , Body , ExprKind , FnDecl , HirId , MatchSource } ;
3
- use rustc:: lint:: { LateContext , LateLintPass , LintArray , LintPass } ;
4
- use rustc:: { declare_lint_pass, declare_tool_lint} ;
1
+ use crate :: utils:: {
2
+ in_macro_or_desugar, match_def_path,
3
+ paths:: { BEGIN_PANIC , BEGIN_PANIC_FMT } ,
4
+ resolve_node, snippet_opt, span_lint_and_then,
5
+ } ;
6
+ use if_chain:: if_chain;
7
+ use rustc:: {
8
+ declare_lint_pass, declare_tool_lint,
9
+ hir:: { intravisit:: FnKind , Body , Expr , ExprKind , FnDecl , HirId , MatchSource , StmtKind } ,
10
+ lint:: { LateContext , LateLintPass , LintArray , LintPass } ,
11
+ } ;
5
12
use rustc_errors:: Applicability ;
6
- use syntax :: source_map :: Span ;
13
+ use syntax_pos :: Span ;
7
14
8
15
declare_clippy_lint ! {
9
16
/// **What it does:** Checks for missing return statements at the end of a block.
@@ -35,71 +42,98 @@ declare_clippy_lint! {
35
42
36
43
declare_lint_pass ! ( ImplicitReturn => [ IMPLICIT_RETURN ] ) ;
37
44
38
- impl ImplicitReturn {
39
- fn lint ( cx : & LateContext < ' _ , ' _ > , outer_span : syntax_pos:: Span , inner_span : syntax_pos:: Span , msg : & str ) {
40
- span_lint_and_then ( cx, IMPLICIT_RETURN , outer_span, "missing return statement" , |db| {
41
- if let Some ( snippet) = snippet_opt ( cx, inner_span) {
42
- db. span_suggestion (
43
- outer_span,
44
- msg,
45
- format ! ( "return {}" , snippet) ,
46
- Applicability :: MachineApplicable ,
47
- ) ;
48
- }
49
- } ) ;
45
+ fn lint ( cx : & LateContext < ' _ , ' _ > , outer_span : Span , inner_span : Span , msg : & str ) {
46
+ let outer_span = span_to_outer_expn ( outer_span) ;
47
+ let inner_span = span_to_outer_expn ( inner_span) ;
48
+
49
+ span_lint_and_then ( cx, IMPLICIT_RETURN , outer_span, "missing return statement" , |db| {
50
+ if let Some ( snippet) = snippet_opt ( cx, inner_span) {
51
+ db. span_suggestion (
52
+ outer_span,
53
+ msg,
54
+ format ! ( "return {}" , snippet) ,
55
+ Applicability :: MachineApplicable ,
56
+ ) ;
57
+ }
58
+ } ) ;
59
+ }
60
+
61
+ fn span_to_outer_expn ( span : Span ) -> Span {
62
+ if let Some ( expr) = span. ctxt ( ) . outer_expn_info ( ) {
63
+ span_to_outer_expn ( expr. call_site )
64
+ } else {
65
+ span
50
66
}
67
+ }
51
68
52
- fn expr_match ( cx : & LateContext < ' _ , ' _ > , expr : & rustc :: hir :: Expr ) {
53
- match & expr. node {
54
- // loops could be using `break` instead of `return`
55
- ExprKind :: Block ( block, ..) | ExprKind :: Loop ( block, ..) => {
56
- if let Some ( expr) = & block. expr {
57
- Self :: expr_match ( cx, expr) ;
58
- }
59
- // only needed in the case of `break` with `;` at the end
60
- else if let Some ( stmt) = block. stmts . last ( ) {
61
- if let rustc :: hir :: StmtKind :: Semi ( expr , .. ) = & stmt . node {
62
- // make sure it's a break, otherwise we want to skip
63
- if let ExprKind :: Break ( .. , break_expr ) = & expr . node {
64
- if let Some ( break_expr) = break_expr {
65
- Self :: lint ( cx , expr . span , break_expr. span , "change `break` to `return` as shown" ) ;
66
- }
67
- }
69
+ fn expr_match ( cx : & LateContext < ' _ , ' _ > , expr : & Expr , ret_ty_is_never : bool ) {
70
+ match & expr. node {
71
+ // loops could be using `break` instead of `return`
72
+ ExprKind :: Block ( block, ..) | ExprKind :: Loop ( block, ..) => {
73
+ if let Some ( expr) = & block. expr {
74
+ expr_match ( cx, expr, ret_ty_is_never ) ;
75
+ }
76
+ // only needed in the case of `break` with `;` at the end
77
+ else if let Some ( stmt) = block. stmts . last ( ) {
78
+ if_chain ! {
79
+ if let StmtKind :: Semi ( expr , .. ) = & stmt . node ;
80
+ // make sure it's a break, otherwise we want to skip
81
+ if let ExprKind :: Break ( .. , break_expr) = & expr . node ;
82
+ if let Some ( break_expr) = break_expr ;
83
+ then {
84
+ lint ( cx , expr . span , break_expr . span , "change `break` to `return` as shown" ) ;
68
85
}
69
86
}
70
- } ,
71
- // use `return` instead of `break`
72
- ExprKind :: Break ( .., break_expr) => {
73
- if let Some ( break_expr) = break_expr {
74
- Self :: lint ( cx, expr. span , break_expr. span , "change `break` to `return` as shown" ) ;
75
- }
76
- } ,
77
- ExprKind :: Match ( .., arms, source) => {
78
- let check_all_arms = match source {
79
- MatchSource :: IfLetDesugar {
80
- contains_else_clause : has_else,
81
- } => * has_else,
82
- _ => true ,
83
- } ;
87
+ }
88
+ } ,
89
+ // use `return` instead of `break`
90
+ ExprKind :: Break ( .., break_expr) => {
91
+ if let Some ( break_expr) = break_expr {
92
+ lint ( cx, expr. span , break_expr. span , "change `break` to `return` as shown" ) ;
93
+ }
94
+ } ,
95
+ ExprKind :: Match ( .., arms, source) => {
96
+ let check_all_arms = match source {
97
+ MatchSource :: IfLetDesugar {
98
+ contains_else_clause : has_else,
99
+ } => * has_else,
100
+ _ => true ,
101
+ } ;
84
102
85
- if check_all_arms {
86
- for arm in arms {
87
- Self :: expr_match ( cx, & arm. body ) ;
103
+ if check_all_arms {
104
+ for arm in arms {
105
+ expr_match ( cx, & arm. body , ret_ty_is_never) ;
106
+ }
107
+ } else {
108
+ expr_match (
109
+ cx,
110
+ & arms. first ( ) . expect ( "if let doesn't have a single arm" ) . body ,
111
+ ret_ty_is_never,
112
+ ) ;
113
+ }
114
+ } ,
115
+ // skip if it already has a return statement
116
+ ExprKind :: Ret ( ..) => ( ) ,
117
+ // make sure it's not a call that panics unless we intend to return a panic
118
+ ExprKind :: Call ( expr, ..) => {
119
+ if_chain ! {
120
+ if let ExprKind :: Path ( qpath) = & expr. node;
121
+ if let Some ( path_def_id) = resolve_node( cx, qpath, expr. hir_id) . opt_def_id( ) ;
122
+ if match_def_path( cx, path_def_id, & BEGIN_PANIC ) ||
123
+ match_def_path( cx, path_def_id, & BEGIN_PANIC_FMT ) ;
124
+ then {
125
+ // only put `return` on panics if the return type of the function/closure is a panic
126
+ if ret_ty_is_never {
127
+ lint( cx, expr. span, expr. span, "add `return` as shown" )
88
128
}
89
- } else {
90
- Self :: expr_match ( cx, & arms. first ( ) . expect ( "if let doesn't have a single arm" ) . body ) ;
91
129
}
92
- } ,
93
- // skip if it already has a return statement
94
- ExprKind :: Ret ( ..) => ( ) ,
95
- // everything else is missing `return`
96
- _ => {
97
- // make sure it's not just an unreachable expression
98
- if is_expn_of ( expr. span , "unreachable" ) . is_none ( ) {
99
- Self :: lint ( cx, expr. span , expr. span , "add `return` as shown" )
130
+ else {
131
+ lint( cx, expr. span, expr. span, "add `return` as shown" )
100
132
}
101
- } ,
102
- }
133
+ }
134
+ } ,
135
+ // everything else is missing `return`
136
+ _ => lint ( cx, expr. span , expr. span , "add `return` as shown" ) ,
103
137
}
104
138
}
105
139
@@ -110,16 +144,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitReturn {
110
144
_: FnKind < ' tcx > ,
111
145
_: & ' tcx FnDecl ,
112
146
body : & ' tcx Body ,
113
- span : Span ,
147
+ span : syntax :: source_map :: Span ,
114
148
_: HirId ,
115
149
) {
116
150
let def_id = cx. tcx . hir ( ) . body_owner_def_id ( body. id ( ) ) ;
117
151
let mir = cx. tcx . optimized_mir ( def_id) ;
152
+ let ret_ty = mir. return_ty ( ) ;
118
153
119
154
// checking return type through MIR, HIR is not able to determine inferred closure return types
120
155
// make sure it's not a macro
121
- if !mir . return_ty ( ) . is_unit ( ) && !in_macro_or_desugar ( span) {
122
- Self :: expr_match ( cx, & body. value ) ;
156
+ if !ret_ty . is_unit ( ) && !in_macro_or_desugar ( span) {
157
+ expr_match ( cx, & body. value , ret_ty . is_never ( ) ) ;
123
158
}
124
159
}
125
160
}
0 commit comments