Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Fix a bug in macro-by-example matching #19498

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions crates/hir-def/src/macro_expansion_tests/mbe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1864,7 +1864,7 @@
foo!(::::);
}
"#,
expect![[r#"

Check failure on line 1867 in crates/hir-def/src/macro_expansion_tests/mbe.rs

View workflow job for this annotation

GitHub Actions / Rust (ubuntu-latest)

expect test failed
macro_rules! foo {
(: : :) => { "1 1 1" };
(: ::) => { "1 2" };
Expand Down Expand Up @@ -1979,3 +1979,51 @@
"#]],
);
}

#[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;
"#]],
);
}
8 changes: 4 additions & 4 deletions crates/mbe/src/expander/matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -955,7 +955,7 @@ fn expect_separator<S: Copy>(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);
Expand All @@ -975,7 +975,7 @@ fn expect_tt<S: Copy>(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(())?;
Expand Down
11 changes: 7 additions & 4 deletions crates/mbe/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -96,7 +99,7 @@ pub(crate) enum Op {
delimiter: tt::Delimiter<Span>,
},
Literal(tt::Literal<Span>),
Punct(Box<ArrayVec<tt::Punct<Span>, 3>>),
Punct(Box<ArrayVec<tt::Punct<Span>, MAX_GLUED_PUNCT_LEN>>),
Ident(tt::Ident<Span>),
}

Expand Down Expand Up @@ -151,7 +154,7 @@ pub(crate) enum MetaVarKind {
pub(crate) enum Separator {
Literal(tt::Literal<Span>),
Ident(tt::Ident<Span>),
Puncts(ArrayVec<tt::Punct<Span>, 3>),
Puncts(ArrayVec<tt::Punct<Span>, MAX_GLUED_PUNCT_LEN>),
}

// Note that when we compare a Separator, we just care about its textual value.
Expand Down Expand Up @@ -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))
}

Expand Down
15 changes: 11 additions & 4 deletions crates/tt/src/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down Expand Up @@ -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<ArrayVec<Punct<S>, 3>, ()> {
pub fn expect_glued_punct(
&mut self,
max_len: usize,
) -> Result<ArrayVec<Punct<S>, MAX_GLUED_PUNCT_LEN>, ()> {
let TtElement::Leaf(&Leaf::Punct(first)) = self.next().ok_or(())? else {
return Err(());
};
Expand All @@ -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);
Expand All @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions crates/tt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading