Skip to content

Commit 89ee6aa

Browse files
committed
Auto merge of #8667 - Jarcho:proc_macro_check, r=flip1995
Don't lint various match lints when expanded by a proc-macro fixes #4952 As always for proc-macro output this is a hack-job of a fix. It would be really nice if more proc-macro authors would set spans correctly. changelog: Don't lint various lints on proc-macro output.
2 parents 131ff87 + 63f6a79 commit 89ee6aa

File tree

5 files changed

+77
-8
lines changed

5 files changed

+77
-8
lines changed

clippy_lints/src/matches/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use clippy_utils::source::{snippet_opt, walk_span_to_context};
1+
use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context};
22
use clippy_utils::{meets_msrv, msrvs};
33
use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
44
use rustc_lexer::{tokenize, TokenKind};
@@ -653,6 +653,9 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
653653
}
654654

655655
if let ExprKind::Match(ex, arms, source) = expr.kind {
656+
if !span_starts_with(cx, expr.span, "match") {
657+
return;
658+
}
656659
if !contains_cfg_arm(cx, expr, ex, arms) {
657660
if source == MatchSource::Normal {
658661
if !(meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO)

clippy_utils/src/source.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,28 @@ use rustc_errors::Applicability;
77
use rustc_hir::{Expr, ExprKind};
88
use rustc_lint::{LateContext, LintContext};
99
use rustc_span::hygiene;
10+
use rustc_span::source_map::SourceMap;
1011
use rustc_span::{BytePos, Pos, Span, SyntaxContext};
1112
use std::borrow::Cow;
1213

14+
/// Checks if the span starts with the given text. This will return false if the span crosses
15+
/// multiple files or if source is not available.
16+
///
17+
/// This is used to check for proc macros giving unhelpful spans to things.
18+
pub fn span_starts_with<T: LintContext>(cx: &T, span: Span, text: &str) -> bool {
19+
fn helper(sm: &SourceMap, span: Span, text: &str) -> bool {
20+
let pos = sm.lookup_byte_offset(span.lo());
21+
let Some(ref src) = pos.sf.src else {
22+
return false;
23+
};
24+
let end = span.hi() - pos.sf.start_pos;
25+
src.get(pos.pos.0 as usize..end.0 as usize)
26+
// Expression spans can include wrapping parenthesis. Remove them first.
27+
.map_or(false, |s| s.trim_start_matches('(').starts_with(text))
28+
}
29+
helper(cx.sess().source_map(), span, text)
30+
}
31+
1332
/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
1433
/// Also takes an `Option<String>` which can be put inside the braces.
1534
pub fn expr_block<'a, T: LintContext>(
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// compile-flags: --emit=link
2+
// no-prefer-dynamic
3+
4+
#![crate_type = "proc-macro"]
5+
6+
extern crate proc_macro;
7+
8+
use proc_macro::{token_stream::IntoIter, Group, Span, TokenStream, TokenTree};
9+
10+
#[proc_macro]
11+
pub fn with_span(input: TokenStream) -> TokenStream {
12+
let mut iter = input.into_iter();
13+
let span = iter.next().unwrap().span();
14+
let mut res = TokenStream::new();
15+
write_with_span(span, iter, &mut res);
16+
res
17+
}
18+
19+
fn write_with_span(s: Span, input: IntoIter, out: &mut TokenStream) {
20+
for mut tt in input {
21+
if let TokenTree::Group(g) = tt {
22+
let mut stream = TokenStream::new();
23+
write_with_span(s, g.stream().into_iter(), &mut stream);
24+
let mut group = Group::new(g.delimiter(), stream);
25+
group.set_span(s);
26+
out.extend([TokenTree::Group(group)]);
27+
} else {
28+
tt.set_span(s);
29+
out.extend([tt]);
30+
}
31+
}
32+
}

tests/ui/single_match_else.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
// aux-build: proc_macro_with_span.rs
2+
13
#![warn(clippy::single_match_else)]
24
#![allow(clippy::needless_return)]
35
#![allow(clippy::no_effect)]
46

7+
extern crate proc_macro_with_span;
8+
use proc_macro_with_span::with_span;
9+
510
enum ExprNode {
611
ExprAddrOf,
712
Butterflies,
@@ -11,13 +16,22 @@ enum ExprNode {
1116
static NODE: ExprNode = ExprNode::Unicorns;
1217

1318
fn unwrap_addr() -> Option<&'static ExprNode> {
14-
match ExprNode::Butterflies {
19+
let _ = match ExprNode::Butterflies {
1520
ExprNode::ExprAddrOf => Some(&NODE),
1621
_ => {
1722
let x = 5;
1823
None
1924
},
20-
}
25+
};
26+
27+
// Don't lint
28+
with_span!(span match ExprNode::Butterflies {
29+
ExprNode::ExprAddrOf => Some(&NODE),
30+
_ => {
31+
let x = 5;
32+
None
33+
},
34+
})
2135
}
2236

2337
macro_rules! unwrap_addr {

tests/ui/single_match_else.stderr

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
2-
--> $DIR/single_match_else.rs:14:5
2+
--> $DIR/single_match_else.rs:19:13
33
|
4-
LL | / match ExprNode::Butterflies {
4+
LL | let _ = match ExprNode::Butterflies {
5+
| _____________^
56
LL | | ExprNode::ExprAddrOf => Some(&NODE),
67
LL | | _ => {
78
LL | | let x = 5;
89
LL | | None
910
LL | | },
10-
LL | | }
11+
LL | | };
1112
| |_____^
1213
|
1314
= note: `-D clippy::single-match-else` implied by `-D warnings`
1415
help: try this
1516
|
16-
LL ~ if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else {
17+
LL ~ let _ = if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else {
1718
LL + let x = 5;
1819
LL + None
19-
LL + }
20+
LL ~ };
2021
|
2122

2223
error: aborting due to previous error

0 commit comments

Comments
 (0)