From a6de02d4141e0f2ec15542b48ff026c4856035f8 Mon Sep 17 00:00:00 2001 From: Katy Moe Date: Wed, 21 Sep 2022 22:34:28 +0100 Subject: [PATCH 01/55] add oss-fuzz build script --- fuzz/oss_fuzz_build.sh | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100755 fuzz/oss_fuzz_build.sh diff --git a/fuzz/oss_fuzz_build.sh b/fuzz/oss_fuzz_build.sh new file mode 100755 index 00000000..cc159a71 --- /dev/null +++ b/fuzz/oss_fuzz_build.sh @@ -0,0 +1,36 @@ +#!/bin/bash -eu +# Copyright 2021 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +################################################################################ + +FUZZERS_BASE=$SRC/hcl/hclsyntax/fuzz +FUZZERS_PACKAGE=github.com/hashicorp/hcl/v2/hclsyntax/fuzz +FUZZER_CLASS=Fuzz + +for THE_FUZZER in config expr template traversal +do + THE_FUZZER_NAME="fuzz_"$THE_FUZZER + compile_go_fuzzer $FUZZERS_PACKAGE/$THE_FUZZER $FUZZER_CLASS $THE_FUZZER_NAME + + OUTDIR=$OUT/$THE_FUZZER_NAME"_seed_corpus" + mkdir $OUTDIR + find $FUZZERS_BASE/$THE_FUZZER/corpus -type f | while read FNAME + do + SHASUM_NAME=$(shasum "$FNAME" | awk '{print $1}') + cp "$FNAME" $OUTDIR + done + zip -r $OUTDIR".zip" $OUTDIR + rm -rf $OUTDIR +done From 95eb6b23af801d68111f48d18826c9952ee499d0 Mon Sep 17 00:00:00 2001 From: "hashicorp-tsccr[bot]" Date: Fri, 29 Sep 2023 07:12:11 +0000 Subject: [PATCH 02/55] Result of tsccr-helper -log-level=info -pin-all-workflows . --- .github/workflows/push.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 34c1eec9..a16f7b6b 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -28,9 +28,9 @@ jobs: run: | git config --global core.autocrlf false - name: "Fetch source code" - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # https://github.com/actions/checkout/releases/tag/v3.2.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Install Go - uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # https://github.com/actions/setup-go/releases/tag/v3.5.0 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: go-version: 1.18 - name: Go test @@ -43,9 +43,9 @@ jobs: steps: - name: "Fetch source code" - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # https://github.com/actions/checkout/releases/tag/v3.2.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Install Go - uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # https://github.com/actions/setup-go/releases/tag/v3.5.0 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: go-version: 1.18 - name: "Check vet" From 5b881c6b475734927293f27a619c289a019917b9 Mon Sep 17 00:00:00 2001 From: Jakub Martin Date: Thu, 5 Oct 2023 12:14:31 +0200 Subject: [PATCH 03/55] Fix error of conditionals with an unknown condition and marked branch. Signed-off-by: Jakub Martin --- hclsyntax/expression.go | 6 +++++- hclsyntax/expression_test.go | 12 ++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/hclsyntax/expression.go b/hclsyntax/expression.go index 5df423fe..5ed35273 100644 --- a/hclsyntax/expression.go +++ b/hclsyntax/expression.go @@ -742,13 +742,17 @@ func (e *ConditionalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostic } } } + _, condResultMarks := condResult.Unmark() + trueResult, trueResultMarks := trueResult.Unmark() + falseResult, falseResultMarks := falseResult.Unmark() + trueRng := trueResult.Range() falseRng := falseResult.Range() ret := cty.UnknownVal(resultType) if trueRng.DefinitelyNotNull() && falseRng.DefinitelyNotNull() { ret = ret.RefineNotNull() } - return ret.WithSameMarks(condResult).WithSameMarks(trueResult).WithSameMarks(falseResult), diags + return ret.WithMarks(condResultMarks, trueResultMarks, falseResultMarks), diags } condResult, err := convert.Convert(condResult, cty.Bool) if err != nil { diff --git a/hclsyntax/expression_test.go b/hclsyntax/expression_test.go index 5a6fedbc..6f761023 100644 --- a/hclsyntax/expression_test.go +++ b/hclsyntax/expression_test.go @@ -2015,6 +2015,18 @@ EOT cty.NumberIntVal(1).Mark("sensitive"), 0, }, + { + `test ? sensitiveString : ""`, + &hcl.EvalContext{ + Functions: map[string]function.Function{}, + Variables: map[string]cty.Value{ + "test": cty.UnknownVal(cty.Bool), + "sensitiveString": cty.StringVal("test").Mark("sensitive"), + }, + }, + cty.UnknownVal(cty.String).RefineNotNull().Mark("sensitive"), + 0, + }, } for _, test := range tests { From a1178d26585345a7aaf65c557e46c640806bb63d Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Thu, 5 Oct 2023 18:44:43 -0700 Subject: [PATCH 04/55] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d523b52..56537471 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # HCL Changelog +## v2.18.1 (October 5, 2023) + +### Bugs Fixed + +* hclsyntax: Conditional expressions will no longer panic when one or both of their results are "marked", as is the case for situations like how HashiCorp Terraform tracks its concept of "sensitive values". ([#630](https://github.com/hashicorp/hcl/pull/630)) + ## v2.18.0 (August 30, 2023) ### Enhancements From 6ec71247d9d00d0e22cc0d33636844380bc11463 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Fri, 6 Oct 2023 10:39:26 -0700 Subject: [PATCH 05/55] hclsyntax: New tests for marks+refinments together The interactions between value marks and unknown value refinements can be a little tricky, so this pair of new tests cover two examples of that interaction that are currently working and ought to stay that way. --- hclsyntax/expression_test.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/hclsyntax/expression_test.go b/hclsyntax/expression_test.go index 6f761023..1af93dad 100644 --- a/hclsyntax/expression_test.go +++ b/hclsyntax/expression_test.go @@ -1397,6 +1397,26 @@ upper( }).Mark("sensitive"), 0, }, + { // splat with sensitive collection that's unknown + `maps.*.enabled`, + &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "maps": cty.UnknownVal(cty.List(cty.Map(cty.Bool))).Mark("sensitive"), + }, + }, + cty.UnknownVal(cty.List(cty.Bool)).RefineNotNull().Mark("sensitive"), + 0, + }, + { // splat with sensitive collection that's unknown and not null + `maps.*.enabled`, + &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "maps": cty.UnknownVal(cty.List(cty.Map(cty.Bool))).RefineNotNull().Mark("sensitive"), + }, + }, + cty.UnknownVal(cty.List(cty.Bool)).RefineNotNull().Mark("sensitive"), + 0, + }, { // splat with collection with sensitive elements `maps.*.x`, &hcl.EvalContext{ From 63067e819cb6e8044d34b0a32d198a4941f34e42 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Fri, 6 Oct 2023 10:40:55 -0700 Subject: [PATCH 06/55] hcldec: New test for marks+refinements together The interactions between value marks and unknown value refinements can be a little tricky, so this new addition to the "RefineWith" tests confirms that it does indeed handle marked values correctly when passing through the refinement spec. --- hcldec/spec_test.go | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/hcldec/spec_test.go b/hcldec/spec_test.go index 25c5d6cb..1b0594d1 100644 --- a/hcldec/spec_test.go +++ b/hcldec/spec_test.go @@ -12,6 +12,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/zclconf/go-cty-debug/ctydebug" "github.com/zclconf/go-cty/cty" + "github.com/zclconf/go-cty/cty/function" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" @@ -218,6 +219,7 @@ func TestRefineValueSpec(t *testing.T) { foo = "hello" bar = unk dyn = dyn +marked = mark(unk) ` f, diags := hclsyntax.ParseConfig([]byte(config), "", hcl.InitialPos) @@ -256,9 +258,10 @@ dyn = dyn } } spec := &ObjectSpec{ - "foo": attrSpec("foo"), - "bar": attrSpec("bar"), - "dyn": attrSpec("dyn"), + "foo": attrSpec("foo"), + "bar": attrSpec("bar"), + "dyn": attrSpec("dyn"), + "marked": attrSpec("marked"), } got, diags := Decode(f.Body, spec, &hcl.EvalContext{ @@ -266,6 +269,26 @@ dyn = dyn "unk": cty.UnknownVal(cty.String), "dyn": cty.DynamicVal, }, + Functions: map[string]function.Function{ + "mark": function.New(&function.Spec{ + Params: []function.Parameter{ + { + Name: "v", + Type: cty.DynamicPseudoType, + AllowMarked: true, + AllowNull: true, + AllowUnknown: true, + AllowDynamicType: true, + }, + }, + Type: func(args []cty.Value) (cty.Type, error) { + return args[0].Type(), nil + }, + Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { + return args[0].Mark("boop"), nil + }, + }), + }, }) if diags.HasErrors() { t.Fatal(diags.Error()) @@ -284,6 +307,10 @@ dyn = dyn // Correct behavior here requires that we convert the DynamicVal // to an unknown string first and then refine it. "dyn": cty.UnknownVal(cty.String).RefineNotNull(), + + // This argument had a mark applied, which should be preserved + // despite the refinement. + "marked": cty.UnknownVal(cty.String).RefineNotNull().Mark("boop"), }) if diff := cmp.Diff(want, got, ctydebug.CmpOptions); diff != "" { t.Errorf("wrong result\n%s", diff) From 3a3033373598fe15e8cf82f6151168880a8dcbc9 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 11 Oct 2023 10:13:14 -0400 Subject: [PATCH 07/55] refinements of collections must use Range() When attempting to determine the final length range for a conditional expression with collections, the length values may still be unknown. Always use `Range()` to get the lower and upper bounds. --- hclsyntax/expression.go | 16 +++++++--------- hclsyntax/expression_test.go | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/hclsyntax/expression.go b/hclsyntax/expression.go index 5ed35273..71eb46ee 100644 --- a/hclsyntax/expression.go +++ b/hclsyntax/expression.go @@ -725,16 +725,14 @@ func (e *ConditionalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostic falseLen := falseResult.Length() if gt := trueLen.GreaterThan(falseLen); gt.IsKnown() { b := cty.UnknownVal(resultType).Refine() - trueLen, _ := trueLen.AsBigFloat().Int64() - falseLen, _ := falseLen.AsBigFloat().Int64() if gt.True() { b = b. - CollectionLengthLowerBound(int(falseLen)). - CollectionLengthUpperBound(int(trueLen)) + CollectionLengthLowerBound(falseResult.Range().LengthLowerBound()). + CollectionLengthUpperBound(trueResult.Range().LengthUpperBound()) } else { b = b. - CollectionLengthLowerBound(int(trueLen)). - CollectionLengthUpperBound(int(falseLen)) + CollectionLengthLowerBound(trueResult.Range().LengthLowerBound()). + CollectionLengthUpperBound(falseResult.Range().LengthUpperBound()) } b = b.NotNull() // If neither of the results is null then the result can't be either return b.NewValue().WithSameMarks(condResult).WithSameMarks(trueResult).WithSameMarks(falseResult), diags @@ -1244,9 +1242,9 @@ func (e *ObjectConsKeyExpr) UnwrapExpression() Expression { // ForExpr represents iteration constructs: // -// tuple = [for i, v in list: upper(v) if i > 2] -// object = {for k, v in map: k => upper(v)} -// object_of_tuples = {for v in list: v.key: v...} +// tuple = [for i, v in list: upper(v) if i > 2] +// object = {for k, v in map: k => upper(v)} +// object_of_tuples = {for v in list: v.key: v...} type ForExpr struct { KeyVar string // empty if ignoring the key ValVar string diff --git a/hclsyntax/expression_test.go b/hclsyntax/expression_test.go index 1af93dad..e64047a0 100644 --- a/hclsyntax/expression_test.go +++ b/hclsyntax/expression_test.go @@ -1957,6 +1957,20 @@ EOT cty.ListValEmpty(cty.String), // deduced through refinements 0, }, + { + `unknown ? ar : br`, + &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "unknown": cty.UnknownVal(cty.Bool), + "ar": cty.UnknownVal(cty.Set(cty.String)).Refine(). + CollectionLengthLowerBound(1).CollectionLengthUpperBound(2).NewValue(), + "br": cty.UnknownVal(cty.Set(cty.String)).Refine(). + CollectionLengthLowerBound(3).CollectionLengthUpperBound(4).NewValue(), + }, + }, + cty.UnknownVal(cty.Set(cty.String)).Refine().NotNull().CollectionLengthLowerBound(1).CollectionLengthUpperBound(4).NewValue(), // deduced through refinements + 0, + }, { `unknown ? a : b`, &hcl.EvalContext{ From e4589e3cfa1d12e457d5f06cc37812b8264f7d0c Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 11 Oct 2023 14:19:52 -0400 Subject: [PATCH 08/55] Range() calls must always be unmarked --- hclsyntax/expression.go | 23 +++++++++++++++-------- hclsyntax/expression_test.go | 14 ++++++++++++++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/hclsyntax/expression.go b/hclsyntax/expression.go index 71eb46ee..8192d43a 100644 --- a/hclsyntax/expression.go +++ b/hclsyntax/expression.go @@ -721,18 +721,24 @@ func (e *ConditionalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostic if trueResult.Type().IsCollectionType() && falseResult.Type().IsCollectionType() { if trueResult.Type().Equals(falseResult.Type()) { if !(trueResult.IsNull() || falseResult.IsNull()) { - trueLen := trueResult.Length() - falseLen := falseResult.Length() - if gt := trueLen.GreaterThan(falseLen); gt.IsKnown() { + // the bounds are not part of the final result value, so + // the marks are not needed + tr, _ := trueResult.Unmark() + fr, _ := falseResult.Unmark() + trueRange := tr.Range() + falseRange := fr.Range() + + if gt := trueResult.Length().GreaterThan(falseResult.Length()); gt.IsKnown() { + gt, _ := gt.Unmark() b := cty.UnknownVal(resultType).Refine() if gt.True() { b = b. - CollectionLengthLowerBound(falseResult.Range().LengthLowerBound()). - CollectionLengthUpperBound(trueResult.Range().LengthUpperBound()) + CollectionLengthLowerBound(falseRange.LengthLowerBound()). + CollectionLengthUpperBound(trueRange.LengthUpperBound()) } else { b = b. - CollectionLengthLowerBound(trueResult.Range().LengthLowerBound()). - CollectionLengthUpperBound(falseResult.Range().LengthUpperBound()) + CollectionLengthLowerBound(trueRange.LengthLowerBound()). + CollectionLengthUpperBound(falseRange.LengthUpperBound()) } b = b.NotNull() // If neither of the results is null then the result can't be either return b.NewValue().WithSameMarks(condResult).WithSameMarks(trueResult).WithSameMarks(falseResult), diags @@ -1740,7 +1746,8 @@ func (e *SplatExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { if ty.IsListType() && sourceVal.Type().IsCollectionType() { // We can refine the length of an unknown list result based on // the source collection's own length. - sourceRng := sourceVal.Range() + sv, _ := sourceVal.Unmark() + sourceRng := sv.Range() ret = ret.Refine(). CollectionLengthLowerBound(sourceRng.LengthLowerBound()). CollectionLengthUpperBound(sourceRng.LengthUpperBound()). diff --git a/hclsyntax/expression_test.go b/hclsyntax/expression_test.go index e64047a0..3bfef63c 100644 --- a/hclsyntax/expression_test.go +++ b/hclsyntax/expression_test.go @@ -1971,6 +1971,20 @@ EOT cty.UnknownVal(cty.Set(cty.String)).Refine().NotNull().CollectionLengthLowerBound(1).CollectionLengthUpperBound(4).NewValue(), // deduced through refinements 0, }, + { + `unknown ? amr : bmr`, + &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "unknown": cty.UnknownVal(cty.Bool), + "amr": cty.UnknownVal(cty.Set(cty.String)).Mark("test").Refine(). + CollectionLengthLowerBound(1).CollectionLengthUpperBound(2).NewValue(), + "bmr": cty.UnknownVal(cty.Set(cty.String)).Mark("test").Refine(). + CollectionLengthLowerBound(3).CollectionLengthUpperBound(4).NewValue(), + }, + }, + cty.UnknownVal(cty.Set(cty.String)).Refine().NotNull().CollectionLengthLowerBound(1).CollectionLengthUpperBound(4).NewValue().Mark("test"), // deduced through refinements + 0, + }, { `unknown ? a : b`, &hcl.EvalContext{ From bad33d51fc6add9a3f777405e6efb0a9a0f87b29 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 11 Oct 2023 17:31:35 -0400 Subject: [PATCH 09/55] further refine refinement handling Correct mark handling for some conditional values. Find correct refinement for overlapping ranges which could not have been compared with `GreaterThan`. Also map inclusive flags for numeric ranges. Correct handling of DefinitelyNotNull collections. Return a known null early when both conditional branches are null. --- hclsyntax/expression.go | 119 +++++++++++++++++++++-------------- hclsyntax/expression_test.go | 104 ++++++++++++++++++++++++++++-- 2 files changed, 172 insertions(+), 51 deletions(-) diff --git a/hclsyntax/expression.go b/hclsyntax/expression.go index 8192d43a..e0de1c3d 100644 --- a/hclsyntax/expression.go +++ b/hclsyntax/expression.go @@ -696,68 +696,95 @@ func (e *ConditionalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostic return cty.UnknownVal(resultType), diags } if !condResult.IsKnown() { + // we use the unmarked values throughout the unknown branch + _, condResultMarks := condResult.Unmark() + trueResult, trueResultMarks := trueResult.Unmark() + falseResult, falseResultMarks := falseResult.Unmark() + + // use a value to merge marks + _, resMarks := cty.DynamicVal.WithMarks(condResultMarks, trueResultMarks, falseResultMarks).Unmark() + + trueRange := trueResult.Range() + falseRange := falseResult.Range() + + // if both branches are known to be null, then the result must still be null + if trueResult.IsNull() && falseResult.IsNull() { + return cty.NullVal(resultType).WithMarks(resMarks), diags + } + // We might be able to offer a refined range for the result based on // the two possible outcomes. if trueResult.Type() == cty.Number && falseResult.Type() == cty.Number { - // This case deals with the common case of (predicate ? 1 : 0) and - // significantly decreases the range of the result in that case. - if !(trueResult.IsNull() || falseResult.IsNull()) { - if gt := trueResult.GreaterThan(falseResult); gt.IsKnown() { - b := cty.UnknownVal(cty.Number).Refine() - if gt.True() { - b = b. - NumberRangeLowerBound(falseResult, true). - NumberRangeUpperBound(trueResult, true) - } else { - b = b. - NumberRangeLowerBound(trueResult, true). - NumberRangeUpperBound(falseResult, true) - } - b = b.NotNull() // If neither of the results is null then the result can't be either - return b.NewValue().WithSameMarks(condResult).WithSameMarks(trueResult).WithSameMarks(falseResult), diags + ref := cty.UnknownVal(cty.Number).Refine() + if trueRange.DefinitelyNotNull() && falseRange.DefinitelyNotNull() { + ref = ref.NotNull() + } + + falseLo, falseLoInc := falseRange.NumberLowerBound() + falseHi, falseHiInc := falseRange.NumberUpperBound() + trueLo, trueLoInc := trueRange.NumberLowerBound() + trueHi, trueHiInc := trueRange.NumberUpperBound() + + if falseLo.IsKnown() && trueLo.IsKnown() { + lo, loInc := falseLo, falseLoInc + switch { + case trueLo.LessThan(falseLo).True(): + lo, loInc = trueLo, trueLoInc + case trueLo.Equals(falseLo).True(): + loInc = trueLoInc || falseLoInc } + + ref = ref.NumberRangeLowerBound(lo, loInc) } + + if falseHi.IsKnown() && trueHi.IsKnown() { + hi, hiInc := falseHi, falseHiInc + switch { + case trueHi.GreaterThan(falseHi).True(): + hi, hiInc = trueHi, trueHiInc + case trueHi.Equals(falseHi).True(): + hiInc = trueHiInc || falseHiInc + } + ref = ref.NumberRangeUpperBound(hi, hiInc) + } + + return ref.NewValue().WithMarks(resMarks), diags } + if trueResult.Type().IsCollectionType() && falseResult.Type().IsCollectionType() { if trueResult.Type().Equals(falseResult.Type()) { - if !(trueResult.IsNull() || falseResult.IsNull()) { - // the bounds are not part of the final result value, so - // the marks are not needed - tr, _ := trueResult.Unmark() - fr, _ := falseResult.Unmark() - trueRange := tr.Range() - falseRange := fr.Range() - - if gt := trueResult.Length().GreaterThan(falseResult.Length()); gt.IsKnown() { - gt, _ := gt.Unmark() - b := cty.UnknownVal(resultType).Refine() - if gt.True() { - b = b. - CollectionLengthLowerBound(falseRange.LengthLowerBound()). - CollectionLengthUpperBound(trueRange.LengthUpperBound()) - } else { - b = b. - CollectionLengthLowerBound(trueRange.LengthLowerBound()). - CollectionLengthUpperBound(falseRange.LengthUpperBound()) - } - b = b.NotNull() // If neither of the results is null then the result can't be either - return b.NewValue().WithSameMarks(condResult).WithSameMarks(trueResult).WithSameMarks(falseResult), diags - } + ref := cty.UnknownVal(resultType).Refine() + if trueRange.DefinitelyNotNull() && falseRange.DefinitelyNotNull() { + ref = ref.NotNull() + } + + falseLo := falseRange.LengthLowerBound() + falseHi := falseRange.LengthUpperBound() + trueLo := trueRange.LengthLowerBound() + trueHi := trueRange.LengthUpperBound() + + lo := falseLo + if trueLo < falseLo { + lo = trueLo } + + hi := falseHi + if trueHi > falseHi { + hi = trueHi + } + + ref = ref.CollectionLengthLowerBound(lo).CollectionLengthUpperBound(hi) + return ref.NewValue().WithMarks(resMarks), diags } } - _, condResultMarks := condResult.Unmark() - trueResult, trueResultMarks := trueResult.Unmark() - falseResult, falseResultMarks := falseResult.Unmark() - trueRng := trueResult.Range() - falseRng := falseResult.Range() ret := cty.UnknownVal(resultType) - if trueRng.DefinitelyNotNull() && falseRng.DefinitelyNotNull() { + if trueRange.DefinitelyNotNull() && falseRange.DefinitelyNotNull() { ret = ret.RefineNotNull() } - return ret.WithMarks(condResultMarks, trueResultMarks, falseResultMarks), diags + return ret.WithMarks(resMarks), diags } + condResult, err := convert.Convert(condResult, cty.Bool) if err != nil { diags = append(diags, &hcl.Diagnostic{ diff --git a/hclsyntax/expression_test.go b/hclsyntax/expression_test.go index 3bfef63c..e71dd310 100644 --- a/hclsyntax/expression_test.go +++ b/hclsyntax/expression_test.go @@ -1933,6 +1933,74 @@ EOT NewValue(), 0, }, + { + `unknown ? i : j`, + &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "unknown": cty.UnknownVal(cty.Bool), + "i": cty.NullVal(cty.Number), + "j": cty.NullVal(cty.Number), + }, + }, + cty.NullVal(cty.Number), + 0, + }, + { + `unknown ? im : jm`, + &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "unknown": cty.UnknownVal(cty.Bool), + "im": cty.NullVal(cty.Number).Mark("a"), + "jm": cty.NullVal(cty.Number).Mark("b"), + }, + }, + cty.NullVal(cty.Number).Mark("a").Mark("b"), + 0, + }, + { + `unknown ? im : jm`, + &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "unknown": cty.UnknownVal(cty.Bool).Mark("a"), + "im": cty.UnknownVal(cty.Number), + "jm": cty.UnknownVal(cty.Number).Mark("b"), + }, + }, + // the empty refinement may eventually be removed, but does nothing here + cty.UnknownVal(cty.Number).Refine().NewValue().Mark("a").Mark("b"), + 0, + }, + { + `unknown ? ix : jx`, + &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "unknown": cty.UnknownVal(cty.Bool), + "ix": cty.UnknownVal(cty.Number), + "jx": cty.UnknownVal(cty.Number), + }, + }, + // the empty refinement may eventually be removed, but does nothing here + cty.UnknownVal(cty.Number).Refine().NewValue(), + 0, + }, + { + `unknown ? ir : jr`, + &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "unknown": cty.UnknownVal(cty.Bool), + "ir": cty.UnknownVal(cty.Number).Refine(). + NumberRangeLowerBound(cty.NumberIntVal(1), false). + NumberRangeUpperBound(cty.NumberIntVal(3), false).NewValue(), + "jr": cty.UnknownVal(cty.Number).Refine(). + NumberRangeLowerBound(cty.NumberIntVal(2), true). + NumberRangeUpperBound(cty.NumberIntVal(4), true).NewValue(), + }, + }, + cty.UnknownVal(cty.Number).Refine(). + NumberRangeLowerBound(cty.NumberIntVal(1), false). + NumberRangeUpperBound(cty.NumberIntVal(4), true).NewValue(), + 0, + }, { `unknown ? a : b`, &hcl.EvalContext{ @@ -1946,25 +2014,51 @@ EOT 0, }, { - `unknown ? a : b`, + `unknown ? al : bl`, &hcl.EvalContext{ Variables: map[string]cty.Value{ "unknown": cty.UnknownVal(cty.Bool), - "a": cty.ListValEmpty(cty.String), - "b": cty.ListValEmpty(cty.String), + "al": cty.ListValEmpty(cty.String), + "bl": cty.ListValEmpty(cty.String), }, }, cty.ListValEmpty(cty.String), // deduced through refinements 0, }, + { + `unknown ? am : bm`, + &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "unknown": cty.UnknownVal(cty.Bool), + "am": cty.MapValEmpty(cty.String), + "bm": cty.MapValEmpty(cty.String).Mark("test"), + }, + }, + cty.MapValEmpty(cty.String).Mark("test"), // deduced through refinements + 0, + }, { `unknown ? ar : br`, &hcl.EvalContext{ Variables: map[string]cty.Value{ "unknown": cty.UnknownVal(cty.Bool), "ar": cty.UnknownVal(cty.Set(cty.String)).Refine(). - CollectionLengthLowerBound(1).CollectionLengthUpperBound(2).NewValue(), + CollectionLengthLowerBound(1).CollectionLengthUpperBound(3).NewValue(), "br": cty.UnknownVal(cty.Set(cty.String)).Refine(). + CollectionLengthLowerBound(2).CollectionLengthUpperBound(4).NewValue(), + }, + }, + cty.UnknownVal(cty.Set(cty.String)).Refine().CollectionLengthLowerBound(1).CollectionLengthUpperBound(4).NewValue(), // deduced through refinements + 0, + }, + { + `unknown ? arn : brn`, + &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "unknown": cty.UnknownVal(cty.Bool), + "arn": cty.UnknownVal(cty.Set(cty.String)).Refine().NotNull(). + CollectionLengthLowerBound(1).CollectionLengthUpperBound(2).NewValue(), + "brn": cty.UnknownVal(cty.Set(cty.String)).Refine().NotNull(). CollectionLengthLowerBound(3).CollectionLengthUpperBound(4).NewValue(), }, }, @@ -1982,7 +2076,7 @@ EOT CollectionLengthLowerBound(3).CollectionLengthUpperBound(4).NewValue(), }, }, - cty.UnknownVal(cty.Set(cty.String)).Refine().NotNull().CollectionLengthLowerBound(1).CollectionLengthUpperBound(4).NewValue().Mark("test"), // deduced through refinements + cty.UnknownVal(cty.Set(cty.String)).Refine().CollectionLengthLowerBound(1).CollectionLengthUpperBound(4).NewValue().Mark("test"), // deduced through refinements 0, }, { From 0af4fe25743d8b0126b745f3c78148b5ae95f5fe Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Thu, 12 Oct 2023 10:06:02 -0700 Subject: [PATCH 10/55] ext/dynblock: Allow callers to veto for_each values Callers might have additional rules for what's acceptable in a for_each value for a dynamic block. For example, Terraform wants to forbid using sensitive values here because it would cause the expansion to disclose the length of the given collection. Therefore this provides a hook point for callers to insert additional checks just after the for_each expression has been evaluated and before any of the built-in checks are run. This introduces the "functional options" pattern for ExpandBlock for the first time, as a way to extend the API without breaking compatibility with existing callers. There is currently only this one option. --- ext/dynblock/expand_body.go | 4 ++ ext/dynblock/expand_body_test.go | 85 ++++++++++++++++++++++++++++++++ ext/dynblock/expand_spec.go | 10 ++++ ext/dynblock/options.go | 23 +++++++++ ext/dynblock/public.go | 38 +++++++------- 5 files changed, 143 insertions(+), 17 deletions(-) create mode 100644 ext/dynblock/options.go diff --git a/ext/dynblock/expand_body.go b/ext/dynblock/expand_body.go index 0d870842..2734e937 100644 --- a/ext/dynblock/expand_body.go +++ b/ext/dynblock/expand_body.go @@ -17,6 +17,8 @@ type expandBody struct { forEachCtx *hcl.EvalContext iteration *iteration // non-nil if we're nested inside another "dynamic" block + checkForEach []func(cty.Value, hcl.Expression, *hcl.EvalContext) hcl.Diagnostics + // These are used with PartialContent to produce a "remaining items" // body to return. They are nil on all bodies fresh out of the transformer. // @@ -66,6 +68,7 @@ func (b *expandBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, h original: b.original, forEachCtx: b.forEachCtx, iteration: b.iteration, + checkForEach: b.checkForEach, hiddenAttrs: make(map[string]struct{}), hiddenBlocks: make(map[string]hcl.BlockHeaderSchema), } @@ -236,6 +239,7 @@ func (b *expandBody) expandChild(child hcl.Body, i *iteration) hcl.Body { chiCtx := i.EvalContext(b.forEachCtx) ret := Expand(child, chiCtx) ret.(*expandBody).iteration = i + ret.(*expandBody).checkForEach = b.checkForEach return ret } diff --git a/ext/dynblock/expand_body_test.go b/ext/dynblock/expand_body_test.go index ccc4d303..fd440783 100644 --- a/ext/dynblock/expand_body_test.go +++ b/ext/dynblock/expand_body_test.go @@ -7,9 +7,11 @@ import ( "strings" "testing" + "github.com/davecgh/go-spew/spew" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/hcl/v2/hcltest" + "github.com/zclconf/go-cty-debug/ctydebug" "github.com/zclconf/go-cty/cty" ) @@ -336,6 +338,89 @@ func TestExpand(t *testing.T) { } +func TestExpandWithForEachCheck(t *testing.T) { + forEachExpr := hcltest.MockExprLiteral(cty.MapValEmpty(cty.String).Mark("boop")) + evalCtx := &hcl.EvalContext{} + srcContent := &hcl.BodyContent{ + Blocks: hcl.Blocks{ + { + Type: "dynamic", + Labels: []string{"foo"}, + LabelRanges: []hcl.Range{{}}, + Body: hcltest.MockBody(&hcl.BodyContent{ + Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ + "for_each": forEachExpr, + }), + Blocks: hcl.Blocks{ + { + Type: "content", + Body: hcltest.MockBody(&hcl.BodyContent{}), + }, + }, + }), + }, + }, + } + srcBody := hcltest.MockBody(srcContent) + + hookCalled := false + var gotV cty.Value + var gotEvalCtx *hcl.EvalContext + + expBody := Expand( + srcBody, evalCtx, + OptCheckForEach(func(v cty.Value, e hcl.Expression, ec *hcl.EvalContext) hcl.Diagnostics { + hookCalled = true + gotV = v + gotEvalCtx = ec + return hcl.Diagnostics{ + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Bad for_each", + Detail: "I don't like it.", + Expression: e, + EvalContext: ec, + Extra: "diagnostic extra", + }, + } + }), + ) + + _, diags := expBody.Content(&hcl.BodySchema{ + Blocks: []hcl.BlockHeaderSchema{ + { + Type: "foo", + }, + }, + }) + if !diags.HasErrors() { + t.Fatal("succeeded; want an error") + } + if len(diags) != 1 { + t.Fatalf("wrong number of diagnostics; want only one\n%s", spew.Sdump(diags)) + } + if got, want := diags[0].Summary, "Bad for_each"; got != want { + t.Fatalf("wrong error\ngot: %s\nwant: %s\n\n%s", got, want, spew.Sdump(diags[0])) + } + if got, want := diags[0].Extra, "diagnostic extra"; got != want { + // This is important to allow the application which provided the + // hook to pass application-specific extra values through this + // API in case the hook's diagnostics need some sort of special + // treatment. + t.Fatalf("diagnostic didn't preserve 'extra' field\ngot: %s\nwant: %s\n\n%s", got, want, spew.Sdump(diags[0])) + } + + if !hookCalled { + t.Fatal("check hook wasn't called") + } + if !gotV.HasMark("boop") { + t.Errorf("wrong value passed to check hook; want the value marked \"boop\"\n%s", ctydebug.ValueString(gotV)) + } + if gotEvalCtx != evalCtx { + t.Error("wrong EvalContext passed to check hook; want the one passed to Expand") + } +} + func TestExpandUnknownBodies(t *testing.T) { srcContent := &hcl.BodyContent{ Blocks: hcl.Blocks{ diff --git a/ext/dynblock/expand_spec.go b/ext/dynblock/expand_spec.go index 23295d33..9585172e 100644 --- a/ext/dynblock/expand_spec.go +++ b/ext/dynblock/expand_spec.go @@ -43,6 +43,16 @@ func (b *expandBody) decodeSpec(blockS *hcl.BlockHeaderSchema, rawSpec *hcl.Bloc eachAttr := specContent.Attributes["for_each"] eachVal, eachDiags := eachAttr.Expr.Value(b.forEachCtx) diags = append(diags, eachDiags...) + if diags.HasErrors() { + return nil, diags + } + for _, check := range b.checkForEach { + moreDiags := check(eachVal, eachAttr.Expr, b.forEachCtx) + diags = append(diags, moreDiags...) + if moreDiags.HasErrors() { + return nil, diags + } + } if !eachVal.CanIterateElements() && eachVal.Type() != cty.DynamicPseudoType { // We skip this error for DynamicPseudoType because that means we either diff --git a/ext/dynblock/options.go b/ext/dynblock/options.go new file mode 100644 index 00000000..fc7dc0bd --- /dev/null +++ b/ext/dynblock/options.go @@ -0,0 +1,23 @@ +package dynblock + +import ( + "github.com/hashicorp/hcl/v2" + "github.com/zclconf/go-cty/cty" +) + +type ExpandOption interface { + applyExpandOption(*expandBody) +} + +type optCheckForEach struct { + check func(cty.Value, hcl.Expression, *hcl.EvalContext) hcl.Diagnostics +} + +func OptCheckForEach(check func(cty.Value, hcl.Expression, *hcl.EvalContext) hcl.Diagnostics) ExpandOption { + return optCheckForEach{check} +} + +// applyExpandOption implements ExpandOption. +func (o optCheckForEach) applyExpandOption(body *expandBody) { + body.checkForEach = append(body.checkForEach, o.check) +} diff --git a/ext/dynblock/public.go b/ext/dynblock/public.go index abee67c4..79b6144c 100644 --- a/ext/dynblock/public.go +++ b/ext/dynblock/public.go @@ -27,24 +27,28 @@ import ( // multi-dimensional iteration. However, it is not possible to // dynamically-generate the "dynamic" blocks themselves except through nesting. // -// parent { -// dynamic "child" { -// for_each = child_objs -// content { -// dynamic "grandchild" { -// for_each = child.value.children -// labels = [grandchild.key] -// content { -// parent_key = child.key -// value = grandchild.value -// } -// } -// } -// } -// } -func Expand(body hcl.Body, ctx *hcl.EvalContext) hcl.Body { - return &expandBody{ +// parent { +// dynamic "child" { +// for_each = child_objs +// content { +// dynamic "grandchild" { +// for_each = child.value.children +// labels = [grandchild.key] +// content { +// parent_key = child.key +// value = grandchild.value +// } +// } +// } +// } +// } +func Expand(body hcl.Body, ctx *hcl.EvalContext, opts ...ExpandOption) hcl.Body { + ret := &expandBody{ original: body, forEachCtx: ctx, } + for _, opt := range opts { + opt.applyExpandOption(ret) + } + return ret } From 925bfe8bf2ca08ab926e41aece4957befb069a8b Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Mon, 16 Oct 2023 09:26:03 -0700 Subject: [PATCH 11/55] CHANGELOG: prepare for v2.19.0 release --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56537471..f3fe93d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # HCL Changelog +## v2.19.0 (October 16, 2023) + +### Enhancements + +* ext/dynblock: `dynblock.Expand` now supports an optional hook for calling applications to check and potentially veto (by returning error diagnostics) particular `for_each` values. The behavior is unchanged for callers that don't set the new option. ([#634](https://github.com/hashicorp/hcl/pull/634)) + +### Bugs Fixed + +* hclsyntax: Further fixes for treatment of "marked" values in the conditional expression, and better tracking of refined values into the conditional expression results, building on the fixes from v2.18.1. ([#633](https://github.com/hashicorp/hcl/pull/633)) + ## v2.18.1 (October 5, 2023) ### Bugs Fixed From 9847e90ca72456cec739068169fae12ce6f1ce5c Mon Sep 17 00:00:00 2001 From: James Bardin Date: Tue, 17 Oct 2023 15:51:30 -0400 Subject: [PATCH 12/55] hcldec must use WithoutOptionalAttributesDeep When hcldec needs to return a synthetic value, it must use WithoutOptionalAttributesDeep to ensure the correct type for Null or Unknown values. Optional attributes are normally removed from the value type when decoding, but since hcldec is dealing with the decoder spec directly, the optional attr types are still going to be present in these cases. --- hcldec/public_test.go | 28 ++++++++++++++++++++++++++++ hcldec/spec.go | 31 +++++++++++++++---------------- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/hcldec/public_test.go b/hcldec/public_test.go index edd0c534..0087c051 100644 --- a/hcldec/public_test.go +++ b/hcldec/public_test.go @@ -137,6 +137,20 @@ func TestDecode(t *testing.T) { cty.NullVal(cty.Number), 1, // attribute "a" is required }, + { + ``, + &AttrSpec{ + Name: "a", + Type: cty.ObjectWithOptionalAttrs(map[string]cty.Type{ + "attr": cty.String, + }, []string{"attr"}), + }, + nil, + cty.NullVal(cty.Object(map[string]cty.Type{ + "attr": cty.String, + })), + 0, + }, { ` @@ -328,6 +342,20 @@ b { cty.NullVal(cty.Map(cty.String)), 1, // missing b block }, + { + ``, + &BlockAttrsSpec{ + TypeName: "b", + ElementType: cty.ObjectWithOptionalAttrs(map[string]cty.Type{ + "attr": cty.String, + }, []string{"attr"}), + }, + nil, + cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{ + "attr": cty.String, + }))), + 0, + }, { ` b { diff --git a/hcldec/spec.go b/hcldec/spec.go index 7fc1ffbf..2bebc433 100644 --- a/hcldec/spec.go +++ b/hcldec/spec.go @@ -200,13 +200,13 @@ func (s *AttrSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ct if !exists { // We don't need to check required and emit a diagnostic here, because // that would already have happened when building "content". - return cty.NullVal(s.Type), nil + return cty.NullVal(s.Type.WithoutOptionalAttributesDeep()), nil } if decodeFn := customdecode.CustomExpressionDecoderForType(s.Type); decodeFn != nil { v, diags := decodeFn(attr.Expr, ctx) if v == cty.NilVal { - v = cty.UnknownVal(s.Type) + v = cty.UnknownVal(s.Type.WithoutOptionalAttributesDeep()) } return v, diags } @@ -229,7 +229,7 @@ func (s *AttrSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ct }) // We'll return an unknown value of the _correct_ type so that the // incomplete result can still be used for some analysis use-cases. - val = cty.UnknownVal(s.Type) + val = cty.UnknownVal(s.Type.WithoutOptionalAttributesDeep()) } else { val = convVal } @@ -381,7 +381,7 @@ func (s *BlockSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, c Subject: &content.MissingItemRange, }) } - return cty.NullVal(s.Nested.impliedType()), diags + return cty.NullVal(s.Nested.impliedType().WithoutOptionalAttributesDeep()), diags } if s.Nested == nil { @@ -478,7 +478,7 @@ func (s *BlockListSpec) decode(content *hcl.BodyContent, blockLabels []blockLabe if u.Unknown() { // If any block Body is unknown, then the entire block value // must be unknown - return cty.UnknownVal(s.impliedType()), diags + return cty.UnknownVal(s.impliedType().WithoutOptionalAttributesDeep()), diags } } @@ -640,7 +640,7 @@ func (s *BlockTupleSpec) decode(content *hcl.BodyContent, blockLabels []blockLab if u.Unknown() { // If any block Body is unknown, then the entire block value // must be unknown - return cty.UnknownVal(s.impliedType()), diags + return cty.UnknownVal(s.impliedType().WithoutOptionalAttributesDeep()), diags } } @@ -763,7 +763,7 @@ func (s *BlockSetSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel if u.Unknown() { // If any block Body is unknown, then the entire block value // must be unknown - return cty.UnknownVal(s.impliedType()), diags + return cty.UnknownVal(s.impliedType().WithoutOptionalAttributesDeep()), diags } } @@ -922,7 +922,7 @@ func (s *BlockMapSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel if u.Unknown() { // If any block Body is unknown, then the entire block value // must be unknown - return cty.UnknownVal(s.impliedType()), diags + return cty.UnknownVal(s.impliedType().WithoutOptionalAttributesDeep()), diags } } @@ -1076,7 +1076,7 @@ func (s *BlockObjectSpec) decode(content *hcl.BodyContent, blockLabels []blockLa if u.Unknown() { // If any block Body is unknown, then the entire block value // must be unknown - return cty.UnknownVal(s.impliedType()), diags + return cty.UnknownVal(s.impliedType().WithoutOptionalAttributesDeep()), diags } } @@ -1250,7 +1250,7 @@ func (s *BlockAttrsSpec) decode(content *hcl.BodyContent, blockLabels []blockLab Subject: &content.MissingItemRange, }) } - return cty.NullVal(cty.Map(s.ElementType)), diags + return cty.NullVal(cty.Map(s.ElementType).WithoutOptionalAttributesDeep()), diags } if other != nil { diags = append(diags, &hcl.Diagnostic{ @@ -1513,7 +1513,7 @@ func (s *TransformExprSpec) decode(content *hcl.BodyContent, blockLabels []block // We won't try to run our function in this case, because it'll probably // generate confusing additional errors that will distract from the // root cause. - return cty.UnknownVal(s.impliedType()), diags + return cty.UnknownVal(s.impliedType().WithoutOptionalAttributesDeep()), diags } chiCtx := s.TransformCtx.NewChild() @@ -1569,7 +1569,7 @@ func (s *TransformFuncSpec) decode(content *hcl.BodyContent, blockLabels []block // We won't try to run our function in this case, because it'll probably // generate confusing additional errors that will distract from the // root cause. - return cty.UnknownVal(s.impliedType()), diags + return cty.UnknownVal(s.impliedType().WithoutOptionalAttributesDeep()), diags } resultVal, err := s.Func.Call([]cty.Value{wrappedVal}) @@ -1583,7 +1583,7 @@ func (s *TransformFuncSpec) decode(content *hcl.BodyContent, blockLabels []block Detail: fmt.Sprintf("Decoder transform returned an error: %s", err), Subject: s.sourceRange(content, blockLabels).Ptr(), }) - return cty.UnknownVal(s.impliedType()), diags + return cty.UnknownVal(s.impliedType().WithoutOptionalAttributesDeep()), diags } return resultVal, diags @@ -1637,7 +1637,7 @@ func (s *RefineValueSpec) decode(content *hcl.BodyContent, blockLabels []blockLa // We won't try to run our function in this case, because it'll probably // generate confusing additional errors that will distract from the // root cause. - return cty.UnknownVal(s.impliedType()), diags + return cty.UnknownVal(s.impliedType().WithoutOptionalAttributesDeep()), diags } return wrappedVal.RefineWith(s.Refine), diags @@ -1658,7 +1658,6 @@ func (s *RefineValueSpec) sourceRange(content *hcl.BodyContent, blockLabels []bl // The Subject field of the returned Diagnostic is optional. If not // specified, it is automatically populated with the range covered by // the wrapped spec. -// type ValidateSpec struct { Wrapped Spec Func func(value cty.Value) hcl.Diagnostics @@ -1674,7 +1673,7 @@ func (s *ValidateSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel // We won't try to run our function in this case, because it'll probably // generate confusing additional errors that will distract from the // root cause. - return cty.UnknownVal(s.impliedType()), diags + return cty.UnknownVal(s.impliedType().WithoutOptionalAttributesDeep()), diags } validateDiags := s.Func(wrappedVal) From d23a20ac41349e5f1cc7b60e76d1408069db498b Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Thu, 9 Jun 2022 14:52:04 -0700 Subject: [PATCH 13/55] Run "stringer" using "go run" In the modern Go Modules-based toolchain we can avoid the need to globally install this tool first by running it this way. As a bonus, the toolchain will also install the version of the module we have specified in go.mod, thereby locking us in to a particular version until we intentionally upgrade. The other third-party generator tools we use here aren't written in Go and so we can't do the same for those right now, but maybe we'll find a nicer way to handle those later too. --- hclsyntax/generate.go | 2 +- json/scanner.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hclsyntax/generate.go b/hclsyntax/generate.go index 383ec6b8..66486074 100644 --- a/hclsyntax/generate.go +++ b/hclsyntax/generate.go @@ -9,4 +9,4 @@ package hclsyntax //go:generate gofmt -w scan_tokens.go //go:generate ragel -Z scan_string_lit.rl //go:generate gofmt -w scan_string_lit.go -//go:generate stringer -type TokenType -output token_type_string.go +//go:generate go run golang.org/x/tools/cmd/stringer -type TokenType -output token_type_string.go diff --git a/json/scanner.go b/json/scanner.go index cae84859..a5e21305 100644 --- a/json/scanner.go +++ b/json/scanner.go @@ -10,7 +10,7 @@ import ( "github.com/hashicorp/hcl/v2" ) -//go:generate stringer -type tokenType scanner.go +//go:generate go run golang.org/x/tools/cmd/stringer -type tokenType scanner.go type tokenType rune const ( From 83c95d29eef525594a70dd05027d93ff2f8f7c55 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Thu, 9 Jun 2022 16:29:19 -0700 Subject: [PATCH 14/55] hclsyntax: Initial work on namespaced functions This introduces a new syntax which allows function names to have namespace prefixes, with the different name parts separated by a double-colon "::" as is common in various other C-derived languages which need to distinguish between scope resolution and attribute/field traversal. Because HCL has separate namespaces for functions and variables, we need to use different punctuation for each to avoid creating parsing ambiguity that could be resolved only with infinite lookahead. We cannot retroactively change the representation of function names to be a slice of names without breaking the existing API, and so we instead adopt a convention of packing the multi-part names into single strings which the parser guarantees will always be a series of valid identifiers separated by the literal "::" sequence. That means that applications will make namespaced functions available in the EvalContext by naming them in a way that matches this convention. This is still a subtle compatibility break for any implementation of the syntax-agnostic HCL API against another syntax, because it may now encounter function names in the function table that are not entirely valid identifiers. However, that's okay in practice because a calling application is always in full control of both which syntaxes it supports and which functions it places in the function table, and so an application using some other syntax can simply avoid using namespaced functions until that syntax is updated to understand the new convention. This initial commit only includes the basic functionality and does not yet update the specification or specification test suite. It also has only minimal unit tests of the parser and evaluator. Before finalizing this in a release we would need to complete that work to make sure everything is consistent and that we have sufficient regression tests for this new capability. --- hclsyntax/expression.go | 62 ++ hclsyntax/expression_test.go | 20 + hclsyntax/parser.go | 53 +- hclsyntax/scan_tokens.go | 1086 ++++++++++++++++---------------- hclsyntax/scan_tokens.rl | 2 + hclsyntax/scan_tokens_test.go | 60 ++ hclsyntax/token.go | 5 +- hclsyntax/token_type_string.go | 67 +- 8 files changed, 744 insertions(+), 611 deletions(-) diff --git a/hclsyntax/expression.go b/hclsyntax/expression.go index e0de1c3d..e81553b3 100644 --- a/hclsyntax/expression.go +++ b/hclsyntax/expression.go @@ -6,6 +6,7 @@ package hclsyntax import ( "fmt" "sort" + "strings" "sync" "github.com/hashicorp/hcl/v2" @@ -251,6 +252,67 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti } } + // 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 + // between an invalid namespace or an invalid name within a valid + // namespace in order to give the user better feedback about what + // is wrong. + // + // The parser guarantees that a function name will always + // be a series of valid identifiers separated by "::" with no + // other content, so we can be relatively unforgiving in our processing + // here. + if sepIdx := strings.LastIndex(e.Name, "::"); sepIdx != -1 { + namespace := e.Name[:sepIdx+2] + name := e.Name[sepIdx+2:] + + avail := make([]string, 0, len(ctx.Functions)) + for availName := range ctx.Functions { + if strings.HasPrefix(availName, namespace) { + avail = append(avail, availName) + } + } + + if len(avail) == 0 { + // TODO: Maybe use nameSuggestion for the other available + // namespaces? But that'd require us to go scan the function + // table again, so we'll wait to see if it's really warranted. + // For now, we're assuming people are more likely to misremember + // the function names than the namespaces, because in many + // applications there will be relatively few namespaces compared + // to the number of distinct functions. + return cty.DynamicVal, hcl.Diagnostics{ + { + Severity: hcl.DiagError, + Summary: "Call to unknown function", + Detail: fmt.Sprintf("There are no functions in namespace %q.", namespace), + Subject: &e.NameRange, + Context: e.Range().Ptr(), + Expression: e, + EvalContext: ctx, + }, + } + } else { + suggestion := nameSuggestion(name, avail) + if suggestion != "" { + suggestion = fmt.Sprintf(" Did you mean %s%s?", namespace, suggestion) + } + + return cty.DynamicVal, hcl.Diagnostics{ + { + Severity: hcl.DiagError, + Summary: "Call to unknown function", + Detail: fmt.Sprintf("There is no function named %q in namespace %s.%s", name, namespace, suggestion), + Subject: &e.NameRange, + Context: e.Range().Ptr(), + Expression: e, + EvalContext: ctx, + }, + } + } + } + avail := make([]string, 0, len(ctx.Functions)) for name := range ctx.Functions { avail = append(avail, name) diff --git a/hclsyntax/expression_test.go b/hclsyntax/expression_test.go index e71dd310..72ff1596 100644 --- a/hclsyntax/expression_test.go +++ b/hclsyntax/expression_test.go @@ -342,6 +342,26 @@ upper( cty.DynamicVal, 0, }, + { + `foo::upper("foo")`, + &hcl.EvalContext{ + Functions: map[string]function.Function{ + "foo::upper": stdlib.UpperFunc, + }, + }, + cty.StringVal("FOO"), + 0, + }, + { + `foo :: upper("foo")`, // spaces are non-idomatic, but valid + &hcl.EvalContext{ + Functions: map[string]function.Function{ + "foo::upper": stdlib.UpperFunc, + }, + }, + cty.StringVal("FOO"), + 0, + }, { `misbehave()`, &hcl.EvalContext{ diff --git a/hclsyntax/parser.go b/hclsyntax/parser.go index aa147afe..20fa1481 100644 --- a/hclsyntax/parser.go +++ b/hclsyntax/parser.go @@ -999,7 +999,7 @@ func (p *parser) parseExpressionTerm() (Expression, hcl.Diagnostics) { case TokenIdent: tok := p.Read() // eat identifier token - if p.Peek().Type == TokenOParen { + if p.Peek().Type == TokenOParen || p.Peek().Type == TokenDoubleColon { return p.finishParsingFunctionCall(tok) } @@ -1145,16 +1145,57 @@ func (p *parser) numberLitValue(tok Token) (cty.Value, hcl.Diagnostics) { // finishParsingFunctionCall parses a function call assuming that the function // name was already read, and so the peeker should be pointing at the opening -// parenthesis after the name. +// parenthesis after the name, or at the double-colon after the initial +// function scope name. func (p *parser) finishParsingFunctionCall(name Token) (Expression, hcl.Diagnostics) { + var diags hcl.Diagnostics + openTok := p.Read() - if openTok.Type != TokenOParen { + if openTok.Type != TokenOParen && openTok.Type != TokenDoubleColon { // should never happen if callers behave - panic("finishParsingFunctionCall called with non-parenthesis as next token") + panic("finishParsingFunctionCall called with unsupported next token") + } + + nameStr := string(name.Bytes) + for openTok.Type == TokenDoubleColon { + nextName := p.Read() + if nextName.Type != TokenIdent { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Missing function name", + Detail: "Function scope resolution symbol :: must be followed by a function name in this scope.", + Subject: &nextName.Range, + Context: hcl.RangeBetween(name.Range, nextName.Range).Ptr(), + }) + p.recoverOver(TokenOParen) + return nil, diags + } + + // Initial versions of HCLv2 didn't support function namespaces, and + // so for backward compatibility we just treat namespaced functions + // as weird names with "::" separators in them, saved as a string + // to keep the API unchanged. FunctionCallExpr also has some special + // handling of names containing :: when referring to a function that + // doesn't exist in EvalContext, to return better error messages + // when namespaces are used incorrectly. + nameStr = nameStr + "::" + string(nextName.Bytes) + + openTok = p.Read() + } + + if openTok.Type != TokenOParen { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Missing open parenthesis", + Detail: "Function selector must be followed by an open parenthesis to begin the function call.", + Subject: &openTok.Range, + Context: hcl.RangeBetween(name.Range, openTok.Range).Ptr(), + }) + p.recoverOver(TokenOParen) + return nil, diags } var args []Expression - var diags hcl.Diagnostics var expandFinal bool var closeTok Token @@ -1245,7 +1286,7 @@ Token: p.PopIncludeNewlines() return &FunctionCallExpr{ - Name: string(name.Bytes), + Name: nameStr, Args: args, ExpandFinal: expandFinal, diff --git a/hclsyntax/scan_tokens.go b/hclsyntax/scan_tokens.go index 1bbbb927..3691f115 100644 --- a/hclsyntax/scan_tokens.go +++ b/hclsyntax/scan_tokens.go @@ -33,13 +33,13 @@ var _hcltok_actions []byte = []byte{ 1, 71, 1, 72, 1, 73, 1, 74, 1, 75, 1, 76, 1, 77, 1, 78, 1, 79, 1, 80, 1, 81, 1, 82, - 1, 83, 1, 84, 1, 85, 2, 0, - 14, 2, 0, 25, 2, 0, 29, 2, - 0, 37, 2, 0, 41, 2, 1, 2, - 2, 4, 5, 2, 4, 6, 2, 4, - 21, 2, 4, 22, 2, 4, 33, 2, - 4, 34, 2, 4, 45, 2, 4, 46, - 2, 4, 54, 2, 4, 55, + 1, 83, 1, 84, 1, 85, 1, 86, + 2, 0, 14, 2, 0, 25, 2, 0, + 29, 2, 0, 37, 2, 0, 41, 2, + 1, 2, 2, 4, 5, 2, 4, 6, + 2, 4, 21, 2, 4, 22, 2, 4, + 33, 2, 4, 34, 2, 4, 45, 2, + 4, 46, 2, 4, 54, 2, 4, 55, } var _hcltok_key_offsets []int16 = []int16{ @@ -225,22 +225,22 @@ var _hcltok_key_offsets []int16 = []int16{ 9153, 9171, 9172, 9182, 9183, 9192, 9200, 9202, 9205, 9207, 9209, 9211, 9216, 9229, 9233, 9248, 9277, 9288, 9290, 9294, 9298, 9303, 9307, 9309, - 9316, 9320, 9328, 9332, 9407, 9409, 9410, 9411, - 9412, 9413, 9414, 9416, 9421, 9423, 9425, 9426, - 9470, 9471, 9472, 9474, 9479, 9483, 9483, 9485, - 9487, 9498, 9508, 9516, 9517, 9519, 9520, 9524, - 9528, 9538, 9542, 9549, 9560, 9567, 9571, 9577, - 9588, 9620, 9669, 9684, 9699, 9704, 9706, 9711, - 9743, 9751, 9753, 9775, 9797, 9799, 9815, 9831, - 9833, 9835, 9835, 9836, 9837, 9838, 9840, 9841, - 9853, 9855, 9857, 9859, 9873, 9887, 9889, 9892, - 9895, 9897, 9898, 9899, 9901, 9903, 9905, 9919, - 9933, 9935, 9938, 9941, 9943, 9944, 9945, 9947, - 9949, 9951, 10000, 10044, 10046, 10051, 10055, 10055, - 10057, 10059, 10070, 10080, 10088, 10089, 10091, 10092, - 10096, 10100, 10110, 10114, 10121, 10132, 10139, 10143, - 10149, 10160, 10192, 10241, 10256, 10271, 10276, 10278, - 10283, 10315, 10323, 10325, 10347, 10369, + 9316, 9320, 9328, 9332, 9408, 9410, 9411, 9412, + 9413, 9414, 9415, 9417, 9422, 9423, 9425, 9427, + 9428, 9472, 9473, 9474, 9476, 9481, 9485, 9485, + 9487, 9489, 9500, 9510, 9518, 9519, 9521, 9522, + 9526, 9530, 9540, 9544, 9551, 9562, 9569, 9573, + 9579, 9590, 9622, 9671, 9686, 9701, 9706, 9708, + 9713, 9745, 9753, 9755, 9777, 9799, 9801, 9817, + 9833, 9835, 9837, 9837, 9838, 9839, 9840, 9842, + 9843, 9855, 9857, 9859, 9861, 9875, 9889, 9891, + 9894, 9897, 9899, 9900, 9901, 9903, 9905, 9907, + 9921, 9935, 9937, 9940, 9943, 9945, 9946, 9947, + 9949, 9951, 9953, 10002, 10046, 10048, 10053, 10057, + 10057, 10059, 10061, 10072, 10082, 10090, 10091, 10093, + 10094, 10098, 10102, 10112, 10116, 10123, 10134, 10141, + 10145, 10151, 10162, 10194, 10243, 10258, 10273, 10278, + 10280, 10285, 10317, 10325, 10327, 10349, 10371, } var _hcltok_trans_keys []byte = []byte{ @@ -1411,136 +1411,136 @@ var _hcltok_trans_keys []byte = []byte{ 187, 191, 192, 255, 162, 191, 192, 255, 160, 168, 128, 159, 161, 167, 169, 191, 158, 191, 192, 255, 9, 10, 13, 32, - 33, 34, 35, 38, 46, 47, 60, 61, - 62, 64, 92, 95, 123, 124, 125, 126, - 127, 194, 195, 198, 199, 203, 204, 205, - 206, 207, 210, 212, 213, 214, 215, 216, - 217, 219, 220, 221, 222, 223, 224, 225, - 226, 227, 228, 233, 234, 237, 238, 239, - 240, 0, 36, 37, 45, 48, 57, 58, - 63, 65, 90, 91, 96, 97, 122, 192, - 193, 196, 218, 229, 236, 241, 247, 9, - 32, 10, 61, 10, 38, 46, 42, 47, - 46, 69, 101, 48, 57, 60, 61, 61, - 62, 61, 45, 95, 194, 195, 198, 199, - 203, 204, 205, 206, 207, 210, 212, 213, - 214, 215, 216, 217, 219, 220, 221, 222, - 223, 224, 225, 226, 227, 228, 233, 234, - 237, 239, 240, 243, 48, 57, 65, 90, - 97, 122, 196, 218, 229, 236, 124, 125, - 128, 191, 170, 181, 186, 128, 191, 151, - 183, 128, 255, 192, 255, 0, 127, 173, - 130, 133, 146, 159, 165, 171, 175, 191, - 192, 255, 181, 190, 128, 175, 176, 183, - 184, 185, 186, 191, 134, 139, 141, 162, - 128, 135, 136, 255, 182, 130, 137, 176, - 151, 152, 154, 160, 136, 191, 192, 255, - 128, 143, 144, 170, 171, 175, 176, 178, - 179, 191, 128, 159, 160, 191, 176, 128, - 138, 139, 173, 174, 255, 148, 150, 164, - 167, 173, 176, 185, 189, 190, 192, 255, - 144, 128, 145, 146, 175, 176, 191, 128, - 140, 141, 255, 166, 176, 178, 191, 192, - 255, 186, 128, 137, 138, 170, 171, 179, - 180, 181, 182, 191, 160, 161, 162, 164, - 165, 166, 167, 168, 169, 170, 171, 172, - 173, 174, 175, 176, 177, 178, 179, 180, - 181, 182, 183, 184, 185, 186, 187, 188, - 189, 190, 128, 191, 128, 129, 130, 131, - 137, 138, 139, 140, 141, 142, 143, 144, - 153, 154, 155, 156, 157, 158, 159, 160, - 161, 162, 163, 164, 165, 166, 167, 168, - 169, 170, 171, 172, 173, 174, 175, 176, - 177, 178, 179, 180, 182, 183, 184, 188, - 189, 190, 191, 132, 187, 129, 130, 132, - 133, 134, 176, 177, 178, 179, 180, 181, - 182, 183, 128, 191, 128, 129, 130, 131, - 132, 133, 134, 135, 144, 136, 143, 145, - 191, 192, 255, 182, 183, 184, 128, 191, - 128, 191, 191, 128, 190, 192, 255, 128, - 146, 147, 148, 152, 153, 154, 155, 156, - 158, 159, 160, 161, 162, 163, 164, 165, - 166, 167, 168, 169, 170, 171, 172, 173, - 174, 175, 176, 129, 191, 192, 255, 158, - 159, 128, 157, 160, 191, 192, 255, 128, - 191, 164, 169, 171, 172, 173, 174, 175, - 180, 181, 182, 183, 184, 185, 187, 188, - 189, 190, 191, 128, 163, 165, 186, 144, - 145, 146, 147, 148, 150, 151, 152, 155, - 157, 158, 160, 170, 171, 172, 175, 128, - 159, 161, 169, 173, 191, 128, 191, 10, - 13, 34, 36, 37, 92, 128, 191, 192, - 223, 224, 239, 240, 247, 248, 255, 10, - 13, 34, 92, 36, 37, 128, 191, 192, - 223, 224, 239, 240, 247, 248, 255, 10, - 13, 36, 123, 123, 126, 126, 37, 123, - 126, 10, 13, 128, 191, 192, 223, 224, - 239, 240, 247, 248, 255, 128, 191, 128, + 33, 34, 35, 38, 46, 47, 58, 60, + 61, 62, 64, 92, 95, 123, 124, 125, + 126, 127, 194, 195, 198, 199, 203, 204, + 205, 206, 207, 210, 212, 213, 214, 215, + 216, 217, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 233, 234, 237, 238, + 239, 240, 0, 36, 37, 45, 48, 57, + 59, 63, 65, 90, 91, 96, 97, 122, + 192, 193, 196, 218, 229, 236, 241, 247, + 9, 32, 10, 61, 10, 38, 46, 42, + 47, 46, 69, 101, 48, 57, 58, 60, + 61, 61, 62, 61, 45, 95, 194, 195, + 198, 199, 203, 204, 205, 206, 207, 210, + 212, 213, 214, 215, 216, 217, 219, 220, + 221, 222, 223, 224, 225, 226, 227, 228, + 233, 234, 237, 239, 240, 243, 48, 57, + 65, 90, 97, 122, 196, 218, 229, 236, + 124, 125, 128, 191, 170, 181, 186, 128, + 191, 151, 183, 128, 255, 192, 255, 0, + 127, 173, 130, 133, 146, 159, 165, 171, + 175, 191, 192, 255, 181, 190, 128, 175, + 176, 183, 184, 185, 186, 191, 134, 139, + 141, 162, 128, 135, 136, 255, 182, 130, + 137, 176, 151, 152, 154, 160, 136, 191, + 192, 255, 128, 143, 144, 170, 171, 175, + 176, 178, 179, 191, 128, 159, 160, 191, + 176, 128, 138, 139, 173, 174, 255, 148, + 150, 164, 167, 173, 176, 185, 189, 190, + 192, 255, 144, 128, 145, 146, 175, 176, + 191, 128, 140, 141, 255, 166, 176, 178, + 191, 192, 255, 186, 128, 137, 138, 170, + 171, 179, 180, 181, 182, 191, 160, 161, + 162, 164, 165, 166, 167, 168, 169, 170, + 171, 172, 173, 174, 175, 176, 177, 178, + 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 128, 191, 128, 129, + 130, 131, 137, 138, 139, 140, 141, 142, + 143, 144, 153, 154, 155, 156, 157, 158, + 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 182, 183, + 184, 188, 189, 190, 191, 132, 187, 129, + 130, 132, 133, 134, 176, 177, 178, 179, + 180, 181, 182, 183, 128, 191, 128, 129, + 130, 131, 132, 133, 134, 135, 144, 136, + 143, 145, 191, 192, 255, 182, 183, 184, + 128, 191, 128, 191, 191, 128, 190, 192, + 255, 128, 146, 147, 148, 152, 153, 154, + 155, 156, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 129, 191, 192, + 255, 158, 159, 128, 157, 160, 191, 192, + 255, 128, 191, 164, 169, 171, 172, 173, + 174, 175, 180, 181, 182, 183, 184, 185, + 187, 188, 189, 190, 191, 128, 163, 165, + 186, 144, 145, 146, 147, 148, 150, 151, + 152, 155, 157, 158, 160, 170, 171, 172, + 175, 128, 159, 161, 169, 173, 191, 128, + 191, 10, 13, 34, 36, 37, 92, 128, + 191, 192, 223, 224, 239, 240, 247, 248, + 255, 10, 13, 34, 92, 36, 37, 128, + 191, 192, 223, 224, 239, 240, 247, 248, + 255, 10, 13, 36, 123, 123, 126, 126, + 37, 123, 126, 10, 13, 128, 191, 192, + 223, 224, 239, 240, 247, 248, 255, 128, + 191, 128, 191, 128, 191, 10, 13, 36, + 37, 128, 191, 192, 223, 224, 239, 240, + 247, 248, 255, 10, 13, 36, 37, 128, + 191, 192, 223, 224, 239, 240, 247, 248, + 255, 10, 13, 10, 13, 123, 10, 13, + 126, 10, 13, 126, 126, 128, 191, 128, 191, 128, 191, 10, 13, 36, 37, 128, 191, 192, 223, 224, 239, 240, 247, 248, 255, 10, 13, 36, 37, 128, 191, 192, 223, 224, 239, 240, 247, 248, 255, 10, 13, 10, 13, 123, 10, 13, 126, 10, 13, 126, 126, 128, 191, 128, 191, 128, - 191, 10, 13, 36, 37, 128, 191, 192, - 223, 224, 239, 240, 247, 248, 255, 10, - 13, 36, 37, 128, 191, 192, 223, 224, - 239, 240, 247, 248, 255, 10, 13, 10, - 13, 123, 10, 13, 126, 10, 13, 126, - 126, 128, 191, 128, 191, 128, 191, 95, - 194, 195, 198, 199, 203, 204, 205, 206, - 207, 210, 212, 213, 214, 215, 216, 217, - 219, 220, 221, 222, 223, 224, 225, 226, - 227, 228, 233, 234, 237, 238, 239, 240, - 65, 90, 97, 122, 128, 191, 192, 193, - 196, 218, 229, 236, 241, 247, 248, 255, - 45, 95, 194, 195, 198, 199, 203, 204, + 191, 95, 194, 195, 198, 199, 203, 204, 205, 206, 207, 210, 212, 213, 214, 215, 216, 217, 219, 220, 221, 222, 223, 224, - 225, 226, 227, 228, 233, 234, 237, 239, - 240, 243, 48, 57, 65, 90, 97, 122, - 196, 218, 229, 236, 128, 191, 170, 181, - 186, 128, 191, 151, 183, 128, 255, 192, - 255, 0, 127, 173, 130, 133, 146, 159, - 165, 171, 175, 191, 192, 255, 181, 190, - 128, 175, 176, 183, 184, 185, 186, 191, - 134, 139, 141, 162, 128, 135, 136, 255, - 182, 130, 137, 176, 151, 152, 154, 160, - 136, 191, 192, 255, 128, 143, 144, 170, - 171, 175, 176, 178, 179, 191, 128, 159, - 160, 191, 176, 128, 138, 139, 173, 174, - 255, 148, 150, 164, 167, 173, 176, 185, - 189, 190, 192, 255, 144, 128, 145, 146, - 175, 176, 191, 128, 140, 141, 255, 166, - 176, 178, 191, 192, 255, 186, 128, 137, - 138, 170, 171, 179, 180, 181, 182, 191, - 160, 161, 162, 164, 165, 166, 167, 168, - 169, 170, 171, 172, 173, 174, 175, 176, - 177, 178, 179, 180, 181, 182, 183, 184, - 185, 186, 187, 188, 189, 190, 128, 191, - 128, 129, 130, 131, 137, 138, 139, 140, - 141, 142, 143, 144, 153, 154, 155, 156, - 157, 158, 159, 160, 161, 162, 163, 164, - 165, 166, 167, 168, 169, 170, 171, 172, - 173, 174, 175, 176, 177, 178, 179, 180, - 182, 183, 184, 188, 189, 190, 191, 132, - 187, 129, 130, 132, 133, 134, 176, 177, - 178, 179, 180, 181, 182, 183, 128, 191, - 128, 129, 130, 131, 132, 133, 134, 135, - 144, 136, 143, 145, 191, 192, 255, 182, - 183, 184, 128, 191, 128, 191, 191, 128, - 190, 192, 255, 128, 146, 147, 148, 152, - 153, 154, 155, 156, 158, 159, 160, 161, - 162, 163, 164, 165, 166, 167, 168, 169, - 170, 171, 172, 173, 174, 175, 176, 129, - 191, 192, 255, 158, 159, 128, 157, 160, - 191, 192, 255, 128, 191, 164, 169, 171, - 172, 173, 174, 175, 180, 181, 182, 183, - 184, 185, 187, 188, 189, 190, 191, 128, - 163, 165, 186, 144, 145, 146, 147, 148, - 150, 151, 152, 155, 157, 158, 160, 170, - 171, 172, 175, 128, 159, 161, 169, 173, - 191, 128, 191, + 225, 226, 227, 228, 233, 234, 237, 238, + 239, 240, 65, 90, 97, 122, 128, 191, + 192, 193, 196, 218, 229, 236, 241, 247, + 248, 255, 45, 95, 194, 195, 198, 199, + 203, 204, 205, 206, 207, 210, 212, 213, + 214, 215, 216, 217, 219, 220, 221, 222, + 223, 224, 225, 226, 227, 228, 233, 234, + 237, 239, 240, 243, 48, 57, 65, 90, + 97, 122, 196, 218, 229, 236, 128, 191, + 170, 181, 186, 128, 191, 151, 183, 128, + 255, 192, 255, 0, 127, 173, 130, 133, + 146, 159, 165, 171, 175, 191, 192, 255, + 181, 190, 128, 175, 176, 183, 184, 185, + 186, 191, 134, 139, 141, 162, 128, 135, + 136, 255, 182, 130, 137, 176, 151, 152, + 154, 160, 136, 191, 192, 255, 128, 143, + 144, 170, 171, 175, 176, 178, 179, 191, + 128, 159, 160, 191, 176, 128, 138, 139, + 173, 174, 255, 148, 150, 164, 167, 173, + 176, 185, 189, 190, 192, 255, 144, 128, + 145, 146, 175, 176, 191, 128, 140, 141, + 255, 166, 176, 178, 191, 192, 255, 186, + 128, 137, 138, 170, 171, 179, 180, 181, + 182, 191, 160, 161, 162, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, + 183, 184, 185, 186, 187, 188, 189, 190, + 128, 191, 128, 129, 130, 131, 137, 138, + 139, 140, 141, 142, 143, 144, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, + 163, 164, 165, 166, 167, 168, 169, 170, + 171, 172, 173, 174, 175, 176, 177, 178, + 179, 180, 182, 183, 184, 188, 189, 190, + 191, 132, 187, 129, 130, 132, 133, 134, + 176, 177, 178, 179, 180, 181, 182, 183, + 128, 191, 128, 129, 130, 131, 132, 133, + 134, 135, 144, 136, 143, 145, 191, 192, + 255, 182, 183, 184, 128, 191, 128, 191, + 191, 128, 190, 192, 255, 128, 146, 147, + 148, 152, 153, 154, 155, 156, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, + 176, 129, 191, 192, 255, 158, 159, 128, + 157, 160, 191, 192, 255, 128, 191, 164, + 169, 171, 172, 173, 174, 175, 180, 181, + 182, 183, 184, 185, 187, 188, 189, 190, + 191, 128, 163, 165, 186, 144, 145, 146, + 147, 148, 150, 151, 152, 155, 157, 158, + 160, 170, 171, 172, 175, 128, 159, 161, + 169, 173, 191, 128, 191, } var _hcltok_single_lengths []byte = []byte{ @@ -1726,22 +1726,22 @@ var _hcltok_single_lengths []byte = []byte{ 12, 1, 4, 1, 5, 2, 0, 3, 2, 2, 2, 1, 7, 0, 7, 17, 3, 0, 2, 0, 3, 0, 0, 1, - 0, 2, 0, 53, 2, 1, 1, 1, - 1, 1, 2, 3, 2, 2, 1, 34, - 1, 1, 0, 3, 2, 0, 0, 0, - 1, 2, 4, 1, 0, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, 1, - 30, 47, 13, 9, 3, 0, 1, 28, - 2, 0, 18, 16, 0, 6, 4, 2, - 2, 0, 1, 1, 1, 2, 1, 2, - 0, 0, 0, 4, 2, 2, 3, 3, - 2, 1, 1, 0, 0, 0, 4, 2, - 2, 3, 3, 2, 1, 1, 0, 0, - 0, 33, 34, 0, 3, 2, 0, 0, + 0, 2, 0, 54, 2, 1, 1, 1, + 1, 1, 2, 3, 1, 2, 2, 1, + 34, 1, 1, 0, 3, 2, 0, 0, 0, 1, 2, 4, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 30, 47, 13, 9, 3, 0, 1, - 28, 2, 0, 18, 16, 0, + 28, 2, 0, 18, 16, 0, 6, 4, + 2, 2, 0, 1, 1, 1, 2, 1, + 2, 0, 0, 0, 4, 2, 2, 3, + 3, 2, 1, 1, 0, 0, 0, 4, + 2, 2, 3, 3, 2, 1, 1, 0, + 0, 0, 33, 34, 0, 3, 2, 0, + 0, 0, 1, 2, 4, 1, 0, 1, + 0, 0, 0, 0, 1, 1, 1, 0, + 0, 1, 30, 47, 13, 9, 3, 0, + 1, 28, 2, 0, 18, 16, 0, } var _hcltok_range_lengths []byte = []byte{ @@ -1928,21 +1928,21 @@ var _hcltok_range_lengths []byte = []byte{ 0, 0, 0, 2, 3, 2, 4, 6, 4, 1, 1, 2, 1, 2, 1, 3, 2, 3, 2, 11, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 5, - 0, 0, 1, 1, 1, 0, 1, 1, - 5, 4, 2, 0, 1, 0, 2, 2, - 5, 2, 3, 5, 3, 2, 3, 5, - 1, 1, 1, 3, 1, 1, 2, 2, - 3, 1, 2, 3, 1, 5, 6, 0, - 0, 0, 0, 0, 0, 0, 0, 5, - 1, 1, 1, 5, 6, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 5, 6, - 0, 0, 0, 0, 0, 0, 1, 1, - 1, 8, 5, 1, 1, 1, 0, 1, + 0, 0, 0, 1, 0, 0, 0, 0, + 5, 0, 0, 1, 1, 1, 0, 1, 1, 5, 4, 2, 0, 1, 0, 2, 2, 5, 2, 3, 5, 3, 2, 3, 5, 1, 1, 1, 3, 1, 1, 2, - 2, 3, 1, 2, 3, 1, + 2, 3, 1, 2, 3, 1, 5, 6, + 0, 0, 0, 0, 0, 0, 0, 0, + 5, 1, 1, 1, 5, 6, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 5, + 6, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 8, 5, 1, 1, 1, 0, + 1, 1, 5, 4, 2, 0, 1, 0, + 2, 2, 5, 2, 3, 5, 3, 2, + 3, 5, 1, 1, 1, 3, 1, 1, + 2, 2, 3, 1, 2, 3, 1, } var _hcltok_index_offsets []int16 = []int16{ @@ -2128,22 +2128,22 @@ var _hcltok_index_offsets []int16 = []int16{ 7187, 7203, 7205, 7213, 7215, 7223, 7229, 7231, 7235, 7238, 7241, 7244, 7248, 7259, 7262, 7274, 7298, 7306, 7308, 7312, 7315, 7320, 7323, 7325, - 7330, 7333, 7339, 7342, 7407, 7410, 7412, 7414, - 7416, 7418, 7420, 7423, 7428, 7431, 7434, 7436, - 7476, 7478, 7480, 7482, 7487, 7491, 7492, 7494, - 7496, 7503, 7510, 7517, 7519, 7521, 7523, 7526, - 7529, 7535, 7538, 7543, 7550, 7555, 7558, 7562, - 7569, 7601, 7650, 7665, 7678, 7683, 7685, 7689, - 7720, 7726, 7728, 7749, 7769, 7771, 7783, 7794, - 7797, 7800, 7801, 7803, 7805, 7807, 7810, 7812, - 7820, 7822, 7824, 7826, 7836, 7845, 7848, 7852, - 7856, 7859, 7861, 7863, 7865, 7867, 7869, 7879, - 7888, 7891, 7895, 7899, 7902, 7904, 7906, 7908, - 7910, 7912, 7954, 7994, 7996, 8001, 8005, 8006, - 8008, 8010, 8017, 8024, 8031, 8033, 8035, 8037, - 8040, 8043, 8049, 8052, 8057, 8064, 8069, 8072, - 8076, 8083, 8115, 8164, 8179, 8192, 8197, 8199, - 8203, 8234, 8240, 8242, 8263, 8283, + 7330, 7333, 7339, 7342, 7408, 7411, 7413, 7415, + 7417, 7419, 7421, 7424, 7429, 7431, 7434, 7437, + 7439, 7479, 7481, 7483, 7485, 7490, 7494, 7495, + 7497, 7499, 7506, 7513, 7520, 7522, 7524, 7526, + 7529, 7532, 7538, 7541, 7546, 7553, 7558, 7561, + 7565, 7572, 7604, 7653, 7668, 7681, 7686, 7688, + 7692, 7723, 7729, 7731, 7752, 7772, 7774, 7786, + 7797, 7800, 7803, 7804, 7806, 7808, 7810, 7813, + 7815, 7823, 7825, 7827, 7829, 7839, 7848, 7851, + 7855, 7859, 7862, 7864, 7866, 7868, 7870, 7872, + 7882, 7891, 7894, 7898, 7902, 7905, 7907, 7909, + 7911, 7913, 7915, 7957, 7997, 7999, 8004, 8008, + 8009, 8011, 8013, 8020, 8027, 8034, 8036, 8038, + 8040, 8043, 8046, 8052, 8055, 8060, 8067, 8072, + 8075, 8079, 8086, 8118, 8167, 8182, 8195, 8200, + 8202, 8206, 8237, 8243, 8245, 8266, 8286, } var _hcltok_indicies []int16 = []int16{ @@ -3066,123 +3066,123 @@ var _hcltok_indicies []int16 = []int16{ 1045, 801, 1046, 1045, 795, 1050, 1141, 1047, 1059, 1047, 1045, 1046, 1045, 795, 1142, 1143, 1144, 1142, 1145, 1146, 1147, 1149, 1150, 1151, - 1152, 1153, 1154, 670, 670, 419, 1155, 1156, - 1157, 1158, 670, 1161, 1162, 1164, 1165, 1166, - 1160, 1167, 1168, 1169, 1170, 1171, 1172, 1173, + 1152, 1153, 1154, 1155, 670, 670, 419, 1156, + 1157, 1158, 1159, 670, 1162, 1163, 1165, 1166, + 1167, 1161, 1168, 1169, 1170, 1171, 1172, 1173, 1174, 1175, 1176, 1177, 1178, 1179, 1180, 1181, - 1182, 1183, 1184, 1185, 1186, 1188, 1189, 1190, - 1191, 1192, 1193, 670, 1148, 7, 1148, 419, - 1148, 419, 1160, 1163, 1187, 1194, 1159, 1142, - 1142, 1195, 1143, 1196, 1198, 1197, 4, 1147, - 1200, 1197, 1201, 1197, 2, 1147, 1197, 6, - 8, 8, 7, 1202, 1203, 1204, 1197, 1205, - 1206, 1197, 1207, 1197, 419, 419, 1209, 1210, - 489, 470, 1211, 470, 1212, 1213, 1214, 1215, - 1216, 1217, 1218, 1219, 1220, 1221, 1222, 544, - 1223, 520, 1224, 1225, 1226, 1227, 1228, 1229, - 1230, 1231, 1232, 1233, 1234, 1235, 419, 419, - 419, 425, 565, 1208, 1236, 1197, 1237, 1197, - 670, 1238, 419, 419, 419, 670, 1238, 670, - 670, 419, 1238, 419, 1238, 419, 1238, 419, - 670, 670, 670, 670, 670, 1238, 419, 670, - 670, 670, 419, 670, 419, 1238, 419, 670, - 670, 670, 670, 419, 1238, 670, 419, 670, - 419, 670, 419, 670, 670, 419, 670, 1238, - 419, 670, 419, 670, 419, 670, 1238, 670, - 419, 1238, 670, 419, 670, 419, 1238, 670, - 670, 670, 670, 670, 1238, 419, 419, 670, - 419, 670, 1238, 670, 419, 1238, 670, 670, - 1238, 419, 419, 670, 419, 670, 419, 670, - 1238, 1239, 1240, 1241, 1242, 1243, 1244, 1245, - 1246, 1247, 1248, 1249, 715, 1250, 1251, 1252, - 1253, 1254, 1255, 1256, 1257, 1258, 1259, 1260, - 1261, 1260, 1262, 1263, 1264, 1265, 1266, 671, - 1238, 1267, 1268, 1269, 1270, 1271, 1272, 1273, - 1274, 1275, 1276, 1277, 1278, 1279, 1280, 1281, - 1282, 1283, 1284, 1285, 725, 1286, 1287, 1288, - 692, 1289, 1290, 1291, 1292, 1293, 1294, 671, - 1295, 1296, 1297, 1298, 1299, 1300, 1301, 1302, - 674, 1303, 671, 674, 1304, 1305, 1306, 1307, - 683, 1238, 1308, 1309, 1310, 1311, 703, 1312, - 1313, 683, 1314, 1315, 1316, 1317, 1318, 671, - 1238, 1319, 1278, 1320, 1321, 1322, 683, 1323, - 1324, 674, 671, 683, 425, 1238, 1288, 671, - 674, 683, 425, 683, 425, 1325, 683, 1238, - 425, 674, 1326, 1327, 674, 1328, 1329, 681, - 1330, 1331, 1332, 1333, 1334, 1284, 1335, 1336, - 1337, 1338, 1339, 1340, 1341, 1342, 1343, 1344, - 1345, 1346, 1303, 1347, 674, 683, 425, 1238, - 1348, 1349, 683, 671, 1238, 425, 671, 1238, - 674, 1350, 731, 1351, 1352, 1353, 1354, 1355, - 1356, 1357, 1358, 671, 1359, 1360, 1361, 1362, - 1363, 1364, 671, 683, 1238, 1366, 1367, 1368, - 1369, 1370, 1371, 1372, 1373, 1374, 1375, 1376, - 1372, 1378, 1379, 1380, 1381, 1365, 1377, 1365, - 1238, 1365, 1238, 1382, 1382, 1383, 1384, 1385, - 1386, 1387, 1388, 1389, 1390, 1387, 767, 1391, - 1391, 1391, 1392, 1391, 1391, 768, 769, 770, - 1391, 767, 1382, 1382, 1393, 1396, 1397, 1395, - 1398, 1399, 1398, 1400, 1391, 1402, 1401, 1396, - 1403, 1395, 1405, 1404, 1394, 1394, 1394, 768, - 769, 770, 1394, 767, 767, 1406, 773, 1406, - 1407, 1406, 775, 1408, 1409, 1410, 1411, 1412, - 1413, 1414, 1411, 776, 775, 1408, 1415, 1415, - 777, 779, 1416, 1415, 776, 1418, 1419, 1417, - 1418, 1419, 1420, 1417, 775, 1408, 1421, 1415, - 775, 1408, 1415, 1423, 1422, 1425, 1424, 776, - 1426, 777, 1426, 779, 1426, 785, 1427, 1428, - 1429, 1430, 1431, 1432, 1433, 1430, 786, 785, - 1427, 1434, 1434, 787, 789, 1435, 1434, 786, - 1437, 1438, 1436, 1437, 1438, 1439, 1436, 785, - 1427, 1440, 1434, 785, 1427, 1434, 1442, 1441, - 1444, 1443, 786, 1445, 787, 1445, 789, 1445, - 795, 1448, 1449, 1451, 1452, 1453, 1447, 1454, - 1455, 1456, 1457, 1458, 1459, 1460, 1461, 1462, - 1463, 1464, 1465, 1466, 1467, 1468, 1469, 1470, - 1471, 1472, 1473, 1475, 1476, 1477, 1478, 1479, - 1480, 795, 795, 1446, 1447, 1450, 1474, 1481, - 1446, 1046, 795, 795, 1483, 1484, 865, 846, - 1485, 846, 1486, 1487, 1488, 1489, 1490, 1491, - 1492, 1493, 1494, 1495, 1496, 920, 1497, 896, - 1498, 1499, 1500, 1501, 1502, 1503, 1504, 1505, - 1506, 1507, 1508, 1509, 795, 795, 795, 801, - 941, 1482, 1046, 1510, 795, 795, 795, 1046, - 1510, 1046, 1046, 795, 1510, 795, 1510, 795, - 1510, 795, 1046, 1046, 1046, 1046, 1046, 1510, - 795, 1046, 1046, 1046, 795, 1046, 795, 1510, - 795, 1046, 1046, 1046, 1046, 795, 1510, 1046, - 795, 1046, 795, 1046, 795, 1046, 1046, 795, - 1046, 1510, 795, 1046, 795, 1046, 795, 1046, - 1510, 1046, 795, 1510, 1046, 795, 1046, 795, - 1510, 1046, 1046, 1046, 1046, 1046, 1510, 795, - 795, 1046, 795, 1046, 1510, 1046, 795, 1510, - 1046, 1046, 1510, 795, 795, 1046, 795, 1046, - 795, 1046, 1510, 1511, 1512, 1513, 1514, 1515, - 1516, 1517, 1518, 1519, 1520, 1521, 1091, 1522, - 1523, 1524, 1525, 1526, 1527, 1528, 1529, 1530, - 1531, 1532, 1533, 1532, 1534, 1535, 1536, 1537, - 1538, 1047, 1510, 1539, 1540, 1541, 1542, 1543, - 1544, 1545, 1546, 1547, 1548, 1549, 1550, 1551, - 1552, 1553, 1554, 1555, 1556, 1557, 1101, 1558, - 1559, 1560, 1068, 1561, 1562, 1563, 1564, 1565, - 1566, 1047, 1567, 1568, 1569, 1570, 1571, 1572, - 1573, 1574, 1050, 1575, 1047, 1050, 1576, 1577, - 1578, 1579, 1059, 1510, 1580, 1581, 1582, 1583, - 1079, 1584, 1585, 1059, 1586, 1587, 1588, 1589, - 1590, 1047, 1510, 1591, 1550, 1592, 1593, 1594, - 1059, 1595, 1596, 1050, 1047, 1059, 801, 1510, - 1560, 1047, 1050, 1059, 801, 1059, 801, 1597, - 1059, 1510, 801, 1050, 1598, 1599, 1050, 1600, - 1601, 1057, 1602, 1603, 1604, 1605, 1606, 1556, - 1607, 1608, 1609, 1610, 1611, 1612, 1613, 1614, - 1615, 1616, 1617, 1618, 1575, 1619, 1050, 1059, - 801, 1510, 1620, 1621, 1059, 1047, 1510, 801, - 1047, 1510, 1050, 1622, 1107, 1623, 1624, 1625, - 1626, 1627, 1628, 1629, 1630, 1047, 1631, 1632, - 1633, 1634, 1635, 1636, 1047, 1059, 1510, 1638, - 1639, 1640, 1641, 1642, 1643, 1644, 1645, 1646, - 1647, 1648, 1644, 1650, 1651, 1652, 1653, 1637, - 1649, 1637, 1510, 1637, 1510, + 1182, 1183, 1184, 1185, 1186, 1187, 1189, 1190, + 1191, 1192, 1193, 1194, 670, 1148, 7, 1148, + 419, 1148, 419, 1161, 1164, 1188, 1195, 1160, + 1142, 1142, 1196, 1143, 1197, 1199, 1198, 4, + 1147, 1201, 1198, 1202, 1198, 2, 1147, 1198, + 6, 8, 8, 7, 1203, 1204, 1198, 1205, + 1206, 1198, 1207, 1208, 1198, 1209, 1198, 419, + 419, 1211, 1212, 489, 470, 1213, 470, 1214, + 1215, 1216, 1217, 1218, 1219, 1220, 1221, 1222, + 1223, 1224, 544, 1225, 520, 1226, 1227, 1228, + 1229, 1230, 1231, 1232, 1233, 1234, 1235, 1236, + 1237, 419, 419, 419, 425, 565, 1210, 1238, + 1198, 1239, 1198, 670, 1240, 419, 419, 419, + 670, 1240, 670, 670, 419, 1240, 419, 1240, + 419, 1240, 419, 670, 670, 670, 670, 670, + 1240, 419, 670, 670, 670, 419, 670, 419, + 1240, 419, 670, 670, 670, 670, 419, 1240, + 670, 419, 670, 419, 670, 419, 670, 670, + 419, 670, 1240, 419, 670, 419, 670, 419, + 670, 1240, 670, 419, 1240, 670, 419, 670, + 419, 1240, 670, 670, 670, 670, 670, 1240, + 419, 419, 670, 419, 670, 1240, 670, 419, + 1240, 670, 670, 1240, 419, 419, 670, 419, + 670, 419, 670, 1240, 1241, 1242, 1243, 1244, + 1245, 1246, 1247, 1248, 1249, 1250, 1251, 715, + 1252, 1253, 1254, 1255, 1256, 1257, 1258, 1259, + 1260, 1261, 1262, 1263, 1262, 1264, 1265, 1266, + 1267, 1268, 671, 1240, 1269, 1270, 1271, 1272, + 1273, 1274, 1275, 1276, 1277, 1278, 1279, 1280, + 1281, 1282, 1283, 1284, 1285, 1286, 1287, 725, + 1288, 1289, 1290, 692, 1291, 1292, 1293, 1294, + 1295, 1296, 671, 1297, 1298, 1299, 1300, 1301, + 1302, 1303, 1304, 674, 1305, 671, 674, 1306, + 1307, 1308, 1309, 683, 1240, 1310, 1311, 1312, + 1313, 703, 1314, 1315, 683, 1316, 1317, 1318, + 1319, 1320, 671, 1240, 1321, 1280, 1322, 1323, + 1324, 683, 1325, 1326, 674, 671, 683, 425, + 1240, 1290, 671, 674, 683, 425, 683, 425, + 1327, 683, 1240, 425, 674, 1328, 1329, 674, + 1330, 1331, 681, 1332, 1333, 1334, 1335, 1336, + 1286, 1337, 1338, 1339, 1340, 1341, 1342, 1343, + 1344, 1345, 1346, 1347, 1348, 1305, 1349, 674, + 683, 425, 1240, 1350, 1351, 683, 671, 1240, + 425, 671, 1240, 674, 1352, 731, 1353, 1354, + 1355, 1356, 1357, 1358, 1359, 1360, 671, 1361, + 1362, 1363, 1364, 1365, 1366, 671, 683, 1240, + 1368, 1369, 1370, 1371, 1372, 1373, 1374, 1375, + 1376, 1377, 1378, 1374, 1380, 1381, 1382, 1383, + 1367, 1379, 1367, 1240, 1367, 1240, 1384, 1384, + 1385, 1386, 1387, 1388, 1389, 1390, 1391, 1392, + 1389, 767, 1393, 1393, 1393, 1394, 1393, 1393, + 768, 769, 770, 1393, 767, 1384, 1384, 1395, + 1398, 1399, 1397, 1400, 1401, 1400, 1402, 1393, + 1404, 1403, 1398, 1405, 1397, 1407, 1406, 1396, + 1396, 1396, 768, 769, 770, 1396, 767, 767, + 1408, 773, 1408, 1409, 1408, 775, 1410, 1411, + 1412, 1413, 1414, 1415, 1416, 1413, 776, 775, + 1410, 1417, 1417, 777, 779, 1418, 1417, 776, + 1420, 1421, 1419, 1420, 1421, 1422, 1419, 775, + 1410, 1423, 1417, 775, 1410, 1417, 1425, 1424, + 1427, 1426, 776, 1428, 777, 1428, 779, 1428, + 785, 1429, 1430, 1431, 1432, 1433, 1434, 1435, + 1432, 786, 785, 1429, 1436, 1436, 787, 789, + 1437, 1436, 786, 1439, 1440, 1438, 1439, 1440, + 1441, 1438, 785, 1429, 1442, 1436, 785, 1429, + 1436, 1444, 1443, 1446, 1445, 786, 1447, 787, + 1447, 789, 1447, 795, 1450, 1451, 1453, 1454, + 1455, 1449, 1456, 1457, 1458, 1459, 1460, 1461, + 1462, 1463, 1464, 1465, 1466, 1467, 1468, 1469, + 1470, 1471, 1472, 1473, 1474, 1475, 1477, 1478, + 1479, 1480, 1481, 1482, 795, 795, 1448, 1449, + 1452, 1476, 1483, 1448, 1046, 795, 795, 1485, + 1486, 865, 846, 1487, 846, 1488, 1489, 1490, + 1491, 1492, 1493, 1494, 1495, 1496, 1497, 1498, + 920, 1499, 896, 1500, 1501, 1502, 1503, 1504, + 1505, 1506, 1507, 1508, 1509, 1510, 1511, 795, + 795, 795, 801, 941, 1484, 1046, 1512, 795, + 795, 795, 1046, 1512, 1046, 1046, 795, 1512, + 795, 1512, 795, 1512, 795, 1046, 1046, 1046, + 1046, 1046, 1512, 795, 1046, 1046, 1046, 795, + 1046, 795, 1512, 795, 1046, 1046, 1046, 1046, + 795, 1512, 1046, 795, 1046, 795, 1046, 795, + 1046, 1046, 795, 1046, 1512, 795, 1046, 795, + 1046, 795, 1046, 1512, 1046, 795, 1512, 1046, + 795, 1046, 795, 1512, 1046, 1046, 1046, 1046, + 1046, 1512, 795, 795, 1046, 795, 1046, 1512, + 1046, 795, 1512, 1046, 1046, 1512, 795, 795, + 1046, 795, 1046, 795, 1046, 1512, 1513, 1514, + 1515, 1516, 1517, 1518, 1519, 1520, 1521, 1522, + 1523, 1091, 1524, 1525, 1526, 1527, 1528, 1529, + 1530, 1531, 1532, 1533, 1534, 1535, 1534, 1536, + 1537, 1538, 1539, 1540, 1047, 1512, 1541, 1542, + 1543, 1544, 1545, 1546, 1547, 1548, 1549, 1550, + 1551, 1552, 1553, 1554, 1555, 1556, 1557, 1558, + 1559, 1101, 1560, 1561, 1562, 1068, 1563, 1564, + 1565, 1566, 1567, 1568, 1047, 1569, 1570, 1571, + 1572, 1573, 1574, 1575, 1576, 1050, 1577, 1047, + 1050, 1578, 1579, 1580, 1581, 1059, 1512, 1582, + 1583, 1584, 1585, 1079, 1586, 1587, 1059, 1588, + 1589, 1590, 1591, 1592, 1047, 1512, 1593, 1552, + 1594, 1595, 1596, 1059, 1597, 1598, 1050, 1047, + 1059, 801, 1512, 1562, 1047, 1050, 1059, 801, + 1059, 801, 1599, 1059, 1512, 801, 1050, 1600, + 1601, 1050, 1602, 1603, 1057, 1604, 1605, 1606, + 1607, 1608, 1558, 1609, 1610, 1611, 1612, 1613, + 1614, 1615, 1616, 1617, 1618, 1619, 1620, 1577, + 1621, 1050, 1059, 801, 1512, 1622, 1623, 1059, + 1047, 1512, 801, 1047, 1512, 1050, 1624, 1107, + 1625, 1626, 1627, 1628, 1629, 1630, 1631, 1632, + 1047, 1633, 1634, 1635, 1636, 1637, 1638, 1047, + 1059, 1512, 1640, 1641, 1642, 1643, 1644, 1645, + 1646, 1647, 1648, 1649, 1650, 1646, 1652, 1653, + 1654, 1655, 1639, 1651, 1639, 1512, 1639, 1512, } var _hcltok_trans_targs []int16 = []int16{ @@ -3238,7 +3238,7 @@ var _hcltok_trans_targs []int16 = []int16{ 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 405, 406, 407, 408, 410, - 412, 414, 1459, 1471, 1459, 437, 438, 439, + 412, 414, 1459, 1472, 1459, 437, 438, 439, 440, 417, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, @@ -3281,11 +3281,11 @@ var _hcltok_trans_targs []int16 = []int16{ 888, 889, 890, 891, 892, 895, 896, 898, 899, 900, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 914, 915, 916, - 917, 920, 922, 923, 925, 927, 1509, 1510, - 929, 930, 931, 1509, 1509, 932, 1523, 1523, - 1524, 935, 1523, 936, 1525, 1526, 1529, 1530, - 1534, 1534, 1535, 941, 1534, 942, 1536, 1537, - 1540, 1541, 1545, 1546, 1545, 968, 969, 970, + 917, 920, 922, 923, 925, 927, 1510, 1511, + 929, 930, 931, 1510, 1510, 932, 1524, 1524, + 1525, 935, 1524, 936, 1526, 1527, 1530, 1531, + 1535, 1535, 1536, 941, 1535, 942, 1537, 1538, + 1541, 1542, 1546, 1547, 1546, 968, 969, 970, 971, 948, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, @@ -3316,7 +3316,7 @@ var _hcltok_trans_targs []int16 = []int16{ 1186, 1187, 1188, 1189, 1190, 1191, 1192, 1193, 1194, 1195, 1196, 1197, 1198, 1199, 1200, 1201, 1202, 1204, 1205, 1206, 1207, 1208, 1209, 1211, - 1213, 1215, 1217, 1219, 1220, 1545, 1545, 1221, + 1213, 1215, 1217, 1219, 1220, 1546, 1546, 1221, 1358, 1359, 1290, 1360, 1361, 1362, 1363, 1364, 1365, 1319, 1366, 1255, 1367, 1368, 1369, 1370, 1371, 1372, 1373, 1374, 1275, 1375, 1376, 1377, @@ -3330,78 +3330,78 @@ var _hcltok_trans_targs []int16 = []int16{ 1439, 1440, 1441, 1442, 1443, 1445, 1446, 1447, 1448, 1451, 1453, 1454, 1456, 1458, 1460, 1459, 1461, 1462, 1459, 1463, 1459, 1464, 1465, 1466, - 1468, 1469, 1470, 1459, 1472, 1459, 1473, 1459, - 1474, 1475, 1476, 1477, 1478, 1479, 1480, 1481, + 1468, 1469, 1470, 1471, 1459, 1473, 1459, 1474, + 1459, 1475, 1476, 1477, 1478, 1479, 1480, 1481, 1482, 1483, 1484, 1485, 1486, 1487, 1488, 1489, 1490, 1491, 1492, 1493, 1494, 1495, 1496, 1497, 1498, 1499, 1500, 1501, 1502, 1503, 1504, 1505, - 1506, 1507, 1508, 1459, 1459, 1459, 1459, 1459, - 1459, 1, 1459, 7, 1459, 1459, 1459, 1459, - 1459, 415, 416, 420, 421, 422, 423, 424, - 425, 426, 427, 428, 429, 430, 431, 433, - 435, 436, 468, 509, 524, 531, 533, 535, - 555, 558, 574, 687, 1459, 1459, 1459, 691, - 692, 693, 694, 695, 696, 697, 698, 699, - 700, 701, 703, 704, 705, 706, 707, 708, - 709, 710, 711, 712, 713, 714, 715, 716, - 717, 718, 719, 720, 721, 722, 723, 725, - 726, 727, 728, 729, 730, 731, 732, 733, - 734, 735, 736, 737, 738, 739, 741, 742, - 743, 745, 746, 747, 748, 749, 750, 751, - 752, 753, 754, 755, 756, 757, 758, 760, - 761, 762, 763, 764, 765, 766, 767, 768, - 770, 771, 772, 773, 774, 775, 776, 777, - 778, 779, 780, 781, 782, 783, 784, 785, - 786, 787, 789, 790, 791, 792, 793, 794, - 795, 796, 797, 798, 799, 800, 801, 802, - 803, 804, 805, 806, 807, 808, 809, 811, - 812, 813, 814, 815, 816, 817, 818, 819, - 820, 821, 822, 823, 824, 825, 826, 855, - 880, 883, 884, 886, 893, 894, 897, 901, - 913, 918, 919, 921, 924, 926, 1511, 1509, - 1512, 1517, 1519, 1509, 1520, 1521, 1522, 1509, - 928, 1509, 1509, 1513, 1514, 1516, 1509, 1515, - 1509, 1509, 1509, 1518, 1509, 1509, 1509, 933, - 934, 938, 939, 1523, 1531, 1532, 1533, 1523, - 937, 1523, 1523, 934, 1527, 1528, 1523, 1523, - 1523, 1523, 1523, 940, 944, 945, 1534, 1542, - 1543, 1544, 1534, 943, 1534, 1534, 940, 1538, - 1539, 1534, 1534, 1534, 1534, 1534, 1545, 1547, - 1548, 1549, 1550, 1551, 1552, 1553, 1554, 1555, - 1556, 1557, 1558, 1559, 1560, 1561, 1562, 1563, - 1564, 1565, 1566, 1567, 1568, 1569, 1570, 1571, - 1572, 1573, 1574, 1575, 1576, 1577, 1578, 1579, - 1580, 1581, 1545, 946, 947, 951, 952, 953, - 954, 955, 956, 957, 958, 959, 960, 961, - 962, 964, 966, 967, 999, 1040, 1055, 1062, - 1064, 1066, 1086, 1089, 1105, 1218, 1545, 1222, - 1223, 1224, 1225, 1226, 1227, 1228, 1229, 1230, - 1231, 1232, 1234, 1235, 1236, 1237, 1238, 1239, - 1240, 1241, 1242, 1243, 1244, 1245, 1246, 1247, - 1248, 1249, 1250, 1251, 1252, 1253, 1254, 1256, - 1257, 1258, 1259, 1260, 1261, 1262, 1263, 1264, - 1265, 1266, 1267, 1268, 1269, 1270, 1272, 1273, - 1274, 1276, 1277, 1278, 1279, 1280, 1281, 1282, - 1283, 1284, 1285, 1286, 1287, 1288, 1289, 1291, - 1292, 1293, 1294, 1295, 1296, 1297, 1298, 1299, - 1301, 1302, 1303, 1304, 1305, 1306, 1307, 1308, - 1309, 1310, 1311, 1312, 1313, 1314, 1315, 1316, - 1317, 1318, 1320, 1321, 1322, 1323, 1324, 1325, - 1326, 1327, 1328, 1329, 1330, 1331, 1332, 1333, - 1334, 1335, 1336, 1337, 1338, 1339, 1340, 1342, - 1343, 1344, 1345, 1346, 1347, 1348, 1349, 1350, - 1351, 1352, 1353, 1354, 1355, 1356, 1357, 1386, - 1411, 1414, 1415, 1417, 1424, 1425, 1428, 1432, - 1444, 1449, 1450, 1452, 1455, 1457, + 1506, 1507, 1508, 1509, 1459, 1459, 1459, 1459, + 1459, 1459, 1, 1459, 1459, 7, 1459, 1459, + 1459, 1459, 1459, 415, 416, 420, 421, 422, + 423, 424, 425, 426, 427, 428, 429, 430, + 431, 433, 435, 436, 468, 509, 524, 531, + 533, 535, 555, 558, 574, 687, 1459, 1459, + 1459, 691, 692, 693, 694, 695, 696, 697, + 698, 699, 700, 701, 703, 704, 705, 706, + 707, 708, 709, 710, 711, 712, 713, 714, + 715, 716, 717, 718, 719, 720, 721, 722, + 723, 725, 726, 727, 728, 729, 730, 731, + 732, 733, 734, 735, 736, 737, 738, 739, + 741, 742, 743, 745, 746, 747, 748, 749, + 750, 751, 752, 753, 754, 755, 756, 757, + 758, 760, 761, 762, 763, 764, 765, 766, + 767, 768, 770, 771, 772, 773, 774, 775, + 776, 777, 778, 779, 780, 781, 782, 783, + 784, 785, 786, 787, 789, 790, 791, 792, + 793, 794, 795, 796, 797, 798, 799, 800, + 801, 802, 803, 804, 805, 806, 807, 808, + 809, 811, 812, 813, 814, 815, 816, 817, + 818, 819, 820, 821, 822, 823, 824, 825, + 826, 855, 880, 883, 884, 886, 893, 894, + 897, 901, 913, 918, 919, 921, 924, 926, + 1512, 1510, 1513, 1518, 1520, 1510, 1521, 1522, + 1523, 1510, 928, 1510, 1510, 1514, 1515, 1517, + 1510, 1516, 1510, 1510, 1510, 1519, 1510, 1510, + 1510, 933, 934, 938, 939, 1524, 1532, 1533, + 1534, 1524, 937, 1524, 1524, 934, 1528, 1529, + 1524, 1524, 1524, 1524, 1524, 940, 944, 945, + 1535, 1543, 1544, 1545, 1535, 943, 1535, 1535, + 940, 1539, 1540, 1535, 1535, 1535, 1535, 1535, + 1546, 1548, 1549, 1550, 1551, 1552, 1553, 1554, + 1555, 1556, 1557, 1558, 1559, 1560, 1561, 1562, + 1563, 1564, 1565, 1566, 1567, 1568, 1569, 1570, + 1571, 1572, 1573, 1574, 1575, 1576, 1577, 1578, + 1579, 1580, 1581, 1582, 1546, 946, 947, 951, + 952, 953, 954, 955, 956, 957, 958, 959, + 960, 961, 962, 964, 966, 967, 999, 1040, + 1055, 1062, 1064, 1066, 1086, 1089, 1105, 1218, + 1546, 1222, 1223, 1224, 1225, 1226, 1227, 1228, + 1229, 1230, 1231, 1232, 1234, 1235, 1236, 1237, + 1238, 1239, 1240, 1241, 1242, 1243, 1244, 1245, + 1246, 1247, 1248, 1249, 1250, 1251, 1252, 1253, + 1254, 1256, 1257, 1258, 1259, 1260, 1261, 1262, + 1263, 1264, 1265, 1266, 1267, 1268, 1269, 1270, + 1272, 1273, 1274, 1276, 1277, 1278, 1279, 1280, + 1281, 1282, 1283, 1284, 1285, 1286, 1287, 1288, + 1289, 1291, 1292, 1293, 1294, 1295, 1296, 1297, + 1298, 1299, 1301, 1302, 1303, 1304, 1305, 1306, + 1307, 1308, 1309, 1310, 1311, 1312, 1313, 1314, + 1315, 1316, 1317, 1318, 1320, 1321, 1322, 1323, + 1324, 1325, 1326, 1327, 1328, 1329, 1330, 1331, + 1332, 1333, 1334, 1335, 1336, 1337, 1338, 1339, + 1340, 1342, 1343, 1344, 1345, 1346, 1347, 1348, + 1349, 1350, 1351, 1352, 1353, 1354, 1355, 1356, + 1357, 1386, 1411, 1414, 1415, 1417, 1424, 1425, + 1428, 1432, 1444, 1449, 1450, 1452, 1455, 1457, } var _hcltok_trans_actions []byte = []byte{ - 145, 107, 0, 0, 91, 141, 0, 7, + 147, 109, 0, 0, 91, 143, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 121, 0, 0, 0, + 0, 0, 0, 0, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -3448,7 +3448,7 @@ var _hcltok_trans_actions []byte = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 143, 193, 149, 0, 0, 0, + 0, 0, 145, 195, 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -3479,7 +3479,7 @@ var _hcltok_trans_actions []byte = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 147, 125, 0, + 0, 0, 0, 0, 0, 149, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -3491,11 +3491,11 @@ var _hcltok_trans_actions []byte = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 31, 169, + 0, 0, 0, 0, 0, 0, 31, 171, 0, 0, 0, 35, 33, 0, 55, 41, - 175, 0, 53, 0, 175, 175, 0, 0, - 75, 61, 181, 0, 73, 0, 181, 181, - 0, 0, 85, 187, 89, 0, 0, 0, + 177, 0, 53, 0, 177, 177, 0, 0, + 75, 61, 183, 0, 73, 0, 183, 183, + 0, 0, 85, 189, 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -3539,18 +3539,19 @@ var _hcltok_trans_actions []byte = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, - 0, 0, 119, 0, 111, 0, 7, 7, - 7, 0, 0, 113, 0, 115, 0, 123, - 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 121, 0, 113, 0, 7, 7, + 0, 7, 0, 0, 115, 0, 117, 0, + 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 7, 7, - 7, 196, 196, 196, 196, 196, 196, 7, - 7, 196, 7, 127, 139, 135, 97, 133, - 103, 0, 129, 0, 101, 95, 109, 99, - 131, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 7, + 7, 7, 198, 198, 198, 198, 198, 198, + 7, 7, 198, 7, 129, 141, 137, 97, + 135, 103, 0, 131, 107, 0, 101, 95, + 111, 99, 133, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 105, 117, 137, 0, + 0, 0, 0, 0, 0, 0, 105, 119, + 139, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -3568,23 +3569,23 @@ var _hcltok_trans_actions []byte = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 13, - 0, 0, 172, 17, 0, 7, 7, 23, - 0, 25, 27, 0, 0, 0, 151, 0, - 15, 19, 9, 0, 21, 11, 29, 0, - 0, 0, 0, 43, 0, 178, 178, 49, - 0, 157, 154, 1, 175, 175, 45, 37, - 47, 39, 51, 0, 0, 0, 63, 0, - 184, 184, 69, 0, 163, 160, 1, 181, - 181, 65, 57, 67, 59, 71, 77, 0, + 0, 13, 0, 0, 174, 17, 0, 7, + 7, 23, 0, 25, 27, 0, 0, 0, + 153, 0, 15, 19, 9, 0, 21, 11, + 29, 0, 0, 0, 0, 43, 0, 180, + 180, 49, 0, 159, 156, 1, 177, 177, + 45, 37, 47, 39, 51, 0, 0, 0, + 63, 0, 186, 186, 69, 0, 165, 162, + 1, 183, 183, 65, 57, 67, 59, 71, + 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 7, + 7, 7, 192, 192, 192, 192, 192, 192, + 7, 7, 192, 7, 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 7, 7, 7, - 190, 190, 190, 190, 190, 190, 7, 7, - 190, 7, 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 83, 0, + 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -3602,7 +3603,6 @@ var _hcltok_trans_actions []byte = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, } var _hcltok_to_state_actions []byte = []byte{ @@ -3794,16 +3794,16 @@ var _hcltok_to_state_actions []byte = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 166, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 166, 0, + 0, 0, 0, 0, 168, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 168, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 3, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, } var _hcltok_from_state_actions []byte = []byte{ @@ -3995,16 +3995,16 @@ var _hcltok_from_state_actions []byte = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 5, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 5, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, } var _hcltok_eof_trans []int16 = []int16{ @@ -4190,32 +4190,32 @@ var _hcltok_eof_trans []int16 = []int16{ 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, - 1046, 1046, 1046, 0, 1196, 1197, 1198, 1200, - 1198, 1198, 1198, 1203, 1198, 1198, 1198, 1209, - 1198, 1198, 1239, 1239, 1239, 1239, 1239, 1239, - 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, - 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, - 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, - 1239, 1239, 1239, 1239, 1239, 0, 1392, 1394, - 1395, 1399, 1399, 1392, 1402, 1395, 1405, 1395, - 1407, 1407, 1407, 0, 1416, 1418, 1418, 1416, - 1416, 1423, 1425, 1427, 1427, 1427, 0, 1435, - 1437, 1437, 1435, 1435, 1442, 1444, 1446, 1446, - 1446, 0, 1483, 1511, 1511, 1511, 1511, 1511, - 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, - 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, - 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, - 1511, 1511, 1511, 1511, 1511, 1511, + 1046, 1046, 1046, 0, 1197, 1198, 1199, 1201, + 1199, 1199, 1199, 1204, 1199, 1199, 1199, 1199, + 1211, 1199, 1199, 1241, 1241, 1241, 1241, 1241, + 1241, 1241, 1241, 1241, 1241, 1241, 1241, 1241, + 1241, 1241, 1241, 1241, 1241, 1241, 1241, 1241, + 1241, 1241, 1241, 1241, 1241, 1241, 1241, 1241, + 1241, 1241, 1241, 1241, 1241, 1241, 0, 1394, + 1396, 1397, 1401, 1401, 1394, 1404, 1397, 1407, + 1397, 1409, 1409, 1409, 0, 1418, 1420, 1420, + 1418, 1418, 1425, 1427, 1429, 1429, 1429, 0, + 1437, 1439, 1439, 1437, 1437, 1444, 1446, 1448, + 1448, 1448, 0, 1485, 1513, 1513, 1513, 1513, + 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, + 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, + 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, + 1513, 1513, 1513, 1513, 1513, 1513, 1513, } const hcltok_start int = 1459 const hcltok_first_final int = 1459 const hcltok_error int = 0 -const hcltok_en_stringTemplate int = 1509 -const hcltok_en_heredocTemplate int = 1523 -const hcltok_en_bareTemplate int = 1534 -const hcltok_en_identOnly int = 1545 +const hcltok_en_stringTemplate int = 1510 +const hcltok_en_heredocTemplate int = 1524 +const hcltok_en_bareTemplate int = 1535 +const hcltok_en_identOnly int = 1546 const hcltok_en_main int = 1459 //line scan_tokens.rl:16 @@ -4232,7 +4232,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To StartByte: start.Byte, } -//line scan_tokens.rl:305 +//line scan_tokens.rl:315 // Ragel state p := 0 // "Pointer" into data @@ -4260,7 +4260,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To var retBraces []int // stack of brace levels that cause us to use fret var heredocs []heredocInProgress // stack of heredocs we're currently processing -//line scan_tokens.rl:340 +//line scan_tokens.rl:350 // Make Go compiler happy _ = ts @@ -4280,7 +4280,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To f.emitToken(TokenType(b[0]), ts, te) } -//line scan_tokens.go:4289 +//line scan_tokens.go:4290 { top = 0 ts = 0 @@ -4288,7 +4288,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To act = 0 } -//line scan_tokens.go:4297 +//line scan_tokens.go:4298 { var _klen int var _trans int @@ -4312,7 +4312,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To //line NONE:1 ts = p -//line scan_tokens.go:4320 +//line scan_tokens.go:4321 } } @@ -4384,7 +4384,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To _acts++ switch _hcltok_actions[_acts-1] { case 0: -//line scan_tokens.rl:224 +//line scan_tokens.rl:233 p-- case 4: @@ -4392,13 +4392,13 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To te = p + 1 case 5: -//line scan_tokens.rl:248 +//line scan_tokens.rl:257 act = 4 case 6: -//line scan_tokens.rl:250 +//line scan_tokens.rl:259 act = 6 case 7: -//line scan_tokens.rl:160 +//line scan_tokens.rl:169 te = p + 1 { token(TokenTemplateInterp) @@ -4416,7 +4416,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 8: -//line scan_tokens.rl:170 +//line scan_tokens.rl:179 te = p + 1 { token(TokenTemplateControl) @@ -4434,7 +4434,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 9: -//line scan_tokens.rl:84 +//line scan_tokens.rl:93 te = p + 1 { token(TokenCQuote) @@ -4447,19 +4447,19 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } case 10: -//line scan_tokens.rl:248 +//line scan_tokens.rl:257 te = p + 1 { token(TokenQuotedLit) } case 11: -//line scan_tokens.rl:251 +//line scan_tokens.rl:260 te = p + 1 { token(TokenBadUTF8) } case 12: -//line scan_tokens.rl:160 +//line scan_tokens.rl:169 te = p p-- { @@ -4478,7 +4478,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 13: -//line scan_tokens.rl:170 +//line scan_tokens.rl:179 te = p p-- { @@ -4497,41 +4497,41 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 14: -//line scan_tokens.rl:248 +//line scan_tokens.rl:257 te = p p-- { token(TokenQuotedLit) } case 15: -//line scan_tokens.rl:249 +//line scan_tokens.rl:258 te = p p-- { token(TokenQuotedNewline) } case 16: -//line scan_tokens.rl:250 +//line scan_tokens.rl:259 te = p p-- { token(TokenInvalid) } case 17: -//line scan_tokens.rl:251 +//line scan_tokens.rl:260 te = p p-- { token(TokenBadUTF8) } case 18: -//line scan_tokens.rl:248 +//line scan_tokens.rl:257 p = (te) - 1 { token(TokenQuotedLit) } case 19: -//line scan_tokens.rl:251 +//line scan_tokens.rl:260 p = (te) - 1 { token(TokenBadUTF8) @@ -4552,13 +4552,13 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } case 21: -//line scan_tokens.rl:148 +//line scan_tokens.rl:157 act = 11 case 22: -//line scan_tokens.rl:259 +//line scan_tokens.rl:268 act = 12 case 23: -//line scan_tokens.rl:160 +//line scan_tokens.rl:169 te = p + 1 { token(TokenTemplateInterp) @@ -4576,7 +4576,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 24: -//line scan_tokens.rl:170 +//line scan_tokens.rl:179 te = p + 1 { token(TokenTemplateControl) @@ -4594,7 +4594,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 25: -//line scan_tokens.rl:111 +//line scan_tokens.rl:120 te = p + 1 { // This action is called specificially when a heredoc literal @@ -4639,13 +4639,13 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To token(TokenStringLit) } case 26: -//line scan_tokens.rl:259 +//line scan_tokens.rl:268 te = p + 1 { token(TokenBadUTF8) } case 27: -//line scan_tokens.rl:160 +//line scan_tokens.rl:169 te = p p-- { @@ -4664,7 +4664,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 28: -//line scan_tokens.rl:170 +//line scan_tokens.rl:179 te = p p-- { @@ -4683,7 +4683,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 29: -//line scan_tokens.rl:148 +//line scan_tokens.rl:157 te = p p-- { @@ -4694,14 +4694,14 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To token(TokenStringLit) } case 30: -//line scan_tokens.rl:259 +//line scan_tokens.rl:268 te = p p-- { token(TokenBadUTF8) } case 31: -//line scan_tokens.rl:148 +//line scan_tokens.rl:157 p = (te) - 1 { // This action is called when a heredoc literal _doesn't_ end @@ -4736,13 +4736,13 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } case 33: -//line scan_tokens.rl:156 +//line scan_tokens.rl:165 act = 15 case 34: -//line scan_tokens.rl:266 +//line scan_tokens.rl:275 act = 16 case 35: -//line scan_tokens.rl:160 +//line scan_tokens.rl:169 te = p + 1 { token(TokenTemplateInterp) @@ -4760,7 +4760,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 36: -//line scan_tokens.rl:170 +//line scan_tokens.rl:179 te = p + 1 { token(TokenTemplateControl) @@ -4778,19 +4778,19 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 37: -//line scan_tokens.rl:156 +//line scan_tokens.rl:165 te = p + 1 { token(TokenStringLit) } case 38: -//line scan_tokens.rl:266 +//line scan_tokens.rl:275 te = p + 1 { token(TokenBadUTF8) } case 39: -//line scan_tokens.rl:160 +//line scan_tokens.rl:169 te = p p-- { @@ -4809,7 +4809,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 40: -//line scan_tokens.rl:170 +//line scan_tokens.rl:179 te = p p-- { @@ -4828,21 +4828,21 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 41: -//line scan_tokens.rl:156 +//line scan_tokens.rl:165 te = p p-- { token(TokenStringLit) } case 42: -//line scan_tokens.rl:266 +//line scan_tokens.rl:275 te = p p-- { token(TokenBadUTF8) } case 43: -//line scan_tokens.rl:156 +//line scan_tokens.rl:165 p = (te) - 1 { token(TokenStringLit) @@ -4869,45 +4869,45 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } case 45: -//line scan_tokens.rl:270 +//line scan_tokens.rl:279 act = 17 case 46: -//line scan_tokens.rl:271 +//line scan_tokens.rl:280 act = 18 case 47: -//line scan_tokens.rl:271 +//line scan_tokens.rl:280 te = p + 1 { token(TokenBadUTF8) } case 48: -//line scan_tokens.rl:272 +//line scan_tokens.rl:281 te = p + 1 { token(TokenInvalid) } case 49: -//line scan_tokens.rl:270 +//line scan_tokens.rl:279 te = p p-- { token(TokenIdent) } case 50: -//line scan_tokens.rl:271 +//line scan_tokens.rl:280 te = p p-- { token(TokenBadUTF8) } case 51: -//line scan_tokens.rl:270 +//line scan_tokens.rl:279 p = (te) - 1 { token(TokenIdent) } case 52: -//line scan_tokens.rl:271 +//line scan_tokens.rl:280 p = (te) - 1 { token(TokenBadUTF8) @@ -4928,86 +4928,92 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } case 54: -//line scan_tokens.rl:278 +//line scan_tokens.rl:287 act = 22 case 55: -//line scan_tokens.rl:301 - act = 39 +//line scan_tokens.rl:311 + act = 40 case 56: -//line scan_tokens.rl:280 +//line scan_tokens.rl:289 te = p + 1 { token(TokenComment) } case 57: -//line scan_tokens.rl:281 +//line scan_tokens.rl:290 te = p + 1 { token(TokenNewline) } case 58: -//line scan_tokens.rl:283 +//line scan_tokens.rl:292 te = p + 1 { token(TokenEqualOp) } case 59: -//line scan_tokens.rl:284 +//line scan_tokens.rl:293 te = p + 1 { token(TokenNotEqual) } case 60: -//line scan_tokens.rl:285 +//line scan_tokens.rl:294 te = p + 1 { token(TokenGreaterThanEq) } case 61: -//line scan_tokens.rl:286 +//line scan_tokens.rl:295 te = p + 1 { token(TokenLessThanEq) } case 62: -//line scan_tokens.rl:287 +//line scan_tokens.rl:296 te = p + 1 { token(TokenAnd) } case 63: -//line scan_tokens.rl:288 +//line scan_tokens.rl:297 te = p + 1 { token(TokenOr) } case 64: -//line scan_tokens.rl:289 +//line scan_tokens.rl:298 te = p + 1 { - token(TokenEllipsis) + token(TokenDoubleColon) } case 65: -//line scan_tokens.rl:290 +//line scan_tokens.rl:299 te = p + 1 { - token(TokenFatArrow) + token(TokenEllipsis) } case 66: -//line scan_tokens.rl:291 +//line scan_tokens.rl:300 te = p + 1 { - selfToken() + token(TokenFatArrow) } case 67: -//line scan_tokens.rl:180 +//line scan_tokens.rl:301 + te = p + 1 + { + selfToken() + } + case 68: +//line scan_tokens.rl:189 te = p + 1 { token(TokenOBrace) braces++ } - case 68: -//line scan_tokens.rl:185 + case 69: +//line scan_tokens.rl:194 te = p + 1 { if len(retBraces) > 0 && retBraces[len(retBraces)-1] == braces { @@ -5026,8 +5032,8 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To braces-- } } - case 69: -//line scan_tokens.rl:197 + case 70: +//line scan_tokens.rl:206 te = p + 1 { // Only consume from the retBraces stack and return if we are at @@ -5055,8 +5061,8 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To braces-- } } - case 70: -//line scan_tokens.rl:79 + case 71: +//line scan_tokens.rl:88 te = p + 1 { token(TokenOQuote) @@ -5064,12 +5070,12 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To stack = append(stack, 0) stack[top] = cs top++ - cs = 1509 + cs = 1510 goto _again } } - case 71: -//line scan_tokens.rl:89 + case 72: +//line scan_tokens.rl:98 te = p + 1 { token(TokenOHeredoc) @@ -5094,94 +5100,94 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To stack = append(stack, 0) stack[top] = cs top++ - cs = 1523 + cs = 1524 goto _again } } - case 72: -//line scan_tokens.rl:301 + case 73: +//line scan_tokens.rl:311 te = p + 1 { token(TokenBadUTF8) } - case 73: -//line scan_tokens.rl:302 + case 74: +//line scan_tokens.rl:312 te = p + 1 { token(TokenInvalid) } - case 74: -//line scan_tokens.rl:276 + case 75: +//line scan_tokens.rl:285 te = p p-- - case 75: -//line scan_tokens.rl:277 + case 76: +//line scan_tokens.rl:286 te = p p-- { token(TokenNumberLit) } - case 76: -//line scan_tokens.rl:278 + case 77: +//line scan_tokens.rl:287 te = p p-- { token(TokenIdent) } - case 77: -//line scan_tokens.rl:280 + case 78: +//line scan_tokens.rl:289 te = p p-- { token(TokenComment) } - case 78: -//line scan_tokens.rl:291 + case 79: +//line scan_tokens.rl:301 te = p p-- { selfToken() } - case 79: -//line scan_tokens.rl:301 + case 80: +//line scan_tokens.rl:311 te = p p-- { token(TokenBadUTF8) } - case 80: -//line scan_tokens.rl:302 + case 81: +//line scan_tokens.rl:312 te = p p-- { token(TokenInvalid) } - case 81: -//line scan_tokens.rl:277 + case 82: +//line scan_tokens.rl:286 p = (te) - 1 { token(TokenNumberLit) } - case 82: -//line scan_tokens.rl:278 + case 83: +//line scan_tokens.rl:287 p = (te) - 1 { token(TokenIdent) } - case 83: -//line scan_tokens.rl:291 + case 84: +//line scan_tokens.rl:301 p = (te) - 1 { selfToken() } - case 84: -//line scan_tokens.rl:301 + case 85: +//line scan_tokens.rl:311 p = (te) - 1 { token(TokenBadUTF8) } - case 85: + case 86: //line NONE:1 switch act { case 22: @@ -5189,14 +5195,14 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To p = (te) - 1 token(TokenIdent) } - case 39: + case 40: { p = (te) - 1 token(TokenBadUTF8) } } -//line scan_tokens.go:5055 +//line scan_tokens.go:5060 } } @@ -5215,7 +5221,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To //line NONE:1 act = 0 -//line scan_tokens.go:5073 +//line scan_tokens.go:5078 } } @@ -5241,7 +5247,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } -//line scan_tokens.rl:363 +//line scan_tokens.rl:373 // If we fall out here without being in a final state then we've // encountered something that the scanner can't match, which we'll diff --git a/hclsyntax/scan_tokens.rl b/hclsyntax/scan_tokens.rl index 942ad92b..8d1e08af 100644 --- a/hclsyntax/scan_tokens.rl +++ b/hclsyntax/scan_tokens.rl @@ -53,6 +53,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To LogicalAnd = "&&"; LogicalOr = "||"; + DoubleColon = "::"; Ellipsis = "..."; FatArrow = "=>"; @@ -294,6 +295,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To LessThanEqual => { token(TokenLessThanEq); }; LogicalAnd => { token(TokenAnd); }; LogicalOr => { token(TokenOr); }; + DoubleColon => { token(TokenDoubleColon); }; Ellipsis => { token(TokenEllipsis); }; FatArrow => { token(TokenFatArrow); }; SelfToken => { selfToken() }; diff --git a/hclsyntax/scan_tokens_test.go b/hclsyntax/scan_tokens_test.go index f3b6eeb4..10c42007 100644 --- a/hclsyntax/scan_tokens_test.go +++ b/hclsyntax/scan_tokens_test.go @@ -372,6 +372,66 @@ func TestScanTokens_normal(t *testing.T) { }, }, + // TokenDoubleColon and associated TokenIdent + { + `::`, + []Token{ + { + Type: TokenDoubleColon, + Bytes: []byte(`::`), + Range: hcl.Range{ + Start: hcl.Pos{Byte: 0, Line: 1, Column: 1}, + End: hcl.Pos{Byte: 2, Line: 1, Column: 3}, + }, + }, + { + Type: TokenEOF, + Bytes: []byte{}, + Range: hcl.Range{ + Start: hcl.Pos{Byte: 2, Line: 1, Column: 3}, + End: hcl.Pos{Byte: 2, Line: 1, Column: 3}, + }, + }, + }, + }, + { + `a::b`, + []Token{ + { + Type: TokenIdent, + Bytes: []byte(`a`), + Range: hcl.Range{ + Start: hcl.Pos{Byte: 0, Line: 1, Column: 1}, + End: hcl.Pos{Byte: 1, Line: 1, Column: 2}, + }, + }, + { + Type: TokenDoubleColon, + Bytes: []byte(`::`), + Range: hcl.Range{ + Start: hcl.Pos{Byte: 1, Line: 1, Column: 2}, + End: hcl.Pos{Byte: 3, Line: 1, Column: 4}, + }, + }, + { + Type: TokenIdent, + Bytes: []byte(`b`), + Range: hcl.Range{ + Start: hcl.Pos{Byte: 3, Line: 1, Column: 4}, + End: hcl.Pos{Byte: 4, Line: 1, Column: 5}, + }, + }, + { + Type: TokenEOF, + Bytes: []byte{}, + Range: hcl.Range{ + Start: hcl.Pos{Byte: 4, Line: 1, Column: 5}, + End: hcl.Pos{Byte: 4, Line: 1, Column: 5}, + }, + }, + }, + }, + // Literal-only Templates (string literals, effectively) { `""`, diff --git a/hclsyntax/token.go b/hclsyntax/token.go index afde5f33..47648b8f 100644 --- a/hclsyntax/token.go +++ b/hclsyntax/token.go @@ -63,8 +63,9 @@ const ( TokenDot TokenType = '.' TokenComma TokenType = ',' - TokenEllipsis TokenType = '…' - TokenFatArrow TokenType = '⇒' + TokenDoubleColon TokenType = '⸬' + TokenEllipsis TokenType = '…' + TokenFatArrow TokenType = '⇒' TokenQuestion TokenType = '?' TokenColon TokenType = ':' diff --git a/hclsyntax/token_type_string.go b/hclsyntax/token_type_string.go index c23c4f0b..5a2cd4eb 100644 --- a/hclsyntax/token_type_string.go +++ b/hclsyntax/token_type_string.go @@ -4,67 +4,7 @@ package hclsyntax import "strconv" -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[TokenOBrace-123] - _ = x[TokenCBrace-125] - _ = x[TokenOBrack-91] - _ = x[TokenCBrack-93] - _ = x[TokenOParen-40] - _ = x[TokenCParen-41] - _ = x[TokenOQuote-171] - _ = x[TokenCQuote-187] - _ = x[TokenOHeredoc-72] - _ = x[TokenCHeredoc-104] - _ = x[TokenStar-42] - _ = x[TokenSlash-47] - _ = x[TokenPlus-43] - _ = x[TokenMinus-45] - _ = x[TokenPercent-37] - _ = x[TokenEqual-61] - _ = x[TokenEqualOp-8788] - _ = x[TokenNotEqual-8800] - _ = x[TokenLessThan-60] - _ = x[TokenLessThanEq-8804] - _ = x[TokenGreaterThan-62] - _ = x[TokenGreaterThanEq-8805] - _ = x[TokenAnd-8743] - _ = x[TokenOr-8744] - _ = x[TokenBang-33] - _ = x[TokenDot-46] - _ = x[TokenComma-44] - _ = x[TokenEllipsis-8230] - _ = x[TokenFatArrow-8658] - _ = x[TokenQuestion-63] - _ = x[TokenColon-58] - _ = x[TokenTemplateInterp-8747] - _ = x[TokenTemplateControl-955] - _ = x[TokenTemplateSeqEnd-8718] - _ = x[TokenQuotedLit-81] - _ = x[TokenStringLit-83] - _ = x[TokenNumberLit-78] - _ = x[TokenIdent-73] - _ = x[TokenComment-67] - _ = x[TokenNewline-10] - _ = x[TokenEOF-9220] - _ = x[TokenBitwiseAnd-38] - _ = x[TokenBitwiseOr-124] - _ = x[TokenBitwiseNot-126] - _ = x[TokenBitwiseXor-94] - _ = x[TokenStarStar-10138] - _ = x[TokenApostrophe-39] - _ = x[TokenBacktick-96] - _ = x[TokenSemicolon-59] - _ = x[TokenTabs-9225] - _ = x[TokenInvalid-65533] - _ = x[TokenBadUTF8-128169] - _ = x[TokenQuotedNewline-9252] - _ = x[TokenNil-0] -} - -const _TokenType_name = "TokenNilTokenNewlineTokenBangTokenPercentTokenBitwiseAndTokenApostropheTokenOParenTokenCParenTokenStarTokenPlusTokenCommaTokenMinusTokenDotTokenSlashTokenColonTokenSemicolonTokenLessThanTokenEqualTokenGreaterThanTokenQuestionTokenCommentTokenOHeredocTokenIdentTokenNumberLitTokenQuotedLitTokenStringLitTokenOBrackTokenCBrackTokenBitwiseXorTokenBacktickTokenCHeredocTokenOBraceTokenBitwiseOrTokenCBraceTokenBitwiseNotTokenOQuoteTokenCQuoteTokenTemplateControlTokenEllipsisTokenFatArrowTokenTemplateSeqEndTokenAndTokenOrTokenTemplateInterpTokenEqualOpTokenNotEqualTokenLessThanEqTokenGreaterThanEqTokenEOFTokenTabsTokenQuotedNewlineTokenStarStarTokenInvalidTokenBadUTF8" +const _TokenType_name = "TokenNilTokenNewlineTokenBangTokenPercentTokenBitwiseAndTokenApostropheTokenOParenTokenCParenTokenStarTokenPlusTokenCommaTokenMinusTokenDotTokenSlashTokenColonTokenSemicolonTokenLessThanTokenEqualTokenGreaterThanTokenQuestionTokenCommentTokenOHeredocTokenIdentTokenNumberLitTokenQuotedLitTokenStringLitTokenOBrackTokenCBrackTokenBitwiseXorTokenBacktickTokenCHeredocTokenOBraceTokenBitwiseOrTokenCBraceTokenBitwiseNotTokenOQuoteTokenCQuoteTokenTemplateControlTokenEllipsisTokenFatArrowTokenTemplateSeqEndTokenAndTokenOrTokenTemplateInterpTokenEqualOpTokenNotEqualTokenLessThanEqTokenGreaterThanEqTokenEOFTokenTabsTokenQuotedNewlineTokenStarStarTokenDoubleColonTokenInvalidTokenBadUTF8" var _TokenType_map = map[TokenType]string{ 0: _TokenType_name[0:8], @@ -119,8 +59,9 @@ var _TokenType_map = map[TokenType]string{ 9225: _TokenType_name[603:612], 9252: _TokenType_name[612:630], 10138: _TokenType_name[630:643], - 65533: _TokenType_name[643:655], - 128169: _TokenType_name[655:667], + 11820: _TokenType_name[643:659], + 65533: _TokenType_name[659:671], + 128169: _TokenType_name[671:683], } func (i TokenType) String() string { From 1ce4917789ea5ed149c2c3254d85c65654c27cc4 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 1 Nov 2023 16:56:00 -0400 Subject: [PATCH 15/55] add a few more function scope tests --- hclsyntax/expression_test.go | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/hclsyntax/expression_test.go b/hclsyntax/expression_test.go index 72ff1596..4f3f75f7 100644 --- a/hclsyntax/expression_test.go +++ b/hclsyntax/expression_test.go @@ -362,6 +362,36 @@ upper( cty.StringVal("FOO"), 0, }, + { + `::upper("foo")`, // :: is still not a valid identifier + &hcl.EvalContext{ + Functions: map[string]function.Function{ + "::upper": stdlib.UpperFunc, + }, + }, + cty.DynamicVal, + 1, + }, + { + `double::::upper("foo")`, // missing name after :: + &hcl.EvalContext{ + Functions: map[string]function.Function{ + "double::::upper": stdlib.UpperFunc, + }, + }, + cty.NilVal, + 1, + }, + { + `missing::("foo")`, // missing name after :: + &hcl.EvalContext{ + Functions: map[string]function.Function{ + "missing::": stdlib.UpperFunc, + }, + }, + cty.NilVal, + 1, + }, { `misbehave()`, &hcl.EvalContext{ @@ -2194,8 +2224,12 @@ EOT for _, test := range tests { t.Run(test.input, func(t *testing.T) { expr, parseDiags := ParseExpression([]byte(test.input), "", hcl.Pos{Line: 1, Column: 1, Byte: 0}) + var got cty.Value + var valDiags hcl.Diagnostics - got, valDiags := expr.Value(test.ctx) + if expr != nil { + got, valDiags = expr.Value(test.ctx) + } diagCount := len(parseDiags) + len(valDiags) From 916ac487b471a8c6220be57d711710942c7dd29c Mon Sep 17 00:00:00 2001 From: James Bardin Date: Thu, 2 Nov 2023 14:14:41 -0400 Subject: [PATCH 16/55] add scoped function integration tests Add a locals blocks to the terraformlike tests, with normal and scoped function calls. --- integrationtest/terraformlike_test.go | 65 +++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/integrationtest/terraformlike_test.go b/integrationtest/terraformlike_test.go index 89783462..ae866269 100644 --- a/integrationtest/terraformlike_test.go +++ b/integrationtest/terraformlike_test.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/hcl/v2/hclsyntax" "github.com/hashicorp/hcl/v2/json" "github.com/zclconf/go-cty/cty" + "github.com/zclconf/go-cty/cty/function" ) // TestTerraformLike parses both a native syntax and a JSON representation @@ -53,10 +54,14 @@ func TestTerraformLike(t *testing.T) { Name string `hcl:"name,label"` Providers hcl.Expression `hcl:"providers"` } + type Locals struct { + Config hcl.Body `hcl:",remain"` + } type Root struct { Variables []*Variable `hcl:"variable,block"` Resources []*Resource `hcl:"resource,block"` Modules []*Module `hcl:"module,block"` + Locals []*Locals `hcl:"locals,block"` } instanceDecode := &hcldec.ObjectSpec{ "image_id": &hcldec.AttrSpec{ @@ -343,6 +348,57 @@ func TestTerraformLike(t *testing.T) { t.Errorf("wrong value traversal [1] type %T; want hcl.TraverseAttr", vt[1]) } }) + + t.Run("locals", func(t *testing.T) { + locals := root.Locals[0] + attrs, diags := locals.Config.JustAttributes() + if diags.HasErrors() { + t.Fatal(diags) + } + + ctx := &hcl.EvalContext{ + Functions: map[string]function.Function{ + "func": function.New(&function.Spec{ + Params: []function.Parameter{{Type: cty.String}}, + Type: function.StaticReturnType(cty.String), + Impl: func([]cty.Value, cty.Type) (cty.Value, error) { + return cty.StringVal("func_result"), nil + }, + }), + "scoped::func": function.New(&function.Spec{ + Params: []function.Parameter{{Type: cty.String}}, + Type: function.StaticReturnType(cty.String), + Impl: func([]cty.Value, cty.Type) (cty.Value, error) { + return cty.StringVal("scoped::func_result"), nil + }, + }), + }, + } + + res := attrs["func_result"] + funcVal, diags := res.Expr.Value(ctx) + if diags.HasErrors() { + t.Fatal(diags) + } + + wantVal := cty.StringVal("func_result") + + if !funcVal.RawEquals(wantVal) { + t.Errorf("expected %#v, got %#v", wantVal, funcVal) + } + + res = attrs["scoped_func_result"] + funcVal, diags = res.Expr.Value(ctx) + if diags.HasErrors() { + t.Fatal(diags) + } + + wantVal = cty.StringVal("scoped::func_result") + + if !funcVal.RawEquals(wantVal) { + t.Errorf("expected %#v, got %#v", wantVal, funcVal) + } + }) }) } } @@ -352,6 +408,11 @@ const terraformLikeNativeSyntax = ` variable "image_id" { } +locals { + func_result = func("arg") + scoped_func_result = scoped::func("arg") +} + resource "happycloud_instance" "test" { instance_type = "z3.weedy" image_id = var.image_id @@ -400,6 +461,10 @@ const terraformLikeJSON = ` "variable": { "image_id": {} }, + "locals": { + "func_result": "${func(\"arg\")}", + "scoped_func_result": "${scoped::func(\"arg\")}" + }, "resource": { "happycloud_instance": { "test": { From e1cf84daaa73a8c94605c595ad97dda832efa74f Mon Sep 17 00:00:00 2001 From: Ansgar Mertens Date: Fri, 9 Feb 2024 14:26:37 +0100 Subject: [PATCH 17/55] fix: fix NameRange for namespaced functions Resolves #650 --- hclsyntax/parser.go | 12 ++++- hclsyntax/parser_test.go | 97 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 2 deletions(-) diff --git a/hclsyntax/parser.go b/hclsyntax/parser.go index 20fa1481..cd9d63d2 100644 --- a/hclsyntax/parser.go +++ b/hclsyntax/parser.go @@ -1157,6 +1157,7 @@ func (p *parser) finishParsingFunctionCall(name Token) (Expression, hcl.Diagnost } nameStr := string(name.Bytes) + nameEndPos := name.Range.End for openTok.Type == TokenDoubleColon { nextName := p.Read() if nextName.Type != TokenIdent { @@ -1179,10 +1180,17 @@ func (p *parser) finishParsingFunctionCall(name Token) (Expression, hcl.Diagnost // doesn't exist in EvalContext, to return better error messages // when namespaces are used incorrectly. nameStr = nameStr + "::" + string(nextName.Bytes) + nameEndPos = nextName.Range.End openTok = p.Read() } + nameRange := hcl.Range{ + Filename: name.Range.Filename, + Start: name.Range.Start, + End: nameEndPos, + } + if openTok.Type != TokenOParen { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, @@ -1259,7 +1267,7 @@ Token: diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Unterminated function call", - Detail: "There is no closing parenthesis for this function call before the end of the file. This may be caused by incorrect parethesis nesting elsewhere in this file.", + Detail: "There is no closing parenthesis for this function call before the end of the file. This may be caused by incorrect parenthesis nesting elsewhere in this file.", Subject: hcl.RangeBetween(name.Range, openTok.Range).Ptr(), }) default: @@ -1291,7 +1299,7 @@ Token: ExpandFinal: expandFinal, - NameRange: name.Range, + NameRange: nameRange, OpenParenRange: openTok.Range, CloseParenRange: closeTok.Range, }, diags diff --git a/hclsyntax/parser_test.go b/hclsyntax/parser_test.go index 69636c9f..236daa0d 100644 --- a/hclsyntax/parser_test.go +++ b/hclsyntax/parser_test.go @@ -2462,6 +2462,103 @@ block "valid" {} }, }, }, + { + "a = a::namespaced::func(data.first.ref.attr)\n", + 0, + &Body{ + Attributes: Attributes{ + "a": { + Name: "a", + Expr: &FunctionCallExpr{ + Name: "a::namespaced::func", + Args: []Expression{ + &ScopeTraversalExpr{ + Traversal: hcl.Traversal{ + hcl.TraverseRoot{ + Name: "data", + SrcRange: hcl.Range{ + Filename: "", + Start: hcl.Pos{Line: 1, Column: 25, Byte: 24}, + End: hcl.Pos{Line: 1, Column: 29, Byte: 28}, + }, + }, + hcl.TraverseAttr{ + Name: "first", + SrcRange: hcl.Range{ + Filename: "", + Start: hcl.Pos{Line: 1, Column: 29, Byte: 28}, + End: hcl.Pos{Line: 1, Column: 35, Byte: 34}, + }, + }, + hcl.TraverseAttr{ + Name: "ref", + SrcRange: hcl.Range{ + Filename: "", + Start: hcl.Pos{Line: 1, Column: 35, Byte: 34}, + End: hcl.Pos{Line: 1, Column: 39, Byte: 38}, + }, + }, + hcl.TraverseAttr{ + Name: "attr", + SrcRange: hcl.Range{ + Filename: "", + Start: hcl.Pos{Line: 1, Column: 39, Byte: 38}, + End: hcl.Pos{Line: 1, Column: 44, Byte: 43}, + }, + }, + }, + SrcRange: hcl.Range{ + Filename: "", + Start: hcl.Pos{Line: 1, Column: 25, Byte: 24}, + End: hcl.Pos{Line: 1, Column: 44, Byte: 43}, + }, + }, + }, + ExpandFinal: false, + NameRange: hcl.Range{ + Filename: "", + Start: hcl.Pos{Line: 1, Column: 5, Byte: 4}, + End: hcl.Pos{Line: 1, Column: 24, Byte: 23}, + }, + OpenParenRange: hcl.Range{ + Filename: "", + Start: hcl.Pos{Line: 1, Column: 24, Byte: 23}, + End: hcl.Pos{Line: 1, Column: 25, Byte: 24}, + }, + CloseParenRange: hcl.Range{ + Filename: "", + Start: hcl.Pos{Line: 1, Column: 44, Byte: 43}, + End: hcl.Pos{Line: 1, Column: 45, Byte: 44}, + }, + }, + SrcRange: hcl.Range{ + Filename: "", + Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, + End: hcl.Pos{Line: 1, Column: 45, Byte: 44}, + }, + NameRange: hcl.Range{ + Filename: "", + Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, + End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, + }, + EqualsRange: hcl.Range{ + Filename: "", + Start: hcl.Pos{Line: 1, Column: 3, Byte: 2}, + End: hcl.Pos{Line: 1, Column: 4, Byte: 3}, + }, + }, + }, + Blocks: Blocks{}, + SrcRange: hcl.Range{ + Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, + End: hcl.Pos{Line: 2, Column: 1, Byte: 45}, + }, + EndRange: hcl.Range{ + Start: hcl.Pos{Line: 2, Column: 1, Byte: 45}, + End: hcl.Pos{Line: 2, Column: 1, Byte: 45}, + }, + }, + }, } for _, test := range tests { From 9b78cd3833206e14e76a575f5151ce8819428299 Mon Sep 17 00:00:00 2001 From: Ansgar Mertens Date: Fri, 9 Feb 2024 14:28:46 +0100 Subject: [PATCH 18/55] fix: update test that included typo --- hclsyntax/parser_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hclsyntax/parser_test.go b/hclsyntax/parser_test.go index 236daa0d..6e226a60 100644 --- a/hclsyntax/parser_test.go +++ b/hclsyntax/parser_test.go @@ -3757,7 +3757,7 @@ func TestParseConfigDiagnostics(t *testing.T) { { Severity: hcl.DiagError, Summary: "Unterminated function call", - Detail: "There is no closing parenthesis for this function call before the end of the file. This may be caused by incorrect parethesis nesting elsewhere in this file.", + Detail: "There is no closing parenthesis for this function call before the end of the file. This may be caused by incorrect parenthesis nesting elsewhere in this file.", Subject: &hcl.Range{ Filename: "test.hcl", Start: hcl.Pos{Line: 1, Column: 7, Byte: 6}, From 03587e0ffae6eaaf8e9f65766411e8b41262174f Mon Sep 17 00:00:00 2001 From: Elliot Bonneville Date: Fri, 9 Feb 2024 12:06:44 -0500 Subject: [PATCH 19/55] Typo fix in spec.md --- hclsyntax/spec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hclsyntax/spec.md b/hclsyntax/spec.md index 6d31e352..88925410 100644 --- a/hclsyntax/spec.md +++ b/hclsyntax/spec.md @@ -668,7 +668,7 @@ a == b equal a != b not equal ``` -Two values are equal if the are of identical types and their values are +Two values are equal if they are of identical types and their values are equal as defined in the HCL syntax-agnostic information model. The equality operators are commutative and opposite, such that `(a == b) == !(a != b)` and `(a == b) == (b == a)` for all values `a` and `b`. From d72c32fa2a09e2ba084bc61d028b84645357ac5e Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Wed, 14 Feb 2024 10:15:29 +0100 Subject: [PATCH 20/55] prefer iterator error over for_each If the iterator is misconfigured (e.g. a string instead of a reference) it leads to follow up issues in the validation of for_each which in turn leads to misleading error messages. See hashicorp/terraform#34132 for an example. --- ext/dynblock/expand_body_test.go | 78 ++++++++++++++++++++++++++++++++ ext/dynblock/expand_spec.go | 46 ++++++++++--------- 2 files changed, 102 insertions(+), 22 deletions(-) diff --git a/ext/dynblock/expand_body_test.go b/ext/dynblock/expand_body_test.go index fd440783..bec6c210 100644 --- a/ext/dynblock/expand_body_test.go +++ b/ext/dynblock/expand_body_test.go @@ -709,3 +709,81 @@ func TestExpandUnknownBodies(t *testing.T) { }) } + +func TestExpandInvalidIteratorError(t *testing.T) { + srcBody := hcltest.MockBody(&hcl.BodyContent{ + Blocks: hcl.Blocks{ + { + Type: "dynamic", + Labels: []string{"b"}, + LabelRanges: []hcl.Range{hcl.Range{}}, + Body: hcltest.MockBody(&hcl.BodyContent{ + Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ + "for_each": hcltest.MockExprLiteral(cty.ListVal([]cty.Value{ + cty.StringVal("dynamic b 0"), + cty.StringVal("dynamic b 1"), + })), + "iterator": hcltest.MockExprLiteral(cty.StringVal("dyn_b")), + }), + Blocks: hcl.Blocks{ + { + Type: "content", + Body: hcltest.MockBody(&hcl.BodyContent{ + Blocks: hcl.Blocks{ + { + Type: "c", + Body: hcltest.MockBody(&hcl.BodyContent{ + Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ + "val0": hcltest.MockExprLiteral(cty.StringVal("static c 1")), + "val1": hcltest.MockExprTraversalSrc("dyn_b.value"), + }), + }), + }, + }, + }), + }, + }, + }), + }, + }, + }) + + dynBody := Expand(srcBody, nil) + + t.Run("Decode", func(t *testing.T) { + decSpec := &hcldec.BlockListSpec{ + TypeName: "b", + Nested: &hcldec.BlockListSpec{ + TypeName: "c", + Nested: &hcldec.ObjectSpec{ + "val0": &hcldec.AttrSpec{ + Name: "val0", + Type: cty.String, + }, + "val1": &hcldec.AttrSpec{ + Name: "val1", + Type: cty.String, + }, + }, + }, + } + + var diags hcl.Diagnostics + _, diags = hcldec.Decode(dynBody, decSpec, nil) + + if len(diags) < 1 { + t.Errorf("Expected diagnostics, got none") + } + if len(diags) > 1 { + t.Errorf("Expected one diagnostic message, got %d", len(diags)) + for _, diag := range diags { + t.Logf("- %s", diag) + } + } + + if diags[0].Summary != "Invalid expression" { + t.Errorf("Expected error subject to be invalid expression, instead it was %q", diags[0].Summary) + } + }) + +} diff --git a/ext/dynblock/expand_spec.go b/ext/dynblock/expand_spec.go index 9585172e..0231c4aa 100644 --- a/ext/dynblock/expand_spec.go +++ b/ext/dynblock/expand_spec.go @@ -38,6 +38,29 @@ func (b *expandBody) decodeSpec(blockS *hcl.BlockHeaderSchema, rawSpec *hcl.Bloc return nil, diags } + //// iterator attribute + + iteratorName := blockS.Type + if iteratorAttr := specContent.Attributes["iterator"]; iteratorAttr != nil { + itTraversal, itDiags := hcl.AbsTraversalForExpr(iteratorAttr.Expr) + diags = append(diags, itDiags...) + if itDiags.HasErrors() { + return nil, diags + } + + if len(itTraversal) != 1 { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid dynamic iterator name", + Detail: "Dynamic iterator must be a single variable name.", + Subject: itTraversal.SourceRange().Ptr(), + }) + return nil, diags + } + + iteratorName = itTraversal.RootName() + } + //// for_each attribute eachAttr := specContent.Attributes["for_each"] @@ -80,28 +103,7 @@ func (b *expandBody) decodeSpec(blockS *hcl.BlockHeaderSchema, rawSpec *hcl.Bloc return nil, diags } - //// iterator attribute - - iteratorName := blockS.Type - if iteratorAttr := specContent.Attributes["iterator"]; iteratorAttr != nil { - itTraversal, itDiags := hcl.AbsTraversalForExpr(iteratorAttr.Expr) - diags = append(diags, itDiags...) - if itDiags.HasErrors() { - return nil, diags - } - - if len(itTraversal) != 1 { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid dynamic iterator name", - Detail: "Dynamic iterator must be a single variable name.", - Subject: itTraversal.SourceRange().Ptr(), - }) - return nil, diags - } - - iteratorName = itTraversal.RootName() - } + //// labels attribute var labelExprs []hcl.Expression if labelsAttr := specContent.Attributes["labels"]; labelsAttr != nil { From 159a39d78ed9e72f6608652522685458fe4ad25b Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 14 Feb 2024 14:33:45 -0500 Subject: [PATCH 21/55] Create an error type for unknown function diags Now that we have namespaced functions, and implementations like Terraform can add functions based on configuration, the reason for an unknown function call name becomes a little less clear. Because functions are populated outside of the hcl package scope, there isn't enough context to provide a useful diagnostic to the user. We can create a new Diagnostic.Extra value for FunctionCallUnknownDiagExtra to indicate specifically when a diagnostic is created due to an unknown function name. This will carry back the namespace and function name for the caller to inspect, which will allow refinement of the diagnostic based on information only known to the caller. --- hclsyntax/expression.go | 31 ++++++++++++ hclsyntax/expression_typeparams_test.go | 65 +++++++++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/hclsyntax/expression.go b/hclsyntax/expression.go index e81553b3..c4a353c4 100644 --- a/hclsyntax/expression.go +++ b/hclsyntax/expression.go @@ -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 @@ -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 @@ -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 { @@ -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, }, } } @@ -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, }, } } @@ -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 diff --git a/hclsyntax/expression_typeparams_test.go b/hclsyntax/expression_typeparams_test.go index 4ee9a77e..c7fdbd22 100644 --- a/hclsyntax/expression_typeparams_test.go +++ b/hclsyntax/expression_typeparams_test.go @@ -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()", From d703862140c8b1ea84a2533330978110a7ef988a Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Thu, 15 Feb 2024 15:24:33 +0000 Subject: [PATCH 22/55] hclwrite: Fix formatting of namespaced functions --- hclwrite/format.go | 5 +++++ hclwrite/format_test.go | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/hclwrite/format.go b/hclwrite/format.go index d5f974c3..01a6ba5c 100644 --- a/hclwrite/format.go +++ b/hclwrite/format.go @@ -234,6 +234,11 @@ func spaceAfterToken(subject, before, after *Token) bool { // Don't split a function name from open paren in a call return false + case (subject.Type == hclsyntax.TokenIdent && after.Type == hclsyntax.TokenDoubleColon) || + (subject.Type == hclsyntax.TokenDoubleColon && after.Type == hclsyntax.TokenIdent): + // Don't split namespace segments in a function call + return false + case subject.Type == hclsyntax.TokenDot || after.Type == hclsyntax.TokenDot: // Don't use spaces around attribute access dots return false diff --git a/hclwrite/format_test.go b/hclwrite/format_test.go index e64b551f..08d0ed05 100644 --- a/hclwrite/format_test.go +++ b/hclwrite/format_test.go @@ -615,6 +615,10 @@ module "x" { abcde = "456" }`, }, + { + `attr = provider::framework::example()`, + `attr = provider::framework::example()`, + }, } for i, test := range tests { From 583d01b289c989892dc6d68778999886c8f999aa Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Thu, 15 Feb 2024 15:24:40 +0000 Subject: [PATCH 23/55] gofmt --- hclwrite/format.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hclwrite/format.go b/hclwrite/format.go index 01a6ba5c..be3dce47 100644 --- a/hclwrite/format.go +++ b/hclwrite/format.go @@ -457,9 +457,12 @@ func tokenBracketChange(tok *Token) int { // // lead: always present, representing everything up to one of the others // assign: if line contains an attribute assignment, represents the tokens -// starting at (and including) the equals symbol +// +// starting at (and including) the equals symbol +// // comment: if line contains any non-comment tokens and ends with a -// single-line comment token, represents the comment. +// +// single-line comment token, represents the comment. // // When formatting, the leading spaces of the first tokens in each of these // cells is adjusted to align vertically their occurences on consecutive From f022482158e1110101d8fcedf1e3628548a140e9 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Thu, 15 Feb 2024 15:32:13 +0000 Subject: [PATCH 24/55] hclwrite: Add negative test --- hclwrite/format_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hclwrite/format_test.go b/hclwrite/format_test.go index 08d0ed05..be236b5d 100644 --- a/hclwrite/format_test.go +++ b/hclwrite/format_test.go @@ -619,6 +619,12 @@ module "x" { `attr = provider::framework::example()`, `attr = provider::framework::example()`, }, + { + // This is invalid syntax so formatting it with spaces + // does not have any meaning other than to make the fact more visible + `attr = provider::+example()`, + `attr = provider:: + example()`, + }, } for i, test := range tests { From 8dddc351e4d2782b3a62844f3c3f87d72f9f5e13 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Thu, 15 Feb 2024 16:02:44 +0000 Subject: [PATCH 25/55] hclwrite: add two more test cases --- hclwrite/format_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hclwrite/format_test.go b/hclwrite/format_test.go index be236b5d..5c4eb7a6 100644 --- a/hclwrite/format_test.go +++ b/hclwrite/format_test.go @@ -619,6 +619,14 @@ module "x" { `attr = provider::framework::example()`, `attr = provider::framework::example()`, }, + { + `attr = provider :: framework :: example()`, + `attr = provider::framework::example()`, + }, + { + `attr = provider ::framework:: example()`, + `attr = provider::framework::example()`, + }, { // This is invalid syntax so formatting it with spaces // does not have any meaning other than to make the fact more visible From 9926eaf00193efbfe340f3461f5241c92b6aac6a Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Fri, 16 Feb 2024 09:56:54 +0000 Subject: [PATCH 26/55] correct comment formatting --- hclwrite/format.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/hclwrite/format.go b/hclwrite/format.go index be3dce47..c9722dd2 100644 --- a/hclwrite/format.go +++ b/hclwrite/format.go @@ -455,14 +455,11 @@ func tokenBracketChange(tok *Token) int { // formatLine represents a single line of source code for formatting purposes, // splitting its tokens into up to three "cells": // -// lead: always present, representing everything up to one of the others -// assign: if line contains an attribute assignment, represents the tokens -// -// starting at (and including) the equals symbol -// -// comment: if line contains any non-comment tokens and ends with a -// -// single-line comment token, represents the comment. +// - lead: always present, representing everything up to one of the others +// - assign: if line contains an attribute assignment, represents the tokens +// starting at (and including) the equals symbol +// - comment: if line contains any non-comment tokens and ends with a +// single-line comment token, represents the comment. // // When formatting, the leading spaces of the first tokens in each of these // cells is adjusted to align vertically their occurences on consecutive From 48f2d7977b714f7bead73159b869f5f95df444cc Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Fri, 16 Feb 2024 12:13:58 +0000 Subject: [PATCH 27/55] deps: Track stringer@v0.6.0 as 'tools' dependency In theory we could use the latest (v0.18.0) version of the x/tools module but that would also bring upgrade of x/sys (transitive dependency of go-cty) from current v0.5.0 and full understanding of implications of that upgrade. --- go.mod | 2 ++ go.sum | 5 +++++ tools.go | 11 +++++++++++ 3 files changed, 18 insertions(+) create mode 100644 tools.go diff --git a/go.mod b/go.mod index 4c0c18c7..a8005025 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( github.com/zclconf/go-cty v1.13.0 github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167 + golang.org/x/tools v0.6.0 ) require ( @@ -24,6 +25,7 @@ require ( github.com/kr/text v0.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/testify v1.2.2 // indirect + golang.org/x/mod v0.8.0 // indirect golang.org/x/sys v0.5.0 // indirect golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect golang.org/x/text v0.11.0 // indirect diff --git a/go.sum b/go.sum index a7ce4583..62014eb6 100644 --- a/go.sum +++ b/go.sum @@ -39,8 +39,11 @@ github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b h1:FosyBZYxY3 github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167 h1:O8uGbHCqlTp2P6QJSLmCojM4mN6UemYv8K+dCnmHmu0= golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -49,5 +52,7 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/tools.go b/tools.go new file mode 100644 index 00000000..e8c42ad1 --- /dev/null +++ b/tools.go @@ -0,0 +1,11 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +//go:build tools +// +build tools + +package hcl + +import ( + _ "golang.org/x/tools/cmd/stringer" +) From 2f101e25ef30214dda9bf95fa84a3f68123cf717 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Fri, 16 Feb 2024 11:20:40 +0000 Subject: [PATCH 28/55] hclsyntax: Add license headers to generated code --- hclsyntax/expression_vars_gen.go | 5 ++++- hclsyntax/scan_string_lit.rl | 2 ++ hclsyntax/scan_tokens.rl | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/hclsyntax/expression_vars_gen.go b/hclsyntax/expression_vars_gen.go index 6ab79276..efbc2d82 100644 --- a/hclsyntax/expression_vars_gen.go +++ b/hclsyntax/expression_vars_gen.go @@ -87,7 +87,10 @@ func main() { } -const outputPreamble = `package hclsyntax +const outputPreamble = `// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package hclsyntax // Generated by expression_vars_get.go. DO NOT EDIT. // Run 'go generate' on this package to update the set of functions here. diff --git a/hclsyntax/scan_string_lit.rl b/hclsyntax/scan_string_lit.rl index f8ac1175..21d2c8bc 100644 --- a/hclsyntax/scan_string_lit.rl +++ b/hclsyntax/scan_string_lit.rl @@ -1,3 +1,5 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 package hclsyntax diff --git a/hclsyntax/scan_tokens.rl b/hclsyntax/scan_tokens.rl index 8d1e08af..66bb4714 100644 --- a/hclsyntax/scan_tokens.rl +++ b/hclsyntax/scan_tokens.rl @@ -1,3 +1,5 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 package hclsyntax From a5c83525bd12ef63dbcaed9fef7e77ab9cdecdf5 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Fri, 16 Feb 2024 12:04:58 +0000 Subject: [PATCH 29/55] go generate ./... --- hclsyntax/scan_string_lit.go | 25 +++-- hclsyntax/scan_tokens.go | 187 ++++++++++++++++----------------- hclsyntax/token_type_string.go | 63 ++++++++++- json/tokentype_string.go | 18 ++++ 4 files changed, 185 insertions(+), 108 deletions(-) diff --git a/hclsyntax/scan_string_lit.go b/hclsyntax/scan_string_lit.go index 5d60ff5a..6b44d992 100644 --- a/hclsyntax/scan_string_lit.go +++ b/hclsyntax/scan_string_lit.go @@ -1,13 +1,12 @@ +//line scan_string_lit.rl:1 // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -//line scan_string_lit.rl:1 - package hclsyntax // This file is generated from scan_string_lit.rl. DO NOT EDIT. -//line scan_string_lit.go:9 +//line scan_string_lit.go:11 var _hclstrtok_actions []byte = []byte{ 0, 1, 0, 1, 1, 2, 1, 0, } @@ -117,12 +116,12 @@ const hclstrtok_error int = 0 const hclstrtok_en_quoted int = 10 const hclstrtok_en_unquoted int = 4 -//line scan_string_lit.rl:10 +//line scan_string_lit.rl:12 func scanStringLit(data []byte, quoted bool) [][]byte { var ret [][]byte -//line scan_string_lit.rl:61 +//line scan_string_lit.rl:63 // Ragel state p := 0 // "Pointer" into data @@ -147,11 +146,11 @@ func scanStringLit(data []byte, quoted bool) [][]byte { ret = append(ret, data[ts:te]) }*/ -//line scan_string_lit.go:154 +//line scan_string_lit.go:156 { } -//line scan_string_lit.go:158 +//line scan_string_lit.go:160 { var _klen int var _trans int @@ -232,7 +231,7 @@ func scanStringLit(data []byte, quoted bool) [][]byte { _acts++ switch _hclstrtok_actions[_acts-1] { case 0: -//line scan_string_lit.rl:40 +//line scan_string_lit.rl:42 // If te is behind p then we've skipped over some literal // characters which we must now return. @@ -242,12 +241,12 @@ func scanStringLit(data []byte, quoted bool) [][]byte { ts = p case 1: -//line scan_string_lit.rl:48 +//line scan_string_lit.rl:50 te = p ret = append(ret, data[ts:te]) -//line scan_string_lit.go:253 +//line scan_string_lit.go:255 } } @@ -270,12 +269,12 @@ func scanStringLit(data []byte, quoted bool) [][]byte { __acts++ switch _hclstrtok_actions[__acts-1] { case 1: -//line scan_string_lit.rl:48 +//line scan_string_lit.rl:50 te = p ret = append(ret, data[ts:te]) -//line scan_string_lit.go:278 +//line scan_string_lit.go:280 } } } @@ -285,7 +284,7 @@ func scanStringLit(data []byte, quoted bool) [][]byte { } } -//line scan_string_lit.rl:89 +//line scan_string_lit.rl:91 if te < p { // Collect any leftover literal characters at the end of the input diff --git a/hclsyntax/scan_tokens.go b/hclsyntax/scan_tokens.go index 3691f115..3ed8455f 100644 --- a/hclsyntax/scan_tokens.go +++ b/hclsyntax/scan_tokens.go @@ -1,8 +1,7 @@ +//line scan_tokens.rl:1 // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -//line scan_tokens.rl:1 - package hclsyntax import ( @@ -13,7 +12,7 @@ import ( // This file is generated from scan_tokens.rl. DO NOT EDIT. -//line scan_tokens.go:15 +//line scan_tokens.go:17 var _hcltok_actions []byte = []byte{ 0, 1, 0, 1, 1, 1, 3, 1, 4, 1, 7, 1, 8, 1, 9, 1, 10, @@ -263,7 +262,7 @@ var _hcltok_trans_keys []byte = []byte{ 233, 234, 237, 239, 240, 243, 48, 57, 65, 90, 97, 122, 196, 218, 229, 236, 10, 170, 181, 183, 186, 128, 150, 152, - 182, 184, 255, 192, 255, 128, 255, 173, + 182, 184, 255, 192, 255, 0, 127, 173, 130, 133, 146, 159, 165, 171, 175, 255, 181, 190, 184, 185, 192, 255, 140, 134, 138, 142, 161, 163, 255, 182, 130, 136, @@ -572,7 +571,7 @@ var _hcltok_trans_keys []byte = []byte{ 150, 153, 131, 140, 255, 160, 163, 164, 165, 184, 185, 186, 161, 162, 133, 255, 170, 181, 183, 186, 128, 150, 152, 182, - 184, 255, 192, 255, 128, 255, 173, 130, + 184, 255, 192, 255, 0, 127, 173, 130, 133, 146, 159, 165, 171, 175, 255, 181, 190, 184, 185, 192, 255, 140, 134, 138, 142, 161, 163, 255, 182, 130, 136, 137, @@ -2165,7 +2164,7 @@ var _hcltok_indicies []int16 = []int16{ 61, 62, 37, 39, 63, 41, 64, 65, 66, 11, 11, 11, 14, 38, 0, 44, 0, 11, 11, 11, 11, 0, 11, 11, - 11, 0, 11, 0, 11, 11, 0, 0, + 11, 0, 11, 0, 11, 0, 11, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 11, 11, 11, 11, 11, 0, 0, 11, 0, 0, 11, 0, 11, 0, 0, @@ -2418,7 +2417,7 @@ var _hcltok_indicies []int16 = []int16{ 11, 16, 417, 16, 265, 300, 301, 302, 14, 0, 0, 11, 419, 419, 419, 419, 418, 419, 419, 419, 418, 419, 418, 419, - 419, 418, 418, 418, 418, 418, 418, 419, + 418, 419, 418, 418, 418, 418, 418, 419, 418, 418, 418, 418, 419, 419, 419, 419, 419, 418, 418, 419, 418, 418, 419, 418, 419, 418, 418, 419, 418, 418, 418, 419, @@ -4218,7 +4217,7 @@ const hcltok_en_bareTemplate int = 1535 const hcltok_en_identOnly int = 1546 const hcltok_en_main int = 1459 -//line scan_tokens.rl:16 +//line scan_tokens.rl:18 func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []Token { stripData := stripUTF8BOM(data) @@ -4232,7 +4231,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To StartByte: start.Byte, } -//line scan_tokens.rl:315 +//line scan_tokens.rl:317 // Ragel state p := 0 // "Pointer" into data @@ -4260,7 +4259,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To var retBraces []int // stack of brace levels that cause us to use fret var heredocs []heredocInProgress // stack of heredocs we're currently processing -//line scan_tokens.rl:350 +//line scan_tokens.rl:352 // Make Go compiler happy _ = ts @@ -4280,7 +4279,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To f.emitToken(TokenType(b[0]), ts, te) } -//line scan_tokens.go:4290 +//line scan_tokens.go:4292 { top = 0 ts = 0 @@ -4288,7 +4287,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To act = 0 } -//line scan_tokens.go:4298 +//line scan_tokens.go:4300 { var _klen int var _trans int @@ -4312,7 +4311,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To //line NONE:1 ts = p -//line scan_tokens.go:4321 +//line scan_tokens.go:4323 } } @@ -4384,7 +4383,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To _acts++ switch _hcltok_actions[_acts-1] { case 0: -//line scan_tokens.rl:233 +//line scan_tokens.rl:235 p-- case 4: @@ -4392,13 +4391,13 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To te = p + 1 case 5: -//line scan_tokens.rl:257 +//line scan_tokens.rl:259 act = 4 case 6: -//line scan_tokens.rl:259 +//line scan_tokens.rl:261 act = 6 case 7: -//line scan_tokens.rl:169 +//line scan_tokens.rl:171 te = p + 1 { token(TokenTemplateInterp) @@ -4416,7 +4415,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 8: -//line scan_tokens.rl:179 +//line scan_tokens.rl:181 te = p + 1 { token(TokenTemplateControl) @@ -4434,7 +4433,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 9: -//line scan_tokens.rl:93 +//line scan_tokens.rl:95 te = p + 1 { token(TokenCQuote) @@ -4447,19 +4446,19 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } case 10: -//line scan_tokens.rl:257 +//line scan_tokens.rl:259 te = p + 1 { token(TokenQuotedLit) } case 11: -//line scan_tokens.rl:260 +//line scan_tokens.rl:262 te = p + 1 { token(TokenBadUTF8) } case 12: -//line scan_tokens.rl:169 +//line scan_tokens.rl:171 te = p p-- { @@ -4478,7 +4477,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 13: -//line scan_tokens.rl:179 +//line scan_tokens.rl:181 te = p p-- { @@ -4497,41 +4496,41 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 14: -//line scan_tokens.rl:257 +//line scan_tokens.rl:259 te = p p-- { token(TokenQuotedLit) } case 15: -//line scan_tokens.rl:258 +//line scan_tokens.rl:260 te = p p-- { token(TokenQuotedNewline) } case 16: -//line scan_tokens.rl:259 +//line scan_tokens.rl:261 te = p p-- { token(TokenInvalid) } case 17: -//line scan_tokens.rl:260 +//line scan_tokens.rl:262 te = p p-- { token(TokenBadUTF8) } case 18: -//line scan_tokens.rl:257 +//line scan_tokens.rl:259 p = (te) - 1 { token(TokenQuotedLit) } case 19: -//line scan_tokens.rl:260 +//line scan_tokens.rl:262 p = (te) - 1 { token(TokenBadUTF8) @@ -4552,13 +4551,13 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } case 21: -//line scan_tokens.rl:157 +//line scan_tokens.rl:159 act = 11 case 22: -//line scan_tokens.rl:268 +//line scan_tokens.rl:270 act = 12 case 23: -//line scan_tokens.rl:169 +//line scan_tokens.rl:171 te = p + 1 { token(TokenTemplateInterp) @@ -4576,7 +4575,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 24: -//line scan_tokens.rl:179 +//line scan_tokens.rl:181 te = p + 1 { token(TokenTemplateControl) @@ -4594,7 +4593,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 25: -//line scan_tokens.rl:120 +//line scan_tokens.rl:122 te = p + 1 { // This action is called specificially when a heredoc literal @@ -4639,13 +4638,13 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To token(TokenStringLit) } case 26: -//line scan_tokens.rl:268 +//line scan_tokens.rl:270 te = p + 1 { token(TokenBadUTF8) } case 27: -//line scan_tokens.rl:169 +//line scan_tokens.rl:171 te = p p-- { @@ -4664,7 +4663,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 28: -//line scan_tokens.rl:179 +//line scan_tokens.rl:181 te = p p-- { @@ -4683,7 +4682,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 29: -//line scan_tokens.rl:157 +//line scan_tokens.rl:159 te = p p-- { @@ -4694,14 +4693,14 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To token(TokenStringLit) } case 30: -//line scan_tokens.rl:268 +//line scan_tokens.rl:270 te = p p-- { token(TokenBadUTF8) } case 31: -//line scan_tokens.rl:157 +//line scan_tokens.rl:159 p = (te) - 1 { // This action is called when a heredoc literal _doesn't_ end @@ -4736,13 +4735,13 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } case 33: -//line scan_tokens.rl:165 +//line scan_tokens.rl:167 act = 15 case 34: -//line scan_tokens.rl:275 +//line scan_tokens.rl:277 act = 16 case 35: -//line scan_tokens.rl:169 +//line scan_tokens.rl:171 te = p + 1 { token(TokenTemplateInterp) @@ -4760,7 +4759,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 36: -//line scan_tokens.rl:179 +//line scan_tokens.rl:181 te = p + 1 { token(TokenTemplateControl) @@ -4778,19 +4777,19 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 37: -//line scan_tokens.rl:165 +//line scan_tokens.rl:167 te = p + 1 { token(TokenStringLit) } case 38: -//line scan_tokens.rl:275 +//line scan_tokens.rl:277 te = p + 1 { token(TokenBadUTF8) } case 39: -//line scan_tokens.rl:169 +//line scan_tokens.rl:171 te = p p-- { @@ -4809,7 +4808,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 40: -//line scan_tokens.rl:179 +//line scan_tokens.rl:181 te = p p-- { @@ -4828,21 +4827,21 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 41: -//line scan_tokens.rl:165 +//line scan_tokens.rl:167 te = p p-- { token(TokenStringLit) } case 42: -//line scan_tokens.rl:275 +//line scan_tokens.rl:277 te = p p-- { token(TokenBadUTF8) } case 43: -//line scan_tokens.rl:165 +//line scan_tokens.rl:167 p = (te) - 1 { token(TokenStringLit) @@ -4869,45 +4868,45 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } case 45: -//line scan_tokens.rl:279 +//line scan_tokens.rl:281 act = 17 case 46: -//line scan_tokens.rl:280 +//line scan_tokens.rl:282 act = 18 case 47: -//line scan_tokens.rl:280 +//line scan_tokens.rl:282 te = p + 1 { token(TokenBadUTF8) } case 48: -//line scan_tokens.rl:281 +//line scan_tokens.rl:283 te = p + 1 { token(TokenInvalid) } case 49: -//line scan_tokens.rl:279 +//line scan_tokens.rl:281 te = p p-- { token(TokenIdent) } case 50: -//line scan_tokens.rl:280 +//line scan_tokens.rl:282 te = p p-- { token(TokenBadUTF8) } case 51: -//line scan_tokens.rl:279 +//line scan_tokens.rl:281 p = (te) - 1 { token(TokenIdent) } case 52: -//line scan_tokens.rl:280 +//line scan_tokens.rl:282 p = (te) - 1 { token(TokenBadUTF8) @@ -4928,92 +4927,92 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } case 54: -//line scan_tokens.rl:287 +//line scan_tokens.rl:289 act = 22 case 55: -//line scan_tokens.rl:311 +//line scan_tokens.rl:313 act = 40 case 56: -//line scan_tokens.rl:289 +//line scan_tokens.rl:291 te = p + 1 { token(TokenComment) } case 57: -//line scan_tokens.rl:290 +//line scan_tokens.rl:292 te = p + 1 { token(TokenNewline) } case 58: -//line scan_tokens.rl:292 +//line scan_tokens.rl:294 te = p + 1 { token(TokenEqualOp) } case 59: -//line scan_tokens.rl:293 +//line scan_tokens.rl:295 te = p + 1 { token(TokenNotEqual) } case 60: -//line scan_tokens.rl:294 +//line scan_tokens.rl:296 te = p + 1 { token(TokenGreaterThanEq) } case 61: -//line scan_tokens.rl:295 +//line scan_tokens.rl:297 te = p + 1 { token(TokenLessThanEq) } case 62: -//line scan_tokens.rl:296 +//line scan_tokens.rl:298 te = p + 1 { token(TokenAnd) } case 63: -//line scan_tokens.rl:297 +//line scan_tokens.rl:299 te = p + 1 { token(TokenOr) } case 64: -//line scan_tokens.rl:298 +//line scan_tokens.rl:300 te = p + 1 { token(TokenDoubleColon) } case 65: -//line scan_tokens.rl:299 +//line scan_tokens.rl:301 te = p + 1 { token(TokenEllipsis) } case 66: -//line scan_tokens.rl:300 +//line scan_tokens.rl:302 te = p + 1 { token(TokenFatArrow) } case 67: -//line scan_tokens.rl:301 +//line scan_tokens.rl:303 te = p + 1 { selfToken() } case 68: -//line scan_tokens.rl:189 +//line scan_tokens.rl:191 te = p + 1 { token(TokenOBrace) braces++ } case 69: -//line scan_tokens.rl:194 +//line scan_tokens.rl:196 te = p + 1 { if len(retBraces) > 0 && retBraces[len(retBraces)-1] == braces { @@ -5033,7 +5032,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 70: -//line scan_tokens.rl:206 +//line scan_tokens.rl:208 te = p + 1 { // Only consume from the retBraces stack and return if we are at @@ -5062,7 +5061,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 71: -//line scan_tokens.rl:88 +//line scan_tokens.rl:90 te = p + 1 { token(TokenOQuote) @@ -5075,7 +5074,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 72: -//line scan_tokens.rl:98 +//line scan_tokens.rl:100 te = p + 1 { token(TokenOHeredoc) @@ -5105,84 +5104,84 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } case 73: -//line scan_tokens.rl:311 +//line scan_tokens.rl:313 te = p + 1 { token(TokenBadUTF8) } case 74: -//line scan_tokens.rl:312 +//line scan_tokens.rl:314 te = p + 1 { token(TokenInvalid) } case 75: -//line scan_tokens.rl:285 +//line scan_tokens.rl:287 te = p p-- case 76: -//line scan_tokens.rl:286 +//line scan_tokens.rl:288 te = p p-- { token(TokenNumberLit) } case 77: -//line scan_tokens.rl:287 +//line scan_tokens.rl:289 te = p p-- { token(TokenIdent) } case 78: -//line scan_tokens.rl:289 +//line scan_tokens.rl:291 te = p p-- { token(TokenComment) } case 79: -//line scan_tokens.rl:301 +//line scan_tokens.rl:303 te = p p-- { selfToken() } case 80: -//line scan_tokens.rl:311 +//line scan_tokens.rl:313 te = p p-- { token(TokenBadUTF8) } case 81: -//line scan_tokens.rl:312 +//line scan_tokens.rl:314 te = p p-- { token(TokenInvalid) } case 82: -//line scan_tokens.rl:286 +//line scan_tokens.rl:288 p = (te) - 1 { token(TokenNumberLit) } case 83: -//line scan_tokens.rl:287 +//line scan_tokens.rl:289 p = (te) - 1 { token(TokenIdent) } case 84: -//line scan_tokens.rl:301 +//line scan_tokens.rl:303 p = (te) - 1 { selfToken() } case 85: -//line scan_tokens.rl:311 +//line scan_tokens.rl:313 p = (te) - 1 { token(TokenBadUTF8) @@ -5202,7 +5201,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } -//line scan_tokens.go:5060 +//line scan_tokens.go:5062 } } @@ -5221,7 +5220,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To //line NONE:1 act = 0 -//line scan_tokens.go:5078 +//line scan_tokens.go:5080 } } @@ -5247,7 +5246,7 @@ func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []To } } -//line scan_tokens.rl:373 +//line scan_tokens.rl:375 // If we fall out here without being in a final state then we've // encountered something that the scanner can't match, which we'll diff --git a/hclsyntax/token_type_string.go b/hclsyntax/token_type_string.go index 5a2cd4eb..1453389c 100644 --- a/hclsyntax/token_type_string.go +++ b/hclsyntax/token_type_string.go @@ -1,9 +1,70 @@ -// Code generated by "stringer -type TokenType -output token_type_string.go"; DO NOT EDIT. +// Code generated by "stringer -type TokenType -output token_type_string.go token_type.go"; DO NOT EDIT. package hclsyntax import "strconv" +func _() { + // An "invalid array index" compiler error signifies that the constant values (55) have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[TokenOBrace-123] + _ = x[TokenCBrace-125] + _ = x[TokenOBrack-91] + _ = x[TokenCBrack-93] + _ = x[TokenOParen-40] + _ = x[TokenCParen-41] + _ = x[TokenOQuote-171] + _ = x[TokenCQuote-187] + _ = x[TokenOHeredoc-72] + _ = x[TokenCHeredoc-104] + _ = x[TokenStar-42] + _ = x[TokenSlash-47] + _ = x[TokenPlus-43] + _ = x[TokenMinus-45] + _ = x[TokenPercent-37] + _ = x[TokenEqual-61] + _ = x[TokenEqualOp-8788] + _ = x[TokenNotEqual-8800] + _ = x[TokenLessThan-60] + _ = x[TokenLessThanEq-8804] + _ = x[TokenGreaterThan-62] + _ = x[TokenGreaterThanEq-8805] + _ = x[TokenAnd-8743] + _ = x[TokenOr-8744] + _ = x[TokenBang-33] + _ = x[TokenDot-46] + _ = x[TokenComma-44] + _ = x[TokenDoubleColon-11820] + _ = x[TokenEllipsis-8230] + _ = x[TokenFatArrow-8658] + _ = x[TokenQuestion-63] + _ = x[TokenColon-58] + _ = x[TokenTemplateInterp-8747] + _ = x[TokenTemplateControl-955] + _ = x[TokenTemplateSeqEnd-8718] + _ = x[TokenQuotedLit-81] + _ = x[TokenStringLit-83] + _ = x[TokenNumberLit-78] + _ = x[TokenIdent-73] + _ = x[TokenComment-67] + _ = x[TokenNewline-10] + _ = x[TokenEOF-9220] + _ = x[TokenBitwiseAnd-38] + _ = x[TokenBitwiseOr-124] + _ = x[TokenBitwiseNot-126] + _ = x[TokenBitwiseXor-94] + _ = x[TokenStarStar-10138] + _ = x[TokenApostrophe-39] + _ = x[TokenBacktick-96] + _ = x[TokenSemicolon-59] + _ = x[TokenTabs-9225] + _ = x[TokenInvalid-65533] + _ = x[TokenBadUTF8-128169] + _ = x[TokenQuotedNewline-9252] + _ = x[TokenNil-0] +} + const _TokenType_name = "TokenNilTokenNewlineTokenBangTokenPercentTokenBitwiseAndTokenApostropheTokenOParenTokenCParenTokenStarTokenPlusTokenCommaTokenMinusTokenDotTokenSlashTokenColonTokenSemicolonTokenLessThanTokenEqualTokenGreaterThanTokenQuestionTokenCommentTokenOHeredocTokenIdentTokenNumberLitTokenQuotedLitTokenStringLitTokenOBrackTokenCBrackTokenBitwiseXorTokenBacktickTokenCHeredocTokenOBraceTokenBitwiseOrTokenCBraceTokenBitwiseNotTokenOQuoteTokenCQuoteTokenTemplateControlTokenEllipsisTokenFatArrowTokenTemplateSeqEndTokenAndTokenOrTokenTemplateInterpTokenEqualOpTokenNotEqualTokenLessThanEqTokenGreaterThanEqTokenEOFTokenTabsTokenQuotedNewlineTokenStarStarTokenDoubleColonTokenInvalidTokenBadUTF8" var _TokenType_map = map[TokenType]string{ diff --git a/json/tokentype_string.go b/json/tokentype_string.go index bbcce5b3..82bd7407 100644 --- a/json/tokentype_string.go +++ b/json/tokentype_string.go @@ -4,6 +4,24 @@ package json import "strconv" +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[tokenBraceO-123] + _ = x[tokenBraceC-125] + _ = x[tokenBrackO-91] + _ = x[tokenBrackC-93] + _ = x[tokenComma-44] + _ = x[tokenColon-58] + _ = x[tokenKeyword-75] + _ = x[tokenString-83] + _ = x[tokenNumber-78] + _ = x[tokenEOF-9220] + _ = x[tokenInvalid-0] + _ = x[tokenEquals-61] +} + const _tokenType_name = "tokenInvalidtokenCommatokenColontokenEqualstokenKeywordtokenNumbertokenStringtokenBrackOtokenBrackCtokenBraceOtokenBraceCtokenEOF" var _tokenType_map = map[tokenType]string{ From 0f84b5260517f5ee4a2abdb0fb8da7f6d9c53662 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Fri, 16 Feb 2024 12:20:40 +0000 Subject: [PATCH 30/55] add missing copywrite headers --- ext/dynblock/options.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ext/dynblock/options.go b/ext/dynblock/options.go index fc7dc0bd..82d85865 100644 --- a/ext/dynblock/options.go +++ b/ext/dynblock/options.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package dynblock import ( From aa330fc7b9c6a7f43e3cf3bfb754f2e0e37875dd Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Fri, 16 Feb 2024 12:57:11 +0000 Subject: [PATCH 31/55] github: Rename workflow to reflect reality --- .github/workflows/{push.yml => checks.yml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{push.yml => checks.yml} (98%) diff --git a/.github/workflows/push.yml b/.github/workflows/checks.yml similarity index 98% rename from .github/workflows/push.yml rename to .github/workflows/checks.yml index a16f7b6b..ce17f940 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/checks.yml @@ -1,4 +1,4 @@ -name: Per-commit Checks +name: Checks on: push: From 82351cc777eb4118a8ad348b9a7da831a4a2fa1d Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Fri, 16 Feb 2024 12:57:36 +0000 Subject: [PATCH 32/55] github: Run unit tests on macos/amd64 as well --- .github/workflows/checks.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index ce17f940..4842b6e9 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -14,6 +14,8 @@ jobs: target: linux_amd64 - runs-on: windows-latest target: windows_amd64 + - runs-on: macos-latest + target: darwin_amd64 fail-fast: false name: "Unit Tests on ${{ matrix.target }}" From 97cf603f3cadfdcd5b472e55e2576ad22e971b17 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Fri, 16 Feb 2024 13:04:28 +0000 Subject: [PATCH 33/55] github: Read Go version from go.mod This reduces the number of places to update whenever we upgrade Go version. --- .github/workflows/checks.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 4842b6e9..6aaa038b 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -34,7 +34,7 @@ jobs: - name: Install Go uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: - go-version: 1.18 + go-version-file: go.mod - name: Go test run: | go test ./... -race @@ -49,7 +49,7 @@ jobs: - name: Install Go uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: - go-version: 1.18 + go-version-file: go.mod - name: "Check vet" run: | go vet ./... From b079b8b78743e7bca2ef20b876e16fb0af0e962a Mon Sep 17 00:00:00 2001 From: Daniel Schmidt Date: Fri, 16 Feb 2024 15:22:05 +0100 Subject: [PATCH 34/55] Update changelog --- CHANGELOG.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3fe93d4..6e018a65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # HCL Changelog +## v2.20.0 (unreleased) + +### Bugs Fixed + +* ext/dynblock: if `iterator` is invalid return this error instead of consequential errors. ([#656](https://github.com/hashicorp/hcl/pull/656)) + ## v2.19.0 (October 16, 2023) ### Enhancements @@ -43,7 +49,7 @@ * HCL now uses a newer version of the upstream `cty` library which has improved treatment of unknown values: it can now track additional optional information that reduces the range of an unknown value, which allows some operations against unknown values to return known or partially-known results. ([#590](https://github.com/hashicorp/hcl/pull/590)) **Note:** This change effectively passes on [`cty`'s notion of backward compatibility](https://github.com/zclconf/go-cty/blob/main/COMPATIBILITY.md) whereby unknown values can become "more known" in later releases. In particular, if your caller is using `cty.Value.RawEquals` in its tests against the results of operations with unknown values then you may see those tests begin failing after upgrading, due to the values now being more "refined". - + If so, you should review the refinements with consideration to [the `cty` refinements docs](https://github.com/zclconf/go-cty/blob/7dcbae46a6f247e983efb1fa774d2bb68781a333/docs/refinements.md) and update your expected results to match only if the reported refinements seem correct for the given situation. The `RawEquals` method is intended only for making exact value comparisons in test cases, so main application code should not use it; use `Equals` instead for real logic, which will take refinements into account automatically. ## v2.16.2 (March 9, 2023) @@ -173,7 +179,7 @@ * hclsyntax: Mark objects with keys that are sensitive. ([#440](https://github.com/hashicorp/hcl/pull/440)) ## v2.8.1 (December 17, 2020) - + ### Bugs Fixed * hclsyntax: Fix panic when expanding marked function arguments. ([#429](https://github.com/hashicorp/hcl/pull/429)) From 6055ebc56c8b5495ac32e707f48d0ea1fd7ef619 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Fri, 16 Feb 2024 14:45:50 +0000 Subject: [PATCH 35/55] Update CHANGELOG.md --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e018a65..96156d6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,14 @@ ## v2.20.0 (unreleased) +### Enhancements + +* Support for namespaced functions ([#639](https://github.com/hashicorp/hcl/pull/639)) + ### Bugs Fixed -* ext/dynblock: if `iterator` is invalid return this error instead of consequential errors. ([#656](https://github.com/hashicorp/hcl/pull/656)) +* ext/dynblock: if `iterator` is invalid return this error instead of consequential errors ([#656](https://github.com/hashicorp/hcl/pull/656)) +* hclwrite: Fix formatting of namespaced functions ([#658](https://github.com/hashicorp/hcl/pull/658)) ## v2.19.0 (October 16, 2023) From 63380ba29035340b26bf491429f87e340a3757d0 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Fri, 16 Feb 2024 14:46:06 +0000 Subject: [PATCH 36/55] Update CHANGELOG.md --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96156d6b..c5fe6761 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,6 @@ ### Bugs Fixed * ext/dynblock: if `iterator` is invalid return this error instead of consequential errors ([#656](https://github.com/hashicorp/hcl/pull/656)) -* hclwrite: Fix formatting of namespaced functions ([#658](https://github.com/hashicorp/hcl/pull/658)) ## v2.19.0 (October 16, 2023) From 3cf75729d009ca16bbefb909f66e0807c4d8d19a Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Fri, 16 Feb 2024 14:13:46 +0000 Subject: [PATCH 37/55] Add make targets for common checks + fixes This is to make it easier for existing and new maintainers and contributors to run checks on the codebase. --- Makefile | 18 ++++++++++++++++++ scripts/gofmtcheck.sh | 5 +++++ 2 files changed, 23 insertions(+) create mode 100644 Makefile create mode 100755 scripts/gofmtcheck.sh diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..675178e7 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +fmtcheck: + "$(CURDIR)/scripts/gofmtcheck.sh" + +fmtfix: + gofmt -w ./ + +vetcheck: + go vet ./... + +copyrightcheck: + go run github.com/hashicorp/copywrite@latest headers --plan + +copyrightfix: + go run github.com/hashicorp/copywrite@latest headers + +check: copyrightcheck vetcheck fmtcheck + +fix: copyrightfix fmtfix diff --git a/scripts/gofmtcheck.sh b/scripts/gofmtcheck.sh new file mode 100755 index 00000000..c09fa208 --- /dev/null +++ b/scripts/gofmtcheck.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +if [[ -n $(gofmt -l ./) ]]; then echo "Please run gofmt -w ./ to format code"; exit 1; fi; From 1f6a0f72612b374869933839d2ef0f78a88d99e8 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Fri, 16 Feb 2024 14:16:26 +0000 Subject: [PATCH 38/55] Use make targets in CI --- .github/workflows/checks.yml | 43 +++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 6aaa038b..51e279ae 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -39,10 +39,23 @@ jobs: run: | go test ./... -race - fmt_and_vet: - name: "fmt and lint" + copyright: + name: "copyright headers" runs-on: ubuntu-latest + steps: + - name: "Fetch source code" + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 + - name: Install Go + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 + with: + go-version-file: go.mod + - name: "copyright headers check" + run: | + make copyrightcheck + govet: + name: "go vet" + runs-on: ubuntu-latest steps: - name: "Fetch source code" uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 @@ -50,16 +63,20 @@ jobs: uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: go-version-file: go.mod - - name: "Check vet" + - name: "go vet" run: | - go vet ./... - - name: "Check fmt" + make vetcheck + + gofmt: + name: "gofmt" + runs-on: ubuntu-latest + steps: + - name: "Fetch source code" + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 + - name: Install Go + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 + with: + go-version-file: go.mod + - name: "gofmt" run: | - go fmt ./... - if [[ -z "$(git status --porcelain)" ]]; then - echo "Formatting is consistent with 'go fmt'." - else - echo "Run 'go fmt ./...' to automatically apply standard Go style to all packages." - git status --porcelain - exit 1 - fi + make fmtcheck From 57f8bbf184a628a6dedb36520db1c90bfab7fb06 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Thu, 29 Feb 2024 14:21:12 -0500 Subject: [PATCH 39/55] update CHANGELOG.md for v2.20 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5fe6761..1597a28b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # HCL Changelog -## v2.20.0 (unreleased) +## v2.20.0 (February 29, 2024) ### Enhancements From 1cbb0d41b1499559bd3dddc37d6873c28f179952 Mon Sep 17 00:00:00 2001 From: Ansgar Mertens Date: Tue, 12 Mar 2024 09:36:49 +0100 Subject: [PATCH 40/55] feat: return ExprSyntaxError instead of nil when expression parsing fails for namespaced functions --- hclsyntax/expression.go | 23 +++++++ hclsyntax/expression_test.go | 8 +-- hclsyntax/expression_vars.go | 6 +- hclsyntax/expression_vars_gen.go | 2 +- hclsyntax/parser.go | 21 ++++-- hclsyntax/parser_test.go | 106 +++++++++++++++++++++++++++++++ 6 files changed, 154 insertions(+), 12 deletions(-) diff --git a/hclsyntax/expression.go b/hclsyntax/expression.go index c4a353c4..63bd24c1 100644 --- a/hclsyntax/expression.go +++ b/hclsyntax/expression.go @@ -2013,3 +2013,26 @@ func (e *AnonSymbolExpr) Range() hcl.Range { func (e *AnonSymbolExpr) StartRange() hcl.Range { return e.SrcRange } + +// ExprSyntaxError is a placeholder for an invalid expression that could not +// be parsed due to syntax errors. +type ExprSyntaxError struct { + Placeholder cty.Value + ParseDiags hcl.Diagnostics +} + +func (e *ExprSyntaxError) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { + return e.Placeholder, e.ParseDiags +} + +func (e *ExprSyntaxError) walkChildNodes(w internalWalkFunc) { + // ExprSyntaxError is a leaf node in the tree +} + +func (e *ExprSyntaxError) Range() hcl.Range { + return hcl.Range{} +} + +func (e *ExprSyntaxError) StartRange() hcl.Range { + return hcl.Range{} +} diff --git a/hclsyntax/expression_test.go b/hclsyntax/expression_test.go index 4f3f75f7..b18214cd 100644 --- a/hclsyntax/expression_test.go +++ b/hclsyntax/expression_test.go @@ -379,8 +379,8 @@ upper( "double::::upper": stdlib.UpperFunc, }, }, - cty.NilVal, - 1, + cty.DynamicVal, + 2, }, { `missing::("foo")`, // missing name after :: @@ -389,8 +389,8 @@ upper( "missing::": stdlib.UpperFunc, }, }, - cty.NilVal, - 1, + cty.DynamicVal, + 2, }, { `misbehave()`, diff --git a/hclsyntax/expression_vars.go b/hclsyntax/expression_vars.go index ce5a5cb7..6c3e472c 100755 --- a/hclsyntax/expression_vars.go +++ b/hclsyntax/expression_vars.go @@ -3,7 +3,7 @@ package hclsyntax -// Generated by expression_vars_get.go. DO NOT EDIT. +// Generated by expression_vars_gen.go. DO NOT EDIT. // Run 'go generate' on this package to update the set of functions here. import ( @@ -22,6 +22,10 @@ func (e *ConditionalExpr) Variables() []hcl.Traversal { return Variables(e) } +func (e *ExprSyntaxError) Variables() []hcl.Traversal { + return Variables(e) +} + func (e *ForExpr) Variables() []hcl.Traversal { return Variables(e) } diff --git a/hclsyntax/expression_vars_gen.go b/hclsyntax/expression_vars_gen.go index efbc2d82..d0888025 100644 --- a/hclsyntax/expression_vars_gen.go +++ b/hclsyntax/expression_vars_gen.go @@ -92,7 +92,7 @@ const outputPreamble = `// Copyright (c) HashiCorp, Inc. package hclsyntax -// Generated by expression_vars_get.go. DO NOT EDIT. +// Generated by expression_vars_gen.go. DO NOT EDIT. // Run 'go generate' on this package to update the set of functions here. import ( diff --git a/hclsyntax/parser.go b/hclsyntax/parser.go index cd9d63d2..e9e84065 100644 --- a/hclsyntax/parser.go +++ b/hclsyntax/parser.go @@ -1161,15 +1161,19 @@ func (p *parser) finishParsingFunctionCall(name Token) (Expression, hcl.Diagnost for openTok.Type == TokenDoubleColon { nextName := p.Read() if nextName.Type != TokenIdent { - diags = append(diags, &hcl.Diagnostic{ + diag := hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Missing function name", Detail: "Function scope resolution symbol :: must be followed by a function name in this scope.", Subject: &nextName.Range, Context: hcl.RangeBetween(name.Range, nextName.Range).Ptr(), - }) + } + diags = append(diags, &diag) p.recoverOver(TokenOParen) - return nil, diags + return &ExprSyntaxError{ + ParseDiags: hcl.Diagnostics{&diag}, + Placeholder: cty.DynamicVal, + }, diags } // Initial versions of HCLv2 didn't support function namespaces, and @@ -1192,15 +1196,20 @@ func (p *parser) finishParsingFunctionCall(name Token) (Expression, hcl.Diagnost } if openTok.Type != TokenOParen { - diags = append(diags, &hcl.Diagnostic{ + diag := hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Missing open parenthesis", Detail: "Function selector must be followed by an open parenthesis to begin the function call.", Subject: &openTok.Range, Context: hcl.RangeBetween(name.Range, openTok.Range).Ptr(), - }) + } + + diags = append(diags, &diag) p.recoverOver(TokenOParen) - return nil, diags + return &ExprSyntaxError{ + ParseDiags: hcl.Diagnostics{&diag}, + Placeholder: cty.DynamicVal, + }, diags } var args []Expression diff --git a/hclsyntax/parser_test.go b/hclsyntax/parser_test.go index 6e226a60..2231fdba 100644 --- a/hclsyntax/parser_test.go +++ b/hclsyntax/parser_test.go @@ -2559,6 +2559,112 @@ block "valid" {} }, }, }, + { + "a = partial::namespaced\n", + 1, + &Body{ + Attributes: Attributes{ + "a": { + Name: "a", + Expr: &ExprSyntaxError{ + Placeholder: cty.DynamicVal, + ParseDiags: hcl.Diagnostics{ + { + Severity: hcl.DiagError, + Summary: "Missing open parenthesis", + Detail: "Function selector must be followed by an open parenthesis to begin the function call.", + Subject: &hcl.Range{ + Start: hcl.Pos{Line: 1, Column: 24, Byte: 23}, + End: hcl.Pos{Line: 2, Column: 1, Byte: 24}, + }, + Context: &hcl.Range{ + Start: hcl.Pos{Line: 1, Column: 5, Byte: 4}, + End: hcl.Pos{Line: 2, Column: 1, Byte: 24}, + }, + }, + }, + }, + SrcRange: hcl.Range{ + Filename: "", + Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, + End: hcl.Pos{Line: 2, Column: 1, Byte: 24}, + }, + NameRange: hcl.Range{ + Filename: "", + Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, + End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, + }, + EqualsRange: hcl.Range{ + Filename: "", + Start: hcl.Pos{Line: 1, Column: 3, Byte: 2}, + End: hcl.Pos{Line: 1, Column: 4, Byte: 3}, + }, + }, + }, + Blocks: Blocks{}, + SrcRange: hcl.Range{ + Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, + End: hcl.Pos{Line: 2, Column: 1, Byte: 24}, + }, + EndRange: hcl.Range{ + Start: hcl.Pos{Line: 2, Column: 1, Byte: 24}, + End: hcl.Pos{Line: 2, Column: 1, Byte: 24}, + }, + }, + }, + { + "a = partial::\n", + 1, + &Body{ + Attributes: Attributes{ + "a": { + Name: "a", + Expr: &ExprSyntaxError{ + Placeholder: cty.DynamicVal, + ParseDiags: hcl.Diagnostics{ + { + Severity: hcl.DiagError, + Summary: "Missing function name", + Detail: "Function scope resolution symbol :: must be followed by a function name in this scope.", + Subject: &hcl.Range{ + Start: hcl.Pos{Line: 1, Column: 14, Byte: 13}, + End: hcl.Pos{Line: 2, Column: 1, Byte: 14}, + }, + Context: &hcl.Range{ + Start: hcl.Pos{Line: 1, Column: 5, Byte: 4}, + End: hcl.Pos{Line: 2, Column: 1, Byte: 14}, + }, + }, + }, + }, + SrcRange: hcl.Range{ + Filename: "", + Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, + End: hcl.Pos{Line: 2, Column: 1, Byte: 14}, + }, + NameRange: hcl.Range{ + Filename: "", + Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, + End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, + }, + EqualsRange: hcl.Range{ + Filename: "", + Start: hcl.Pos{Line: 1, Column: 3, Byte: 2}, + End: hcl.Pos{Line: 1, Column: 4, Byte: 3}, + }, + }, + }, + Blocks: Blocks{}, + SrcRange: hcl.Range{ + Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, + End: hcl.Pos{Line: 2, Column: 1, Byte: 14}, + }, + EndRange: hcl.Range{ + Start: hcl.Pos{Line: 2, Column: 1, Byte: 14}, + End: hcl.Pos{Line: 2, Column: 1, Byte: 14}, + }, + }, + }, } for _, test := range tests { From 53ee54e0da34370e928b8cf6d72f8d25f24e6ee4 Mon Sep 17 00:00:00 2001 From: Ansgar Mertens Date: Tue, 12 Mar 2024 09:37:20 +0100 Subject: [PATCH 41/55] chore: add test from #665 --- hclsyntax/expression_template_test.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/hclsyntax/expression_template_test.go b/hclsyntax/expression_template_test.go index c78094f5..aa33c9d9 100644 --- a/hclsyntax/expression_template_test.go +++ b/hclsyntax/expression_template_test.go @@ -438,6 +438,28 @@ trim`, } +func TestTemplateExprGracefulValue(t *testing.T) { + // we don't care about diags since we know it's invalid config + expr, _ := ParseTemplate([]byte(`prefix${provider::}`), "", hcl.Pos{Line: 1, Column: 1, Byte: 0}) + + got, _ := expr.Value(nil) // this should not panic + + if !got.RawEquals(cty.UnknownVal(cty.String).RefineNotNull()) { + t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, cty.UnknownVal(cty.String).RefineNotNull()) + } +} + +func TestTemplateExprWrappedGracefulValue(t *testing.T) { + // we don't care about diags since we know it's invalid config + expr, _ := ParseTemplate([]byte(`${provider::}`), "", hcl.Pos{Line: 1, Column: 1, Byte: 0}) + + got, _ := expr.Value(nil) // this should not panic + + if !got.RawEquals(cty.DynamicVal) { + t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, cty.NilVal) + } +} + func TestTemplateExprIsStringLiteral(t *testing.T) { tests := map[string]bool{ // A simple string value is a string literal From 54e4175c12b085892d61aa506b9562a591fde6a7 Mon Sep 17 00:00:00 2001 From: Ansgar Mertens Date: Wed, 13 Mar 2024 09:17:03 +0100 Subject: [PATCH 42/55] add SrcRange to ExprSyntaxError --- hclsyntax/expression.go | 5 +++-- hclsyntax/parser.go | 2 ++ hclsyntax/parser_test.go | 8 ++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/hclsyntax/expression.go b/hclsyntax/expression.go index 63bd24c1..81597399 100644 --- a/hclsyntax/expression.go +++ b/hclsyntax/expression.go @@ -2019,6 +2019,7 @@ func (e *AnonSymbolExpr) StartRange() hcl.Range { type ExprSyntaxError struct { Placeholder cty.Value ParseDiags hcl.Diagnostics + SrcRange hcl.Range } func (e *ExprSyntaxError) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { @@ -2030,9 +2031,9 @@ func (e *ExprSyntaxError) walkChildNodes(w internalWalkFunc) { } func (e *ExprSyntaxError) Range() hcl.Range { - return hcl.Range{} + return e.SrcRange } func (e *ExprSyntaxError) StartRange() hcl.Range { - return hcl.Range{} + return e.SrcRange } diff --git a/hclsyntax/parser.go b/hclsyntax/parser.go index e9e84065..ce96ae35 100644 --- a/hclsyntax/parser.go +++ b/hclsyntax/parser.go @@ -1173,6 +1173,7 @@ func (p *parser) finishParsingFunctionCall(name Token) (Expression, hcl.Diagnost return &ExprSyntaxError{ ParseDiags: hcl.Diagnostics{&diag}, Placeholder: cty.DynamicVal, + SrcRange: hcl.RangeBetween(name.Range, nextName.Range), }, diags } @@ -1209,6 +1210,7 @@ func (p *parser) finishParsingFunctionCall(name Token) (Expression, hcl.Diagnost return &ExprSyntaxError{ ParseDiags: hcl.Diagnostics{&diag}, Placeholder: cty.DynamicVal, + SrcRange: hcl.RangeBetween(name.Range, openTok.Range), }, diags } diff --git a/hclsyntax/parser_test.go b/hclsyntax/parser_test.go index 2231fdba..90a75832 100644 --- a/hclsyntax/parser_test.go +++ b/hclsyntax/parser_test.go @@ -2583,6 +2583,10 @@ block "valid" {} }, }, }, + SrcRange: hcl.Range{ + Start: hcl.Pos{Line: 1, Column: 5, Byte: 4}, + End: hcl.Pos{Line: 2, Column: 1, Byte: 24}, + }, }, SrcRange: hcl.Range{ Filename: "", @@ -2636,6 +2640,10 @@ block "valid" {} }, }, }, + SrcRange: hcl.Range{ + Start: hcl.Pos{Line: 1, Column: 5, Byte: 4}, + End: hcl.Pos{Line: 2, Column: 1, Byte: 14}, + }, }, SrcRange: hcl.Range{ Filename: "", From cc3af98c59dce4f86374165653b0fb2b738a45d3 Mon Sep 17 00:00:00 2001 From: Ansgar Mertens Date: Wed, 13 Mar 2024 11:45:03 +0100 Subject: [PATCH 43/55] fix test error message if wrong type --- hclsyntax/expression_template_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hclsyntax/expression_template_test.go b/hclsyntax/expression_template_test.go index aa33c9d9..31198458 100644 --- a/hclsyntax/expression_template_test.go +++ b/hclsyntax/expression_template_test.go @@ -456,7 +456,7 @@ func TestTemplateExprWrappedGracefulValue(t *testing.T) { got, _ := expr.Value(nil) // this should not panic if !got.RawEquals(cty.DynamicVal) { - t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, cty.NilVal) + t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, cty.DynamicVal) } } From 2a0a3f049ccff74b1d45315d5ba3fa09713e6929 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Tue, 12 Mar 2024 15:24:34 -0700 Subject: [PATCH 44/55] Standardize on only two value dumping/diffing libraries Due to the quite messy heritage of this codebase -- including a large part of it being just a fork of my earlier personal project ZCL -- there were many different conventions for how to pretty-print and diff values in the tests in different parts of the codebase. To reduce the dependency sprawl, this commit now standardizes on: - github.com/davecgh/go-spew for pretty-printing - github.com/google/go-cmp for diffing These two dependencies were already present anyway, are the most general out of all of the candidates, and are also already in use by at least some of HCL's most significant callers, such as HashiCorp Terraform. The version of go-cmp we were previously using seems to have a bug that causes the tests to crash when run under the Go race detector, so I've also upgraded that dependency to latest here to clear that bug. --- go.mod | 9 +-------- go.sum | 14 ++------------ hcldec/spec_test.go | 12 ++++++++++-- hclsyntax/parser_test.go | 37 +++++++++++++++++++++++-------------- hclsyntax/structure_test.go | 30 ++++++++++++------------------ hclsyntax/variables_test.go | 7 +++++-- hclwrite/parser_test.go | 33 ++++++--------------------------- hclwrite/round_trip_test.go | 8 ++------ 8 files changed, 61 insertions(+), 89 deletions(-) diff --git a/go.mod b/go.mod index a8005025..d9d4da45 100644 --- a/go.mod +++ b/go.mod @@ -4,15 +4,11 @@ go 1.18 require ( github.com/agext/levenshtein v1.2.1 - github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3 github.com/apparentlymart/go-textseg/v15 v15.0.0 github.com/davecgh/go-spew v1.1.1 github.com/go-test/deep v1.0.3 - github.com/google/go-cmp v0.3.1 - github.com/kr/pretty v0.1.0 - github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 + github.com/google/go-cmp v0.6.0 github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 - github.com/sergi/go-diff v1.0.0 github.com/spf13/pflag v1.0.2 github.com/zclconf/go-cty v1.13.0 github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b @@ -22,9 +18,6 @@ require ( require ( github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect - github.com/kr/text v0.1.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/testify v1.2.2 // indirect golang.org/x/mod v0.8.0 // indirect golang.org/x/sys v0.5.0 // indirect golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect diff --git a/go.sum b/go.sum index 62014eb6..08ed23ed 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8= github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= -github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3 h1:ZSTrOEhiM5J5RFxEaFvMZVEAM1KvT1YzbEOwB2EAGjA= -github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= @@ -12,25 +10,17 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4= github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM= github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/spf13/pflag v1.0.2 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc= github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= github.com/zclconf/go-cty v1.13.0 h1:It5dfKTTZHe9aeppbNOda3mN7Ag7sg6QkBNm6TkyFa0= diff --git a/hcldec/spec_test.go b/hcldec/spec_test.go index 1b0594d1..a88066b5 100644 --- a/hcldec/spec_test.go +++ b/hcldec/spec_test.go @@ -8,7 +8,6 @@ import ( "reflect" "testing" - "github.com/apparentlymart/go-dump/dump" "github.com/google/go-cmp/cmp" "github.com/zclconf/go-cty-debug/ctydebug" "github.com/zclconf/go-cty/cty" @@ -98,7 +97,16 @@ bar = barval }, } if !reflect.DeepEqual(gotVars, wantVars) { - t.Errorf("wrong Variables result\ngot: %s\nwant: %s", dump.Value(gotVars), dump.Value(wantVars)) + t.Errorf( + "wrong Variables result\n%s", + cmp.Diff( + wantVars, gotVars, + cmp.AllowUnexported( + hcl.TraverseRoot{}, + ), + ctydebug.CmpOptions, + ), + ) } ctx := &hcl.EvalContext{ diff --git a/hclsyntax/parser_test.go b/hclsyntax/parser_test.go index 90a75832..10825f15 100644 --- a/hclsyntax/parser_test.go +++ b/hclsyntax/parser_test.go @@ -5,19 +5,16 @@ package hclsyntax import ( "fmt" + "sync" "testing" - "github.com/go-test/deep" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/hashicorp/hcl/v2" + "github.com/zclconf/go-cty-debug/ctydebug" "github.com/zclconf/go-cty/cty" ) -func init() { - deep.MaxDepth = 999 -} - func TestParseConfig(t *testing.T) { tests := []struct { input string @@ -2197,6 +2194,7 @@ block "valid" {} "a": { Name: "a", Expr: &LiteralValueExpr{ + Val: cty.DynamicVal, SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 5, Byte: 4}, End: hcl.Pos{Line: 1, Column: 6, Byte: 5}, @@ -2235,6 +2233,7 @@ block "valid" {} "a": { Name: "a", Expr: &LiteralValueExpr{ + Val: cty.DynamicVal, SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 5, Byte: 4}, End: hcl.Pos{Line: 1, Column: 6, Byte: 5}, @@ -2687,11 +2686,23 @@ block "valid" {} } got := file.Body - - if diff := deep.Equal(got, test.want); diff != nil { - for _, problem := range diff { - t.Errorf(problem) - } + diff := cmp.Diff( + test.want, got, + cmp.AllowUnexported( + Body{}, + AnonSymbolExpr{}, + hcl.TraverseRoot{}, + hcl.TraverseAttr{}, + hcl.TraverseIndex{}, + ), + cmpopts.IgnoreUnexported( + sync.Mutex{}, + sync.RWMutex{}, + ), + ctydebug.CmpOptions, + ) + if diff != "" { + t.Errorf("wrong result\n%s", diff) } }) } @@ -4006,10 +4017,8 @@ func TestParseConfigDiagnostics(t *testing.T) { t.Logf("\n%s", test.input) _, diags := ParseConfig([]byte(test.input), "test.hcl", hcl.InitialPos) - if diff := deep.Equal(diags, test.want); diff != nil { - for _, problem := range diff { - t.Errorf(problem) - } + if diff := cmp.Diff(test.want, diags); diff != "" { + t.Errorf("wrong diagnostics\n%s", diff) } }) } diff --git a/hclsyntax/structure_test.go b/hclsyntax/structure_test.go index c86dc328..180425bf 100644 --- a/hclsyntax/structure_test.go +++ b/hclsyntax/structure_test.go @@ -8,8 +8,10 @@ import ( "reflect" "testing" + "github.com/davecgh/go-spew/spew" + "github.com/google/go-cmp/cmp" "github.com/hashicorp/hcl/v2" - "github.com/kylelemons/godebug/pretty" + "github.com/zclconf/go-cty-debug/ctydebug" "github.com/zclconf/go-cty/cty" ) @@ -390,12 +392,6 @@ func TestBodyContent(t *testing.T) { }, } - prettyConfig := &pretty.Config{ - Diffable: true, - IncludeUnexported: true, - PrintStringers: true, - } - for i, test := range tests { t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) { var got *hcl.BodyContent @@ -416,7 +412,7 @@ func TestBodyContent(t *testing.T) { if !reflect.DeepEqual(got, test.want) { t.Errorf( "wrong result\ndiff: %s", - prettyConfig.Compare(test.want, got), + cmp.Diff(test.want, got), ) } }) @@ -499,7 +495,7 @@ func TestBodyJustAttributes(t *testing.T) { }, }, hiddenAttrs: map[string]struct{}{ - "foo": struct{}{}, + "foo": {}, }, }, hcl.Attributes{}, @@ -507,12 +503,6 @@ func TestBodyJustAttributes(t *testing.T) { }, } - prettyConfig := &pretty.Config{ - Diffable: true, - IncludeUnexported: true, - PrintStringers: true, - } - for i, test := range tests { t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) { got, diags := test.body.JustAttributes() @@ -524,11 +514,15 @@ func TestBodyJustAttributes(t *testing.T) { } } - if !reflect.DeepEqual(got, test.want) { + diff := cmp.Diff( + test.want, got, + ctydebug.CmpOptions, + ) + if diff != "" { t.Errorf( "wrong result\nbody: %s\ndiff: %s", - prettyConfig.Sprint(test.body), - prettyConfig.Compare(test.want, got), + spew.Sdump(test.body), + diff, ) } }) diff --git a/hclsyntax/variables_test.go b/hclsyntax/variables_test.go index c841d94a..9eb3d411 100644 --- a/hclsyntax/variables_test.go +++ b/hclsyntax/variables_test.go @@ -8,8 +8,8 @@ import ( "reflect" "testing" + "github.com/davecgh/go-spew/spew" "github.com/hashicorp/hcl/v2" - "github.com/kr/pretty" "github.com/zclconf/go-cty/cty" ) @@ -300,7 +300,10 @@ func TestVariables(t *testing.T) { got := Variables(test.Expr) if !reflect.DeepEqual(got, test.Want) { - t.Errorf("wrong result\ngot: %s\nwant: %s", pretty.Sprint(got), pretty.Sprint(test.Want)) + t.Errorf( + "wrong result\ngot: %s\nwant: %s", + spew.Sdump(got), spew.Sdump(test.Want), + ) } }) } diff --git a/hclwrite/parser_test.go b/hclwrite/parser_test.go index e557940e..8ccae993 100644 --- a/hclwrite/parser_test.go +++ b/hclwrite/parser_test.go @@ -5,15 +5,11 @@ package hclwrite import ( "fmt" - "reflect" "testing" "github.com/davecgh/go-spew/spew" - "github.com/google/go-cmp/cmp" - "github.com/kylelemons/godebug/pretty" - "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" ) @@ -1360,12 +1356,6 @@ func TestPartitionTokens(t *testing.T) { }, } - prettyConfig := &pretty.Config{ - Diffable: true, - IncludeUnexported: true, - PrintStringers: true, - } - for i, test := range tests { t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) { gotStart, gotEnd := partitionTokens(test.tokens, test.rng) @@ -1373,7 +1363,7 @@ func TestPartitionTokens(t *testing.T) { if gotStart != test.wantStart || gotEnd != test.wantEnd { t.Errorf( "wrong result\ntokens: %s\nrange: %#v\ngot: %d, %d\nwant: %d, %d", - prettyConfig.Sprint(test.tokens), test.rng, + spew.Sdump(test.tokens), test.rng, gotStart, test.wantStart, gotEnd, test.wantEnd, ) @@ -1437,12 +1427,6 @@ func TestPartitionLeadCommentTokens(t *testing.T) { }, } - prettyConfig := &pretty.Config{ - Diffable: true, - IncludeUnexported: true, - PrintStringers: true, - } - for i, test := range tests { t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) { gotStart := partitionLeadCommentTokens(test.tokens) @@ -1450,7 +1434,7 @@ func TestPartitionLeadCommentTokens(t *testing.T) { if gotStart != test.wantStart { t.Errorf( "wrong result\ntokens: %s\ngot: %d\nwant: %d", - prettyConfig.Sprint(test.tokens), + spew.Sdump(test.tokens), gotStart, test.wantStart, ) } @@ -1589,20 +1573,15 @@ foo "bar" "baz" { }, } - prettyConfig := &pretty.Config{ - Diffable: true, - IncludeUnexported: true, - PrintStringers: true, - } - for _, test := range tests { t.Run(test.input, func(t *testing.T) { got := lexConfig([]byte(test.input)) - if !reflect.DeepEqual(got, test.want) { - diff := prettyConfig.Compare(test.want, got) + diff := cmp.Diff(test.want, got) + if diff != "" { t.Errorf( - "wrong result\ninput: %s\ndiff: %s", test.input, diff, + "wrong result\ninput: %s\ndiff:\n%s", + test.input, diff, ) } }) diff --git a/hclwrite/round_trip_test.go b/hclwrite/round_trip_test.go index bf306de5..9dbb1526 100644 --- a/hclwrite/round_trip_test.go +++ b/hclwrite/round_trip_test.go @@ -7,7 +7,7 @@ import ( "bytes" "testing" - "github.com/sergi/go-diff/diffmatchpatch" + "github.com/google/go-cmp/cmp" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/function" "github.com/zclconf/go-cty/cty/function/stdlib" @@ -71,12 +71,8 @@ block { } result := wr.Bytes() - if !bytes.Equal(result, src) { - dmp := diffmatchpatch.New() - diffs := dmp.DiffMain(string(src), string(result), false) - // t.Errorf("wrong result\nresult:\n%s\ninput:\n%s", result, src) - t.Errorf("wrong result\ndiff: (red indicates missing lines, and green indicates unexpected lines)\n%s", dmp.DiffPrettyText(diffs)) + t.Errorf("wrong result\ndiff:\n%s", cmp.Diff(string(src), string(result))) } }) } From 303be6113391cd39e05f26423d724e9bc4fb07f5 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Tue, 26 Mar 2024 14:54:38 +0000 Subject: [PATCH 45/55] Update CHANGELOG for 2.20.1 --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1597a28b..2eebedbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # HCL Changelog +## v2.20.1 (March 26, 2024) + +### Bugs Fixed + +* Return `ExprSyntaxError` when an invalid namespaced function is encountered during parsing ([#668](https://github.com/hashicorp/hcl/pull/668)) + +### Internal + +* Standardize on only two value dumping/diffing libraries ([#669](https://github.com/hashicorp/hcl/pull/669)) + ## v2.20.0 (February 29, 2024) ### Enhancements From f7cd61ac04cc66dcbb42ba84dfe640c976762021 Mon Sep 17 00:00:00 2001 From: Liam Cervante Date: Mon, 22 Apr 2024 14:20:01 +0200 Subject: [PATCH 46/55] Add additional function for parsing traversals with [*] keys (#673) * Add additional function for parsing traversals with [*] keys * add more context around skipped test cases --- hclsyntax/parse_traversal_test.go | 81 ++++++++++++++++++++++++++++++- hclsyntax/parser_traversal.go | 51 ++++++++++++++++++- hclsyntax/public.go | 31 ++++++++++++ 3 files changed, 161 insertions(+), 2 deletions(-) diff --git a/hclsyntax/parse_traversal_test.go b/hclsyntax/parse_traversal_test.go index 3ca5fc2b..5d8d24ea 100644 --- a/hclsyntax/parse_traversal_test.go +++ b/hclsyntax/parse_traversal_test.go @@ -4,11 +4,13 @@ package hclsyntax import ( + "fmt" "testing" "github.com/go-test/deep" - "github.com/hashicorp/hcl/v2" "github.com/zclconf/go-cty/cty" + + "github.com/hashicorp/hcl/v2" ) func TestParseTraversalAbs(t *testing.T) { @@ -208,10 +210,63 @@ func TestParseTraversalAbs(t *testing.T) { }, 1, // extra junk after traversal }, + + { + "foo[*]", + hcl.Traversal{ + hcl.TraverseRoot{ + Name: "foo", + SrcRange: hcl.Range{ + Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, + End: hcl.Pos{Line: 1, Column: 4, Byte: 3}, + }, + }, + hcl.TraverseSplat{ + SrcRange: hcl.Range{ + Start: hcl.Pos{Line: 1, Column: 4, Byte: 3}, + End: hcl.Pos{Line: 1, Column: 7, Byte: 6}, + }, + }, + }, + 0, + }, + { + "foo.*", // Still not supporting this. + hcl.Traversal{ + hcl.TraverseRoot{ + Name: "foo", + SrcRange: hcl.Range{ + Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, + End: hcl.Pos{Line: 1, Column: 4, Byte: 3}, + }, + }, + }, + 1, + }, + { + "foo[*].bar", // Run this through the unsupported function. + hcl.Traversal{ + hcl.TraverseRoot{ + Name: "foo", + SrcRange: hcl.Range{ + Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, + End: hcl.Pos{Line: 1, Column: 4, Byte: 3}, + }, + }, + }, + 1, + }, } for _, test := range tests { t.Run(test.src, func(t *testing.T) { + if test.src == "foo[*]" { + // The foo[*] test will fail because the function we test in + // this branch does not support the splat syntax. So we will + // skip this test case here. + t.Skip("skipping test for unsupported splat syntax") + } + got, diags := ParseTraversalAbs([]byte(test.src), "", hcl.Pos{Line: 1, Column: 1}) if len(diags) != test.diagCount { for _, diag := range diags { @@ -226,5 +281,29 @@ func TestParseTraversalAbs(t *testing.T) { } } }) + + t.Run(fmt.Sprintf("partial_%s", test.src), func(t *testing.T) { + if test.src == "foo[*].bar" { + // The foo[*].bar test will fail because the function we test in + // this branch does support the splat syntax and this test is + // designed to make sure that the other branch still fails with + // the splat syntax. So we will skip this test case here. + t.Skip("skipping test that fails for splat syntax") + } + + got, diags := ParseTraversalPartial([]byte(test.src), "", hcl.Pos{Line: 1, Column: 1}) + if len(diags) != test.diagCount { + for _, diag := range diags { + t.Logf(" - %s", diag.Error()) + } + t.Errorf("wrong number of diagnostics %d; want %d", len(diags), test.diagCount) + } + + if diff := deep.Equal(got, test.want); diff != nil { + for _, problem := range diff { + t.Error(problem) + } + } + }) } } diff --git a/hclsyntax/parser_traversal.go b/hclsyntax/parser_traversal.go index 3afa6ab0..f7d4062f 100644 --- a/hclsyntax/parser_traversal.go +++ b/hclsyntax/parser_traversal.go @@ -4,8 +4,9 @@ package hclsyntax import ( - "github.com/hashicorp/hcl/v2" "github.com/zclconf/go-cty/cty" + + "github.com/hashicorp/hcl/v2" ) // ParseTraversalAbs parses an absolute traversal that is assumed to consume @@ -13,6 +14,26 @@ import ( // behavior is not supported here because traversals are not expected to // be parsed as part of a larger program. func (p *parser) ParseTraversalAbs() (hcl.Traversal, hcl.Diagnostics) { + return p.parseTraversal(false) +} + +// ParseTraversalPartial parses an absolute traversal that is permitted +// to contain splat ([*]) expressions. Only splat expressions within square +// brackets are permitted ([*]); splat expressions within attribute names are +// not permitted (.*). +// +// The meaning of partial here is that the traversal may be incomplete, in that +// any splat expression indicates reference to a potentially unknown number of +// elements. +// +// Traversals that include splats cannot be automatically traversed by HCL using +// the TraversalAbs or TraversalRel methods. Instead, the caller must handle +// the traversals manually. +func (p *parser) ParseTraversalPartial() (hcl.Traversal, hcl.Diagnostics) { + return p.parseTraversal(true) +} + +func (p *parser) parseTraversal(allowSplats bool) (hcl.Traversal, hcl.Diagnostics) { var ret hcl.Traversal var diags hcl.Diagnostics @@ -127,6 +148,34 @@ func (p *parser) ParseTraversalAbs() (hcl.Traversal, hcl.Diagnostics) { return ret, diags } + case TokenStar: + if allowSplats { + + p.Read() // Eat the star. + close := p.Read() + if close.Type != TokenCBrack { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Unclosed index brackets", + Detail: "Index key must be followed by a closing bracket.", + Subject: &close.Range, + Context: hcl.RangeBetween(open.Range, close.Range).Ptr(), + }) + } + + ret = append(ret, hcl.TraverseSplat{ + SrcRange: hcl.RangeBetween(open.Range, close.Range), + }) + + if diags.HasErrors() { + return ret, diags + } + + continue + } + + // Otherwise, return the error below for the star. + fallthrough default: if next.Type == TokenStar { diags = append(diags, &hcl.Diagnostic{ diff --git a/hclsyntax/public.go b/hclsyntax/public.go index d56f8e50..17dc1ed4 100644 --- a/hclsyntax/public.go +++ b/hclsyntax/public.go @@ -118,6 +118,37 @@ func ParseTraversalAbs(src []byte, filename string, start hcl.Pos) (hcl.Traversa return expr, diags } +// ParseTraversalPartial matches the behavior of ParseTraversalAbs except +// that it allows splat expressions ([*]) to appear in the traversal. +// +// The returned traversals are "partial" in that the splat expression indicates +// an unknown value for the index. +// +// Traversals that include splats cannot be automatically traversed by HCL using +// the TraversalAbs or TraversalRel methods. Instead, the caller must handle +// the traversals manually. +func ParseTraversalPartial(src []byte, filename string, start hcl.Pos) (hcl.Traversal, hcl.Diagnostics) { + tokens, diags := LexExpression(src, filename, start) + peeker := newPeeker(tokens, false) + parser := &parser{peeker: peeker} + + // Bare traverals are always parsed in "ignore newlines" mode, as if + // they were wrapped in parentheses. + parser.PushIncludeNewlines(false) + + expr, parseDiags := parser.ParseTraversalPartial() + diags = append(diags, parseDiags...) + + parser.PopIncludeNewlines() + + // Panic if the parser uses incorrect stack discipline with the peeker's + // newlines stack, since otherwise it will produce confusing downstream + // errors. + peeker.AssertEmptyIncludeNewlinesStack() + + return expr, diags +} + // LexConfig performs lexical analysis on the given buffer, treating it as a // whole HCL config file, and returns the resulting tokens. // From 1c5ae8fc88a656ab7bd46da4ff27a20c5a97497b Mon Sep 17 00:00:00 2001 From: Liam Cervante Date: Mon, 22 Apr 2024 14:24:19 +0200 Subject: [PATCH 47/55] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2eebedbc..a33c5a68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # HCL Changelog +## v2.21.0 (Unreleased) + +### Enhancements + +* Introduce `ParseTraversalPartial`, which allows traversals that include the splat (`[*]`) index operator. ([#673](https://github.com/hashicorp/hcl/pull/673)) + ## v2.20.1 (March 26, 2024) ### Bugs Fixed From 4521ae92f155259e24118bddbe5fc4ef23d2a7ff Mon Sep 17 00:00:00 2001 From: "hashicorp-tsccr[bot]" <129506189+hashicorp-tsccr[bot]@users.noreply.github.com> Date: Wed, 8 May 2024 12:56:20 +0200 Subject: [PATCH 48/55] github: Pin action refs to latest trusted by TSCCR (#677) Co-authored-by: hashicorp-tsccr[bot] --- .github/workflows/checks.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 51e279ae..5102728d 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -30,9 +30,9 @@ jobs: run: | git config --global core.autocrlf false - name: "Fetch source code" - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Install Go - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 + uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: go-version-file: go.mod - name: Go test @@ -44,9 +44,9 @@ jobs: runs-on: ubuntu-latest steps: - name: "Fetch source code" - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Install Go - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 + uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: go-version-file: go.mod - name: "copyright headers check" @@ -58,9 +58,9 @@ jobs: runs-on: ubuntu-latest steps: - name: "Fetch source code" - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Install Go - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 + uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: go-version-file: go.mod - name: "go vet" @@ -72,9 +72,9 @@ jobs: runs-on: ubuntu-latest steps: - name: "Fetch source code" - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Install Go - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 + uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: go-version-file: go.mod - name: "gofmt" From bf546973d01a1ec4a306cbcc789bd86ea70e44be Mon Sep 17 00:00:00 2001 From: Nara Kasbergen Kwon <855115+xiehan@users.noreply.github.com> Date: Wed, 8 May 2024 13:00:10 +0200 Subject: [PATCH 49/55] github: Set up Dependabot to manage HashiCorp-owned Actions versioning --- .github/dependabot.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..61e0aff8 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,22 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: monthly + labels: + - dependencies + - automated + reviewers: + - hashicorp/terraform-core + # only update HashiCorp actions, external actions managed by TSCCR + allow: + - dependency-name: hashicorp/* + groups: + github-actions-breaking: + update-types: + - major + github-actions-backward-compatible: + update-types: + - minor + - patch From bc757658ca11c5d6d17f328d5672ac447c3efcff Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 8 May 2024 16:09:28 -0700 Subject: [PATCH 50/55] hclsyntax: Don't panic if splat operand is unknown and marked We were calling .Range() on any unknown sourceVal, without first checking whether it was marked. That method panics if called on a marked value, so we need to strip that off first. While testing this I found some return paths that weren't properly transferring the source value's marks to the output, and so this also addresses those so that all return paths preserve whatever markings are present on the source value. In particular, if a non-list/set/tuple value gets "upgraded" into a tuple then we must transfer its marks onto the tuple, because the decision about constructing that value was based on characteristics of the source value. --- hclsyntax/expression.go | 15 ++++++++------- hclsyntax/expression_test.go | 10 ++++++++++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/hclsyntax/expression.go b/hclsyntax/expression.go index 81597399..577a50fa 100644 --- a/hclsyntax/expression.go +++ b/hclsyntax/expression.go @@ -1780,7 +1780,7 @@ func (e *SplatExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { if sourceVal.IsNull() { if autoUpgrade { - return cty.EmptyTupleVal, diags + return cty.EmptyTupleVal.WithSameMarks(sourceVal), diags } diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, @@ -1798,7 +1798,7 @@ func (e *SplatExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { // If we don't even know the _type_ of our source value yet then // we'll need to defer all processing, since we can't decide our // result type either. - return cty.DynamicVal, diags + return cty.DynamicVal.WithSameMarks(sourceVal), diags } upgradedUnknown := false @@ -1813,13 +1813,14 @@ func (e *SplatExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { // list of a single attribute, but we still need to check if that // attribute actually exists. if !sourceVal.IsKnown() { - sourceRng := sourceVal.Range() + unmarkedVal, _ := sourceVal.Unmark() + sourceRng := unmarkedVal.Range() if sourceRng.CouldBeNull() { upgradedUnknown = true } } - sourceVal = cty.TupleVal([]cty.Value{sourceVal}) + sourceVal = cty.TupleVal([]cty.Value{sourceVal}).WithSameMarks(sourceVal) sourceTy = sourceVal.Type() } @@ -1900,14 +1901,14 @@ func (e *SplatExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { e.Item.clearValue(ctx) // clean up our temporary value if upgradedUnknown { - return cty.DynamicVal, diags + return cty.DynamicVal.WithMarks(marks), diags } if !isKnown { // We'll ingore the resultTy diagnostics in this case since they // will just be the same errors we saw while iterating above. ty, _ := resultTy() - return cty.UnknownVal(ty), diags + return cty.UnknownVal(ty).WithMarks(marks), diags } switch { @@ -1915,7 +1916,7 @@ func (e *SplatExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { if len(vals) == 0 { ty, tyDiags := resultTy() diags = append(diags, tyDiags...) - return cty.ListValEmpty(ty.ElementType()), diags + return cty.ListValEmpty(ty.ElementType()).WithMarks(marks), diags } return cty.ListVal(vals).WithMarks(marks), diags default: diff --git a/hclsyntax/expression_test.go b/hclsyntax/expression_test.go index b18214cd..b50dc137 100644 --- a/hclsyntax/expression_test.go +++ b/hclsyntax/expression_test.go @@ -1457,6 +1457,16 @@ upper( cty.UnknownVal(cty.List(cty.Bool)).RefineNotNull().Mark("sensitive"), 0, }, + { // splat with sensitive non-collection that's unknown + `not_a_list.*`, + &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "not_a_list": cty.UnknownVal(cty.EmptyObject).RefineNotNull().Mark("sensitive"), + }, + }, + cty.TupleVal([]cty.Value{cty.UnknownVal(cty.EmptyObject).RefineNotNull().Mark("sensitive")}).Mark("sensitive"), + 0, + }, { // splat with sensitive collection that's unknown and not null `maps.*.enabled`, &hcl.EvalContext{ From 9a64c17c75059d9c8f5d94f2265c00026ac48781 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 8 May 2024 17:52:51 -0700 Subject: [PATCH 51/55] dynblock: Preserve marks from for_each expression into result Previously if the for_each expression was marked then expansion would fail because marked expressions are never directly iterable. Now instead we'll allow marked for_each and preserve the marks into the values produced by the resulting block as much as we can. This runs into the classic problem that HCL blocks are not values themselves and so cannot carry marks directly, but we can at least make sure that the values of any leaf arguments end up marked. --- ext/dynblock/expand_body.go | 39 +++++++++++++------ ext/dynblock/expand_body_test.go | 64 ++++++++++++++++++++++++++++++++ ext/dynblock/expand_spec.go | 27 +++++++++++++- ext/dynblock/expr_wrap.go | 24 +++++++++++- ext/dynblock/unknown_body.go | 23 +++++++++--- 5 files changed, 157 insertions(+), 20 deletions(-) diff --git a/ext/dynblock/expand_body.go b/ext/dynblock/expand_body.go index 2734e937..e630f184 100644 --- a/ext/dynblock/expand_body.go +++ b/ext/dynblock/expand_body.go @@ -16,6 +16,7 @@ type expandBody struct { original hcl.Body forEachCtx *hcl.EvalContext iteration *iteration // non-nil if we're nested inside another "dynamic" block + valueMarks cty.ValueMarks checkForEach []func(cty.Value, hcl.Expression, *hcl.EvalContext) hcl.Diagnostics @@ -125,7 +126,7 @@ func (b *expandBody) extendSchema(schema *hcl.BodySchema) *hcl.BodySchema { } func (b *expandBody) prepareAttributes(rawAttrs hcl.Attributes) hcl.Attributes { - if len(b.hiddenAttrs) == 0 && b.iteration == nil { + if len(b.hiddenAttrs) == 0 && b.iteration == nil && len(b.valueMarks) == 0 { // Easy path: just pass through the attrs from the original body verbatim return rawAttrs } @@ -142,13 +143,24 @@ func (b *expandBody) prepareAttributes(rawAttrs hcl.Attributes) hcl.Attributes { if b.iteration != nil { attr := *rawAttr // shallow copy so we can mutate it attr.Expr = exprWrap{ - Expression: attr.Expr, - i: b.iteration, + Expression: attr.Expr, + i: b.iteration, + resultMarks: b.valueMarks, } attrs[name] = &attr } else { - // If we have no active iteration then no wrapping is required. - attrs[name] = rawAttr + // If we have no active iteration then no wrapping is required + // unless we have marks to apply. + if len(b.valueMarks) != 0 { + attr := *rawAttr // shallow copy so we can mutate it + attr.Expr = exprWrap{ + Expression: attr.Expr, + resultMarks: b.valueMarks, + } + attrs[name] = &attr + } else { + attrs[name] = rawAttr + } } } return attrs @@ -192,8 +204,9 @@ func (b *expandBody) expandBlocks(schema *hcl.BodySchema, rawBlocks hcl.Blocks, continue } - if spec.forEachVal.IsKnown() { - for it := spec.forEachVal.ElementIterator(); it.Next(); { + forEachVal, marks := spec.forEachVal.Unmark() + if forEachVal.IsKnown() { + for it := forEachVal.ElementIterator(); it.Next(); { key, value := it.Element() i := b.iteration.MakeChild(spec.iteratorName, key, value) @@ -202,7 +215,7 @@ func (b *expandBody) expandBlocks(schema *hcl.BodySchema, rawBlocks hcl.Blocks, if block != nil { // Attach our new iteration context so that attributes // and other nested blocks can refer to our iterator. - block.Body = b.expandChild(block.Body, i) + block.Body = b.expandChild(block.Body, i, marks) blocks = append(blocks, block) } } @@ -214,7 +227,10 @@ func (b *expandBody) expandBlocks(schema *hcl.BodySchema, rawBlocks hcl.Blocks, block, blockDiags := spec.newBlock(i, b.forEachCtx) diags = append(diags, blockDiags...) if block != nil { - block.Body = unknownBody{b.expandChild(block.Body, i)} + block.Body = unknownBody{ + template: b.expandChild(block.Body, i, marks), + valueMarks: marks, + } blocks = append(blocks, block) } } @@ -226,7 +242,7 @@ func (b *expandBody) expandBlocks(schema *hcl.BodySchema, rawBlocks hcl.Blocks, // case it contains expressions that refer to our inherited // iterators, or nested "dynamic" blocks. expandedBlock := *rawBlock // shallow copy - expandedBlock.Body = b.expandChild(rawBlock.Body, b.iteration) + expandedBlock.Body = b.expandChild(rawBlock.Body, b.iteration, nil) blocks = append(blocks, &expandedBlock) } } @@ -235,11 +251,12 @@ func (b *expandBody) expandBlocks(schema *hcl.BodySchema, rawBlocks hcl.Blocks, return blocks, diags } -func (b *expandBody) expandChild(child hcl.Body, i *iteration) hcl.Body { +func (b *expandBody) expandChild(child hcl.Body, i *iteration, valueMarks cty.ValueMarks) hcl.Body { chiCtx := i.EvalContext(b.forEachCtx) ret := Expand(child, chiCtx) ret.(*expandBody).iteration = i ret.(*expandBody).checkForEach = b.checkForEach + ret.(*expandBody).valueMarks = valueMarks return ret } diff --git a/ext/dynblock/expand_body_test.go b/ext/dynblock/expand_body_test.go index bec6c210..3b245ee8 100644 --- a/ext/dynblock/expand_body_test.go +++ b/ext/dynblock/expand_body_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/davecgh/go-spew/spew" + "github.com/google/go-cmp/cmp" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/hcl/v2/hcltest" @@ -710,6 +711,69 @@ func TestExpandUnknownBodies(t *testing.T) { } +func TestExpandMarkedForEach(t *testing.T) { + srcBody := hcltest.MockBody(&hcl.BodyContent{ + Blocks: hcl.Blocks{ + { + Type: "dynamic", + Labels: []string{"b"}, + LabelRanges: []hcl.Range{{}}, + Body: hcltest.MockBody(&hcl.BodyContent{ + Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ + "for_each": hcltest.MockExprLiteral(cty.TupleVal([]cty.Value{ + cty.StringVal("hey"), + }).Mark("boop")), + "iterator": hcltest.MockExprTraversalSrc("dyn_b"), + }), + Blocks: hcl.Blocks{ + { + Type: "content", + Body: hcltest.MockBody(&hcl.BodyContent{ + Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ + "val0": hcltest.MockExprLiteral(cty.StringVal("static c 1")), + "val1": hcltest.MockExprTraversalSrc("dyn_b.value"), + }), + }), + }, + }, + }), + }, + }, + }) + + dynBody := Expand(srcBody, nil) + + t.Run("Decode", func(t *testing.T) { + decSpec := &hcldec.BlockListSpec{ + TypeName: "b", + Nested: &hcldec.ObjectSpec{ + "val0": &hcldec.AttrSpec{ + Name: "val0", + Type: cty.String, + }, + "val1": &hcldec.AttrSpec{ + Name: "val1", + Type: cty.String, + }, + }, + } + + want := cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "val0": cty.StringVal("static c 1").Mark("boop"), + "val1": cty.StringVal("hey").Mark("boop"), + }), + }) + got, diags := hcldec.Decode(dynBody, decSpec, nil) + if diags.HasErrors() { + t.Fatalf("unexpected errors\n%s", diags.Error()) + } + if diff := cmp.Diff(want, got, ctydebug.CmpOptions); diff != "" { + t.Errorf("wrong result\n%s", diff) + } + }) +} + func TestExpandInvalidIteratorError(t *testing.T) { srcBody := hcltest.MockBody(&hcl.BodyContent{ Blocks: hcl.Blocks{ diff --git a/ext/dynblock/expand_spec.go b/ext/dynblock/expand_spec.go index 0231c4aa..55a69ba9 100644 --- a/ext/dynblock/expand_spec.go +++ b/ext/dynblock/expand_spec.go @@ -77,7 +77,8 @@ func (b *expandBody) decodeSpec(blockS *hcl.BlockHeaderSchema, rawSpec *hcl.Bloc } } - if !eachVal.CanIterateElements() && eachVal.Type() != cty.DynamicPseudoType { + unmarkedEachVal, _ := eachVal.Unmark() + if !unmarkedEachVal.CanIterateElements() && unmarkedEachVal.Type() != cty.DynamicPseudoType { // We skip this error for DynamicPseudoType because that means we either // have a null (which is checked immediately below) or an unknown // (which is handled in the expandBody Content methods). @@ -91,7 +92,7 @@ func (b *expandBody) decodeSpec(blockS *hcl.BlockHeaderSchema, rawSpec *hcl.Bloc }) return nil, diags } - if eachVal.IsNull() { + if unmarkedEachVal.IsNull() { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid dynamic for_each value", @@ -212,6 +213,28 @@ func (s *expandSpec) newBlock(i *iteration, ctx *hcl.EvalContext) (*hcl.Block, h }) return nil, diags } + if labelVal.IsMarked() { + // This situation is tricky because HCL just works generically + // with marks and so doesn't have any good language to talk about + // the meaning of specific mark types, but yet we cannot allow + // marked values here because the HCL API guarantees that a block's + // labels are always known static constant Go strings. + // Therefore this is a low-quality error message but at least + // better than panicking below when we call labelVal.AsString. + // If this becomes a problem then we could potentially add a new + // option for the public function [Expand] to allow calling + // applications to specify custom label validation functions that + // could then supersede this generic message. + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid dynamic block label", + Detail: "This value has dynamic marks that make it unsuitable for use as a block label.", + Subject: labelExpr.Range().Ptr(), + Expression: labelExpr, + EvalContext: lCtx, + }) + return nil, diags + } labels = append(labels, labelVal.AsString()) labelRanges = append(labelRanges, labelExpr.Range()) diff --git a/ext/dynblock/expr_wrap.go b/ext/dynblock/expr_wrap.go index 625bf9cc..57636411 100644 --- a/ext/dynblock/expr_wrap.go +++ b/ext/dynblock/expr_wrap.go @@ -11,6 +11,19 @@ import ( type exprWrap struct { hcl.Expression i *iteration + + // resultMarks is a set of marks that must be applied to whatever + // value results from this expression. We do this whenever a + // dynamic block's for_each expression produced a marked result, + // since in that case any nested expressions inside are treated + // as being derived from that for_each expression. + // + // (calling applications might choose to reject marks by passing + // an [OptCheckForEach] to [Expand] and returning an error when + // marks are present, but this mechanism is here to help achieve + // reasonable behavior for situations where marks are permitted, + // which is the default.) + resultMarks cty.ValueMarks } func (e exprWrap) Variables() []hcl.Traversal { @@ -34,8 +47,13 @@ func (e exprWrap) Variables() []hcl.Traversal { } func (e exprWrap) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { + if e.i == nil { + // If we don't have an active iteration then we can just use the + // given EvalContext directly. + return e.prepareValue(e.Expression.Value(ctx)) + } extCtx := e.i.EvalContext(ctx) - return e.Expression.Value(extCtx) + return e.prepareValue(e.Expression.Value(extCtx)) } // UnwrapExpression returns the expression being wrapped by this instance. @@ -43,3 +61,7 @@ func (e exprWrap) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { func (e exprWrap) UnwrapExpression() hcl.Expression { return e.Expression } + +func (e exprWrap) prepareValue(val cty.Value, diags hcl.Diagnostics) (cty.Value, hcl.Diagnostics) { + return val.WithMarks(e.resultMarks), diags +} diff --git a/ext/dynblock/unknown_body.go b/ext/dynblock/unknown_body.go index 6cd59c77..b1fdfc07 100644 --- a/ext/dynblock/unknown_body.go +++ b/ext/dynblock/unknown_body.go @@ -5,6 +5,7 @@ package dynblock import ( "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/zclconf/go-cty/cty" ) @@ -18,16 +19,26 @@ import ( // we instead arrange for everything _inside_ the block to be unknown instead, // to give the best possible approximation. type unknownBody struct { - template hcl.Body + template hcl.Body + valueMarks cty.ValueMarks } var _ hcl.Body = unknownBody{} -// hcldec.UnkownBody impl +// hcldec.UnknownBody impl func (b unknownBody) Unknown() bool { return true } +// hcldec.MarkedBody impl +func (b unknownBody) BodyValueMarks() cty.ValueMarks { + // We'll pass through to our template if it is a MarkedBody + if t, ok := b.template.(hcldec.MarkedBody); ok { + return t.BodyValueMarks() + } + return nil +} + func (b unknownBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) { content, diags := b.template.Content(schema) content = b.fixupContent(content) @@ -41,7 +52,7 @@ func (b unknownBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diag func (b unknownBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) { content, remain, diags := b.template.PartialContent(schema) content = b.fixupContent(content) - remain = unknownBody{remain} // remaining content must also be wrapped + remain = unknownBody{template: remain, valueMarks: b.valueMarks} // remaining content must also be wrapped // We're intentionally preserving the diagnostics reported from the // inner body so that we can still report where the template body doesn't @@ -69,8 +80,8 @@ func (b unknownBody) fixupContent(got *hcl.BodyContent) *hcl.BodyContent { if len(got.Blocks) > 0 { ret.Blocks = make(hcl.Blocks, 0, len(got.Blocks)) for _, gotBlock := range got.Blocks { - new := *gotBlock // shallow copy - new.Body = unknownBody{gotBlock.Body} // nested content must also be marked unknown + new := *gotBlock // shallow copy + new.Body = unknownBody{template: gotBlock.Body, valueMarks: b.valueMarks} // nested content must also be marked unknown ret.Blocks = append(ret.Blocks, &new) } } @@ -85,7 +96,7 @@ func (b unknownBody) fixupAttrs(got hcl.Attributes) hcl.Attributes { ret := make(hcl.Attributes, len(got)) for name, gotAttr := range got { new := *gotAttr // shallow copy - new.Expr = hcl.StaticExpr(cty.DynamicVal, gotAttr.Expr.Range()) + new.Expr = hcl.StaticExpr(cty.DynamicVal.WithMarks(b.valueMarks), gotAttr.Expr.Range()) ret[name] = &new } return ret From 318bbfebb5e1eeea09765b1edb1aec303f75a268 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 8 May 2024 17:55:39 -0700 Subject: [PATCH 52/55] hcldec: Allow body-derived values to be marked Similar to the previously-added UnknownBody, the new optional interface MarkedBody allows hcl.Body implementations to suggest a set of marks that ought to be applied to any value that's generated to represent the content of that body. The dynblock extension then uses this to get hcldec to mark the whole object representing any block that was generated by a dynamic block whose for_each was marked, for a better representation of the fact that a block's existence was decided based on a marked value. --- ext/dynblock/expand_body.go | 5 +++++ ext/dynblock/expand_body_test.go | 2 +- go.mod | 2 +- go.sum | 4 ++++ hcldec/spec.go | 19 +++++++++++++++++++ 5 files changed, 30 insertions(+), 2 deletions(-) diff --git a/ext/dynblock/expand_body.go b/ext/dynblock/expand_body.go index e630f184..2cafae5f 100644 --- a/ext/dynblock/expand_body.go +++ b/ext/dynblock/expand_body.go @@ -270,3 +270,8 @@ func (b *expandBody) JustAttributes() (hcl.Attributes, hcl.Diagnostics) { func (b *expandBody) MissingItemRange() hcl.Range { return b.original.MissingItemRange() } + +// hcldec.MarkedBody impl +func (b *expandBody) BodyValueMarks() cty.ValueMarks { + return b.valueMarks +} diff --git a/ext/dynblock/expand_body_test.go b/ext/dynblock/expand_body_test.go index 3b245ee8..0941f291 100644 --- a/ext/dynblock/expand_body_test.go +++ b/ext/dynblock/expand_body_test.go @@ -762,7 +762,7 @@ func TestExpandMarkedForEach(t *testing.T) { cty.ObjectVal(map[string]cty.Value{ "val0": cty.StringVal("static c 1").Mark("boop"), "val1": cty.StringVal("hey").Mark("boop"), - }), + }).Mark("boop"), }) got, diags := hcldec.Decode(dynBody, decSpec, nil) if diags.HasErrors() { diff --git a/go.mod b/go.mod index d9d4da45..56c414fa 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 github.com/spf13/pflag v1.0.2 github.com/zclconf/go-cty v1.13.0 - github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b + github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167 golang.org/x/tools v0.6.0 ) diff --git a/go.sum b/go.sum index 08ed23ed..53611d83 100644 --- a/go.sum +++ b/go.sum @@ -27,6 +27,10 @@ github.com/zclconf/go-cty v1.13.0 h1:It5dfKTTZHe9aeppbNOda3mN7Ag7sg6QkBNm6TkyFa0 github.com/zclconf/go-cty v1.13.0/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0= github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b h1:FosyBZYxY34Wul7O/MSKey3txpPYyCqVO5ZyceuQJEI= github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= +github.com/zclconf/go-cty-debug v0.0.0-20240417160409-8c45e122ae1a h1:/o/Emn22dZIQ7AhyA0aLOKo528WG/WRAM5tqzIoQIOs= +github.com/zclconf/go-cty-debug v0.0.0-20240417160409-8c45e122ae1a/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= +github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= +github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167 h1:O8uGbHCqlTp2P6QJSLmCojM4mN6UemYv8K+dCnmHmu0= golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= diff --git a/hcldec/spec.go b/hcldec/spec.go index 2bebc433..b07a5534 100644 --- a/hcldec/spec.go +++ b/hcldec/spec.go @@ -73,6 +73,12 @@ type UnknownBody interface { Unknown() bool } +// MarkedBody can be optionally implemented by an hcl.Body instance to +// indicate that a value created from it ought to be marked. +type MarkedBody interface { + BodyValueMarks() cty.ValueMarks +} + func (s ObjectSpec) visitSameBodyChildren(cb visitFunc) { for _, c := range s { cb(c) @@ -473,6 +479,7 @@ func (s *BlockListSpec) decode(content *hcl.BodyContent, blockLabels []blockLabe val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false) diags = append(diags, childDiags...) + val = prepareBodyVal(val, childBlock.Body) if u, ok := childBlock.Body.(UnknownBody); ok { if u.Unknown() { @@ -635,6 +642,7 @@ func (s *BlockTupleSpec) decode(content *hcl.BodyContent, blockLabels []blockLab val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false) diags = append(diags, childDiags...) + val = prepareBodyVal(val, childBlock.Body) if u, ok := childBlock.Body.(UnknownBody); ok { if u.Unknown() { @@ -758,6 +766,7 @@ func (s *BlockSetSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false) diags = append(diags, childDiags...) + val = prepareBodyVal(val, childBlock.Body) if u, ok := childBlock.Body.(UnknownBody); ok { if u.Unknown() { @@ -928,6 +937,7 @@ func (s *BlockMapSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel childLabels := labelsForBlock(childBlock) val, _, childDiags := decode(childBlock.Body, childLabels[len(s.LabelNames):], ctx, s.Nested, false) + val = prepareBodyVal(val, childBlock.Body) targetMap := elems for _, key := range childBlock.Labels[:len(s.LabelNames)-1] { if _, exists := targetMap[key]; !exists { @@ -1082,6 +1092,7 @@ func (s *BlockObjectSpec) decode(content *hcl.BodyContent, blockLabels []blockLa childLabels := labelsForBlock(childBlock) val, _, childDiags := decode(childBlock.Body, childLabels[len(s.LabelNames):], ctx, s.Nested, false) + val = prepareBodyVal(val, childBlock.Body) targetMap := elems for _, key := range childBlock.Labels[:len(s.LabelNames)-1] { if _, exists := targetMap[key]; !exists { @@ -1720,3 +1731,11 @@ func (s noopSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel Filename: "noopSpec", } } + +func prepareBodyVal(decodeResult cty.Value, body hcl.Body) cty.Value { + if m, ok := body.(MarkedBody); ok { + marks := m.BodyValueMarks() + return decodeResult.WithMarks(marks) + } + return decodeResult +} From 212a40e528766634a1aa6dd1e820d7936762196e Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Thu, 9 May 2024 11:50:35 -0700 Subject: [PATCH 53/55] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a33c5a68..e8d06e47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ ### Enhancements * Introduce `ParseTraversalPartial`, which allows traversals that include the splat (`[*]`) index operator. ([#673](https://github.com/hashicorp/hcl/pull/673)) +* ext/dynblock: Now accepts marked values in `for_each`, and will transfer those marks (as much as technically possible) to values in the generated blocks. ([#679](https://github.com/hashicorp/hcl/pull/679)) + +### Bugs Fixed + +* Expression evaluation will no longer panic if the splat operator is applied to an unknown value that has cty marks. ([#678](https://github.com/hashicorp/hcl/pull/678)) ## v2.20.1 (March 26, 2024) From f7e093a382df24049d3ebbaeb059550b2b755b6f Mon Sep 17 00:00:00 2001 From: "hashicorp-tsccr[bot]" <129506189+hashicorp-tsccr[bot]@users.noreply.github.com> Date: Fri, 14 Jun 2024 15:41:56 +0200 Subject: [PATCH 54/55] github: Pin action refs to latest trusted by TSCCR (#683) Co-authored-by: hashicorp-tsccr[bot] --- .github/workflows/checks.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 5102728d..a5f6b7af 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -30,7 +30,7 @@ jobs: run: | git config --global core.autocrlf false - name: "Fetch source code" - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Install Go uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: @@ -44,7 +44,7 @@ jobs: runs-on: ubuntu-latest steps: - name: "Fetch source code" - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Install Go uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: @@ -58,7 +58,7 @@ jobs: runs-on: ubuntu-latest steps: - name: "Fetch source code" - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Install Go uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: @@ -72,7 +72,7 @@ jobs: runs-on: ubuntu-latest steps: - name: "Fetch source code" - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Install Go uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: From 360ae579460fab69e9939599af85c8f255a59007 Mon Sep 17 00:00:00 2001 From: Liam Cervante Date: Wed, 19 Jun 2024 13:34:52 +0200 Subject: [PATCH 55/55] prepare for v2.21.0 release --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8d06e47..f81110dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # HCL Changelog -## v2.21.0 (Unreleased) +## v2.21.0 (June 19, 2024) ### Enhancements