From cea35d620675149207594918b95e183fdd58cb52 Mon Sep 17 00:00:00 2001 From: James Humphries Date: Wed, 22 Oct 2025 10:40:53 +0100 Subject: [PATCH] Bump our hcl fork to include fix for Provider defined functions in parentheses (#3402) Signed-off-by: James Humphries --- go.mod | 2 +- go.sum | 4 +- internal/lang/functions.go | 3 +- internal/lang/references_test.go | 102 +++++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 internal/lang/references_test.go diff --git a/go.mod b/go.mod index 70caa37744..fc8366ec66 100644 --- a/go.mod +++ b/go.mod @@ -317,7 +317,7 @@ require ( sigs.k8s.io/yaml v1.6.0 // indirect ) -replace github.com/hashicorp/hcl/v2 v2.20.1 => github.com/opentofu/hcl/v2 v2.20.2-0.20250121132637-504036cd70e7 +replace github.com/hashicorp/hcl/v2 v2.20.1 => github.com/opentofu/hcl/v2 v2.20.2-0.20251021132045-587d123c2828 tool ( github.com/hashicorp/copywrite diff --git a/go.sum b/go.sum index 3ec67d37b6..e43cde1949 100644 --- a/go.sum +++ b/go.sum @@ -674,8 +674,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/opentofu/hcl/v2 v2.20.2-0.20250121132637-504036cd70e7 h1:QHUIrylb/q3pQdQcnAr74cGWsXS1lmA8GqP+RWFMK6U= -github.com/opentofu/hcl/v2 v2.20.2-0.20250121132637-504036cd70e7/go.mod h1:k+HgkLpoWu9OS81sy4j1XKDXaWm/rLysG33v5ibdDnc= +github.com/opentofu/hcl/v2 v2.20.2-0.20251021132045-587d123c2828 h1:r2WiJxljn/Cxwpq9zMZ5HomJcNwmGNjKmeZnFsxkpBg= +github.com/opentofu/hcl/v2 v2.20.2-0.20251021132045-587d123c2828/go.mod h1:k+HgkLpoWu9OS81sy4j1XKDXaWm/rLysG33v5ibdDnc= github.com/opentofu/registry-address/v2 v2.0.0-20250611143131-d0a99bd8acdd h1:YAAnzmyOoMvm5SuGXL4hhlfBgqz92XDfORGPV3kmQFc= github.com/opentofu/registry-address/v2 v2.0.0-20250611143131-d0a99bd8acdd/go.mod h1:7M92SvuJm1WBriIpa4j0XmruU9pxkgPXmRdc6FfAvAk= github.com/opentofu/svchost v0.0.0-20250610175836-86c9e5e3d8c8 h1:J3pmsVB+nGdfNp5HWdEAC96asYgc7S6J724ICrYDCTk= diff --git a/internal/lang/functions.go b/internal/lang/functions.go index a7c6cbd22c..e45271e61e 100644 --- a/internal/lang/functions.go +++ b/internal/lang/functions.go @@ -25,7 +25,8 @@ var impureFunctions = []string{ "uuid", } -// This should probably be replaced with addrs.Function everywhere +// CoreNamespace defines the string prefix used for all core namespaced functions +// TODO: This should probably be replaced with addrs.Function everywhere const CoreNamespace = addrs.FunctionNamespaceCore + "::" // Functions returns the set of functions that should be used to when evaluating diff --git a/internal/lang/references_test.go b/internal/lang/references_test.go new file mode 100644 index 0000000000..8b87cdf97a --- /dev/null +++ b/internal/lang/references_test.go @@ -0,0 +1,102 @@ +// Copyright (c) The OpenTofu Authors +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) 2023 HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package lang + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hclsyntax" + + "github.com/opentofu/opentofu/internal/addrs" +) + +func TestReferencesInExpr(t *testing.T) { + // Note for developers, this list is non-exhaustive right now + // and there are many more expression types. This test is mainly to ensure that provider + // defined function references are returned from references with parentheses + // to resolve issue opentofu/opentofu#3401 + tests := map[string]struct { + exprSrc string + wantFuncRefs []string // List of expected provider function references + }{ + "no functions": { + exprSrc: `"literal"`, + wantFuncRefs: nil, + }, + "provider function without parentheses": { + exprSrc: `provider::testing::echo("hello")`, + wantFuncRefs: []string{"provider::testing::echo"}, + }, + "provider function with parentheses": { + exprSrc: `(provider::testing::echo("hello"))`, + wantFuncRefs: []string{"provider::testing::echo"}, + }, + "provider function in binary expression": { + exprSrc: `(provider::testing::add(1, 2)) + 3`, + wantFuncRefs: []string{"provider::testing::add"}, + }, + "nested parentheses": { + exprSrc: `((provider::testing::echo("hello")))`, + wantFuncRefs: []string{"provider::testing::echo"}, + }, + "provider function in conditional true": { + exprSrc: `true ? provider::testing::echo("yes") : "no"`, + wantFuncRefs: []string{"provider::testing::echo"}, + }, + "provider function in conditional false": { + exprSrc: `false ? "yes" : provider::testing::echo("no")`, + wantFuncRefs: []string{"provider::testing::echo"}, + }, + "provider function in conditional condition": { + exprSrc: `provider::testing::is_true() ? "yes" : "no"`, + wantFuncRefs: []string{"provider::testing::is_true"}, + }, + "multiple provider functions": { + exprSrc: `(provider::testing::add(1, 2)) + (provider::testing::mul(3, 4))`, + wantFuncRefs: []string{"provider::testing::add", "provider::testing::mul"}, + }, + "provider function with alias": { + exprSrc: `(provider::testing::custom::echo("hello"))`, + wantFuncRefs: []string{"provider::testing::custom::echo"}, + }, + "core function not included": { + exprSrc: `(core::max(1, 2))`, + wantFuncRefs: nil, // core functions are not provider functions + }, + "builtin function not included": { + exprSrc: `(length([1, 2, 3]))`, + wantFuncRefs: nil, // builtin functions are not provider functions + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + expr, diags := hclsyntax.ParseExpression([]byte(test.exprSrc), "test.tf", hcl.Pos{Line: 1, Column: 1}) + if diags.HasErrors() { + t.Fatalf("Failed to parse expression: %s", diags.Error()) + } + + refs, refDiags := ReferencesInExpr(addrs.ParseRef, expr) + if refDiags.HasErrors() { + t.Errorf("Unexpected diagnostics: %s", refDiags.Err()) + } + + // Extract provider function references + var gotFuncRefs []string + for _, ref := range refs { + if provFunc, ok := ref.Subject.(addrs.ProviderFunction); ok { + gotFuncRefs = append(gotFuncRefs, provFunc.String()) + } + } + + if diff := cmp.Diff(test.wantFuncRefs, gotFuncRefs); diff != "" { + t.Errorf("Wrong provider function references (-want +got):\n%s", diff) + } + }) + } +}