From da8eb29a2f70a58122903bf087bd6c1d0fbd6d3f Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 3 Apr 2020 15:38:42 +0200 Subject: [PATCH] Macro patterns are not confused with expressions. We treat macro calls as expressions (there's appropriate Into impl), which causes problem if there's expresison and non-expression macro in the same node (like in the match arm). We fix this problem by nesting macor patterns into another node (the same way we nest path into PathExpr or PathPat). Ideally, we probably should add a similar nesting for macro expressions, but that needs some careful thinking about macros in blocks: `{ am_i_expression!() }`. --- crates/ra_hir_def/src/body/lower.rs | 3 +- crates/ra_hir_ty/src/tests/macros.rs | 31 +++++++++++++- crates/ra_parser/src/grammar/patterns.rs | 4 +- crates/ra_parser/src/syntax_kind/generated.rs | 1 + crates/ra_syntax/src/ast/generated.rs | 42 ++++++++++++++++++- .../parser/inline/ok/0129_marco_pat.txt | 21 +++++----- xtask/src/ast_src.rs | 3 ++ 7 files changed, 88 insertions(+), 17 deletions(-) diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs index 28c570c769..8d4b8b0f0f 100644 --- a/crates/ra_hir_def/src/body/lower.rs +++ b/crates/ra_hir_def/src/body/lower.rs @@ -672,8 +672,7 @@ impl ExprCollector<'_> { } // FIXME: implement - ast::Pat::BoxPat(_) => Pat::Missing, - ast::Pat::RangePat(_) => Pat::Missing, + ast::Pat::BoxPat(_) | ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing, }; let ptr = AstPtr::new(&pat); self.alloc_pat(pattern, Either::Left(ptr)) diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs index eb97288f18..ff4599b712 100644 --- a/crates/ra_hir_ty/src/tests/macros.rs +++ b/crates/ra_hir_ty/src/tests/macros.rs @@ -1,8 +1,10 @@ -use super::{infer, type_at, type_at_pos}; -use crate::test_db::TestDB; use insta::assert_snapshot; use ra_db::fixture::WithFixture; +use super::{infer, type_at, type_at_pos}; + +use crate::test_db::TestDB; + #[test] fn cfg_impl_def() { let (db, pos) = TestDB::with_position( @@ -658,3 +660,28 @@ fn test() { ); assert_eq!("S", type_at_pos(&db, pos)); } + +#[test] +fn macro_in_arm() { + assert_snapshot!( + infer(r#" +macro_rules! unit { + () => { () }; +} + +fn main() { + let x = match () { + unit!() => 92u32, + }; +} +"#), + @r###" + [52; 111) '{ ... }; }': () + [62; 63) 'x': u32 + [66; 108) 'match ... }': u32 + [72; 74) '()': () + [85; 92) 'unit!()': () + [96; 101) '92u32': u32 + "### + ); +} diff --git a/crates/ra_parser/src/grammar/patterns.rs b/crates/ra_parser/src/grammar/patterns.rs index 9bfdcfd41a..936d27575c 100644 --- a/crates/ra_parser/src/grammar/patterns.rs +++ b/crates/ra_parser/src/grammar/patterns.rs @@ -84,7 +84,7 @@ fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option { // Checks the token after an IDENT to see if a pattern is a path (Struct { .. }) or macro // (T![x]). T!['('] | T!['{'] | T![!] => path_or_macro_pat(p), - T![:] if p.nth_at(1, T![::]) => path_pat(p), + T![:] if p.nth_at(1, T![::]) => path_or_macro_pat(p), _ => bind_pat(p, true), }, @@ -156,7 +156,7 @@ fn path_or_macro_pat(p: &mut Parser) -> CompletedMarker { // } T![!] => { items::macro_call_after_excl(p); - MACRO_CALL + return m.complete(p, MACRO_CALL).precede(p).complete(p, MACRO_PAT); } _ => PATH_PAT, }; diff --git a/crates/ra_parser/src/syntax_kind/generated.rs b/crates/ra_parser/src/syntax_kind/generated.rs index dfc30d7278..4c16cf1cd9 100644 --- a/crates/ra_parser/src/syntax_kind/generated.rs +++ b/crates/ra_parser/src/syntax_kind/generated.rs @@ -167,6 +167,7 @@ pub enum SyntaxKind { SLICE_PAT, RANGE_PAT, LITERAL_PAT, + MACRO_PAT, TUPLE_EXPR, ARRAY_EXPR, PAREN_EXPR, diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 7204ca5b17..0c339b9879 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -2563,6 +2563,38 @@ impl LiteralPat { } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct MacroPat { + pub(crate) syntax: SyntaxNode, +} +impl std::fmt::Display for MacroPat { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl AstNode for MacroPat { + fn can_cast(kind: SyntaxKind) -> bool { + match kind { + MACRO_PAT => true, + _ => false, + } + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +impl MacroPat { + pub fn macro_call(&self) -> Option { + AstChildren::new(&self.syntax).next() + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct RecordPat { pub(crate) syntax: SyntaxNode, } @@ -4600,6 +4632,7 @@ pub enum Pat { SlicePat(SlicePat), RangePat(RangePat), LiteralPat(LiteralPat), + MacroPat(MacroPat), } impl From for Pat { fn from(node: OrPat) -> Pat { @@ -4671,6 +4704,11 @@ impl From for Pat { Pat::LiteralPat(node) } } +impl From for Pat { + fn from(node: MacroPat) -> Pat { + Pat::MacroPat(node) + } +} impl std::fmt::Display for Pat { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -4681,7 +4719,7 @@ impl AstNode for Pat { match kind { OR_PAT | PAREN_PAT | REF_PAT | BOX_PAT | BIND_PAT | PLACEHOLDER_PAT | DOT_DOT_PAT | PATH_PAT | RECORD_PAT | TUPLE_STRUCT_PAT | TUPLE_PAT | SLICE_PAT | RANGE_PAT - | LITERAL_PAT => true, + | LITERAL_PAT | MACRO_PAT => true, _ => false, } } @@ -4701,6 +4739,7 @@ impl AstNode for Pat { SLICE_PAT => Pat::SlicePat(SlicePat { syntax }), RANGE_PAT => Pat::RangePat(RangePat { syntax }), LITERAL_PAT => Pat::LiteralPat(LiteralPat { syntax }), + MACRO_PAT => Pat::MacroPat(MacroPat { syntax }), _ => return None, }; Some(res) @@ -4721,6 +4760,7 @@ impl AstNode for Pat { Pat::SlicePat(it) => &it.syntax, Pat::RangePat(it) => &it.syntax, Pat::LiteralPat(it) => &it.syntax, + Pat::MacroPat(it) => &it.syntax, } } } diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0129_marco_pat.txt b/crates/ra_syntax/test_data/parser/inline/ok/0129_marco_pat.txt index b05ccc0ed5..36d8f4a5f5 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0129_marco_pat.txt +++ b/crates/ra_syntax/test_data/parser/inline/ok/0129_marco_pat.txt @@ -15,16 +15,17 @@ SOURCE_FILE@[0; 33) LET_STMT@[16; 30) LET_KW@[16; 19) "let" WHITESPACE@[19; 20) " " - MACRO_CALL@[20; 25) - PATH@[20; 21) - PATH_SEGMENT@[20; 21) - NAME_REF@[20; 21) - IDENT@[20; 21) "m" - EXCL@[21; 22) "!" - TOKEN_TREE@[22; 25) - L_PAREN@[22; 23) "(" - IDENT@[23; 24) "x" - R_PAREN@[24; 25) ")" + MACRO_PAT@[20; 25) + MACRO_CALL@[20; 25) + PATH@[20; 21) + PATH_SEGMENT@[20; 21) + NAME_REF@[20; 21) + IDENT@[20; 21) "m" + EXCL@[21; 22) "!" + TOKEN_TREE@[22; 25) + L_PAREN@[22; 23) "(" + IDENT@[23; 24) "x" + R_PAREN@[24; 25) ")" WHITESPACE@[25; 26) " " EQ@[26; 27) "=" WHITESPACE@[27; 28) " " diff --git a/xtask/src/ast_src.rs b/xtask/src/ast_src.rs index 99bd601989..d9f51ec399 100644 --- a/xtask/src/ast_src.rs +++ b/xtask/src/ast_src.rs @@ -138,6 +138,7 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc { "SLICE_PAT", "RANGE_PAT", "LITERAL_PAT", + "MACRO_PAT", // atoms "TUPLE_EXPR", "ARRAY_EXPR", @@ -440,6 +441,7 @@ pub(crate) const AST_SRC: AstSrc = AstSrc { struct SlicePat { args: [Pat] } struct RangePat {} struct LiteralPat { Literal } + struct MacroPat { MacroCall } struct RecordPat { RecordFieldPatList, Path } struct RecordFieldPatList { @@ -622,6 +624,7 @@ pub(crate) const AST_SRC: AstSrc = AstSrc { SlicePat, RangePat, LiteralPat, + MacroPat, } enum AttrInput { Literal, TokenTree }