Skip to content

Commit 8f1684b

Browse files
committed
add arg metavar
1 parent 26d6bcc commit 8f1684b

22 files changed

Lines changed: 761 additions & 75 deletions

docs/RuleSpec.md

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -161,16 +161,18 @@ binder, label, constructor, type-name, or qualified-identifier positions infer
161161
`id`. For example, in `for $counter = 0; $counter < $limit; ...`, `$counter`
162162
infers `id` from the binder position and `$limit` infers `exp`.
163163

164-
Only `exp`, `id`, `const`, and `pat` are supported. A name may be repeated
164+
Only `exp`, `id`, `const`, `arg`, and `pat` are supported. A name may be repeated
165165
within one kind, but using the same payload across multiple kinds such as
166-
`$(name:exp)`, `$(name:id)`, `$(name:const)`, and `$(name:pat)` in one shape is
167-
invalid.
166+
`$(name:exp)`, `$(name:id)`, `$(name:const)`, `$(name:arg)`, and
167+
`$(name:pat)` in one shape is invalid.
168168

169169
Bare `$name` inference is intentionally conservative. It does not default to
170-
`const` or `pat`. A simple pattern variable such as `match input { $item => body
171-
}` is ambiguous between `id`, `const`, and `pat`; write `$(item:id)`,
172-
`$(item:const)`, or `$(item:pat)` to choose. An explicit same-name occurrence
173-
also fixes later bare occurrences when the positions are compatible.
170+
`const`, `arg`, or `pat`. A simple pattern variable such as
171+
`match input { $item => body }` is ambiguous between `id`, `const`, and `pat`;
172+
write `$(item:id)`, `$(item:const)`, or `$(item:pat)` to choose. Bare `$name`
173+
does not infer `arg`; use explicit `$(name:arg)` for whole call arguments. An
174+
explicit same-name occurrence also fixes later bare occurrences when the
175+
positions are compatible.
174176

175177
The old YAML `metavars` key is not supported. Pattern objects that contain it
176178
are rejected as using an unsupported key.
@@ -214,6 +216,19 @@ Use `$(name:const)` for literal constants. It is valid only as a whole bare
214216
identifier expression or as a simple pattern variable position, and it matches
215217
only parsed MoonBit constants.
216218

219+
Use `$(name:arg)` for a whole function-call argument slot. It is valid only as
220+
an entire bare positional argument in a call pattern:
221+
222+
```yaml
223+
patterns:
224+
- shape: sink($(arg:arg))
225+
```
226+
227+
The placeholder can match a candidate positional argument, labelled argument,
228+
labelled pun, optional labelled argument, or optional labelled pun. The captured
229+
value is the whole `Argument` AST node, including argument kind, label, and
230+
value.
231+
217232
Use `$(name:pat)` for a whole pattern AST capture. It is valid only as a simple
218233
pattern variable position:
219234

@@ -355,6 +370,41 @@ patterns:
355370
- shape: match input { $(lit:const) => lit }
356371
```
357372

373+
### `arg`
374+
375+
An `arg` metavar captures a complete call argument node. It is useful when a
376+
rule should accept any argument spelling in one slot while still comparing the
377+
entire slot on repeated occurrences.
378+
379+
Example:
380+
381+
```yaml
382+
patterns:
383+
- shape: sink($(arg:arg))
384+
```
385+
386+
This can match all of these one-argument calls:
387+
388+
```moonbit
389+
sink(value)
390+
sink(label=value)
391+
sink(label~)
392+
sink(label?=value)
393+
sink(label?)
394+
```
395+
396+
Repeating the same `arg` name requires the full argument nodes to be
397+
structurally equal, ignoring source locations. Argument kind, label, and value
398+
must all match. The pattern `sink($(arg:arg), $(arg:arg))` can match
399+
`sink(value, value)` and `sink(label=value, label=value)`, but not
400+
`sink(value, other)` or `sink(label=value, other=value)`.
401+
402+
`arg` is explicit-only. Bare `$arg` in `sink($arg)` still follows normal bare
403+
metavar inference and is an `exp` capture unless the name was explicitly
404+
declared as another kind elsewhere. `$(arg:arg)` must occupy the whole
405+
argument slot; `sink(label=$(arg:arg))`, `sink($(arg:arg) + 1)`, and a root
406+
shape `$(arg:arg)` are invalid.
407+
358408
### `pat`
359409

360410
A `pat` metavar captures the whole candidate `Pattern` AST. It is valid only in
@@ -387,9 +437,9 @@ patterns:
387437
```
388438

