Skip to content

Commit f8ecaa2

Browse files
Harden legacy macros, add support for existsOne macro (#1064)
1 parent 8ad600b commit f8ecaa2

File tree

2 files changed

+49
-4
lines changed

2 files changed

+49
-4
lines changed

parser/macro.go

+15
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,13 @@ var (
259259

260260
// ExistsOneMacro expands "range.exists_one(var, predicate)", which is true if for exactly one
261261
// element in range the predicate holds.
262+
// Deprecated: Use ExistsOneMacroNew
262263
ExistsOneMacro = NewReceiverMacro(operators.ExistsOne, 2, MakeExistsOne)
263264

265+
// ExistsOneMacroNew expands "range.existsOne(var, predicate)", which is true if for exactly one
266+
// element in range the predicate holds.
267+
ExistsOneMacroNew = NewReceiverMacro("existsOne", 2, MakeExistsOne)
268+
264269
// MapMacro expands "range.map(var, function)" into a comprehension which applies the function
265270
// to each element in the range to produce a new list.
266271
MapMacro = NewReceiverMacro(operators.Map, 2, MakeMap)
@@ -280,6 +285,7 @@ var (
280285
AllMacro,
281286
ExistsMacro,
282287
ExistsOneMacro,
288+
ExistsOneMacroNew,
283289
MapMacro,
284290
MapFilterMacro,
285291
FilterMacro,
@@ -336,6 +342,9 @@ func MakeMap(eh ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *common
336342
if !found {
337343
return nil, eh.NewError(args[0].ID(), "argument is not an identifier")
338344
}
345+
if v == AccumulatorName {
346+
return nil, eh.NewError(args[0].ID(), "iteration variable overwrites accumulator variable")
347+
}
339348

340349
var fn ast.Expr
341350
var filter ast.Expr
@@ -366,6 +375,9 @@ func MakeFilter(eh ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *com
366375
if !found {
367376
return nil, eh.NewError(args[0].ID(), "argument is not an identifier")
368377
}
378+
if v == AccumulatorName {
379+
return nil, eh.NewError(args[0].ID(), "iteration variable overwrites accumulator variable")
380+
}
369381

370382
filter := args[1]
371383
init := eh.NewList()
@@ -389,6 +401,9 @@ func makeQuantifier(kind quantifierKind, eh ExprHelper, target ast.Expr, args []
389401
if !found {
390402
return nil, eh.NewError(args[0].ID(), "argument must be a simple name")
391403
}
404+
if v == AccumulatorName {
405+
return nil, eh.NewError(args[0].ID(), "iteration variable overwrites accumulator variable")
406+
}
392407

393408
var init ast.Expr
394409
var condition ast.Expr

parser/parser_test.go

+34-4
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,13 @@ var testCases = []testInfo{
438438
m^#2:*expr.Expr_IdentExpr#.f^#3:*expr.Expr_SelectExpr#
439439
)^#4:has#`,
440440
},
441+
{
442+
I: `has(m)`,
443+
E: `ERROR: <input>:1:5: invalid argument to has() macro
444+
| has(m)
445+
| ....^`,
446+
},
447+
441448
{
442449
I: `m.exists(v, f)`,
443450
P: `__comprehension__(
@@ -495,7 +502,7 @@ var testCases = []testInfo{
495502
)^#11:all#`,
496503
},
497504
{
498-
I: `m.exists_one(v, f)`,
505+
I: `m.existsOne(v, f)`,
499506
P: `__comprehension__(
500507
// Variable
501508
v,
@@ -521,10 +528,16 @@ var testCases = []testInfo{
521528
__result__^#12:*expr.Expr_IdentExpr#,
522529
1^#13:*expr.Constant_Int64Value#
523530
)^#14:*expr.Expr_CallExpr#)^#15:*expr.Expr_ComprehensionExpr#`,
524-
M: `m^#1:*expr.Expr_IdentExpr#.exists_one(
531+
M: `m^#1:*expr.Expr_IdentExpr#.existsOne(
525532
v^#3:*expr.Expr_IdentExpr#,
526533
f^#4:*expr.Expr_IdentExpr#
527-
)^#15:exists_one#`,
534+
)^#15:existsOne#`,
535+
},
536+
{
537+
I: `[].existsOne(__result__, __result__)`,
538+
E: `ERROR: <input>:1:14: iteration variable overwrites accumulator variable
539+
| [].existsOne(__result__, __result__)
540+
| .............^`,
528541
},
529542
{
530543
I: `m.map(v, f)`,
@@ -553,7 +566,12 @@ var testCases = []testInfo{
553566
f^#4:*expr.Expr_IdentExpr#
554567
)^#11:map#`,
555568
},
556-
569+
{
570+
I: `m.map(__result__, __result__)`,
571+
E: `ERROR: <input>:1:7: iteration variable overwrites accumulator variable
572+
| m.map(__result__, __result__)
573+
| ......^`,
574+
},
557575
{
558576
I: `m.map(v, p, f)`,
559577
P: `__comprehension__(
@@ -618,6 +636,18 @@ var testCases = []testInfo{
618636
p^#4:*expr.Expr_IdentExpr#
619637
)^#13:filter#`,
620638
},
639+
{
640+
I: `m.filter(__result__, false)`,
641+
E: `ERROR: <input>:1:10: iteration variable overwrites accumulator variable
642+
| m.filter(__result__, false)
643+
| .........^`,
644+
},
645+
{
646+
I: `m.filter(a.b, false)`,
647+
E: `ERROR: <input>:1:11: argument is not an identifier
648+
| m.filter(a.b, false)
649+
| ..........^`,
650+
},
621651

622652
// Tests from C++ parser
623653
{

0 commit comments

Comments
 (0)