1
1
use clippy_config:: Conf ;
2
2
use clippy_utils:: diagnostics:: { span_lint_and_sugg, span_lint_and_then} ;
3
- use clippy_utils:: source:: {
4
- HasSession , IntoSpan as _, SpanRangeExt , snippet, snippet_block, snippet_block_with_applicability,
5
- } ;
6
- use clippy_utils:: span_contains_comment;
7
- use rustc_ast:: ast;
3
+ use clippy_utils:: source:: { IntoSpan as _, SpanRangeExt , snippet, snippet_block, snippet_block_with_applicability} ;
4
+ use rustc_ast:: BinOpKind ;
8
5
use rustc_errors:: Applicability ;
9
- use rustc_lint:: { EarlyContext , EarlyLintPass } ;
6
+ use rustc_hir:: { Block , Expr , ExprKind , StmtKind } ;
7
+ use rustc_lint:: { LateContext , LateLintPass } ;
10
8
use rustc_session:: impl_lint_pass;
11
9
use rustc_span:: Span ;
12
10
@@ -89,17 +87,16 @@ impl CollapsibleIf {
89
87
}
90
88
}
91
89
92
- fn check_collapsible_else_if ( cx : & EarlyContext < ' _ > , then_span : Span , else_ : & ast:: Expr ) {
93
- if let ast:: ExprKind :: Block ( ref block, _) = else_. kind
94
- && !block_starts_with_comment ( cx, block)
95
- && let Some ( else_) = expr_block ( block)
96
- && else_. attrs . is_empty ( )
90
+ fn check_collapsible_else_if ( cx : & LateContext < ' _ > , then_span : Span , else_block : & Block < ' _ > ) {
91
+ if !block_starts_with_comment ( cx, else_block)
92
+ && let Some ( else_) = expr_block ( else_block)
93
+ && cx. tcx . hir_attrs ( else_. hir_id ) . is_empty ( )
97
94
&& !else_. span . from_expansion ( )
98
- && let ast :: ExprKind :: If ( ..) = else_. kind
95
+ && let ExprKind :: If ( ..) = else_. kind
99
96
{
100
97
// Prevent "elseif"
101
98
// Check that the "else" is followed by whitespace
102
- let up_to_else = then_span. between ( block . span ) ;
99
+ let up_to_else = then_span. between ( else_block . span ) ;
103
100
let requires_space = if let Some ( c) = snippet ( cx, up_to_else, ".." ) . chars ( ) . last ( ) {
104
101
!c. is_whitespace ( )
105
102
} else {
@@ -110,29 +107,28 @@ impl CollapsibleIf {
110
107
span_lint_and_sugg (
111
108
cx,
112
109
COLLAPSIBLE_ELSE_IF ,
113
- block . span ,
110
+ else_block . span ,
114
111
"this `else { if .. }` block can be collapsed" ,
115
112
"collapse nested if block" ,
116
113
format ! (
117
114
"{}{}" ,
118
115
if requires_space { " " } else { "" } ,
119
- snippet_block_with_applicability( cx, else_. span, ".." , Some ( block . span) , & mut applicability)
116
+ snippet_block_with_applicability( cx, else_. span, ".." , Some ( else_block . span) , & mut applicability)
120
117
) ,
121
118
applicability,
122
119
) ;
123
120
}
124
121
}
125
122
126
- fn check_collapsible_if_if ( & self , cx : & EarlyContext < ' _ > , expr : & ast :: Expr , check : & ast :: Expr , then : & ast :: Block ) {
123
+ fn check_collapsible_if_if ( & self , cx : & LateContext < ' _ > , expr : & Expr < ' _ > , check : & Expr < ' _ > , then : & Block < ' _ > ) {
127
124
if let Some ( inner) = expr_block ( then)
128
- && inner. attrs . is_empty ( )
129
- && let ast :: ExprKind :: If ( check_inner, _, None ) = & inner. kind
125
+ && cx . tcx . hir_attrs ( inner. hir_id ) . is_empty ( )
126
+ && let ExprKind :: If ( check_inner, _, None ) = & inner. kind
130
127
// Prevent triggering on `if c { if let a = b { .. } }`.
131
- && !matches ! ( check_inner. kind, ast :: ExprKind :: Let ( ..) )
128
+ && !matches ! ( check_inner. kind, ExprKind :: Let ( ..) )
132
129
&& let ctxt = expr. span . ctxt ( )
133
130
&& inner. span . ctxt ( ) == ctxt
134
- && let contains_comment = span_contains_comment ( cx. sess ( ) . source_map ( ) , check. span . to ( check_inner. span ) )
135
- && ( !contains_comment || self . lint_commented_code )
131
+ && ( self . lint_commented_code || !block_starts_with_comment ( cx, then) )
136
132
{
137
133
span_lint_and_then (
138
134
cx,
@@ -168,44 +164,54 @@ impl CollapsibleIf {
168
164
169
165
impl_lint_pass ! ( CollapsibleIf => [ COLLAPSIBLE_IF , COLLAPSIBLE_ELSE_IF ] ) ;
170
166
171
- impl EarlyLintPass for CollapsibleIf {
172
- fn check_expr ( & mut self , cx : & EarlyContext < ' _ > , expr : & ast :: Expr ) {
173
- if let ast :: ExprKind :: If ( cond, then, else_) = & expr. kind
167
+ impl LateLintPass < ' _ > for CollapsibleIf {
168
+ fn check_expr ( & mut self , cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
169
+ if let ExprKind :: If ( cond, then, else_) = & expr. kind
174
170
&& !expr. span . from_expansion ( )
175
171
{
176
- if let Some ( else_) = else_ {
172
+ if let Some ( else_) = else_
173
+ && let ExprKind :: Block ( else_, None ) = else_. kind
174
+ {
177
175
Self :: check_collapsible_else_if ( cx, then. span , else_) ;
178
- } else if !matches ! ( cond. kind, ast:: ExprKind :: Let ( ..) ) {
176
+ } else if else_. is_none ( )
177
+ && !matches ! ( cond. kind, ExprKind :: Let ( ..) )
178
+ && let ExprKind :: Block ( then, None ) = then. kind
179
+ {
179
180
// Prevent triggering on `if c { if let a = b { .. } }`.
180
- self . check_collapsible_if_if ( cx, expr, cond, then) ;
181
+ self . check_collapsible_if_if ( cx, expr, cond, block ! ( then) ) ;
181
182
}
182
183
}
183
184
}
184
185
}
185
186
186
- fn block_starts_with_comment ( cx : & EarlyContext < ' _ > , expr : & ast :: Block ) -> bool {
187
+ fn block_starts_with_comment ( cx : & LateContext < ' _ > , block : & Block < ' _ > ) -> bool {
187
188
// We trim all opening braces and whitespaces and then check if the next string is a comment.
188
- let trimmed_block_text = snippet_block ( cx, expr . span , ".." , None )
189
+ let trimmed_block_text = snippet_block ( cx, block . span , ".." , None )
189
190
. trim_start_matches ( |c : char | c. is_whitespace ( ) || c == '{' )
190
191
. to_owned ( ) ;
191
192
trimmed_block_text. starts_with ( "//" ) || trimmed_block_text. starts_with ( "/*" )
192
193
}
193
194
194
- /// If the block contains only one expression, return it.
195
- fn expr_block ( block : & ast:: Block ) -> Option < & ast:: Expr > {
196
- if let [ stmt] = & * block. stmts
197
- && let ast:: StmtKind :: Expr ( expr) | ast:: StmtKind :: Semi ( expr) = & stmt. kind
198
- {
199
- Some ( expr)
200
- } else {
201
- None
195
+ /// If `block` is a block with either one expression or a statement containing an expression,
196
+ /// return the expression. We don't peel blocks recursively, as extra blocks might be intentional.
197
+ fn expr_block < ' tcx > ( block : & Block < ' tcx > ) -> Option < & ' tcx Expr < ' tcx > > {
198
+ match block. stmts {
199
+ [ ] => block. expr ,
200
+ [ stmt] => {
201
+ if let StmtKind :: Semi ( expr) = stmt. kind {
202
+ Some ( expr)
203
+ } else {
204
+ None
205
+ }
206
+ } ,
207
+ _ => None ,
202
208
}
203
209
}
204
210
205
211
/// If the expression is a `||`, suggest parentheses around it.
206
- fn parens_around ( expr : & ast :: Expr ) -> Vec < ( Span , String ) > {
207
- if let ast :: ExprKind :: Binary ( op, _, _) = expr. kind
208
- && op. node == ast :: BinOpKind :: Or
212
+ fn parens_around ( expr : & Expr < ' _ > ) -> Vec < ( Span , String ) > {
213
+ if let ExprKind :: Binary ( op, _, _) = expr. peel_drop_temps ( ) . kind
214
+ && op. node == BinOpKind :: Or
209
215
{
210
216
vec ! [
211
217
( expr. span. shrink_to_lo( ) , String :: from( "(" ) ) ,
0 commit comments