389439
Only `id` and `const` captures can be guarded. A guard key that refers to an
390-
`exp` capture, a `pat` capture, or an unknown name is rejected during rule
391-
compilation. Inner `patterns` may guard `id` and `const` captures established by
392-
`inside-expr`.
440+
`exp` capture, an `arg` capture, a `pat` capture, or an unknown name is
441+
rejected during rule compilation. Inner `patterns` may guard `id` and `const`
442+
captures established by `inside-expr`.
393443

394444
Guards are checked after the structural AST match succeeds. All guards in a
395445
single pattern object must match; this is AND semantics. Regex matching uses
@@ -686,8 +736,9 @@ A rule set or rule file is rejected when any of these conditions occurs:
686736
- an metavar uses a reserved name
687737
- `$(name:exp)` appears outside a bare expression position
688738
- `$(name:const)` appears outside a constant expression or constant pattern position
739+
- `$(name:arg)` appears outside a bare argument position
689740
- `$(name:pat)` appears outside a bare pattern position
690-
- a guard key is not `$`-prefixed, or references an unknown, `exp`, or `pat` capture
741+
- a guard key is not `$`-prefixed, or references an unknown, `exp`, `arg`, or `pat` capture
691742
- a guard regex is invalid
692743
- `inside-expr` does not contain exactly one binding-capable `__TARGET__`
693744
- a structural `patterns` or `patterns-not` entry contains binding-capable

docs/RuleSpec_CN.md

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,9 @@ patterns:
131131

132132
moongrep会为同名的所有出现位置推导出单一 kind。只出现在表达式占位位置的裸名称会推导为 `exp`;出现在 binder、标签、构造器、类型名或限定标识符位置的裸名称会推导为 `id`。例如 `for $counter = 0; $counter < $limit; ...` 中,`$counter` 会从 binder 位置推导为 `id`,`$limit` 会推导为 `exp`。
133133

134-
只支持 `exp`、`id`、`const` 和 `pat`。同一个 payload 可以在同一种 kind 中重复使用;同一个 shape 中不能跨 `$(name:exp)`、`$(name:id)`、`$(name:const)` 和 `$(name:pat)` 等多个 kind 使用同一个 payload。
134+
只支持 `exp`、`id`、`const`、`arg` 和 `pat`。同一个 payload 可以在同一种 kind 中重复使用;同一个 shape 中不能跨 `$(name:exp)`、`$(name:id)`、`$(name:const)`、`$(name:arg)` 和 `$(name:pat)` 等多个 kind 使用同一个 payload。
135135

136-
裸 `$name` 的推导是保守的。它不会默认推导为 `const` 或 `pat`。像 `match input { $item => body }` 这样的简单 pattern variable 在 `id`、`const` 和 `pat` 之间有歧义;请写成 `$(item:id)`、`$(item:const)` 或 `$(item:pat)` 来明确选择。同名的显式出现也可以在位置兼容时为后续裸出现确定 kind。
136+
裸 `$name` 的推导是保守的。它不会默认推导为 `const`、`arg` 或 `pat`。像 `match input { $item => body }` 这样的简单 pattern variable 在 `id`、`const` 和 `pat` 之间有歧义;请写成 `$(item:id)`、`$(item:const)` 或 `$(item:pat)` 来明确选择。裸 `$name` 不会推导为 `arg`;完整调用参数请显式写 `$(name:arg)`。同名的显式出现也可以在位置兼容时为后续裸出现确定 kind。
137137

138138
旧的 YAML `metavars` 键不再支持。包含该键的 pattern object 会因为使用不支持的键而被拒绝。
139139

@@ -167,6 +167,15 @@ patterns:
167167

168168
如果需要匹配字面常量,请使用 `$(name:const)`。它只在整个裸标识符表达式位置或简单 pattern variable 位置有效,并且只匹配解析后的 MoonBit 常量。重复使用同一个 `const` kind 名称时,常量 kind 和存储值都必须相等。它不会匹配变量、构造器、标签、操作符、限定标识符或普通 binder。
169169

