Skip to content

Commit 3f74c96

Browse files
authored
Get the block content from the proper context (#15014)
The first statement of the block might have been in a different context from the expression. Walk up to the right context to get bounds properly. Also, switch to `snippet_with_applicability()` since we know that we are in the right context already. changelog: [`if_then_some_else_none`]: emit well-formed suggestion, and do not lint inside macros Fixes #15005
2 parents 034136d + bd33a02 commit 3f74c96

File tree

4 files changed

+133
-16
lines changed

4 files changed

+133
-16
lines changed

clippy_lints/src/if_then_some_else_none.rs

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@ use clippy_config::Conf;
22
use clippy_utils::diagnostics::span_lint_and_then;
33
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
44
use clippy_utils::msrvs::{self, Msrv};
5-
use clippy_utils::source::snippet_with_context;
5+
use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context};
66
use clippy_utils::sugg::Sugg;
77
use clippy_utils::{
88
contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context, is_res_lang_ctor,
9-
path_res, peel_blocks,
9+
path_res, peel_blocks, sym,
1010
};
1111
use rustc_errors::Applicability;
1212
use rustc_hir::LangItem::{OptionNone, OptionSome};
1313
use rustc_hir::{Expr, ExprKind};
14-
use rustc_lint::{LateContext, LateLintPass, LintContext};
14+
use rustc_lint::{LateContext, LateLintPass};
1515
use rustc_session::impl_lint_pass;
1616

