Skip to content
Merged
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
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ parameter interpolation.
form accepts at run time, change `check.go` to match (`builtinSigs` for
builtins), and add the case to `TestCheckMatchesRun`, which runs every
rejected expression to confirm `Run` fails too.
The one intended exception: Check rejects a registered function used as a
value (`[upper, 1]`), which `Run` still evaluates.
- When changing language behavior, update `docs/reference/spec.md` in the same change; if the
change affects how expressions *look*, update `docs/guides/examples.md` and `llms.txt` too.
- Guide pages under `docs/guides/` have runnable companions under `examples/` and
Expand Down
4 changes: 4 additions & 0 deletions check.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,10 @@ func (c *checker) ident(n *ast.Ident, sc *scope) *Type {
return orAny(t)
}
if _, ok := c.p.funcs[name]; ok {
// Run returns the function itself, which fails on a field read
// and when a host stores the result. Run accepts it in a
// condition or a list literal; Check rejects it there too.
c.report(n, "%q is a function, not a value; call it", name)
return Any
}
names := make([]string, 0, len(c.env))
Expand Down
2 changes: 2 additions & 0 deletions check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ func TestCheckRejects(t *testing.T) {
`map(findings.items, it.titel)`: `no field "titel"`,
`findings.items["x"]`: "list index must be a number",
`classify?.missing`: `no field "missing"`,
`first.id`: `"first" is a function, not a value; call it`,
`[upper, 1]`: `"upper" is a function, not a value; call it`,
} {
_, err := checkSrc(t, src)
require.Error(t, err, src)
Expand Down
7 changes: 7 additions & 0 deletions docs/reference/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,13 @@ Check reports, as a `*TypeError` wrapping `ErrType`, every:
- `==`/`!=` that can never be true: different kinds, or a literal outside
the other side's enum (`kind == "bugfix"` when kind is
`"bug_fix" | "feature"`).
- registered function name used as a value instead of called (`first.id`,
`[upper, 1]`). An env entry or bound name with the same name wins.

Check otherwise agrees with Run: what it reports, Run rejects too. A
function used as a value is the one intended exception. Run returns the
function itself, which fails on a field read or when a host stores the
result, but Run accepts it in a condition or a list literal.

`&&` and `||` return one of their operands (see Logical), so they type
as the join of their operand types: `bool` only when both sides are. Joining
Expand Down
9 changes: 5 additions & 4 deletions llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -473,10 +473,11 @@ t, err := p.Check(env, expr.WithFuncType("concat", concatType))

Types: `expr.Any`, `Null()`, `Bool()`, `Number()`, `String()`, `EnumOf(...)`,
`ListOf(t)`, `ObjectOf(fields)` (closed), `OpenObject(fields)`, `t.OrNil()`,
`t.NonNil()`, `Join(a, b)`. Check catches unknown names and fields,
reads of maybe-nil values without `?.`/`try`, wrong-kind operations,
builtin calls with the wrong arguments, and comparisons that are never
true (including against an enum). A nil `*Type` means `Any`.
`t.NonNil()`, `Join(a, b)`. Check catches unknown names and fields, a
registered function used as a value instead of called, reads of
maybe-nil values without `?.`/`try`, wrong-kind operations, builtin
calls with the wrong arguments, and comparisons that are never true
(including against an enum). A nil `*Type` means `Any`.

`Template.Check(env)` checks every `${...}`. `WithFuncType(name, fn)` types
a registered function: `fn(args []*expr.Type) (*expr.Type, error)` returns
Expand Down
Loading