170+
如果需要捕获完整函数调用参数槽,请使用 `$(name:arg)`。它只在调用 pattern 的整个裸位置参数中有效:
171+
172+
```yaml
173+
patterns:
174+
- shape: sink($(arg:arg))
175+
```
176+
177+
该占位符可以匹配候选中的 positional、labelled、labelled pun、optional labelled 和 optional pun 参数。捕获值是完整 `Argument` AST 节点,包括参数 kind、标签和值。
178+
170179
如果需要捕获整个 pattern AST,请使用 `$(name:pat)`。它只在简单 pattern variable 位置有效:
171180

172181
```yaml
@@ -290,6 +299,31 @@ patterns:
290299

291300
内部 body 通过普通 payload 名称 `lit` 引用外层常量捕获。
292301

302+
### `arg`
303+
304+
`arg` 元变量捕获完整调用参数节点。当规则需要在一个参数槽中接受任意参数写法,同时又希望重复出现时比较整个参数槽,使用它很合适。
305+
306+
示例:
307+
308+
```yaml
309+
patterns:
310+
- shape: sink($(arg:arg))
311+
```
312+
313+
它可以匹配这些单参数调用:
314+
315+
```moonbit
316+
sink(value)
317+
sink(label=value)
318+
sink(label~)
319+
sink(label?=value)
320+
sink(label?)
321+
```
322+
323+
重复使用同一个 `arg` 名称时,完整参数节点必须结构相等;源码位置会被忽略。参数 kind、标签和值都必须相同。`sink($(arg:arg), $(arg:arg))` 可以匹配 `sink(value, value)` 和 `sink(label=value, label=value)`,但不会匹配 `sink(value, other)` 或 `sink(label=value, other=value)`。
324+
325+
`arg` 只能显式使用。`sink($arg)` 中的裸 `$arg` 仍按普通裸元变量推导处理,除非该名称在别处已经被显式声明为其他 kind,否则它是 `exp` 捕获。`$(arg:arg)` 必须占据整个参数槽;`sink(label=$(arg:arg))`、`sink($(arg:arg) + 1)` 和根 shape `$(arg:arg)` 都无效。
326+
293327
### `pat`
294328

295329
`pat` 元变量捕获整个候选 `Pattern` AST。它只在简单 pattern variable 位置有效。
@@ -317,8 +351,8 @@ patterns:
317351
```
318352

319353
只有 `id` 和 `const` 捕获可以被 guard 过滤。guard 键如果引用 `exp` 捕获、
320-
`pat` 捕获或未知名称,会在规则编译时报错。内部 `patterns` 可以 guard 由
321-
`inside-expr` 建立的 `id` 和 `const` 捕获。
354+
`arg` 捕获、`pat` 捕获或未知名称,会在规则编译时报错。内部 `patterns` 可以
355+
guard 由 `inside-expr` 建立的 `id` 和 `const` 捕获。
322356

323357
Guard 会在结构 AST 匹配成功后检查。单个 pattern object 中的所有 guard 都必须
324358
匹配,即 AND 语义。正则使用包含匹配语义;如果需要整串匹配,请使用 `^...$`
@@ -552,8 +586,9 @@ taint 命中报告的 pattern index 是匹配 sink 条目的零基索引。
552586
- 内联元变量使用保留名称
553587
- `$(name:exp)` 出现在裸表达式位置之外
554588
- `$(name:const)` 出现在常量表达式或常量 pattern 位置之外
589+
- `$(name:arg)` 出现在裸参数位置之外
555590
- `$(name:pat)` 出现在裸 pattern 位置之外
556-
- guard 键没有 `$` 前缀,或引用未知捕获、`exp` 捕获或 `pat` 捕获
591+
- guard 键没有 `$` 前缀,或引用未知捕获、`exp` 捕获、`arg` 捕获或 `pat` 捕获
557592
- guard 正则无效
558593
- `inside-expr` 没有且只有一个可绑定的 `__TARGET__`
559594
- 结构规则的 `patterns` 或 `patterns-not` 条目包含可绑定的 `__TARGET__`

