Skip to content

Commit

Permalink
Introduce autofix API
Browse files Browse the repository at this point in the history
  • Loading branch information
wata727 committed May 2, 2023
1 parent b8f431f commit f4e30ce
Show file tree
Hide file tree
Showing 7 changed files with 414 additions and 321 deletions.
5 changes: 5 additions & 0 deletions helper/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,11 @@ func (r *Runner) EmitIssue(rule tflint.Rule, message string, location hcl.Range)
return nil
}

func (r *Runner) EmitIssueWithFix(rule tflint.Rule, message string, location hcl.Range, fixFunc func(f *tflint.Fixer) error) error {
// TODO: Implement
return nil
}

// 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 Down
8 changes: 7 additions & 1 deletion plugin/host2plugin/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,13 @@ func (s *GRPCServer) Check(ctx context.Context, req *proto.Check_Request) (*prot
}
defer conn.Close()

runner, err := s.impl.NewRunner(&plugin2host.GRPCClient{Client: proto.NewRunnerClient(conn)})
client := proto.NewRunnerClient(conn)
resp, err := client.GetFiles(ctx, &proto.GetFiles_Request{})
if err != nil {
return nil, toproto.Error(codes.FailedPrecondition, err)
}

runner, err := s.impl.NewRunner(&plugin2host.GRPCClient{Client: client, Fixer: tflint.NewFixer(resp.Files)})
if err != nil {
return nil, toproto.Error(codes.FailedPrecondition, err)
}
Expand Down
15 changes: 15 additions & 0 deletions plugin/plugin2host/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
// GRPCClient is a plugin-side implementation. Plugin can send requests through the client to host's gRPC server.
type GRPCClient struct {
Client proto.RunnerClient
Fixer *tflint.Fixer
}

var _ tflint.Runner = &GRPCClient{}
Expand Down Expand Up @@ -423,6 +424,20 @@ func (c *GRPCClient) EmitIssue(rule tflint.Rule, message string, location hcl.Ra
return nil
}

func (c *GRPCClient) EmitIssueWithFix(rule tflint.Rule, message string, location hcl.Range, fixFunc func(f *tflint.Fixer) error) error {
if err := fixFunc(c.Fixer); err != nil {
return err
}

_, err := c.Client.EmitIssue(context.Background(), &proto.EmitIssue_Request{Rule: toproto.Rule(rule), Message: message, Range: toproto.Range(location), Fixed: c.Fixer.Changes()})
if err != nil {
return fromproto.Error(err)
}
// TODO: ApplyChanges() should be called if the issue is not ignored.
c.Fixer.ApplyChanges()
return nil
}

// EnsureNoError is a helper for error handling. Depending on the type of error generated by EvaluateExpr,
// determine whether to exit, skip, or continue. If it is continued, the passed function will be executed.
//
Expand Down
657 changes: 337 additions & 320 deletions plugin/proto/tflint.pb.go

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions plugin/proto/tflint.proto
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ message EmitIssue {
Rule rule = 1;
string message = 2;
Range range = 3;
map<string, bytes> fixed = 4;
}
message Response {}
}
Expand Down
47 changes: 47 additions & 0 deletions tflint/fixer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package tflint

import (
"bytes"

"github.com/hashicorp/hcl/v2"
)

type Fixer struct {
sources map[string][]byte
changes map[string][]byte
}

func NewFixer(sources map[string][]byte) *Fixer {
return &Fixer{
sources: sources,
changes: map[string][]byte{},
}
}

func (f *Fixer) Replace(rng hcl.Range, replaced string) error {
original := f.sources[rng.Filename]
file := make([]byte, len(original))
copy(file, original)

// TODO: track byte index changes
buf := bytes.NewBuffer(file[:rng.Start.Byte])
buf.WriteString(replaced)
buf.Write(file[rng.End.Byte:])

if bytes.Equal(original, buf.Bytes()) {
return nil
}
f.changes[rng.Filename] = buf.Bytes()
return nil
}

func (f *Fixer) Changes() map[string][]byte {
return f.changes
}

func (f *Fixer) ApplyChanges() {
for filename, content := range f.changes {
f.sources[filename] = content
}
f.changes = map[string][]byte{}
}
2 changes: 2 additions & 0 deletions tflint/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ type Runner interface {
// EmitIssue sends an issue to TFLint. You need to pass the message of the issue and the range.
EmitIssue(rule Rule, message string, issueRange hcl.Range) error

EmitIssueWithFix(rule Rule, message string, issueRange hcl.Range, fixFunc func(f *Fixer) error) error

// EnsureNoError is a helper for error handling. Depending on the type of error generated by EvaluateExpr,
// determine whether to exit, skip, or continue. If it is continued, the passed function will be executed.
//
Expand Down

0 comments on commit f4e30ce

Please sign in to comment.