1717
declare_clippy_lint! {
@@ -71,21 +71,21 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
7171
&& let ExprKind::Block(then_block, _) = then.kind
7272
&& let Some(then_expr) = then_block.expr
7373
&& let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
74-
&& let ctxt = expr.span.ctxt()
75-
&& then_expr.span.ctxt() == ctxt
74+
&& !expr.span.from_expansion()
75+
&& !then_expr.span.from_expansion()
7676
&& is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome)
7777
&& is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone)
7878
&& !is_else_clause(cx.tcx, expr)
7979
&& !is_in_const_context(cx)
80-
&& !expr.span.in_external_macro(cx.sess().source_map())
8180
&& self.msrv.meets(cx, msrvs::BOOL_THEN)
8281
&& !contains_return(then_block.stmts)
8382
{
8483
let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(cx, msrvs::BOOL_THEN_SOME) {
85-
"then_some"
84+
sym::then_some
8685
} else {
87-
"then"
86+
sym::then
8887
};
88+
let ctxt = expr.span.ctxt();
8989

9090
span_lint_and_then(
9191
cx,
@@ -98,16 +98,18 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
9898
}
9999

100100
let mut app = Applicability::MachineApplicable;
101-
let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app)
101+
let cond_snip = Sugg::hir_with_context(cx, cond, ctxt, "[condition]", &mut app)
102102
.maybe_paren()
103103
.to_string();
104104
let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0;
105-
let method_body = if let Some(first_stmt) = then_block.stmts.first() {
106-
let (block_snippet, _) =
107-
snippet_with_context(cx, first_stmt.span.until(then_arg.span), ctxt, "..", &mut app);
108-
let closure = if method_name == "then" { "|| " } else { "" };
109-
format!("{closure} {{ {block_snippet}; {arg_snip} }}")
110-
} else if method_name == "then" {
105+
let method_body = if let Some(first_stmt) = then_block.stmts.first()
106+
&& let Some(first_stmt_span) = walk_span_to_context(first_stmt.span, ctxt)
107+
{
108+
let block_snippet =
109+
snippet_with_applicability(cx, first_stmt_span.until(then_expr.span), "..", &mut app);
110+
let closure = if method_name == sym::then { "|| " } else { "" };
111+
format!("{closure} {{ {} {arg_snip} }}", block_snippet.trim_end())
112+
} else if method_name == sym::then {
111113
(std::borrow::Cow::Borrowed("|| ") + arg_snip).into_owned()
112114
} else {
113115
arg_snip.into_owned()

tests/ui/if_then_some_else_none.fixed

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,44 @@ mod issue15257 {
165165
do_something((i % 2 == 0).then_some(closure_fn));
166166
}
167167
}
168+
169+
fn issue15005() {
170+
struct Counter {
171+
count: u32,
172+
}
173+
174+
impl Counter {
175+
fn new() -> Counter {
176+
Counter { count: 0 }
177+
}
178+
}
179+
180+
impl Iterator for Counter {
181+
type Item = u32;
182+
183+
fn next(&mut self) -> Option<Self::Item> {
184+
//~v if_then_some_else_none
185+
(self.count < 5).then(|| { self.count += 1; self.count })
186+
}
187+
}
188+
}
189+
190+
fn statements_from_macro() {
191+
macro_rules! mac {
192+
() => {
193+
println!("foo");
194+
println!("bar");
195+
};
196+
}
197+
//~v if_then_some_else_none
198+
let _ = true.then(|| { mac!(); 42 });
199+
}
200+
201+
fn dont_lint_inside_macros() {
202+
macro_rules! mac {
203+
($cond:expr, $res:expr) => {
204+
if $cond { Some($res) } else { None }
205+
};
206+
}
207+
let _: Option<u32> = mac!(true, 42);
208+
}

tests/ui/if_then_some_else_none.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,3 +211,54 @@ mod issue15257 {
211211
});
212212
}
213213
}
214+
215+
fn issue15005() {
216+
struct Counter {
217+
count: u32,
218+
}
219+
220+
impl Counter {
221+
fn new() -> Counter {
222+
Counter { count: 0 }
223+
}
224+
}
225+
226+
impl Iterator for Counter {
227+
type Item = u32;
228+
229+
fn next(&mut self) -> Option<Self::Item> {
230+
//~v if_then_some_else_none
231+
if self.count < 5 {
232+
self.count += 1;
233+
Some(self.count)
234+
} else {
235+
None
236+
}
237+
}
238+
}
239+
}
240+
241+
fn statements_from_macro() {
242+
macro_rules! mac {
243+
() => {
244+
println!("foo");
245+
println!("bar");
246+
};
247+
}
248+
//~v if_then_some_else_none
249+
let _ = if true {
250+
mac!();
251+
Some(42)
252+
} else {
253+
None
254+
};
255+
}
256+
257+
fn dont_lint_inside_macros() {
258+
macro_rules! mac {
259+
($cond:expr, $res:expr) => {
260+
if $cond { Some($res) } else { None }
261+
};
262+
}
263+
let _: Option<u32> = mac!(true, 42);
264+
}

tests/ui/if_then_some_else_none.stderr

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,5 +116,28 @@ LL | | None
116116
LL | | });
117117
| |_________^ help: try: `(i % 2 == 0).then_some(closure_fn)`
118118

119-
error: aborting due to 11 previous errors
119+
error: this could be simplified with `bool::then`
120+
--> tests/ui/if_then_some_else_none.rs:231:13
121+
|
122+
LL | / if self.count < 5 {
123+
LL | | self.count += 1;
124+
LL | | Some(self.count)
125+
LL | | } else {
126+
LL | | None
127+
LL | | }
128+
| |_____________^ help: try: `(self.count < 5).then(|| { self.count += 1; self.count })`
129+
130+
error: this could be simplified with `bool::then`
131+
--> tests/ui/if_then_some_else_none.rs:249:13
132+
|
133+
LL | let _ = if true {
134+
| _____________^
135+
LL | | mac!();
136+
LL | | Some(42)
137+
LL | | } else {
138+
LL | | None
139+
LL | | };
140+
| |_____^ help: try: `true.then(|| { mac!(); 42 })`
141+
142+
error: aborting due to 13 previous errors
120143

0 commit comments

Comments
 (0)