diff --git a/internal/configs/module_call_test.go b/internal/configs/module_call_test.go index 39e0ffcaaf..e47641dc07 100644 --- a/internal/configs/module_call_test.go +++ b/internal/configs/module_call_test.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" "github.com/opentofu/opentofu/internal/addrs" + "github.com/zclconf/go-cty/cty" ) func TestLoadModuleCall(t *testing.T) { @@ -29,7 +30,8 @@ func TestLoadModuleCall(t *testing.T) { file, diags := parser.LoadConfigFile("module-calls.tf") assertExactDiagnostics(t, diags, []string{ - `module-calls.tf:20,3-11: Invalid combination of "count" and "for_each"; The "count" and "for_each" meta-arguments are mutually-exclusive. Only one should be used to be explicit about the number of module instances to be created.`, + `module-calls.tf:19,3-20,11: Invalid combination of "count" and "for_each"; The "count" and "for_each" meta-arguments are mutually-exclusive. Only one should be used to be explicit about the number of module instances to be created.`, + `module-calls.tf:34,5-36,8: Invalid combination of "count" and "enabled"; The "count" and "enabled" meta-arguments are mutually-exclusive. Only one should be used to be explicit about the number of module instances to be created.`, }) gotModules := file.ModuleCalls @@ -76,16 +78,16 @@ func TestLoadModuleCall(t *testing.T) { Name: "module", SrcRange: hcl.Range{ Filename: "module-calls.tf", - Start: hcl.Pos{Line: 23, Column: 5, Byte: 295}, - End: hcl.Pos{Line: 23, Column: 11, Byte: 301}, + Start: hcl.Pos{Line: 23, Column: 5, Byte: 298}, + End: hcl.Pos{Line: 23, Column: 11, Byte: 304}, }, }, hcl.TraverseAttr{ Name: "bar", SrcRange: hcl.Range{ Filename: "module-calls.tf", - Start: hcl.Pos{Line: 23, Column: 11, Byte: 301}, - End: hcl.Pos{Line: 23, Column: 15, Byte: 305}, + Start: hcl.Pos{Line: 23, Column: 11, Byte: 304}, + End: hcl.Pos{Line: 23, Column: 15, Byte: 308}, }, }, }, @@ -96,22 +98,22 @@ func TestLoadModuleCall(t *testing.T) { Name: "aws", NameRange: hcl.Range{ Filename: "module-calls.tf", - Start: hcl.Pos{Line: 27, Column: 5, Byte: 332}, - End: hcl.Pos{Line: 27, Column: 8, Byte: 335}, + Start: hcl.Pos{Line: 27, Column: 5, Byte: 335}, + End: hcl.Pos{Line: 27, Column: 8, Byte: 338}, }, }, InParent: &ProviderConfigRef{ Name: "aws", NameRange: hcl.Range{ Filename: "module-calls.tf", - Start: hcl.Pos{Line: 27, Column: 11, Byte: 338}, - End: hcl.Pos{Line: 27, Column: 14, Byte: 341}, + Start: hcl.Pos{Line: 27, Column: 11, Byte: 341}, + End: hcl.Pos{Line: 27, Column: 14, Byte: 344}, }, Alias: "foo", AliasRange: &hcl.Range{ Filename: "module-calls.tf", - Start: hcl.Pos{Line: 27, Column: 14, Byte: 341}, - End: hcl.Pos{Line: 27, Column: 18, Byte: 345}, + Start: hcl.Pos{Line: 27, Column: 14, Byte: 344}, + End: hcl.Pos{Line: 27, Column: 18, Byte: 348}, }, }, }, @@ -122,6 +124,25 @@ func TestLoadModuleCall(t *testing.T) { End: hcl.Pos{Line: 14, Column: 13, Byte: 179}, }, }, + { + Name: "enabled_test", + SourceAddr: addrs.ModuleSourceLocal("./foo"), + SourceAddrRaw: "./foo", + Enabled: &hclsyntax.LiteralValueExpr{ + Val: cty.BoolVal(true), + SrcRange: hcl.Range{ + Filename: "module-calls.tf", + Start: hcl.Pos{Line: 34, Column: 15, Byte: 427}, + End: hcl.Pos{Line: 34, Column: 19, Byte: 431}, + }, + }, + SourceSet: true, + DeclRange: hcl.Range{ + Filename: "module-calls.tf", + Start: hcl.Pos{Line: 31, Column: 1, Byte: 356}, + End: hcl.Pos{Line: 31, Column: 22, Byte: 377}, + }, + }, } // We'll hide all of the bodies/exprs since we're treating them as opaque diff --git a/internal/configs/testdata/invalid-files/module-calls.tf b/internal/configs/testdata/invalid-files/module-calls.tf index dbf29feef6..f8ab2676fd 100644 --- a/internal/configs/testdata/invalid-files/module-calls.tf +++ b/internal/configs/testdata/invalid-files/module-calls.tf @@ -16,7 +16,7 @@ module "baz" { a = 1 - count = 12 + count = 12 for_each = ["a", "b", "c"] depends_on = [ @@ -27,3 +27,11 @@ module "baz" { aws = aws.foo } } + +module "enabled_test" { + source = "./foo" + lifecycle { + enabled = true + } + count = 0 +} diff --git a/internal/configs/validation.go b/internal/configs/validation.go index 174480b1fc..c47a8f0990 100644 --- a/internal/configs/validation.go +++ b/internal/configs/validation.go @@ -28,21 +28,18 @@ func complainRngAndMsg(countRng, enabledRng, forEachRng hcl.Range) (*hcl.Range, complainAttrs = append(complainAttrs, "\"for_each\"") } - if len(complainAttrs) == 0 { - // If there are no valid ranges, we return an empty range and an empty string + if len(complainAttrs) < 2 { + // If there are less than two valid ranges, we return an empty range and an empty string return nil, "" } - // We sort the complain ranges in order to understood who appeared first, - // and we use that as the valid one + // We sort the complain ranges to return the range between the first and last valid range sort.SliceStable(complainRngs, func(i, j int) bool { return complainRngs[i].Start.Byte < complainRngs[j].Start.Byte }) - lastIndex := len(complainAttrs) - 1 - complainRng := complainRngs[lastIndex] - var sep string + lastIndex := len(complainAttrs) - 1 if len(complainAttrs) >= 3 { // Add an oxford comma to the last attribute complainAttrs[lastIndex] = "and " + complainAttrs[lastIndex] @@ -50,8 +47,8 @@ func complainRngAndMsg(countRng, enabledRng, forEachRng hcl.Range) (*hcl.Range, } else { sep = " and " } - complainMsg := strings.Join(complainAttrs, sep) + complainRng := hcl.RangeBetween(complainRngs[0], complainRngs[lastIndex]) - return &complainRng, complainMsg + return complainRng.Ptr(), complainMsg } diff --git a/internal/configs/validation_test.go b/internal/configs/validation_test.go index cc67ef6292..dd12bc20e3 100644 --- a/internal/configs/validation_test.go +++ b/internal/configs/validation_test.go @@ -31,7 +31,7 @@ func TestComplainRngAndMsg(t *testing.T) { }, // The last item (by Byte) is the one that will be used wantRng: &hcl.Range{ - Start: hcl.Pos{Byte: 2}, + Start: hcl.Pos{Byte: 0}, End: hcl.Pos{Byte: 3}, }, wantMsg: "\"count\" and \"enabled\"", @@ -46,7 +46,7 @@ func TestComplainRngAndMsg(t *testing.T) { End: hcl.Pos{Byte: 4}, }, wantRng: &hcl.Range{ - Start: hcl.Pos{Byte: 3}, + Start: hcl.Pos{Byte: 1}, End: hcl.Pos{Byte: 4}, }, wantMsg: "\"count\" and \"for_each\"", @@ -61,7 +61,7 @@ func TestComplainRngAndMsg(t *testing.T) { End: hcl.Pos{Byte: 1}, }, wantRng: &hcl.Range{ - Start: hcl.Pos{Byte: 4}, + Start: hcl.Pos{Byte: 0}, End: hcl.Pos{Byte: 5}, }, wantMsg: "\"enabled\" and \"for_each\"", @@ -80,7 +80,7 @@ func TestComplainRngAndMsg(t *testing.T) { End: hcl.Pos{Byte: 3}, }, wantRng: &hcl.Range{ - Start: hcl.Pos{Byte: 10}, + Start: hcl.Pos{Byte: 0}, End: hcl.Pos{Byte: 11}, }, wantMsg: "\"count\", \"enabled\", and \"for_each\"",