Skip to content

Commit 692a42e

Browse files
committed
Auto merge of rust-lang#17586 - ShoyuVanilla:tuple-arg-macro-rest, r=Veykril
Allow macro expansions into `RestPat` in tuple args work as ellipsis like plain `RestPat` Fixes rust-lang#17292 Currently, Rust Analyzer lowers `ast::Pat::RestPat` into `Pat::Missing` in general cases on the following lines; https://github.com/rust-lang/rust-analyzer/blob/ffbc5ad993d5cd2f3b8bcf9a511165470944ab91/crates/hir-def/src/body/lower.rs#L1359-L1367 And in some proper positions such as `TupleStruct(..)`, it is specially handed on the following lines; https://github.com/rust-lang/rust-analyzer/blob/ffbc5ad993d5cd2f3b8bcf9a511165470944ab91/crates/hir-def/src/body/lower.rs#L1429-L1437 This behavior is reasonable because rustc does similar things in https://github.com/rust-lang/rust/blob/62c068feeafd1f4abbf87243d69cf8862e4dd277/compiler/rustc_ast_lowering/src/pat.rs#L108-L111 and https://github.com/rust-lang/rust/blob/62c068feeafd1f4abbf87243d69cf8862e4dd277/compiler/rustc_ast_lowering/src/pat.rs#L123-L142 But this sometimes works differently because Rust Analyzer expands macros while ast lowering; https://github.com/rust-lang/rust-analyzer/blob/ffbc5ad993d5cd2f3b8bcf9a511165470944ab91/crates/hir-def/src/body/lower.rs#L1386-L1398 https://github.com/rust-lang/rust-analyzer/blob/ffbc5ad993d5cd2f3b8bcf9a511165470944ab91/crates/hir-def/src/body/lower.rs#L941-L963 but rustc uses expanded ast in the corresponding tuple-handling process, so it does not have macro patterns there. https://github.com/rust-lang/rust/blob/62c068feeafd1f4abbf87243d69cf8862e4dd277/compiler/rustc_ast_lowering/src/pat.rs#L114 So, if a macro expansion in a tuple arg results in `..`, rustc permits it like plain `..` pattern, but Rust Analyzer rejects it. This is the root cause of rust-lang#17292 and this PR allows macros expanded into `..` in a tuple arg position work as ellipsis like that.
2 parents 7e52128 + 5a292c9 commit 692a42e

File tree

2 files changed

+108
-5
lines changed

2 files changed

+108
-5
lines changed

src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use std::mem;
55

66
use base_db::CrateId;
7+
use either::Either;
78
use hir_expand::{
89
name::{AsName, Name},
910
ExpandError, InFile,
@@ -1437,14 +1438,12 @@ impl ExprCollector<'_> {
14371438
has_leading_comma: bool,
14381439
binding_list: &mut BindingList,
14391440
) -> (Box<[PatId]>, Option<usize>) {
1441+
let args: Vec<_> = args.map(|p| self.collect_pat_possibly_rest(p, binding_list)).collect();
14401442
// Find the location of the `..`, if there is one. Note that we do not
14411443
// consider the possibility of there being multiple `..` here.
1442-
let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_)));
1444+
let ellipsis = args.iter().position(|p| p.is_right());
14431445
// We want to skip the `..` pattern here, since we account for it above.
1444-
let mut args: Vec<_> = args
1445-
.filter(|p| !matches!(p, ast::Pat::RestPat(_)))
1446-
.map(|p| self.collect_pat(p, binding_list))
1447-
.collect();
1446+
let mut args: Vec<_> = args.into_iter().filter_map(Either::left).collect();
14481447
// if there is a leading comma, the user is most likely to type out a leading pattern
14491448
// so we insert a missing pattern at the beginning for IDE features
14501449
if has_leading_comma {
@@ -1454,6 +1453,41 @@ impl ExprCollector<'_> {
14541453
(args.into_boxed_slice(), ellipsis)
14551454
}
14561455

1456+
// `collect_pat` rejects `ast::Pat::RestPat`, but it should be handled in some cases that
1457+
// it is the macro expansion result of an arg sub-pattern in a slice or tuple pattern.
1458+
fn collect_pat_possibly_rest(
1459+
&mut self,
1460+
pat: ast::Pat,
1461+
binding_list: &mut BindingList,
1462+
) -> Either<PatId, ()> {
1463+
match &pat {
1464+
ast::Pat::RestPat(_) => Either::Right(()),
1465+
ast::Pat::MacroPat(mac) => match mac.macro_call() {
1466+
Some(call) => {
1467+
let macro_ptr = AstPtr::new(&call);
1468+
let src = self.expander.in_file(AstPtr::new(&pat));
1469+
let pat =
1470+
self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
1471+
if let Some(expanded_pat) = expanded_pat {
1472+
this.collect_pat_possibly_rest(expanded_pat, binding_list)
1473+
} else {
1474+
Either::Left(this.missing_pat())
1475+
}
1476+
});
1477+
if let Some(pat) = pat.left() {
1478+
self.source_map.pat_map.insert(src, pat);
1479+
}
1480+
pat
1481+
}
1482+
None => {
1483+
let ptr = AstPtr::new(&pat);
1484+
Either::Left(self.alloc_pat(Pat::Missing, ptr))
1485+
}
1486+
},
1487+
_ => Either::Left(self.collect_pat(pat, binding_list)),
1488+
}
1489+
}
1490+
14571491
// endregion: patterns
14581492

14591493
/// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when

src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,75 @@ impl Foo {
256256
);
257257
}
258258

259+
#[test]
260+
fn rest_pat_in_macro_expansion() {
261+
check_diagnostics(
262+
r#"
263+
// issue #17292
264+
#![allow(dead_code)]
265+
266+
macro_rules! replace_with_2_dots {
267+
( $( $input:tt )* ) => {
268+
..
269+
};
270+
}
271+
272+
macro_rules! enum_str {
273+
(
274+
$(
275+
$variant:ident (
276+
$( $tfield:ty ),*
277+
)
278+
)
279+
,
280+
*
281+
) => {
282+
enum Foo {
283+
$(
284+
$variant ( $( $tfield ),* ),
285+
)*
286+
}
287+
288+
impl Foo {
289+
fn variant_name_as_str(&self) -> &str {
290+
match self {
291+
$(
292+
Self::$variant ( replace_with_2_dots!( $( $tfield ),* ) )
293+
=> "",
294+
)*
295+
}
296+
}
297+
}
298+
};
299+
}
300+
301+
enum_str! {
302+
TupleVariant1(i32),
303+
TupleVariant2(),
304+
TupleVariant3(i8,u8,i128)
305+
}
306+
"#,
307+
);
308+
309+
check_diagnostics(
310+
r#"
311+
#![allow(dead_code)]
312+
macro_rules! two_dots1 {
313+
() => { .. };
314+
}
315+
316+
macro_rules! two_dots2 {
317+
() => { two_dots1!() };
318+
}
319+
320+
fn test() {
321+
let (_, _, two_dots1!()) = ((), 42);
322+
let (_, two_dots2!(), _) = (1, true, 2, false, (), (), 3);
323+
}
324+
"#,
325+
);
326+
}
327+
259328
#[test]
260329
fn varargs() {
261330
check_diagnostics(

0 commit comments

Comments
 (0)