Skip to content

Commit 3cf2831

Browse files
committed
fix subtle bug in is_preferable
and add a test
1 parent cf3141c commit 3cf2831

File tree

2 files changed

+70
-1
lines changed

2 files changed

+70
-1
lines changed

crates/formality-core/src/parse/parser.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,8 @@ where
266266
l1.len() > l2.len() && (0..l2.len()).all(|i| l1[i] == l2[i])
267267
}
268268

269-
prec_i > prec_j || has_prefix(&parse_i.reductions, &parse_j.reductions)
269+
prec_i > prec_j
270+
|| (prec_i == prec_j && has_prefix(&parse_i.reductions, &parse_j.reductions))
270271
}
271272
}
272273

tests/parser-torture-tests/precedence.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,71 @@ fn precedence() {
2424
"#]]
2525
.assert_debug_eq(&term);
2626
}
27+
28+
#[test]
29+
#[should_panic(expected = "extra tokens")]
30+
fn higher_precedence_less_reductions_1() {
31+
// Subtle: In this case, A has higher precedence,
32+
// so even though we COULD parse the entire string,
33+
// we prefer to just parse the first identifier,
34+
// which results in a panic.
35+
//
36+
// In the past, we had a bug in our preference function
37+
// that caused it to be order dependent, sometimes
38+
// panicking and sometimes not. Hence we have a similar
39+
// test with opposite order.
40+
41+
#[term]
42+
pub enum Root {
43+
#[cast]
44+
#[precedence(1)]
45+
A(A),
46+
47+
#[cast]
48+
B(B),
49+
}
50+
51+
#[term($v0)]
52+
pub struct A(Id);
53+
54+
#[term($v0 $v1)]
55+
pub struct B(A, Id);
56+
57+
formality_core::id!(Id);
58+
59+
let term: Root = crate::ptt::term("my String");
60+
expect_test::expect![[r#"
61+
"#]]
62+
.assert_debug_eq(&term);
63+
}
64+
65+
#[test]
66+
#[should_panic(expected = "extra tokens")]
67+
fn higher_precedence_less_reductions_2() {
68+
// Same as `higher_precedence_less_reductions_1` but with
69+
// opposite term order. See explanation in that function.
70+
71+
#[term]
72+
pub enum Root {
73+
#[cast]
74+
B(B),
75+
76+
#[cast]
77+
#[precedence(1)]
78+
A(A),
79+
}
80+
81+
#[term($v0)]
82+
pub struct A(Id);
83+
84+
#[term($v0 $v1)]
85+
pub struct B(A, Id);
86+
87+
formality_core::id!(Id);
88+
89+
let term: Root = crate::ptt::term("my String");
90+
expect_test::expect![[r#"
91+
my String
92+
"#]]
93+
.assert_debug_eq(&term);
94+
}

0 commit comments

Comments
 (0)