command/fmt: Include source snippets in errors

Previously, diagnostic errors would display the filename and line
number, along with "(source code not available)". This is because the
fmt command directly loads and parses the configuration, instead of
using the config loader.

This commit registers the manually parsed source as a synthetic
configuration file, so that the diagnostic formatter can look up the
source for the range with the error and display it.
This commit is contained in:
Alisdair McDiarmid 2020-03-26 13:58:20 -04:00
parent 537c1bedcf
commit 206e2e6d6a
2 changed files with 39 additions and 0 deletions

View File

@ -167,6 +167,10 @@ func (c *FmtCommand) processFile(path string, r io.Reader, w io.Writer, isStdout
return diags return diags
} }
// Register this path as a synthetic configuration source, so that any
// diagnostic errors can include the source code snippet
c.registerSynthConfigSource(path, src)
// File must be parseable as HCL native syntax before we'll try to format // File must be parseable as HCL native syntax before we'll try to format
// it. If not, the formatter is likely to make drastic changes that would // it. If not, the formatter is likely to make drastic changes that would
// be hard for the user to undo. // be hard for the user to undo.

View File

@ -66,6 +66,41 @@ a = 1 +
} }
} }
func TestFmt_snippetInError(t *testing.T) {
tempDir := testTempDir(t)
backendSrc := `terraform {backend "s3" {}}`
err := ioutil.WriteFile(filepath.Join(tempDir, "backend.tf"), []byte(backendSrc), 0644)
if err != nil {
t.Fatal(err)
}
ui := new(cli.MockUi)
c := &FmtCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
},
}
args := []string{tempDir}
if code := c.Run(args); code != 2 {
t.Fatalf("wrong exit code. errors: \n%s", ui.ErrorWriter.String())
}
substrings := []string{
"Argument definition required",
"line 1, in terraform",
`1: terraform {backend "s3" {}}`,
}
for _, substring := range substrings {
if actual := ui.ErrorWriter.String(); !strings.Contains(actual, substring) {
t.Errorf("expected:\n%s\n\nto include: %q", actual, substring)
}
}
}
func TestFmt_tooManyArgs(t *testing.T) { func TestFmt_tooManyArgs(t *testing.T) {
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &FmtCommand{ c := &FmtCommand{