Skip to content

Commit 9df5177

Browse files
committed
Convert the collapsible_if early lint to a late one
1 parent fe4dd5b commit 9df5177

File tree

2 files changed

+47
-41
lines changed

2 files changed

+47
-41
lines changed

Diff for: clippy_lints/src/collapsible_if.rs

+46-40
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
use clippy_config::Conf;
22
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;
85
use rustc_errors::Applicability;
9-
use rustc_lint::{EarlyContext, EarlyLintPass};
6+
use rustc_hir::{Block, Expr, ExprKind, StmtKind};
7+
use rustc_lint::{LateContext, LateLintPass};
108
use rustc_session::impl_lint_pass;
119
use rustc_span::Span;
1210

@@ -89,17 +87,16 @@ impl CollapsibleIf {
8987
}
9088
}
9189

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()
9794
&& !else_.span.from_expansion()
98-
&& let ast::ExprKind::If(..) = else_.kind
95+
&& let ExprKind::If(..) = else_.kind
9996
{
10097
// Prevent "elseif"
10198
// 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);
103100
let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() {
104101
!c.is_whitespace()
105102
} else {
@@ -110,29 +107,28 @@ impl CollapsibleIf {
110107
span_lint_and_sugg(
111108
cx,
112109
COLLAPSIBLE_ELSE_IF,
113-
block.span,
110+
else_block.span,
114111
"this `else { if .. }` block can be collapsed",
115112
"collapse nested if block",
116113
format!(
117114
"{}{}",
118115
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)
120117
),
121118
applicability,
122119
);
123120
}
124121
}
125122

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<'_>) {
127124
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
130127
// Prevent triggering on `if c { if let a = b { .. } }`.
131-
&& !matches!(check_inner.kind, ast::ExprKind::Let(..))
128+
&& !matches!(check_inner.kind, ExprKind::Let(..))
132129
&& let ctxt = expr.span.ctxt()
133130
&& 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))
136132
{
137133
span_lint_and_then(
138134
cx,
@@ -168,44 +164,54 @@ impl CollapsibleIf {
168164

169165
impl_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]);
170166

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
174170
&& !expr.span.from_expansion()
175171
{
176-
if let Some(else_) = else_ {
172+
if let Some(else_) = else_
173+
&& let ExprKind::Block(else_, None) = else_.kind
174+
{
177175
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+
{
179180
// 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));
181182
}
182183
}
183184
}
184185
}
185186

186-
fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool {
187+
fn block_starts_with_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool {
187188
// 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)
189190
.trim_start_matches(|c: char| c.is_whitespace() || c == '{')
190191
.to_owned();
191192
trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*")
192193
}
193194

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,
202208
}
203209
}
204210

205211
/// 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
209215
{
210216
vec![
211217
(expr.span.shrink_to_lo(), String::from("(")),

Diff for: clippy_lints/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -771,7 +771,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
771771
store.register_late_pass(|_| Box::new(redundant_closure_call::RedundantClosureCall));
772772
store.register_early_pass(|| Box::new(unused_unit::UnusedUnit));
773773
store.register_late_pass(|_| Box::new(returns::Return));
774-
store.register_early_pass(move || Box::new(collapsible_if::CollapsibleIf::new(conf)));
774+
store.register_late_pass(move |_| Box::new(collapsible_if::CollapsibleIf::new(conf)));
775775
store.register_late_pass(|_| Box::new(items_after_statements::ItemsAfterStatements));
776776
store.register_early_pass(|| Box::new(precedence::Precedence));
777777
store.register_late_pass(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals));

0 commit comments

Comments
 (0)