@@ -2,11 +2,14 @@ package tflint
2
2
3
3
import (
4
4
"fmt"
5
+ "io/ioutil"
5
6
"log"
6
7
"net"
7
8
"net/rpc"
9
+ "strings"
8
10
9
11
hcl "github.com/hashicorp/hcl/v2"
12
+ "github.com/hashicorp/hcl/v2/hclsyntax"
10
13
"github.com/zclconf/go-cty/cty"
11
14
"github.com/zclconf/go-cty/cty/gocty"
12
15
)
@@ -30,10 +33,20 @@ type AttributesRequest struct {
30
33
31
34
// AttributesResponse is the interface used to communicate via RPC.
32
35
type AttributesResponse struct {
33
- Attributes []* hcl. Attribute
36
+ Attributes []* Attribute
34
37
Err error
35
38
}
36
39
40
+ // Attribute is an intermediate representation of hcl.Attribute.
41
+ // It has an expression as a string of bytes so that hcl.Expression is not transferred via RPC.
42
+ type Attribute struct {
43
+ Name string
44
+ Expr []byte
45
+ ExprRange hcl.Range
46
+ Range hcl.Range
47
+ NameRange hcl.Range
48
+ }
49
+
37
50
// WalkResourceAttributes queries the host process, receives a list of attributes that match the conditions,
38
51
// and passes each to the walker function.
39
52
func (c * Client ) WalkResourceAttributes (resource , attributeName string , walker func (* hcl.Attribute ) error ) error {
@@ -48,7 +61,18 @@ func (c *Client) WalkResourceAttributes(resource, attributeName string, walker f
48
61
}
49
62
50
63
for _ , attribute := range response .Attributes {
51
- if err := walker (attribute ); err != nil {
64
+ expr , diags := parseExpression (attribute .Expr , attribute .ExprRange .Filename , attribute .ExprRange .Start )
65
+ if diags .HasErrors () {
66
+ return diags
67
+ }
68
+ attr := & hcl.Attribute {
69
+ Name : attribute .Name ,
70
+ Expr : expr ,
71
+ Range : attribute .Range ,
72
+ NameRange : attribute .NameRange ,
73
+ }
74
+
75
+ if err := walker (attr ); err != nil {
52
76
return err
53
77
}
54
78
}
@@ -58,8 +82,9 @@ func (c *Client) WalkResourceAttributes(resource, attributeName string, walker f
58
82
59
83
// EvalExprRequest is the interface used to communicate via RPC.
60
84
type EvalExprRequest struct {
61
- Expr hcl.Expression
62
- Ret interface {}
85
+ Expr []byte
86
+ ExprRange hcl.Range
87
+ Ret interface {}
63
88
}
64
89
65
90
// EvalExprResponse is the interface used to communicate with RPC.
@@ -74,7 +99,16 @@ func (c *Client) EvaluateExpr(expr hcl.Expression, ret interface{}) error {
74
99
var response EvalExprResponse
75
100
var err error
76
101
77
- if err := c .rpcClient .Call ("Plugin.EvalExpr" , EvalExprRequest {Expr : expr , Ret : ret }, & response ); err != nil {
102
+ src , err := ioutil .ReadFile (expr .Range ().Filename )
103
+ if err != nil {
104
+ return err
105
+ }
106
+ req := EvalExprRequest {
107
+ Expr : expr .Range ().SliceBytes (src ),
108
+ ExprRange : expr .Range (),
109
+ Ret : ret ,
110
+ }
111
+ if err := c .rpcClient .Call ("Plugin.EvalExpr" , req , & response ); err != nil {
78
112
return err
79
113
}
80
114
if response .Err != nil {
@@ -101,21 +135,28 @@ func (c *Client) EvaluateExpr(expr hcl.Expression, ret interface{}) error {
101
135
102
136
// EmitIssueRequest is the interface used to communicate via RPC.
103
137
type EmitIssueRequest struct {
104
- Rule * RuleObject
105
- Message string
106
- Location hcl.Range
107
- Meta Metadata
138
+ Rule * RuleObject
139
+ Message string
140
+ Location hcl.Range
141
+ Expr []byte
142
+ ExprRange hcl.Range
108
143
}
109
144
110
145
// EmitIssue emits attributes to build the issue to the host process
111
146
// Note that the passed rule need to be converted to generic objects
112
147
// because the custom structure defined in the plugin cannot be sent via RPC.
113
148
func (c * Client ) EmitIssue (rule Rule , message string , location hcl.Range , meta Metadata ) error {
149
+ src , err := ioutil .ReadFile (meta .Expr .Range ().Filename )
150
+ if err != nil {
151
+ return err
152
+ }
153
+
114
154
req := & EmitIssueRequest {
115
- Rule : newObjectFromRule (rule ),
116
- Message : message ,
117
- Location : location ,
118
- Meta : meta ,
155
+ Rule : newObjectFromRule (rule ),
156
+ Message : message ,
157
+ Location : location ,
158
+ Expr : meta .Expr .Range ().SliceBytes (src ),
159
+ ExprRange : meta .Expr .Range (),
119
160
}
120
161
if err := c .rpcClient .Call ("Plugin.EmitIssue" , & req , new (interface {})); err != nil {
121
162
return err
@@ -143,3 +184,21 @@ func (*Client) EnsureNoError(err error, proc func() error) error {
143
184
return err
144
185
}
145
186
}
187
+
188
+ func parseExpression (src []byte , filename string , start hcl.Pos ) (hcl.Expression , hcl.Diagnostics ) {
189
+ if strings .HasSuffix (filename , ".tf" ) {
190
+ return hclsyntax .ParseExpression (src , filename , start )
191
+ }
192
+
193
+ if strings .HasSuffix (filename , ".tf.json" ) {
194
+ return nil , hcl.Diagnostics {
195
+ & hcl.Diagnostic {
196
+ Severity : hcl .DiagError ,
197
+ Summary : "JSON configuration syntax is not supported" ,
198
+ Subject : & hcl.Range {Filename : filename , Start : start , End : start },
199
+ },
200
+ }
201
+ }
202
+
203
+ panic (fmt .Sprintf ("Unexpected file: %s" , filename ))
204
+ }
0 commit comments