diff --git a/.changes/unreleased/ENHANCEMENTS-20241107-164703.yaml b/.changes/unreleased/ENHANCEMENTS-20241107-164703.yaml new file mode 100644 index 000000000..4678957fc --- /dev/null +++ b/.changes/unreleased/ENHANCEMENTS-20241107-164703.yaml @@ -0,0 +1,6 @@ +kind: ENHANCEMENTS +body: Raise HCL Diagnostics during early validation +time: 2024-11-07T16:47:03.069677-05:00 +custom: + Issue: "1500" + Repository: terraform-ls diff --git a/go.mod b/go.mod index b713d2183..690639b7a 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/hashicorp/terraform-exec v0.21.0 github.com/hashicorp/terraform-json v0.23.0 github.com/hashicorp/terraform-registry-address v0.2.3 - github.com/hashicorp/terraform-schema v0.0.0-20241030161413-0438899ff948 + github.com/hashicorp/terraform-schema v0.0.0-20241113181710-ea3872fef6cf github.com/mcuadros/go-defaults v1.2.0 github.com/mh-cbon/go-fmt-fail v0.0.0-20160815164508-67765b3fbcb5 github.com/mitchellh/cli v1.1.5 diff --git a/go.sum b/go.sum index 8afa25114..479aa1dc3 100644 --- a/go.sum +++ b/go.sum @@ -233,8 +233,8 @@ github.com/hashicorp/terraform-json v0.23.0 h1:sniCkExU4iKtTADReHzACkk8fnpQXrdD2 github.com/hashicorp/terraform-json v0.23.0/go.mod h1:MHdXbBAbSg0GvzuWazEGKAn/cyNfIB7mN6y7KJN6y2c= github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI= github.com/hashicorp/terraform-registry-address v0.2.3/go.mod h1:lFHA76T8jfQteVfT7caREqguFrW3c4MFSPhZB7HHgUM= -github.com/hashicorp/terraform-schema v0.0.0-20241030161413-0438899ff948 h1:2yw+7HPYOAEFafRpcw7BOtX9svWmXdc8sK5sSLZIjiI= -github.com/hashicorp/terraform-schema v0.0.0-20241030161413-0438899ff948/go.mod h1:cA6LcD9EWWwgG3ZsZx+Fvyvgil6LYxTl3bknC8LF0B8= +github.com/hashicorp/terraform-schema v0.0.0-20241113181710-ea3872fef6cf h1:rUFHjz0LWE7jM98hz2p5MDGtHq9oA8ZbDCY28VjcZtg= +github.com/hashicorp/terraform-schema v0.0.0-20241113181710-ea3872fef6cf/go.mod h1:hwYMiQp/tVcJtYfbNSxEEK+ilauXwwtZgpLXmeUBVGg= github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc= github.com/hexops/autogold v1.3.1 h1:YgxF9OHWbEIUjhDbpnLhgVsjUDsiHDTyDfy2lrfdlzo= diff --git a/internal/features/stacks/jobs/metadata.go b/internal/features/stacks/jobs/metadata.go index 7e59ef6fd..72c8a6aa6 100644 --- a/internal/features/stacks/jobs/metadata.go +++ b/internal/features/stacks/jobs/metadata.go @@ -7,8 +7,10 @@ import ( "context" "github.com/hashicorp/terraform-ls/internal/document" + "github.com/hashicorp/terraform-ls/internal/features/stacks/ast" "github.com/hashicorp/terraform-ls/internal/features/stacks/state" "github.com/hashicorp/terraform-ls/internal/job" + globalAst "github.com/hashicorp/terraform-ls/internal/terraform/ast" "github.com/hashicorp/terraform-ls/internal/terraform/module/operation" earlydecoder "github.com/hashicorp/terraform-schema/earlydecoder/stacks" ) @@ -17,7 +19,7 @@ import ( // way that enables us to decode the rest of the configuration, // e.g. by knowing provider versions, etc. func LoadStackMetadata(ctx context.Context, stackStore *state.StackStore, stackPath string) error { - stack, err := stackStore.StackRecordByPath(stackPath) + record, err := stackStore.StackRecordByPath(stackPath) if err != nil { return err } @@ -25,7 +27,7 @@ func LoadStackMetadata(ctx context.Context, stackStore *state.StackStore, stackP // TODO: Avoid parsing if upstream (parsing) job reported no changes // Avoid parsing if it is already in progress or already known - if stack.MetaState != operation.OpStateUnknown && !job.IgnoreState(ctx) { + if record.MetaState != operation.OpStateUnknown && !job.IgnoreState(ctx) { return job.StateNotChangedErr{Dir: document.DirHandleFromPath(stackPath)} } @@ -34,13 +36,36 @@ func LoadStackMetadata(ctx context.Context, stackStore *state.StackStore, stackP return err } + meta, diags := earlydecoder.LoadStack(record.Path(), record.ParsedFiles.AsMap()) + var mErr error - meta, diags := earlydecoder.LoadStack(stack.Path(), stack.ParsedFiles.AsMap()) - if len(diags) > 0 { - mErr = diags + sErr := stackStore.UpdateMetadata(stackPath, meta, mErr) + if sErr != nil { + return sErr } - sErr := stackStore.UpdateMetadata(stackPath, meta, mErr) + if len(diags) <= 0 { + // no new diagnostics, so return early + return mErr + } + + // Merge the new diagnostics with the existing ones + existingDiags, ok := record.Diagnostics[globalAst.HCLParsingSource] + if !ok { + existingDiags = make(ast.Diagnostics) + } else { + existingDiags = existingDiags.Copy() + } + + for fileName, diagnostic := range diags { + // Convert the filename to an AST filename + fn := ast.FilenameFromName(fileName) + + // Append the diagnostic to the existing diagnostics if it exists + existingDiags[fn] = existingDiags[fn].Extend(diagnostic) + } + + sErr = stackStore.UpdateDiagnostics(stackPath, globalAst.HCLParsingSource, existingDiags) if sErr != nil { return sErr } diff --git a/internal/langserver/handlers/initialize_test.go b/internal/langserver/handlers/initialize_test.go index faf507365..74ab32ec0 100644 --- a/internal/langserver/handlers/initialize_test.go +++ b/internal/langserver/handlers/initialize_test.go @@ -567,6 +567,12 @@ func TestInitialize_differentWorkspaceLayouts(t *testing.T) { t.Fatal(err) } if len(allModules) != len(tc.expectedModules) { + for _, mods := range tc.expectedModules { + t.Logf("expected module: %s", mods) + } + for _, mods := range allModules { + t.Logf("got module: %s", mods.Path()) + } t.Fatalf("expected %d modules, got %d", len(tc.expectedModules), len(allModules)) } for _, path := range tc.expectedModules {