Skip to content

Commit cd3f3cf

Browse files
committed
Auto merge of rust-lang#7707 - Jarcho:suspicious_else_proc_mac, r=Manishearth
Don't lint `suspicious_else_formatting` inside proc-macros fixes: rust-lang#7650 I'll add a test for this one soon. changelog: Don't lint `suspicious_else_formatting` inside proc-macros
2 parents ef2e2f0 + e69154f commit cd3f3cf

File tree

4 files changed

+124
-35
lines changed

4 files changed

+124
-35
lines changed

clippy_lints/src/formatting.rs

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -286,34 +286,39 @@ fn check_array(cx: &EarlyContext<'_>, expr: &Expr) {
286286
}
287287

288288
fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) {
289-
if !differing_macro_contexts(first.span, second.span)
290-
&& !first.span.from_expansion()
291-
&& is_if(first)
292-
&& (is_block(second) || is_if(second))
293-
{
294-
// where the else would be
295-
let else_span = first.span.between(second.span);
289+
if_chain! {
290+
if !differing_macro_contexts(first.span, second.span);
291+
if !first.span.from_expansion();
292+
if let ExprKind::If(cond_expr, ..) = &first.kind;
293+
if is_block(second) || is_if(second);
296294

297-
if let Some(else_snippet) = snippet_opt(cx, else_span) {
298-
if !else_snippet.contains('\n') {
299-
let (looks_like, next_thing) = if is_if(second) {
300-
("an `else if`", "the second `if`")
301-
} else {
302-
("an `else {..}`", "the next block")
303-
};
295+
// Proc-macros can give weird spans. Make sure this is actually an `if`.
296+
if let Some(if_snip) = snippet_opt(cx, first.span.until(cond_expr.span));
297+
if if_snip.starts_with("if");
304298

305-
span_lint_and_note(
306-
cx,
307-
SUSPICIOUS_ELSE_FORMATTING,
308-
else_span,
309-
&format!("this looks like {} but the `else` is missing", looks_like),
310-
None,
311-
&format!(
312-
"to remove this lint, add the missing `else` or add a new line before {}",
313-
next_thing,
314-
),
315-
);
316-
}
299+
// If there is a line break between the two expressions, don't lint.
300+
// If there is a non-whitespace character, this span came from a proc-macro.
301+
let else_span = first.span.between(second.span);
302+
if let Some(else_snippet) = snippet_opt(cx, else_span);
303+
if !else_snippet.chars().any(|c| c == '\n' || !c.is_whitespace());
304+
then {
305+
let (looks_like, next_thing) = if is_if(second) {
306+
("an `else if`", "the second `if`")
307+
} else {
308+
("an `else {..}`", "the next block")
309+
};
310+
311+
span_lint_and_note(
312+
cx,
313+
SUSPICIOUS_ELSE_FORMATTING,
314+
else_span,
315+
&format!("this looks like {} but the `else` is missing", looks_like),
316+
None,
317+
&format!(
318+
"to remove this lint, add the missing `else` or add a new line before {}",
319+
next_thing,
320+
),
321+
);
317322
}
318323
}
319324
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// compile-flags: --emit=link
2+
// no-prefer-dynamic
3+
4+
#![crate_type = "proc-macro"]
5+
6+
extern crate proc_macro;
7+
use proc_macro::{token_stream, Delimiter, Group, Ident, Span, TokenStream, TokenTree};
8+
use std::iter::FromIterator;
9+
10+
fn read_ident(iter: &mut token_stream::IntoIter) -> Ident {
11+
match iter.next() {
12+
Some(TokenTree::Ident(i)) => i,
13+
_ => panic!("expected ident"),
14+
}
15+
}
16+
17+
#[proc_macro_derive(DeriveBadSpan)]
18+
pub fn derive_bad_span(input: TokenStream) -> TokenStream {
19+
let mut input = input.into_iter();
20+
assert_eq!(read_ident(&mut input).to_string(), "struct");
21+
let ident = read_ident(&mut input);
22+
let mut tys = match input.next() {
23+
Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis => g.stream().into_iter(),
24+
_ => panic!(),
25+
};
26+
let field1 = read_ident(&mut tys);
27+
tys.next();
28+
let field2 = read_ident(&mut tys);
29+
30+
<TokenStream as FromIterator<TokenTree>>::from_iter(
31+
[
32+
Ident::new("impl", Span::call_site()).into(),
33+
ident.into(),
34+
Group::new(
35+
Delimiter::Brace,
36+
<TokenStream as FromIterator<TokenTree>>::from_iter(
37+
[
38+
Ident::new("fn", Span::call_site()).into(),
39+
Ident::new("_foo", Span::call_site()).into(),
40+
Group::new(Delimiter::Parenthesis, TokenStream::new()).into(),
41+
Group::new(
42+
Delimiter::Brace,
43+
<TokenStream as FromIterator<TokenTree>>::from_iter(
44+
[
45+
Ident::new("if", field1.span()).into(),
46+
Ident::new("true", field1.span()).into(),
47+
{
48+
let mut group = Group::new(Delimiter::Brace, TokenStream::new());
49+
group.set_span(field1.span());
50+
group.into()
51+
},
52+
Ident::new("if", field2.span()).into(),
53+
Ident::new("true", field2.span()).into(),
54+
{
55+
let mut group = Group::new(Delimiter::Brace, TokenStream::new());
56+
group.set_span(field2.span());
57+
group.into()
58+
},
59+
]
60+
.iter()
61+
.cloned(),
62+
),
63+
)
64+
.into(),
65+
]
66+
.iter()
67+
.cloned(),
68+
),
69+
)
70+
.into(),
71+
]
72+
.iter()
73+
.cloned(),
74+
)
75+
}

tests/ui/suspicious_else_formatting.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
// aux-build:proc_macro_suspicious_else_formatting.rs
2+
13
#![warn(clippy::suspicious_else_formatting)]
24

5+
extern crate proc_macro_suspicious_else_formatting;
6+
use proc_macro_suspicious_else_formatting::DeriveBadSpan;
7+
38
fn foo() -> bool {
49
true
510
}
@@ -103,3 +108,7 @@ fn main() {
103108
{
104109
}
105110
}
111+
112+
// #7650 - Don't lint. Proc-macro using bad spans for `if` expressions.
113+
#[derive(DeriveBadSpan)]
114+
struct _Foo(u32, u32);

tests/ui/suspicious_else_formatting.stderr

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: this looks like an `else {..}` but the `else` is missing
2-
--> $DIR/suspicious_else_formatting.rs:11:6
2+
--> $DIR/suspicious_else_formatting.rs:16:6
33
|
44
LL | } {
55
| ^
@@ -8,31 +8,31 @@ LL | } {
88
= note: to remove this lint, add the missing `else` or add a new line before the next block
99

1010
error: this looks like an `else if` but the `else` is missing
11-
--> $DIR/suspicious_else_formatting.rs:15:6
11+
--> $DIR/suspicious_else_formatting.rs:20:6
1212
|
1313
LL | } if foo() {
1414
| ^
1515
|
1616
= note: to remove this lint, add the missing `else` or add a new line before the second `if`
1717

1818
error: this looks like an `else if` but the `else` is missing
19-
--> $DIR/suspicious_else_formatting.rs:22:10
19+
--> $DIR/suspicious_else_formatting.rs:27:10
2020
|
2121
LL | } if foo() {
2222
| ^
2323
|
2424
= note: to remove this lint, add the missing `else` or add a new line before the second `if`
2525

2626
error: this looks like an `else if` but the `else` is missing
27-
--> $DIR/suspicious_else_formatting.rs:30:10
27+
--> $DIR/suspicious_else_formatting.rs:35:10
2828
|
2929
LL | } if foo() {
3030
| ^
3131
|
3232
= note: to remove this lint, add the missing `else` or add a new line before the second `if`
3333

3434
error: this is an `else {..}` but the formatting might hide it
35-
--> $DIR/suspicious_else_formatting.rs:39:6
35+
--> $DIR/suspicious_else_formatting.rs:44:6
3636
|
3737
LL | } else
3838
| ______^
@@ -42,7 +42,7 @@ LL | | {
4242
= note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
4343

4444
error: this is an `else if` but the formatting might hide it
45-
--> $DIR/suspicious_else_formatting.rs:51:6
45+
--> $DIR/suspicious_else_formatting.rs:56:6
4646
|
4747
LL | } else
4848
| ______^
@@ -52,7 +52,7 @@ LL | | if foo() { // the span of the above error should continue here
5252
= note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
5353

5454
error: this is an `else if` but the formatting might hide it
55-
--> $DIR/suspicious_else_formatting.rs:56:6
55+
--> $DIR/suspicious_else_formatting.rs:61:6
5656
|
5757
LL | }
5858
| ______^
@@ -63,7 +63,7 @@ LL | | if foo() { // the span of the above error should continue here
6363
= note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
6464

6565
error: this is an `else {..}` but the formatting might hide it
66-
--> $DIR/suspicious_else_formatting.rs:83:6
66+
--> $DIR/suspicious_else_formatting.rs:88:6
6767
|
6868
LL | }
6969
| ______^
@@ -75,7 +75,7 @@ LL | | {
7575
= note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
7676

7777
error: this is an `else {..}` but the formatting might hide it
78-
--> $DIR/suspicious_else_formatting.rs:91:6
78+
--> $DIR/suspicious_else_formatting.rs:96:6
7979
|
8080
LL | }
8181
| ______^

0 commit comments

Comments
 (0)