docs/WritingRules.md

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,8 @@ patterns:
183183
```
184184

185185
Use explicit `$(name:kind)` when inference would be ambiguous or when you need
186-
`const` or `pat`. Use `$(name:const)` when the same literal constant must be
187-
consistent and variables should not match:
186+
`const`, `arg`, or `pat`. Use `$(name:const)` when the same literal constant
187+
must be consistent and variables should not match:
188188

189189
```yaml
190190
patterns:
@@ -198,13 +198,24 @@ patterns:
198198
- shape: match input { $(item:pat) => body }
199199
```
200200

201+
Use `$(name:arg)` for a whole call argument slot:
202+
203+
```yaml
204+
patterns:
205+
- shape: sink($(arg:arg))
206+
```
207+
208+
That pattern can match positional, labelled, labelled pun, optional labelled,
209+
and optional pun arguments in the single slot. Bare `$arg` does not infer
210+
`arg`; write the explicit kind at each argument slot.
211+
201212
A bare simple pattern variable such as `match input { $item => body }` is
202213
ambiguous between `id`, `const`, and `pat`, so rule compilation asks you to
203214
choose explicitly. The `exp` kind is expression-only. The `const` kind is valid
204-
only in constant expression or constant pattern positions. The `pat` kind is
205-
valid only in a simple pattern variable position. If you need a non-expression
206-
source name, use `id` or leave the name literal. Any other kind is a compile
207-
error.
215+
only in constant expression or constant pattern positions. The `arg` kind is
216+
valid only as a whole bare argument in a call pattern. The `pat` kind is valid
217+
only in a simple pattern variable position. If you need a non-expression source
218+
name, use `id` or leave the name literal. Any other kind is a compile error.
208219

209220
The old YAML `metavars` key is invalid.
210221

@@ -220,7 +231,7 @@ When one of these names appears in a supported metavar position, it matches
220231
anything there without binding a value or participating in repeated-name
221232
equality. Repeated ignore placeholders are independent wildcards.
222233

223-
### 3. Choose `exp`, `id`, `const`, or `pat`
234+
### 3. Choose `exp`, `id`, `const`, `arg`, or `pat`
224235

225236
Use `exp` when you want to match and compare a whole expression. Repeating an
226237
`exp` metavar means the repeated captures must be structurally equal according
@@ -273,6 +284,18 @@ patterns:
273284
- shape: match input { $(item:pat) => body }
274285
```
275286

287+
Use `arg` when the candidate must be a complete call argument slot, including
288+
its kind, label, and value. Repeated `arg` captures compare the whole argument
289+
node while ignoring source locations.
290+
291+
```yaml
292+
patterns:
293+
- shape: sink($(arg:arg), $(arg:arg))
294+
```
295+
296+
This can match `sink(value, value)` and `sink(label=value, label=value)`, but
297+
not `sink(value, other)` or `sink(label=value, other=value)`.
298+
276299
### 3.5 Use `inside-expr` when the interesting node must appear inside a larger context
277300

278301
Reach for `inside-expr` when the thing you want to flag is only meaningful
@@ -377,8 +400,8 @@ normalized names such as `name` or `@pkg.name`. `const` guards see parser
377400
constant values, such as `raw` for `"raw"`, `42` for `42`, and `true` for
378401
`true`.
379402

380-
Guards cannot filter `exp` or `pat` captures, and taint clauses do not support
381-
`guard`.
403+
Guards cannot filter `exp`, `arg`, or `pat` captures, and taint clauses do not
404+
support `guard`.
382405

383406
### 5. Add more `patterns` when the message is shared
384407

@@ -481,9 +504,10 @@ valid expression-sized shape, then build back up carefully.
481504

482505
Check, in order:
483506

484-
- the kind annotation is exactly `exp`, `id`, `const`, or `pat`
507+
- the kind annotation is exactly `exp`, `id`, `const`, `arg`, or `pat`
485508
- `$(name:exp)` appears as a whole bare expression placeholder
486509
- `$(name:const)` appears only where a constant expression or constant pattern can match
510+
- `$(name:arg)` appears only as a whole bare call argument
487511
- `$(name:pat)` appears only as a whole bare pattern placeholder
488512
- the same payload name is not used across multiple metavar kinds
489513
- the payload is not a reserved name such as `__`, `__TARGET__`, or `__SOURCE__`

