Skip to content

Commit

Permalink
Introduce autofix API
Browse files Browse the repository at this point in the history
  • Loading branch information
wata727 committed Jun 3, 2023
1 parent 30d991e commit 060104c
Show file tree
Hide file tree
Showing 21 changed files with 3,465 additions and 481 deletions.
31 changes: 30 additions & 1 deletion helper/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/terraform-linters/tflint-plugin-sdk/hclext"
"github.com/terraform-linters/tflint-plugin-sdk/internal"
"github.com/terraform-linters/tflint-plugin-sdk/terraform/addrs"
"github.com/terraform-linters/tflint-plugin-sdk/tflint"
"github.com/zclconf/go-cty/cty"
Expand All @@ -21,8 +22,10 @@ type Runner struct {
Issues Issues

files map[string]*hcl.File
sources map[string][]byte
config Config
variables map[string]*Variable
fixer *internal.Fixer
}

// Variable is an implementation of variables in Terraform language
Expand Down Expand Up @@ -318,6 +321,25 @@ func (r *Runner) EmitIssue(rule tflint.Rule, message string, location hcl.Range)
return nil
}

// EmitIssueWithFix adds an issue and invoke fix.
func (r *Runner) EmitIssueWithFix(rule tflint.Rule, message string, location hcl.Range, fixFunc func(f tflint.Fixer) error) error {
r.fixer.StashChanges()
if err := fixFunc(r.fixer); err != nil {
if errors.Is(err, tflint.ErrFixNotSupported) {
r.fixer.PopChangesFromStash()
return r.EmitIssue(rule, message, location)
}
return err
}
return r.EmitIssue(rule, message, location)
}

// Changes returns formatted changes by the fixer.
func (r *Runner) Changes() map[string][]byte {
r.fixer.FormatChanges()
return r.fixer.Changes()
}

// EnsureNoError is a method that simply runs a function if there is no error.
//
// Deprecated: Use EvaluateExpr with a function callback. e.g. EvaluateExpr(expr, func (val T) error {}, ...)
Expand All @@ -331,7 +353,12 @@ func (r *Runner) EnsureNoError(err error, proc func() error) error {
// NewLocalRunner initialises a new test runner.
// Internal use only.
func NewLocalRunner(files map[string]*hcl.File, issues Issues) *Runner {
return &Runner{files: map[string]*hcl.File{}, variables: map[string]*Variable{}, Issues: issues}
return &Runner{
files: map[string]*hcl.File{},
sources: map[string][]byte{},
variables: map[string]*Variable{},
Issues: issues,
}
}

// AddLocalFile adds a new file to the current mapped files.
Expand All @@ -342,6 +369,7 @@ func (r *Runner) AddLocalFile(name string, file *hcl.File) bool {
}

r.files[name] = file
r.sources[name] = file.Bytes
return true
}

Expand All @@ -365,6 +393,7 @@ func (r *Runner) initFromFiles() error {
}
}
}
r.fixer = internal.NewFixer(r.sources)

return nil
}
Expand Down
31 changes: 25 additions & 6 deletions helper/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import (
// TestRunner returns a mock Runner for testing.
// You can pass the map of file names and their contents in the second argument.
func TestRunner(t *testing.T, files map[string]string) *Runner {
t.Helper()

runner := NewLocalRunner(map[string]*hcl.File{}, Issues{})
parser := hclparse.NewParser()

Expand Down Expand Up @@ -50,25 +52,42 @@ func TestRunner(t *testing.T, files map[string]string) *Runner {
}

// AssertIssues is an assertion helper for comparing issues.
func AssertIssues(t *testing.T, expected Issues, actual Issues) {
func AssertIssues(t *testing.T, want Issues, got Issues) {
t.Helper()

opts := []cmp.Option{
// Byte field will be ignored because it's not important in tests such as positions
cmpopts.IgnoreFields(hcl.Pos{}, "Byte"),
ruleComparer(),
}
if !cmp.Equal(expected, actual, opts...) {
t.Fatalf("Expected issues are not matched:\n %s\n", cmp.Diff(expected, actual, opts...))
if diff := cmp.Diff(want, got, opts...); diff != "" {
t.Fatalf("Expected issues are not matched:\n %s\n", diff)
}
}

// AssertIssuesWithoutRange is an assertion helper for comparing issues except for range.
func AssertIssuesWithoutRange(t *testing.T, expected Issues, actual Issues) {
func AssertIssuesWithoutRange(t *testing.T, want Issues, got Issues) {
t.Helper()

opts := []cmp.Option{
cmpopts.IgnoreFields(Issue{}, "Range"),
ruleComparer(),
}
if !cmp.Equal(expected, actual, opts...) {
t.Fatalf("Expected issues are not matched:\n %s\n", cmp.Diff(expected, actual, opts...))
if diff := cmp.Diff(want, got, opts...); diff != "" {
t.Fatalf("Expected issues are not matched:\n %s\n", diff)
}
}

// AssertChanges is an assertion helper for comparing autofix changes.
func AssertChanges(t *testing.T, want map[string]string, got map[string][]byte) {
t.Helper()

sources := make(map[string]string)
for name, src := range got {
sources[name] = string(src)
}
if diff := cmp.Diff(want, sources); diff != "" {
t.Fatalf("Expected changes are not matched:\n %s\n", diff)
}
}

Expand Down
Loading

0 comments on commit 060104c

Please sign in to comment.