Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create an error type for unknown function diags #657

Merged
merged 1 commit into from
Feb 14, 2024
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
31 changes: 31 additions & 0 deletions hclsyntax/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
}
}

extraUnknown := &functionCallUnknown{
name: e.Name,
}

// For historical reasons, we represent namespaced function names
// as strings with :: separating the names. If this was an attempt
// to call a namespaced function then we'll try to distinguish
Expand All @@ -274,6 +278,9 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
}
}

extraUnknown.name = name
extraUnknown.namespace = namespace

if len(avail) == 0 {
// TODO: Maybe use nameSuggestion for the other available
// namespaces? But that'd require us to go scan the function
Expand All @@ -291,6 +298,7 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
Context: e.Range().Ptr(),
Expression: e,
EvalContext: ctx,
Extra: extraUnknown,
},
}
} else {
Expand All @@ -308,6 +316,7 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
Context: e.Range().Ptr(),
Expression: e,
EvalContext: ctx,
Extra: extraUnknown,
},
}
}
Expand All @@ -331,6 +340,7 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
Context: e.Range().Ptr(),
Expression: e,
EvalContext: ctx,
Extra: extraUnknown,
},
}
}
Expand Down Expand Up @@ -678,6 +688,27 @@ func (e *functionCallDiagExtra) FunctionCallError() error {
return e.functionCallError
}

// FunctionCallUnknownDiagExtra is an interface implemented by a value in the Extra
// field of some diagnostics to indicate when the error was caused by a call to
// an unknown function.
type FunctionCallUnknownDiagExtra interface {
CalledFunctionName() string
CalledFunctionNamespace() string
}

type functionCallUnknown struct {
name string
namespace string
}

func (e *functionCallUnknown) CalledFunctionName() string {
return e.name
}

func (e *functionCallUnknown) CalledFunctionNamespace() string {
return e.namespace
}

type ConditionalExpr struct {
Condition Expression
TrueResult Expression
Expand Down
65 changes: 65 additions & 0 deletions hclsyntax/expression_typeparams_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,71 @@ func TestExpressionDiagnosticExtra(t *testing.T) {
ctx *hcl.EvalContext
assert func(t *testing.T, diags hcl.Diagnostics)
}{
// Errors for unknown function calls
{
"boop()",
&hcl.EvalContext{
Functions: map[string]function.Function{
"zap": function.New(&function.Spec{
Type: function.StaticReturnType(cty.String),
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
return cty.DynamicVal, fmt.Errorf("the expected error")
},
}),
},
},
func(t *testing.T, diags hcl.Diagnostics) {
t.Helper()
for _, diag := range diags {
extra, ok := hcl.DiagnosticExtra[FunctionCallUnknownDiagExtra](diag)
if !ok {
continue
}

if got, want := extra.CalledFunctionName(), "boop"; got != want {
t.Errorf("wrong called function name %q; want %q", got, want)
}
ns := extra.CalledFunctionNamespace()
if ns != "" {
t.Fatal("expected no namespace, got", ns)
}
return
}
t.Fatalf("None of the returned diagnostics implement FunctionCallUnknownDiagExtra\n%s", diags.Error())
},
},
{
"ns::source::boop()",
&hcl.EvalContext{
Functions: map[string]function.Function{
"zap": function.New(&function.Spec{
Type: function.StaticReturnType(cty.String),
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
return cty.DynamicVal, fmt.Errorf("the expected error")
},
}),
},
},
func(t *testing.T, diags hcl.Diagnostics) {
t.Helper()
for _, diag := range diags {
extra, ok := hcl.DiagnosticExtra[FunctionCallUnknownDiagExtra](diag)
if !ok {
continue
}

if got, want := extra.CalledFunctionName(), "boop"; got != want {
t.Errorf("wrong called function name %q; want %q", got, want)
}
ns := extra.CalledFunctionNamespace()
if ns != "ns::source::" {
t.Fatal("expected namespace ns::source::, got", ns)
}
return
}
t.Fatalf("None of the returned diagnostics implement FunctionCallUnknownDiagExtra\n%s", diags.Error())
},
},
// Error messages describing inconsistent result types for conditional expressions.
{
"boop()",
Expand Down
Loading