docs/WritingRules_CN.md

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ patterns:
117117
}
118118
```
119119

120-
当推导有歧义,或需要 `const` 或 `pat` 时,请使用显式 `$(name:kind)`。当同一个字面常量必须保持一致,并且变量不应该匹配时,请使用 `$(name:const)`:
120+
当推导有歧义,或需要 `const`、`arg` 或 `pat` 时,请使用显式 `$(name:kind)`。当同一个字面常量必须保持一致,并且变量不应该匹配时,请使用 `$(name:const)`:
121121

122122
```yaml
123123
patterns:
@@ -131,7 +131,16 @@ patterns:
131131
- shape: match input { $(item:pat) => body }
132132
```
133133

134-
像 `match input { $item => body }` 这样的裸简单 pattern variable 在 `id`、`const` 和 `pat` 之间有歧义,因此规则编译会要求你显式选择。`exp` kind 只能出现在表达式位置。`const` kind 只在常量表达式或常量 pattern 位置有效。`pat` kind 只在简单 pattern variable 位置有效。如果需要非表达式源码名称,请使用 `id`,或者让该名称保持字面量。任何其他 kind 都是编译错误。
134+
使用 `$(name:arg)` 捕获完整调用参数槽:
135+
136+
```yaml
137+
patterns:
138+
- shape: sink($(arg:arg))
139+
```
140+
141+
这个 pattern 可以在单个参数槽中匹配 positional、labelled、labelled pun、optional labelled 和 optional pun 参数。裸 `$arg` 不会推导为 `arg`;请在每个参数槽显式写出 kind。
142+
143+
像 `match input { $item => body }` 这样的裸简单 pattern variable 在 `id`、`const` 和 `pat` 之间有歧义,因此规则编译会要求你显式选择。`exp` kind 只能出现在表达式位置。`const` kind 只在常量表达式或常量 pattern 位置有效。`arg` kind 只在调用 pattern 的整个裸参数位置有效。`pat` kind 只在简单 pattern variable 位置有效。如果需要非表达式源码名称,请使用 `id`,或者让该名称保持字面量。任何其他 kind 都是编译错误。
135144

136145
旧的 YAML `metavars` 键无效。
137146

@@ -144,7 +153,7 @@ patterns:
144153

145154
当这些名称之一出现在支持的元变量位置时,它会匹配该位置上的任何内容,不绑定值,也不参与重复名称相等性检查。重复的忽略占位符是彼此独立的通配符。
146155

147-
### 3. 选择 `exp`、`id`、`const` 或 `pat`
156+
### 3. 选择 `exp`、`id`、`const`、`arg` 或 `pat`
148157

149158
当你想匹配并比较完整表达式时,使用 `exp`。重复使用同一个 `exp` 元变量意味着这些捕获必须根据运行时 matcher 的结构相等规则相等;源码位置会被忽略。
150159

@@ -184,6 +193,15 @@ patterns:
184193
- shape: match input { $(item:pat) => body }
185194
```
186195

196+
当候选必须是完整调用参数槽时,使用 `arg`;它会包含参数 kind、标签和值。重复的 `arg` 捕获会比较完整参数节点,并忽略源码位置。
197+
198+
```yaml
199+
patterns:
200+
- shape: sink($(arg:arg), $(arg:arg))
201+
```
202+
203+
这可以匹配 `sink(value, value)` 和 `sink(label=value, label=value)`,但不会匹配 `sink(value, other)` 或 `sink(label=value, other=value)`。
204+
187205
### 3.5 当关注节点必须出现在更大上下文内时,使用 `inside-expr`
188206

189207
当你想标记的内容只有在特定外层表达式内才有意义,并且你希望内部匹配继承外层捕获时,可以使用 `inside-expr`。
@@ -268,7 +286,7 @@ Guard 键是带 `$` 前缀的捕获名。值是正则字符串,使用包含匹
268286
`@pkg.name`。`const` guard 看到的是 parser 常量值,例如 `"raw"` 对应 `raw`,
269287
`42` 对应 `42`,`true` 对应 `true`。
270288

271-
Guard 不能过滤 `exp` 或 `pat` 捕获,taint 子句也不支持 `guard`。
289+
Guard 不能过滤 `exp`、`arg` 或 `pat` 捕获,taint 子句也不支持 `guard`。
272290

273291
### 5. 当消息共享时添加更多 `patterns`
274292

@@ -365,6 +383,7 @@ patterns:
365383

366384
- `$(name:exp)` 只出现在完整的裸表达式位置
367385
- `$(name:const)` 只出现在可匹配常量表达式或常量 pattern 的位置
386+
- `$(name:arg)` 只出现在完整的裸调用参数位置
368387
- `$(name:pat)` 只出现在完整的裸 pattern 位置
369388
- 非表达式源码名称使用 `$(name:id)`,或者保持字面量
370389
- 没有使用不支持的 kind

0 commit comments

Comments
 (0)