diff --git a/checker/checker.go b/checker/checker.go index e4e2b50d..c214acf3 100644 --- a/checker/checker.go +++ b/checker/checker.go @@ -157,6 +157,12 @@ func (v *checker) IdentifierNode(node *ast.IdentifierNode) (reflect.Type, info) if node.Value == "$env" { return mapType, info{} } + if fn, ok := v.config.Builtins[node.Value]; ok { + return functionType, info{fn: fn} + } + if fn, ok := v.config.Functions[node.Value]; ok { + return functionType, info{fn: fn} + } return v.env(node, node.Value, true) } @@ -166,13 +172,9 @@ type NodeWithIndexes interface { SetMethodIndex(methodIndex int) } +// env method returns type of environment variable. env only lookups for +// environment variables, no builtins, no custom functions. func (v *checker) env(node NodeWithIndexes, name string, strict bool) (reflect.Type, info) { - if fn, ok := v.config.Builtins[name]; ok { - return functionType, info{fn: fn} - } - if fn, ok := v.config.Functions[name]; ok { - return functionType, info{fn: fn} - } if t, ok := v.config.Types[name]; ok { if t.Ambiguous { return v.error(node, "ambiguous identifier %v", name) diff --git a/expr_test.go b/expr_test.go index 873d726f..eb77408b 100644 --- a/expr_test.go +++ b/expr_test.go @@ -1971,7 +1971,32 @@ func TestEnv_keyword(t *testing.T) { }) } +} + +func TestEnv_keyword_with_custom_functions(t *testing.T) { + fn := expr.Function("fn", func(params ...any) (any, error) { + return "ok", nil + }) + + var tests = []struct { + code string + error bool + }{ + {`fn()`, false}, + {`$env.fn()`, true}, + {`$env["fn"]`, true}, + } + for _, tt := range tests { + t.Run(tt.code, func(t *testing.T) { + _, err := expr.Compile(tt.code, expr.Env(mock.Env{}), fn) + if tt.error { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } } func TestIssue401(t *testing.T) {