mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-28 05:53:45 +00:00
Allow macro expansions into RestPat
in tuple args work as ellipsis like plain RestPat
This commit is contained in:
parent
ffbc5ad993
commit
b238855892
2 changed files with 108 additions and 5 deletions
|
@ -4,6 +4,7 @@
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use base_db::CrateId;
|
use base_db::CrateId;
|
||||||
|
use either::Either;
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
name::{name, AsName, Name},
|
name::{name, AsName, Name},
|
||||||
ExpandError, InFile,
|
ExpandError, InFile,
|
||||||
|
@ -1432,14 +1433,12 @@ impl ExprCollector<'_> {
|
||||||
has_leading_comma: bool,
|
has_leading_comma: bool,
|
||||||
binding_list: &mut BindingList,
|
binding_list: &mut BindingList,
|
||||||
) -> (Box<[PatId]>, Option<usize>) {
|
) -> (Box<[PatId]>, Option<usize>) {
|
||||||
|
let args: Vec<_> = args.map(|p| self.collect_pat_possibly_rest(p, binding_list)).collect();
|
||||||
// Find the location of the `..`, if there is one. Note that we do not
|
// Find the location of the `..`, if there is one. Note that we do not
|
||||||
// consider the possibility of there being multiple `..` here.
|
// consider the possibility of there being multiple `..` here.
|
||||||
let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_)));
|
let ellipsis = args.iter().position(|p| p.is_right());
|
||||||
// We want to skip the `..` pattern here, since we account for it above.
|
// We want to skip the `..` pattern here, since we account for it above.
|
||||||
let mut args: Vec<_> = args
|
let mut args: Vec<_> = args.into_iter().filter_map(Either::left).collect();
|
||||||
.filter(|p| !matches!(p, ast::Pat::RestPat(_)))
|
|
||||||
.map(|p| self.collect_pat(p, binding_list))
|
|
||||||
.collect();
|
|
||||||
// if there is a leading comma, the user is most likely to type out a leading pattern
|
// if there is a leading comma, the user is most likely to type out a leading pattern
|
||||||
// so we insert a missing pattern at the beginning for IDE features
|
// so we insert a missing pattern at the beginning for IDE features
|
||||||
if has_leading_comma {
|
if has_leading_comma {
|
||||||
|
@ -1449,6 +1448,41 @@ impl ExprCollector<'_> {
|
||||||
(args.into_boxed_slice(), ellipsis)
|
(args.into_boxed_slice(), ellipsis)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `collect_pat` rejects `ast::Pat::RestPat`, but it should be handled in some cases that
|
||||||
|
// it is the macro expansion result of an arg sub-pattern in a slice or tuple pattern.
|
||||||
|
fn collect_pat_possibly_rest(
|
||||||
|
&mut self,
|
||||||
|
pat: ast::Pat,
|
||||||
|
binding_list: &mut BindingList,
|
||||||
|
) -> Either<PatId, ()> {
|
||||||
|
match &pat {
|
||||||
|
ast::Pat::RestPat(_) => Either::Right(()),
|
||||||
|
ast::Pat::MacroPat(mac) => match mac.macro_call() {
|
||||||
|
Some(call) => {
|
||||||
|
let macro_ptr = AstPtr::new(&call);
|
||||||
|
let src = self.expander.in_file(AstPtr::new(&pat));
|
||||||
|
let pat =
|
||||||
|
self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
|
||||||
|
if let Some(expanded_pat) = expanded_pat {
|
||||||
|
this.collect_pat_possibly_rest(expanded_pat, binding_list)
|
||||||
|
} else {
|
||||||
|
Either::Left(this.missing_pat())
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if let Some(pat) = pat.left() {
|
||||||
|
self.source_map.pat_map.insert(src, pat);
|
||||||
|
}
|
||||||
|
pat
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let ptr = AstPtr::new(&pat);
|
||||||
|
Either::Left(self.alloc_pat(Pat::Missing, ptr))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => Either::Left(self.collect_pat(pat, binding_list)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// endregion: patterns
|
// endregion: patterns
|
||||||
|
|
||||||
/// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when
|
/// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when
|
||||||
|
|
|
@ -256,6 +256,75 @@ impl Foo {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rest_pat_in_macro_expansion() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
// issue #17292
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
macro_rules! replace_with_2_dots {
|
||||||
|
( $( $input:tt )* ) => {
|
||||||
|
..
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! enum_str {
|
||||||
|
(
|
||||||
|
$(
|
||||||
|
$variant:ident (
|
||||||
|
$( $tfield:ty ),*
|
||||||
|
)
|
||||||
|
)
|
||||||
|
,
|
||||||
|
*
|
||||||
|
) => {
|
||||||
|
enum Foo {
|
||||||
|
$(
|
||||||
|
$variant ( $( $tfield ),* ),
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn variant_name_as_str(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
$(
|
||||||
|
Self::$variant ( replace_with_2_dots!( $( $tfield ),* ) )
|
||||||
|
=> "",
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
enum_str! {
|
||||||
|
TupleVariant1(i32),
|
||||||
|
TupleVariant2(),
|
||||||
|
TupleVariant3(i8,u8,i128)
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
#![allow(dead_code)]
|
||||||
|
macro_rules! two_dots1 {
|
||||||
|
() => { .. };
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! two_dots2 {
|
||||||
|
() => { two_dots1!() };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test() {
|
||||||
|
let (_, _, two_dots1!()) = ((), 42);
|
||||||
|
let (_, two_dots2!(), _) = (1, true, 2, false, (), (), 3);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn varargs() {
|
fn varargs() {
|
||||||
check_diagnostics(
|
check_diagnostics(
|
||||||
|
|
Loading…
Reference in a new issue