mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 13:33:31 +00:00
Fixup path fragments upon MBE transcription
This commit is contained in:
parent
037844c8a0
commit
fd7435d463
5 changed files with 89 additions and 5 deletions
|
@ -848,6 +848,37 @@ fn foo() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_type_path_is_transcribed_as_expr_path() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
macro_rules! m {
|
||||||
|
($p:path) => { let $p; }
|
||||||
|
}
|
||||||
|
fn test() {
|
||||||
|
m!(S)
|
||||||
|
m!(S<i32>)
|
||||||
|
m!(S<S<i32>>)
|
||||||
|
m!(S<{ module::CONST < 42 }>)
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
macro_rules! m {
|
||||||
|
($p:path) => { let $p; }
|
||||||
|
}
|
||||||
|
fn test() {
|
||||||
|
let S;
|
||||||
|
let S:: <i32> ;
|
||||||
|
let S:: <S:: <i32>> ;
|
||||||
|
let S:: < {
|
||||||
|
module::CONST<42
|
||||||
|
}
|
||||||
|
> ;
|
||||||
|
}
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_expr() {
|
fn test_expr() {
|
||||||
check(
|
check(
|
||||||
|
|
|
@ -123,4 +123,14 @@ enum Fragment {
|
||||||
/// proc-macro delimiter=none. As we later discovered, "none" delimiters are
|
/// proc-macro delimiter=none. As we later discovered, "none" delimiters are
|
||||||
/// tricky to handle in the parser, and rustc doesn't handle those either.
|
/// tricky to handle in the parser, and rustc doesn't handle those either.
|
||||||
Expr(tt::TokenTree),
|
Expr(tt::TokenTree),
|
||||||
|
/// There are roughly two types of paths: paths in expression context, where a
|
||||||
|
/// separator `::` between an identifier and its following generic argument list
|
||||||
|
/// is mandatory, and paths in type context, where `::` can be omitted.
|
||||||
|
///
|
||||||
|
/// Unlike rustc, we need to transform the parsed fragments back into tokens
|
||||||
|
/// during transcription. When the matched path fragment is a type-context path
|
||||||
|
/// and is trasncribed as an expression-context path, verbatim transcription
|
||||||
|
/// would cause a syntax error. We need to fix it up just before transcribing;
|
||||||
|
/// see `transcriber::fix_up_and_push_path_tt()`.
|
||||||
|
Path(tt::TokenTree),
|
||||||
}
|
}
|
||||||
|
|
|
@ -742,7 +742,11 @@ fn match_meta_var(
|
||||||
is_2021: bool,
|
is_2021: bool,
|
||||||
) -> ExpandResult<Option<Fragment>> {
|
) -> ExpandResult<Option<Fragment>> {
|
||||||
let fragment = match kind {
|
let fragment = match kind {
|
||||||
MetaVarKind::Path => parser::PrefixEntryPoint::Path,
|
MetaVarKind::Path => {
|
||||||
|
return input
|
||||||
|
.expect_fragment(parser::PrefixEntryPoint::Path)
|
||||||
|
.map(|it| it.map(Fragment::Path));
|
||||||
|
}
|
||||||
MetaVarKind::Ty => parser::PrefixEntryPoint::Ty,
|
MetaVarKind::Ty => parser::PrefixEntryPoint::Ty,
|
||||||
MetaVarKind::Pat if is_2021 => parser::PrefixEntryPoint::PatTop,
|
MetaVarKind::Pat if is_2021 => parser::PrefixEntryPoint::PatTop,
|
||||||
MetaVarKind::Pat => parser::PrefixEntryPoint::Pat,
|
MetaVarKind::Pat => parser::PrefixEntryPoint::Pat,
|
||||||
|
@ -771,7 +775,7 @@ fn match_meta_var(
|
||||||
.expect_fragment(parser::PrefixEntryPoint::Expr)
|
.expect_fragment(parser::PrefixEntryPoint::Expr)
|
||||||
.map(|tt| tt.map(Fragment::Expr));
|
.map(|tt| tt.map(Fragment::Expr));
|
||||||
}
|
}
|
||||||
_ => {
|
MetaVarKind::Ident | MetaVarKind::Tt | MetaVarKind::Lifetime | MetaVarKind::Literal => {
|
||||||
let tt_result = match kind {
|
let tt_result = match kind {
|
||||||
MetaVarKind::Ident => input
|
MetaVarKind::Ident => input
|
||||||
.expect_ident()
|
.expect_ident()
|
||||||
|
@ -799,7 +803,7 @@ fn match_meta_var(
|
||||||
})
|
})
|
||||||
.map_err(|()| ExpandError::binding_error("expected literal"))
|
.map_err(|()| ExpandError::binding_error("expected literal"))
|
||||||
}
|
}
|
||||||
_ => Err(ExpandError::UnexpectedToken),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
return tt_result.map(|it| Some(Fragment::Tokens(it))).into();
|
return tt_result.map(|it| Some(Fragment::Tokens(it))).into();
|
||||||
}
|
}
|
||||||
|
|
|
@ -400,7 +400,8 @@ fn push_fragment(buf: &mut Vec<tt::TokenTree>, fragment: Fragment) {
|
||||||
}
|
}
|
||||||
buf.push(tt.into())
|
buf.push(tt.into())
|
||||||
}
|
}
|
||||||
Fragment::Tokens(tt) | Fragment::Expr(tt) => buf.push(tt),
|
Fragment::Path(tt::TokenTree::Subtree(tt)) => fix_up_and_push_path_tt(buf, tt),
|
||||||
|
Fragment::Tokens(tt) | Fragment::Expr(tt) | Fragment::Path(tt) => buf.push(tt),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,6 +412,45 @@ fn push_subtree(buf: &mut Vec<tt::TokenTree>, tt: tt::Subtree) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Inserts the path separator `::` between an identifier and its following generic
|
||||||
|
/// argument list, and then pushes into the buffer. See [`Fragment::Path`] for why
|
||||||
|
/// we need this fixup.
|
||||||
|
fn fix_up_and_push_path_tt(buf: &mut Vec<tt::TokenTree>, subtree: tt::Subtree) {
|
||||||
|
stdx::always!(matches!(subtree.delimiter.kind, tt::DelimiterKind::Invisible));
|
||||||
|
let mut prev_was_ident = false;
|
||||||
|
// Note that we only need to fix up the top-level `TokenTree`s because the
|
||||||
|
// context of the paths in the descendant `Subtree`s won't be changed by the
|
||||||
|
// mbe transcription.
|
||||||
|
for tt in subtree.token_trees {
|
||||||
|
if prev_was_ident {
|
||||||
|
// Pedantically, `(T) -> U` in `FnOnce(T) -> U` is treated as a generic
|
||||||
|
// argument list and thus needs `::` between it and `FnOnce`. However in
|
||||||
|
// today's Rust this type of path *semantically* cannot appear as a
|
||||||
|
// top-level expression-context path, so we can safely ignore it.
|
||||||
|
if let tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '<', .. })) = tt {
|
||||||
|
buf.push(
|
||||||
|
tt::Leaf::Punct(tt::Punct {
|
||||||
|
char: ':',
|
||||||
|
spacing: tt::Spacing::Joint,
|
||||||
|
span: tt::Span::unspecified(),
|
||||||
|
})
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
buf.push(
|
||||||
|
tt::Leaf::Punct(tt::Punct {
|
||||||
|
char: ':',
|
||||||
|
spacing: tt::Spacing::Alone,
|
||||||
|
span: tt::Span::unspecified(),
|
||||||
|
})
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev_was_ident = matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Ident(_)));
|
||||||
|
buf.push(tt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Handles `${count(t, depth)}`. `our_depth` is the recursion depth and `count_depth` is the depth
|
/// Handles `${count(t, depth)}`. `our_depth` is the recursion depth and `count_depth` is the depth
|
||||||
/// defined by the metavar expression.
|
/// defined by the metavar expression.
|
||||||
fn count(
|
fn count(
|
||||||
|
|
|
@ -122,7 +122,6 @@ impl_from!(Literal<Span>, Punct<Span>, Ident<Span> for Leaf);
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct Subtree<Span> {
|
pub struct Subtree<Span> {
|
||||||
// FIXME, this should not be Option
|
|
||||||
pub delimiter: Delimiter<Span>,
|
pub delimiter: Delimiter<Span>,
|
||||||
pub token_trees: Vec<TokenTree<Span>>,
|
pub token_trees: Vec<TokenTree<Span>>,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue