diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs index ddf1a213d7bd..7eaefd9b0937 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -1979,3 +1979,51 @@ fn f() { "#]], ); } + +#[test] +fn foo() { + check( + r#" +macro_rules! bug { + ($id: expr) => { + true + }; + ($id: expr; $($attr: ident),*) => { + true + }; + ($id: expr; $($attr: ident),*; $norm: expr) => { + true + }; + ($id: expr; $($attr: ident),*;; $print: expr) => { + true + }; + ($id: expr; $($attr: ident),*; $norm: expr; $print: expr) => { + true + }; +} + +let _ = bug!(a;;;test); + "#, + expect![[r#" +macro_rules! bug { + ($id: expr) => { + true + }; + ($id: expr; $($attr: ident),*) => { + true + }; + ($id: expr; $($attr: ident),*; $norm: expr) => { + true + }; + ($id: expr; $($attr: ident),*;; $print: expr) => { + true + }; + ($id: expr; $($attr: ident),*; $norm: expr; $print: expr) => { + true + }; +} + +let _ = true; + "#]], + ); +} diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs index 940aaacb02ed..60e812903699 100644 --- a/crates/mbe/src/expander/matcher.rs +++ b/crates/mbe/src/expander/matcher.rs @@ -65,7 +65,7 @@ use intern::{Symbol, sym}; use smallvec::{SmallVec, smallvec}; use span::{Edition, Span}; use tt::{ - DelimSpan, + DelimSpan, MAX_GLUED_PUNCT_LEN, iter::{TtElement, TtIter}, }; @@ -558,7 +558,7 @@ fn match_loop_inner<'t>( } OpDelimited::Op(Op::Punct(lhs)) => { let mut fork = src.clone(); - let error = if let Ok(rhs) = fork.expect_glued_punct() { + let error = if let Ok(rhs) = fork.expect_glued_punct(lhs.len()) { let first_is_single_quote = rhs[0].char == '\''; let lhs = lhs.iter().map(|it| it.char); let rhs_ = rhs.iter().map(|it| it.char); @@ -955,7 +955,7 @@ fn expect_separator(iter: &mut TtIter<'_, S>, separator: &Separator) -> }, Err(_) => false, }, - Separator::Puncts(lhs) => match fork.expect_glued_punct() { + Separator::Puncts(lhs) => match fork.expect_glued_punct(lhs.len()) { Ok(rhs) => { let lhs = lhs.iter().map(|it| it.char); let rhs = rhs.iter().map(|it| it.char); @@ -975,7 +975,7 @@ fn expect_tt(iter: &mut TtIter<'_, S>) -> Result<(), ()> { if punct.char == '\'' { expect_lifetime(iter)?; } else { - iter.expect_glued_punct()?; + iter.expect_glued_punct(MAX_GLUED_PUNCT_LEN)?; } } else { iter.next().ok_or(())?; diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs index 7be49cbc7e11..6196c15e7911 100644 --- a/crates/mbe/src/parser.rs +++ b/crates/mbe/src/parser.rs @@ -6,7 +6,10 @@ use std::sync::Arc; use arrayvec::ArrayVec; use intern::{Symbol, sym}; use span::{Edition, Span, SyntaxContext}; -use tt::iter::{TtElement, TtIter}; +use tt::{ + MAX_GLUED_PUNCT_LEN, + iter::{TtElement, TtIter}, +}; use crate::ParseError; @@ -96,7 +99,7 @@ pub(crate) enum Op { delimiter: tt::Delimiter, }, Literal(tt::Literal), - Punct(Box, 3>>), + Punct(Box, MAX_GLUED_PUNCT_LEN>>), Ident(tt::Ident), } @@ -151,7 +154,7 @@ pub(crate) enum MetaVarKind { pub(crate) enum Separator { Literal(tt::Literal), Ident(tt::Ident), - Puncts(ArrayVec, 3>), + Puncts(ArrayVec, MAX_GLUED_PUNCT_LEN>), } // Note that when we compare a Separator, we just care about its textual value. @@ -273,7 +276,7 @@ fn next_op( TtElement::Leaf(tt::Leaf::Punct(_)) => { // There's at least one punct so this shouldn't fail. - let puncts = src.expect_glued_punct().unwrap(); + let puncts = src.expect_glued_punct(MAX_GLUED_PUNCT_LEN).unwrap(); Op::Punct(Box::new(puncts)) } diff --git a/crates/tt/src/iter.rs b/crates/tt/src/iter.rs index 1d88218810de..0f88df42e031 100644 --- a/crates/tt/src/iter.rs +++ b/crates/tt/src/iter.rs @@ -6,7 +6,7 @@ use std::fmt; use arrayvec::ArrayVec; use intern::sym; -use crate::{Ident, Leaf, Punct, Spacing, Subtree, TokenTree, TokenTreesView}; +use crate::{Ident, Leaf, MAX_GLUED_PUNCT_LEN, Punct, Spacing, Subtree, TokenTree, TokenTreesView}; #[derive(Clone)] pub struct TtIter<'a, S> { @@ -111,7 +111,10 @@ impl<'a, S: Copy> TtIter<'a, S> { /// /// This method currently may return a single quotation, which is part of lifetime ident and /// conceptually not a punct in the context of mbe. Callers should handle this. - pub fn expect_glued_punct(&mut self) -> Result, 3>, ()> { + pub fn expect_glued_punct( + &mut self, + max_len: usize, + ) -> Result, MAX_GLUED_PUNCT_LEN>, ()> { let TtElement::Leaf(&Leaf::Punct(first)) = self.next().ok_or(())? else { return Err(()); }; @@ -136,7 +139,9 @@ impl<'a, S: Copy> TtIter<'a, S> { }; match (first.char, second.char, third.map(|it| it.char)) { - ('.', '.', Some('.' | '=')) | ('<', '<', Some('=')) | ('>', '>', Some('=')) => { + ('.', '.', Some('.' | '=')) | ('<', '<', Some('=')) | ('>', '>', Some('=')) + if max_len >= 3 => + { let _ = self.next().unwrap(); let _ = self.next().unwrap(); res.push(first); @@ -151,7 +156,9 @@ impl<'a, S: Copy> TtIter<'a, S> { | ('.', '.', _) | ('&', '&', _) | ('<', '<', _) - | ('|', '|', _) => { + | ('|', '|', _) + if max_len >= 2 => + { let _ = self.next().unwrap(); res.push(first); res.push(*second); diff --git a/crates/tt/src/lib.rs b/crates/tt/src/lib.rs index 916e00b73bad..0e1958f557a3 100644 --- a/crates/tt/src/lib.rs +++ b/crates/tt/src/lib.rs @@ -22,6 +22,8 @@ use stdx::{impl_from, itertools::Itertools as _}; pub use text_size::{TextRange, TextSize}; +pub const MAX_GLUED_PUNCT_LEN: usize = 3; + #[derive(Clone, PartialEq, Debug)] pub struct Lit { pub kind: LitKind,