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+ } ;
512use rustc_errors:: Applicability ;
6- use syntax :: source_map :: Span ;
13+ use syntax_pos :: Span ;
714
815declare_clippy_lint ! {
916 /// **What it does:** Checks for missing return statements at the end of a block.
@@ -35,71 +42,98 @@ declare_clippy_lint! {
3542
3643declare_lint_pass ! ( ImplicitReturn => [ IMPLICIT_RETURN ] ) ;
3744
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
5066 }
67+ }
5168
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" ) ;
6885 }
6986 }
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+ } ;
84102
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" )
88128 }
89- } else {
90- Self :: expr_match ( cx, & arms. first ( ) . expect ( "if let doesn't have a single arm" ) . body ) ;
91129 }
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" )
100132 }
101- } ,
102- }
133+ }
134+ } ,
135+ // everything else is missing `return`
136+ _ => lint ( cx, expr. span , expr. span , "add `return` as shown" ) ,
103137 }
104138}
105139
@@ -110,16 +144,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitReturn {
110144 _: FnKind < ' tcx > ,
111145 _: & ' tcx FnDecl ,
112146 body : & ' tcx Body ,
113- span : Span ,
147+ span : syntax :: source_map :: Span ,
114148 _: HirId ,
115149 ) {
116150 let def_id = cx. tcx . hir ( ) . body_owner_def_id ( body. id ( ) ) ;
117151 let mir = cx. tcx . optimized_mir ( def_id) ;
152+ let ret_ty = mir. return_ty ( ) ;
118153
119154 // checking return type through MIR, HIR is not able to determine inferred closure return types
120155 // 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 ( ) ) ;
123158 }
124159 }
125160}
0 commit comments