diff options
Diffstat (limited to 'vendor/github.com/hashicorp/hcl2/hcl/hclsyntax/expression.go')
-rw-r--r-- | vendor/github.com/hashicorp/hcl2/hcl/hclsyntax/expression.go | 1275 |
1 files changed, 1275 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/hcl2/hcl/hclsyntax/expression.go b/vendor/github.com/hashicorp/hcl2/hcl/hclsyntax/expression.go new file mode 100644 index 0000000..cfc7cd9 --- /dev/null +++ b/vendor/github.com/hashicorp/hcl2/hcl/hclsyntax/expression.go | |||
@@ -0,0 +1,1275 @@ | |||
1 | package hclsyntax | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | |||
6 | "github.com/hashicorp/hcl2/hcl" | ||
7 | "github.com/zclconf/go-cty/cty" | ||
8 | "github.com/zclconf/go-cty/cty/convert" | ||
9 | "github.com/zclconf/go-cty/cty/function" | ||
10 | ) | ||
11 | |||
12 | // Expression is the abstract type for nodes that behave as HCL expressions. | ||
13 | type Expression interface { | ||
14 | Node | ||
15 | |||
16 | // The hcl.Expression methods are duplicated here, rather than simply | ||
17 | // embedded, because both Node and hcl.Expression have a Range method | ||
18 | // and so they conflict. | ||
19 | |||
20 | Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) | ||
21 | Variables() []hcl.Traversal | ||
22 | StartRange() hcl.Range | ||
23 | } | ||
24 | |||
25 | // Assert that Expression implements hcl.Expression | ||
26 | var assertExprImplExpr hcl.Expression = Expression(nil) | ||
27 | |||
28 | // LiteralValueExpr is an expression that just always returns a given value. | ||
29 | type LiteralValueExpr struct { | ||
30 | Val cty.Value | ||
31 | SrcRange hcl.Range | ||
32 | } | ||
33 | |||
34 | func (e *LiteralValueExpr) walkChildNodes(w internalWalkFunc) { | ||
35 | // Literal values have no child nodes | ||
36 | } | ||
37 | |||
38 | func (e *LiteralValueExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { | ||
39 | return e.Val, nil | ||
40 | } | ||
41 | |||
42 | func (e *LiteralValueExpr) Range() hcl.Range { | ||
43 | return e.SrcRange | ||
44 | } | ||
45 | |||
46 | func (e *LiteralValueExpr) StartRange() hcl.Range { | ||
47 | return e.SrcRange | ||
48 | } | ||
49 | |||
50 | // Implementation for hcl.AbsTraversalForExpr. | ||
51 | func (e *LiteralValueExpr) AsTraversal() hcl.Traversal { | ||
52 | // This one's a little weird: the contract for AsTraversal is to interpret | ||
53 | // an expression as if it were traversal syntax, and traversal syntax | ||
54 | // doesn't have the special keywords "null", "true", and "false" so these | ||
55 | // are expected to be treated like variables in that case. | ||
56 | // Since our parser already turned them into LiteralValueExpr by the time | ||
57 | // we get here, we need to undo this and infer the name that would've | ||
58 | // originally led to our value. | ||
59 | // We don't do anything for any other values, since they don't overlap | ||
60 | // with traversal roots. | ||
61 | |||
62 | if e.Val.IsNull() { | ||
63 | // In practice the parser only generates null values of the dynamic | ||
64 | // pseudo-type for literals, so we can safely assume that any null | ||
65 | // was orignally the keyword "null". | ||
66 | return hcl.Traversal{ | ||
67 | hcl.TraverseRoot{ | ||
68 | Name: "null", | ||
69 | SrcRange: e.SrcRange, | ||
70 | }, | ||
71 | } | ||
72 | } | ||
73 | |||
74 | switch e.Val { | ||
75 | case cty.True: | ||
76 | return hcl.Traversal{ | ||
77 | hcl.TraverseRoot{ | ||
78 | Name: "true", | ||
79 | SrcRange: e.SrcRange, | ||
80 | }, | ||
81 | } | ||
82 | case cty.False: | ||
83 | return hcl.Traversal{ | ||
84 | hcl.TraverseRoot{ | ||
85 | Name: "false", | ||
86 | SrcRange: e.SrcRange, | ||
87 | }, | ||
88 | } | ||
89 | default: | ||
90 | // No traversal is possible for any other value. | ||
91 | return nil | ||
92 | } | ||
93 | } | ||
94 | |||
95 | // ScopeTraversalExpr is an Expression that retrieves a value from the scope | ||
96 | // using a traversal. | ||
97 | type ScopeTraversalExpr struct { | ||
98 | Traversal hcl.Traversal | ||
99 | SrcRange hcl.Range | ||
100 | } | ||
101 | |||
102 | func (e *ScopeTraversalExpr) walkChildNodes(w internalWalkFunc) { | ||
103 | // Scope traversals have no child nodes | ||
104 | } | ||
105 | |||
106 | func (e *ScopeTraversalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { | ||
107 | return e.Traversal.TraverseAbs(ctx) | ||
108 | } | ||
109 | |||
110 | func (e *ScopeTraversalExpr) Range() hcl.Range { | ||
111 | return e.SrcRange | ||
112 | } | ||
113 | |||
114 | func (e *ScopeTraversalExpr) StartRange() hcl.Range { | ||
115 | return e.SrcRange | ||
116 | } | ||
117 | |||
118 | // Implementation for hcl.AbsTraversalForExpr. | ||
119 | func (e *ScopeTraversalExpr) AsTraversal() hcl.Traversal { | ||
120 | return e.Traversal | ||
121 | } | ||
122 | |||
123 | // RelativeTraversalExpr is an Expression that retrieves a value from another | ||
124 | // value using a _relative_ traversal. | ||
125 | type RelativeTraversalExpr struct { | ||
126 | Source Expression | ||
127 | Traversal hcl.Traversal | ||
128 | SrcRange hcl.Range | ||
129 | } | ||
130 | |||
131 | func (e *RelativeTraversalExpr) walkChildNodes(w internalWalkFunc) { | ||
132 | // Scope traversals have no child nodes | ||
133 | } | ||
134 | |||
135 | func (e *RelativeTraversalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { | ||
136 | src, diags := e.Source.Value(ctx) | ||
137 | ret, travDiags := e.Traversal.TraverseRel(src) | ||
138 | diags = append(diags, travDiags...) | ||
139 | return ret, diags | ||
140 | } | ||
141 | |||
142 | func (e *RelativeTraversalExpr) Range() hcl.Range { | ||
143 | return e.SrcRange | ||
144 | } | ||
145 | |||
146 | func (e *RelativeTraversalExpr) StartRange() hcl.Range { | ||
147 | return e.SrcRange | ||
148 | } | ||
149 | |||
150 | // Implementation for hcl.AbsTraversalForExpr. | ||
151 | func (e *RelativeTraversalExpr) AsTraversal() hcl.Traversal { | ||
152 | // We can produce a traversal only if our source can. | ||
153 | st, diags := hcl.AbsTraversalForExpr(e.Source) | ||
154 | if diags.HasErrors() { | ||
155 | return nil | ||
156 | } | ||
157 | |||
158 | ret := make(hcl.Traversal, len(st)+len(e.Traversal)) | ||
159 | copy(ret, st) | ||
160 | copy(ret[len(st):], e.Traversal) | ||
161 | return ret | ||
162 | } | ||
163 | |||
164 | // FunctionCallExpr is an Expression that calls a function from the EvalContext | ||
165 | // and returns its result. | ||
166 | type FunctionCallExpr struct { | ||
167 | Name string | ||
168 | Args []Expression | ||
169 | |||
170 | // If true, the final argument should be a tuple, list or set which will | ||
171 | // expand to be one argument per element. | ||
172 | ExpandFinal bool | ||
173 | |||
174 | NameRange hcl.Range | ||
175 | OpenParenRange hcl.Range | ||
176 | CloseParenRange hcl.Range | ||
177 | } | ||
178 | |||
179 | func (e *FunctionCallExpr) walkChildNodes(w internalWalkFunc) { | ||
180 | for i, arg := range e.Args { | ||
181 | e.Args[i] = w(arg).(Expression) | ||
182 | } | ||
183 | } | ||
184 | |||
185 | func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { | ||
186 | var diags hcl.Diagnostics | ||
187 | |||
188 | var f function.Function | ||
189 | exists := false | ||
190 | hasNonNilMap := false | ||
191 | thisCtx := ctx | ||
192 | for thisCtx != nil { | ||
193 | if thisCtx.Functions == nil { | ||
194 | thisCtx = thisCtx.Parent() | ||
195 | continue | ||
196 | } | ||
197 | hasNonNilMap = true | ||
198 | f, exists = thisCtx.Functions[e.Name] | ||
199 | if exists { | ||
200 | break | ||
201 | } | ||
202 | thisCtx = thisCtx.Parent() | ||
203 | } | ||
204 | |||
205 | if !exists { | ||
206 | if !hasNonNilMap { | ||
207 | return cty.DynamicVal, hcl.Diagnostics{ | ||
208 | { | ||
209 | Severity: hcl.DiagError, | ||
210 | Summary: "Function calls not allowed", | ||
211 | Detail: "Functions may not be called here.", | ||
212 | Subject: e.Range().Ptr(), | ||
213 | }, | ||
214 | } | ||
215 | } | ||
216 | |||
217 | avail := make([]string, 0, len(ctx.Functions)) | ||
218 | for name := range ctx.Functions { | ||
219 | avail = append(avail, name) | ||
220 | } | ||
221 | suggestion := nameSuggestion(e.Name, avail) | ||
222 | if suggestion != "" { | ||
223 | suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) | ||
224 | } | ||
225 | |||
226 | return cty.DynamicVal, hcl.Diagnostics{ | ||
227 | { | ||
228 | Severity: hcl.DiagError, | ||
229 | Summary: "Call to unknown function", | ||
230 | Detail: fmt.Sprintf("There is no function named %q.%s", e.Name, suggestion), | ||
231 | Subject: &e.NameRange, | ||
232 | Context: e.Range().Ptr(), | ||
233 | }, | ||
234 | } | ||
235 | } | ||
236 | |||
237 | params := f.Params() | ||
238 | varParam := f.VarParam() | ||
239 | |||
240 | args := e.Args | ||
241 | if e.ExpandFinal { | ||
242 | if len(args) < 1 { | ||
243 | // should never happen if the parser is behaving | ||
244 | panic("ExpandFinal set on function call with no arguments") | ||
245 | } | ||
246 | expandExpr := args[len(args)-1] | ||
247 | expandVal, expandDiags := expandExpr.Value(ctx) | ||
248 | diags = append(diags, expandDiags...) | ||
249 | if expandDiags.HasErrors() { | ||
250 | return cty.DynamicVal, diags | ||
251 | } | ||
252 | |||
253 | switch { | ||
254 | case expandVal.Type().IsTupleType() || expandVal.Type().IsListType() || expandVal.Type().IsSetType(): | ||
255 | if expandVal.IsNull() { | ||
256 | diags = append(diags, &hcl.Diagnostic{ | ||
257 | Severity: hcl.DiagError, | ||
258 | Summary: "Invalid expanding argument value", | ||
259 | Detail: "The expanding argument (indicated by ...) must not be null.", | ||
260 | Context: expandExpr.Range().Ptr(), | ||
261 | Subject: e.Range().Ptr(), | ||
262 | }) | ||
263 | return cty.DynamicVal, diags | ||
264 | } | ||
265 | if !expandVal.IsKnown() { | ||
266 | return cty.DynamicVal, diags | ||
267 | } | ||
268 | |||
269 | newArgs := make([]Expression, 0, (len(args)-1)+expandVal.LengthInt()) | ||
270 | newArgs = append(newArgs, args[:len(args)-1]...) | ||
271 | it := expandVal.ElementIterator() | ||
272 | for it.Next() { | ||
273 | _, val := it.Element() | ||
274 | newArgs = append(newArgs, &LiteralValueExpr{ | ||
275 | Val: val, | ||
276 | SrcRange: expandExpr.Range(), | ||
277 | }) | ||
278 | } | ||
279 | args = newArgs | ||
280 | default: | ||
281 | diags = append(diags, &hcl.Diagnostic{ | ||
282 | Severity: hcl.DiagError, | ||
283 | Summary: "Invalid expanding argument value", | ||
284 | Detail: "The expanding argument (indicated by ...) must be of a tuple, list, or set type.", | ||
285 | Context: expandExpr.Range().Ptr(), | ||
286 | Subject: e.Range().Ptr(), | ||
287 | }) | ||
288 | return cty.DynamicVal, diags | ||
289 | } | ||
290 | } | ||
291 | |||
292 | if len(args) < len(params) { | ||
293 | missing := params[len(args)] | ||
294 | qual := "" | ||
295 | if varParam != nil { | ||
296 | qual = " at least" | ||
297 | } | ||
298 | return cty.DynamicVal, hcl.Diagnostics{ | ||
299 | { | ||
300 | Severity: hcl.DiagError, | ||
301 | Summary: "Not enough function arguments", | ||
302 | Detail: fmt.Sprintf( | ||
303 | "Function %q expects%s %d argument(s). Missing value for %q.", | ||
304 | e.Name, qual, len(params), missing.Name, | ||
305 | ), | ||
306 | Subject: &e.CloseParenRange, | ||
307 | Context: e.Range().Ptr(), | ||
308 | }, | ||
309 | } | ||
310 | } | ||
311 | |||
312 | if varParam == nil && len(args) > len(params) { | ||
313 | return cty.DynamicVal, hcl.Diagnostics{ | ||
314 | { | ||
315 | Severity: hcl.DiagError, | ||
316 | Summary: "Too many function arguments", | ||
317 | Detail: fmt.Sprintf( | ||
318 | "Function %q expects only %d argument(s).", | ||
319 | e.Name, len(params), | ||
320 | ), | ||
321 | Subject: args[len(params)].StartRange().Ptr(), | ||
322 | Context: e.Range().Ptr(), | ||
323 | }, | ||
324 | } | ||
325 | } | ||
326 | |||
327 | argVals := make([]cty.Value, len(args)) | ||
328 | |||
329 | for i, argExpr := range args { | ||
330 | var param *function.Parameter | ||
331 | if i < len(params) { | ||
332 | param = ¶ms[i] | ||
333 | } else { | ||
334 | param = varParam | ||
335 | } | ||
336 | |||
337 | val, argDiags := argExpr.Value(ctx) | ||
338 | if len(argDiags) > 0 { | ||
339 | diags = append(diags, argDiags...) | ||
340 | } | ||
341 | |||
342 | // Try to convert our value to the parameter type | ||
343 | val, err := convert.Convert(val, param.Type) | ||
344 | if err != nil { | ||
345 | diags = append(diags, &hcl.Diagnostic{ | ||
346 | Severity: hcl.DiagError, | ||
347 | Summary: "Invalid function argument", | ||
348 | Detail: fmt.Sprintf( | ||
349 | "Invalid value for %q parameter: %s.", | ||
350 | param.Name, err, | ||
351 | ), | ||
352 | Subject: argExpr.StartRange().Ptr(), | ||
353 | Context: e.Range().Ptr(), | ||
354 | }) | ||
355 | } | ||
356 | |||
357 | argVals[i] = val | ||
358 | } | ||
359 | |||
360 | if diags.HasErrors() { | ||
361 | // Don't try to execute the function if we already have errors with | ||
362 | // the arguments, because the result will probably be a confusing | ||
363 | // error message. | ||
364 | return cty.DynamicVal, diags | ||
365 | } | ||
366 | |||
367 | resultVal, err := f.Call(argVals) | ||
368 | if err != nil { | ||
369 | switch terr := err.(type) { | ||
370 | case function.ArgError: | ||
371 | i := terr.Index | ||
372 | var param *function.Parameter | ||
373 | if i < len(params) { | ||
374 | param = ¶ms[i] | ||
375 | } else { | ||
376 | param = varParam | ||
377 | } | ||
378 | argExpr := e.Args[i] | ||
379 | |||
380 | // TODO: we should also unpick a PathError here and show the | ||
381 | // path to the deep value where the error was detected. | ||
382 | diags = append(diags, &hcl.Diagnostic{ | ||
383 | Severity: hcl.DiagError, | ||
384 | Summary: "Invalid function argument", | ||
385 | Detail: fmt.Sprintf( | ||
386 | "Invalid value for %q parameter: %s.", | ||
387 | param.Name, err, | ||
388 | ), | ||
389 | Subject: argExpr.StartRange().Ptr(), | ||
390 | Context: e.Range().Ptr(), | ||
391 | }) | ||
392 | |||
393 | default: | ||
394 | diags = append(diags, &hcl.Diagnostic{ | ||
395 | Severity: hcl.DiagError, | ||
396 | Summary: "Error in function call", | ||
397 | Detail: fmt.Sprintf( | ||
398 | "Call to function %q failed: %s.", | ||
399 | e.Name, err, | ||
400 | ), | ||
401 | Subject: e.StartRange().Ptr(), | ||
402 | Context: e.Range().Ptr(), | ||
403 | }) | ||
404 | } | ||
405 | |||
406 | return cty.DynamicVal, diags | ||
407 | } | ||
408 | |||
409 | return resultVal, diags | ||
410 | } | ||
411 | |||
412 | func (e *FunctionCallExpr) Range() hcl.Range { | ||
413 | return hcl.RangeBetween(e.NameRange, e.CloseParenRange) | ||
414 | } | ||
415 | |||
416 | func (e *FunctionCallExpr) StartRange() hcl.Range { | ||
417 | return hcl.RangeBetween(e.NameRange, e.OpenParenRange) | ||
418 | } | ||
419 | |||
420 | // Implementation for hcl.ExprCall. | ||
421 | func (e *FunctionCallExpr) ExprCall() *hcl.StaticCall { | ||
422 | ret := &hcl.StaticCall{ | ||
423 | Name: e.Name, | ||
424 | NameRange: e.NameRange, | ||
425 | Arguments: make([]hcl.Expression, len(e.Args)), | ||
426 | ArgsRange: hcl.RangeBetween(e.OpenParenRange, e.CloseParenRange), | ||
427 | } | ||
428 | // Need to convert our own Expression objects into hcl.Expression. | ||
429 | for i, arg := range e.Args { | ||
430 | ret.Arguments[i] = arg | ||
431 | } | ||
432 | return ret | ||
433 | } | ||
434 | |||
435 | type ConditionalExpr struct { | ||
436 | Condition Expression | ||
437 | TrueResult Expression | ||
438 | FalseResult Expression | ||
439 | |||
440 | SrcRange hcl.Range | ||
441 | } | ||
442 | |||
443 | func (e *ConditionalExpr) walkChildNodes(w internalWalkFunc) { | ||
444 | e.Condition = w(e.Condition).(Expression) | ||
445 | e.TrueResult = w(e.TrueResult).(Expression) | ||
446 | e.FalseResult = w(e.FalseResult).(Expression) | ||
447 | } | ||
448 | |||
449 | func (e *ConditionalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { | ||
450 | trueResult, trueDiags := e.TrueResult.Value(ctx) | ||
451 | falseResult, falseDiags := e.FalseResult.Value(ctx) | ||
452 | var diags hcl.Diagnostics | ||
453 | |||
454 | // Try to find a type that both results can be converted to. | ||
455 | resultType, convs := convert.UnifyUnsafe([]cty.Type{trueResult.Type(), falseResult.Type()}) | ||
456 | if resultType == cty.NilType { | ||
457 | return cty.DynamicVal, hcl.Diagnostics{ | ||
458 | { | ||
459 | Severity: hcl.DiagError, | ||
460 | Summary: "Inconsistent conditional result types", | ||
461 | Detail: fmt.Sprintf( | ||
462 | // FIXME: Need a helper function for showing natural-language type diffs, | ||
463 | // since this will generate some useless messages in some cases, like | ||
464 | // "These expressions are object and object respectively" if the | ||
465 | // object types don't exactly match. | ||
466 | "The true and false result expressions must have consistent types. The given expressions are %s and %s, respectively.", | ||
467 | trueResult.Type(), falseResult.Type(), | ||
468 | ), | ||
469 | Subject: hcl.RangeBetween(e.TrueResult.Range(), e.FalseResult.Range()).Ptr(), | ||
470 | Context: &e.SrcRange, | ||
471 | }, | ||
472 | } | ||
473 | } | ||
474 | |||
475 | condResult, condDiags := e.Condition.Value(ctx) | ||
476 | diags = append(diags, condDiags...) | ||
477 | if condResult.IsNull() { | ||
478 | diags = append(diags, &hcl.Diagnostic{ | ||
479 | Severity: hcl.DiagError, | ||
480 | Summary: "Null condition", | ||
481 | Detail: "The condition value is null. Conditions must either be true or false.", | ||
482 | Subject: e.Condition.Range().Ptr(), | ||
483 | Context: &e.SrcRange, | ||
484 | }) | ||
485 | return cty.UnknownVal(resultType), diags | ||
486 | } | ||
487 | if !condResult.IsKnown() { | ||
488 | return cty.UnknownVal(resultType), diags | ||
489 | } | ||
490 | condResult, err := convert.Convert(condResult, cty.Bool) | ||
491 | if err != nil { | ||
492 | diags = append(diags, &hcl.Diagnostic{ | ||
493 | Severity: hcl.DiagError, | ||
494 | Summary: "Incorrect condition type", | ||
495 | Detail: fmt.Sprintf("The condition expression must be of type bool."), | ||
496 | Subject: e.Condition.Range().Ptr(), | ||
497 | Context: &e.SrcRange, | ||
498 | }) | ||
499 | return cty.UnknownVal(resultType), diags | ||
500 | } | ||
501 | |||
502 | if condResult.True() { | ||
503 | diags = append(diags, trueDiags...) | ||
504 | if convs[0] != nil { | ||
505 | var err error | ||
506 | trueResult, err = convs[0](trueResult) | ||
507 | if err != nil { | ||
508 | // Unsafe conversion failed with the concrete result value | ||
509 | diags = append(diags, &hcl.Diagnostic{ | ||
510 | Severity: hcl.DiagError, | ||
511 | Summary: "Inconsistent conditional result types", | ||
512 | Detail: fmt.Sprintf( | ||
513 | "The true result value has the wrong type: %s.", | ||
514 | err.Error(), | ||
515 | ), | ||
516 | Subject: e.TrueResult.Range().Ptr(), | ||
517 | Context: &e.SrcRange, | ||
518 | }) | ||
519 | trueResult = cty.UnknownVal(resultType) | ||
520 | } | ||
521 | } | ||
522 | return trueResult, diags | ||
523 | } else { | ||
524 | diags = append(diags, falseDiags...) | ||
525 | if convs[1] != nil { | ||
526 | var err error | ||
527 | falseResult, err = convs[1](falseResult) | ||
528 | if err != nil { | ||
529 | // Unsafe conversion failed with the concrete result value | ||
530 | diags = append(diags, &hcl.Diagnostic{ | ||
531 | Severity: hcl.DiagError, | ||
532 | Summary: "Inconsistent conditional result types", | ||
533 | Detail: fmt.Sprintf( | ||
534 | "The false result value has the wrong type: %s.", | ||
535 | err.Error(), | ||
536 | ), | ||
537 | Subject: e.TrueResult.Range().Ptr(), | ||
538 | Context: &e.SrcRange, | ||
539 | }) | ||
540 | falseResult = cty.UnknownVal(resultType) | ||
541 | } | ||
542 | } | ||
543 | return falseResult, diags | ||
544 | } | ||
545 | } | ||
546 | |||
547 | func (e *ConditionalExpr) Range() hcl.Range { | ||
548 | return e.SrcRange | ||
549 | } | ||
550 | |||
551 | func (e *ConditionalExpr) StartRange() hcl.Range { | ||
552 | return e.Condition.StartRange() | ||
553 | } | ||
554 | |||
555 | type IndexExpr struct { | ||
556 | Collection Expression | ||
557 | Key Expression | ||
558 | |||
559 | SrcRange hcl.Range | ||
560 | OpenRange hcl.Range | ||
561 | } | ||
562 | |||
563 | func (e *IndexExpr) walkChildNodes(w internalWalkFunc) { | ||
564 | e.Collection = w(e.Collection).(Expression) | ||
565 | e.Key = w(e.Key).(Expression) | ||
566 | } | ||
567 | |||
568 | func (e *IndexExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { | ||
569 | var diags hcl.Diagnostics | ||
570 | coll, collDiags := e.Collection.Value(ctx) | ||
571 | key, keyDiags := e.Key.Value(ctx) | ||
572 | diags = append(diags, collDiags...) | ||
573 | diags = append(diags, keyDiags...) | ||
574 | |||
575 | return hcl.Index(coll, key, &e.SrcRange) | ||
576 | } | ||
577 | |||
578 | func (e *IndexExpr) Range() hcl.Range { | ||
579 | return e.SrcRange | ||
580 | } | ||
581 | |||
582 | func (e *IndexExpr) StartRange() hcl.Range { | ||
583 | return e.OpenRange | ||
584 | } | ||
585 | |||
586 | type TupleConsExpr struct { | ||
587 | Exprs []Expression | ||
588 | |||
589 | SrcRange hcl.Range | ||
590 | OpenRange hcl.Range | ||
591 | } | ||
592 | |||
593 | func (e *TupleConsExpr) walkChildNodes(w internalWalkFunc) { | ||
594 | for i, expr := range e.Exprs { | ||
595 | e.Exprs[i] = w(expr).(Expression) | ||
596 | } | ||
597 | } | ||
598 | |||
599 | func (e *TupleConsExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { | ||
600 | var vals []cty.Value | ||
601 | var diags hcl.Diagnostics | ||
602 | |||
603 | vals = make([]cty.Value, len(e.Exprs)) | ||
604 | for i, expr := range e.Exprs { | ||
605 | val, valDiags := expr.Value(ctx) | ||
606 | vals[i] = val | ||
607 | diags = append(diags, valDiags...) | ||
608 | } | ||
609 | |||
610 | return cty.TupleVal(vals), diags | ||
611 | } | ||
612 | |||
613 | func (e *TupleConsExpr) Range() hcl.Range { | ||
614 | return e.SrcRange | ||
615 | } | ||
616 | |||
617 | func (e *TupleConsExpr) StartRange() hcl.Range { | ||
618 | return e.OpenRange | ||
619 | } | ||
620 | |||
621 | // Implementation for hcl.ExprList | ||
622 | func (e *TupleConsExpr) ExprList() []hcl.Expression { | ||
623 | ret := make([]hcl.Expression, len(e.Exprs)) | ||
624 | for i, expr := range e.Exprs { | ||
625 | ret[i] = expr | ||
626 | } | ||
627 | return ret | ||
628 | } | ||
629 | |||
630 | type ObjectConsExpr struct { | ||
631 | Items []ObjectConsItem | ||
632 | |||
633 | SrcRange hcl.Range | ||
634 | OpenRange hcl.Range | ||
635 | } | ||
636 | |||
637 | type ObjectConsItem struct { | ||
638 | KeyExpr Expression | ||
639 | ValueExpr Expression | ||
640 | } | ||
641 | |||
642 | func (e *ObjectConsExpr) walkChildNodes(w internalWalkFunc) { | ||
643 | for i, item := range e.Items { | ||
644 | e.Items[i].KeyExpr = w(item.KeyExpr).(Expression) | ||
645 | e.Items[i].ValueExpr = w(item.ValueExpr).(Expression) | ||
646 | } | ||
647 | } | ||
648 | |||
649 | func (e *ObjectConsExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { | ||
650 | var vals map[string]cty.Value | ||
651 | var diags hcl.Diagnostics | ||
652 | |||
653 | // This will get set to true if we fail to produce any of our keys, | ||
654 | // either because they are actually unknown or if the evaluation produces | ||
655 | // errors. In all of these case we must return DynamicPseudoType because | ||
656 | // we're unable to know the full set of keys our object has, and thus | ||
657 | // we can't produce a complete value of the intended type. | ||
658 | // | ||
659 | // We still evaluate all of the item keys and values to make sure that we | ||
660 | // get as complete as possible a set of diagnostics. | ||
661 | known := true | ||
662 | |||
663 | vals = make(map[string]cty.Value, len(e.Items)) | ||
664 | for _, item := range e.Items { | ||
665 | key, keyDiags := item.KeyExpr.Value(ctx) | ||
666 | diags = append(diags, keyDiags...) | ||
667 | |||
668 | val, valDiags := item.ValueExpr.Value(ctx) | ||
669 | diags = append(diags, valDiags...) | ||
670 | |||
671 | if keyDiags.HasErrors() { | ||
672 | known = false | ||
673 | continue | ||
674 | } | ||
675 | |||
676 | if key.IsNull() { | ||
677 | diags = append(diags, &hcl.Diagnostic{ | ||
678 | Severity: hcl.DiagError, | ||
679 | Summary: "Null value as key", | ||
680 | Detail: "Can't use a null value as a key.", | ||
681 | Subject: item.ValueExpr.Range().Ptr(), | ||
682 | }) | ||
683 | known = false | ||
684 | continue | ||
685 | } | ||
686 | |||
687 | var err error | ||
688 | key, err = convert.Convert(key, cty.String) | ||
689 | if err != nil { | ||
690 | diags = append(diags, &hcl.Diagnostic{ | ||
691 | Severity: hcl.DiagError, | ||
692 | Summary: "Incorrect key type", | ||
693 | Detail: fmt.Sprintf("Can't use this value as a key: %s.", err.Error()), | ||
694 | Subject: item.ValueExpr.Range().Ptr(), | ||
695 | }) | ||
696 | known = false | ||
697 | continue | ||
698 | } | ||
699 | |||
700 | if !key.IsKnown() { | ||
701 | known = false | ||
702 | continue | ||
703 | } | ||
704 | |||
705 | keyStr := key.AsString() | ||
706 | |||
707 | vals[keyStr] = val | ||
708 | } | ||
709 | |||
710 | if !known { | ||
711 | return cty.DynamicVal, diags | ||
712 | } | ||
713 | |||
714 | return cty.ObjectVal(vals), diags | ||
715 | } | ||
716 | |||
717 | func (e *ObjectConsExpr) Range() hcl.Range { | ||
718 | return e.SrcRange | ||
719 | } | ||
720 | |||
721 | func (e *ObjectConsExpr) StartRange() hcl.Range { | ||
722 | return e.OpenRange | ||
723 | } | ||
724 | |||
725 | // Implementation for hcl.ExprMap | ||
726 | func (e *ObjectConsExpr) ExprMap() []hcl.KeyValuePair { | ||
727 | ret := make([]hcl.KeyValuePair, len(e.Items)) | ||
728 | for i, item := range e.Items { | ||
729 | ret[i] = hcl.KeyValuePair{ | ||
730 | Key: item.KeyExpr, | ||
731 | Value: item.ValueExpr, | ||
732 | } | ||
733 | } | ||
734 | return ret | ||
735 | } | ||
736 | |||
737 | // ObjectConsKeyExpr is a special wrapper used only for ObjectConsExpr keys, | ||
738 | // which deals with the special case that a naked identifier in that position | ||
739 | // must be interpreted as a literal string rather than evaluated directly. | ||
740 | type ObjectConsKeyExpr struct { | ||
741 | Wrapped Expression | ||
742 | } | ||
743 | |||
744 | func (e *ObjectConsKeyExpr) literalName() string { | ||
745 | // This is our logic for deciding whether to behave like a literal string. | ||
746 | // We lean on our AbsTraversalForExpr implementation here, which already | ||
747 | // deals with some awkward cases like the expression being the result | ||
748 | // of the keywords "null", "true" and "false" which we'd want to interpret | ||
749 | // as keys here too. | ||
750 | return hcl.ExprAsKeyword(e.Wrapped) | ||
751 | } | ||
752 | |||
753 | func (e *ObjectConsKeyExpr) walkChildNodes(w internalWalkFunc) { | ||
754 | // We only treat our wrapped expression as a real expression if we're | ||
755 | // not going to interpret it as a literal. | ||
756 | if e.literalName() == "" { | ||
757 | e.Wrapped = w(e.Wrapped).(Expression) | ||
758 | } | ||
759 | } | ||
760 | |||
761 | func (e *ObjectConsKeyExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { | ||
762 | if ln := e.literalName(); ln != "" { | ||
763 | return cty.StringVal(ln), nil | ||
764 | } | ||
765 | return e.Wrapped.Value(ctx) | ||
766 | } | ||
767 | |||
768 | func (e *ObjectConsKeyExpr) Range() hcl.Range { | ||
769 | return e.Wrapped.Range() | ||
770 | } | ||
771 | |||
772 | func (e *ObjectConsKeyExpr) StartRange() hcl.Range { | ||
773 | return e.Wrapped.StartRange() | ||
774 | } | ||
775 | |||
776 | // Implementation for hcl.AbsTraversalForExpr. | ||
777 | func (e *ObjectConsKeyExpr) AsTraversal() hcl.Traversal { | ||
778 | // We can produce a traversal only if our wrappee can. | ||
779 | st, diags := hcl.AbsTraversalForExpr(e.Wrapped) | ||
780 | if diags.HasErrors() { | ||
781 | return nil | ||
782 | } | ||
783 | |||
784 | return st | ||
785 | } | ||
786 | |||
787 | func (e *ObjectConsKeyExpr) UnwrapExpression() Expression { | ||
788 | return e.Wrapped | ||
789 | } | ||
790 | |||
791 | // ForExpr represents iteration constructs: | ||
792 | // | ||
793 | // tuple = [for i, v in list: upper(v) if i > 2] | ||
794 | // object = {for k, v in map: k => upper(v)} | ||
795 | // object_of_tuples = {for v in list: v.key: v...} | ||
796 | type ForExpr struct { | ||
797 | KeyVar string // empty if ignoring the key | ||
798 | ValVar string | ||
799 | |||
800 | CollExpr Expression | ||
801 | |||
802 | KeyExpr Expression // nil when producing a tuple | ||
803 | ValExpr Expression | ||
804 | CondExpr Expression // null if no "if" clause is present | ||
805 | |||
806 | Group bool // set if the ellipsis is used on the value in an object for | ||
807 | |||
808 | SrcRange hcl.Range | ||
809 | OpenRange hcl.Range | ||
810 | CloseRange hcl.Range | ||
811 | } | ||
812 | |||
813 | func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { | ||
814 | var diags hcl.Diagnostics | ||
815 | |||
816 | collVal, collDiags := e.CollExpr.Value(ctx) | ||
817 | diags = append(diags, collDiags...) | ||
818 | |||
819 | if collVal.IsNull() { | ||
820 | diags = append(diags, &hcl.Diagnostic{ | ||
821 | Severity: hcl.DiagError, | ||
822 | Summary: "Iteration over null value", | ||
823 | Detail: "A null value cannot be used as the collection in a 'for' expression.", | ||
824 | Subject: e.CollExpr.Range().Ptr(), | ||
825 | Context: &e.SrcRange, | ||
826 | }) | ||
827 | return cty.DynamicVal, diags | ||
828 | } | ||
829 | if collVal.Type() == cty.DynamicPseudoType { | ||
830 | return cty.DynamicVal, diags | ||
831 | } | ||
832 | if !collVal.CanIterateElements() { | ||
833 | diags = append(diags, &hcl.Diagnostic{ | ||
834 | Severity: hcl.DiagError, | ||
835 | Summary: "Iteration over non-iterable value", | ||
836 | Detail: fmt.Sprintf( | ||
837 | "A value of type %s cannot be used as the collection in a 'for' expression.", | ||
838 | collVal.Type().FriendlyName(), | ||
839 | ), | ||
840 | Subject: e.CollExpr.Range().Ptr(), | ||
841 | Context: &e.SrcRange, | ||
842 | }) | ||
843 | return cty.DynamicVal, diags | ||
844 | } | ||
845 | if !collVal.IsKnown() { | ||
846 | return cty.DynamicVal, diags | ||
847 | } | ||
848 | |||
849 | childCtx := ctx.NewChild() | ||
850 | childCtx.Variables = map[string]cty.Value{} | ||
851 | |||
852 | // Before we start we'll do an early check to see if any CondExpr we've | ||
853 | // been given is of the wrong type. This isn't 100% reliable (it may | ||
854 | // be DynamicVal until real values are given) but it should catch some | ||
855 | // straightforward cases and prevent a barrage of repeated errors. | ||
856 | if e.CondExpr != nil { | ||
857 | if e.KeyVar != "" { | ||
858 | childCtx.Variables[e.KeyVar] = cty.DynamicVal | ||
859 | } | ||
860 | childCtx.Variables[e.ValVar] = cty.DynamicVal | ||
861 | |||
862 | result, condDiags := e.CondExpr.Value(childCtx) | ||
863 | diags = append(diags, condDiags...) | ||
864 | if result.IsNull() { | ||
865 | diags = append(diags, &hcl.Diagnostic{ | ||
866 | Severity: hcl.DiagError, | ||
867 | Summary: "Condition is null", | ||
868 | Detail: "The value of the 'if' clause must not be null.", | ||
869 | Subject: e.CondExpr.Range().Ptr(), | ||
870 | Context: &e.SrcRange, | ||
871 | }) | ||
872 | return cty.DynamicVal, diags | ||
873 | } | ||
874 | _, err := convert.Convert(result, cty.Bool) | ||
875 | if err != nil { | ||
876 | diags = append(diags, &hcl.Diagnostic{ | ||
877 | Severity: hcl.DiagError, | ||
878 | Summary: "Invalid 'for' condition", | ||
879 | Detail: fmt.Sprintf("The 'if' clause value is invalid: %s.", err.Error()), | ||
880 | Subject: e.CondExpr.Range().Ptr(), | ||
881 | Context: &e.SrcRange, | ||
882 | }) | ||
883 | return cty.DynamicVal, diags | ||
884 | } | ||
885 | if condDiags.HasErrors() { | ||
886 | return cty.DynamicVal, diags | ||
887 | } | ||
888 | } | ||
889 | |||
890 | if e.KeyExpr != nil { | ||
891 | // Producing an object | ||
892 | var vals map[string]cty.Value | ||
893 | var groupVals map[string][]cty.Value | ||
894 | if e.Group { | ||
895 | groupVals = map[string][]cty.Value{} | ||
896 | } else { | ||
897 | vals = map[string]cty.Value{} | ||
898 | } | ||
899 | |||
900 | it := collVal.ElementIterator() | ||
901 | |||
902 | known := true | ||
903 | for it.Next() { | ||
904 | k, v := it.Element() | ||
905 | if e.KeyVar != "" { | ||
906 | childCtx.Variables[e.KeyVar] = k | ||
907 | } | ||
908 | childCtx.Variables[e.ValVar] = v | ||
909 | |||
910 | if e.CondExpr != nil { | ||
911 | includeRaw, condDiags := e.CondExpr.Value(childCtx) | ||
912 | diags = append(diags, condDiags...) | ||
913 | if includeRaw.IsNull() { | ||
914 | if known { | ||
915 | diags = append(diags, &hcl.Diagnostic{ | ||
916 | Severity: hcl.DiagError, | ||
917 | Summary: "Condition is null", | ||
918 | Detail: "The value of the 'if' clause must not be null.", | ||
919 | Subject: e.CondExpr.Range().Ptr(), | ||
920 | Context: &e.SrcRange, | ||
921 | }) | ||
922 | } | ||
923 | known = false | ||
924 | continue | ||
925 | } | ||
926 | include, err := convert.Convert(includeRaw, cty.Bool) | ||
927 | if err != nil { | ||
928 | if known { | ||
929 | diags = append(diags, &hcl.Diagnostic{ | ||
930 | Severity: hcl.DiagError, | ||
931 | Summary: "Invalid 'for' condition", | ||
932 | Detail: fmt.Sprintf("The 'if' clause value is invalid: %s.", err.Error()), | ||
933 | Subject: e.CondExpr.Range().Ptr(), | ||
934 | Context: &e.SrcRange, | ||
935 | }) | ||
936 | } | ||
937 | known = false | ||
938 | continue | ||
939 | } | ||
940 | if !include.IsKnown() { | ||
941 | known = false | ||
942 | continue | ||
943 | } | ||
944 | |||
945 | if include.False() { | ||
946 | // Skip this element | ||
947 | continue | ||
948 | } | ||
949 | } | ||
950 | |||
951 | keyRaw, keyDiags := e.KeyExpr.Value(childCtx) | ||
952 | diags = append(diags, keyDiags...) | ||
953 | if keyRaw.IsNull() { | ||
954 | if known { | ||
955 | diags = append(diags, &hcl.Diagnostic{ | ||
956 | Severity: hcl.DiagError, | ||
957 | Summary: "Invalid object key", | ||
958 | Detail: "Key expression in 'for' expression must not produce a null value.", | ||
959 | Subject: e.KeyExpr.Range().Ptr(), | ||
960 | Context: &e.SrcRange, | ||
961 | }) | ||
962 | } | ||
963 | known = false | ||
964 | continue | ||
965 | } | ||
966 | if !keyRaw.IsKnown() { | ||
967 | known = false | ||
968 | continue | ||
969 | } | ||
970 | |||
971 | key, err := convert.Convert(keyRaw, cty.String) | ||
972 | if err != nil { | ||
973 | if known { | ||
974 | diags = append(diags, &hcl.Diagnostic{ | ||
975 | Severity: hcl.DiagError, | ||
976 | Summary: "Invalid object key", | ||
977 | Detail: fmt.Sprintf("The key expression produced an invalid result: %s.", err.Error()), | ||
978 | Subject: e.KeyExpr.Range().Ptr(), | ||
979 | Context: &e.SrcRange, | ||
980 | }) | ||
981 | } | ||
982 | known = false | ||
983 | continue | ||
984 | } | ||
985 | |||
986 | val, valDiags := e.ValExpr.Value(childCtx) | ||
987 | diags = append(diags, valDiags...) | ||
988 | |||
989 | if e.Group { | ||
990 | k := key.AsString() | ||
991 | groupVals[k] = append(groupVals[k], val) | ||
992 | } else { | ||
993 | k := key.AsString() | ||
994 | if _, exists := vals[k]; exists { | ||
995 | diags = append(diags, &hcl.Diagnostic{ | ||
996 | Severity: hcl.DiagError, | ||
997 | Summary: "Duplicate object key", | ||
998 | Detail: fmt.Sprintf( | ||
999 | "Two different items produced the key %q in this for expression. If duplicates are expected, use the ellipsis (...) after the value expression to enable grouping by key.", | ||
1000 | k, | ||
1001 | ), | ||
1002 | Subject: e.KeyExpr.Range().Ptr(), | ||
1003 | Context: &e.SrcRange, | ||
1004 | }) | ||
1005 | } else { | ||
1006 | vals[key.AsString()] = val | ||
1007 | } | ||
1008 | } | ||
1009 | } | ||
1010 | |||
1011 | if !known { | ||
1012 | return cty.DynamicVal, diags | ||
1013 | } | ||
1014 | |||
1015 | if e.Group { | ||
1016 | vals = map[string]cty.Value{} | ||
1017 | for k, gvs := range groupVals { | ||
1018 | vals[k] = cty.TupleVal(gvs) | ||
1019 | } | ||
1020 | } | ||
1021 | |||
1022 | return cty.ObjectVal(vals), diags | ||
1023 | |||
1024 | } else { | ||
1025 | // Producing a tuple | ||
1026 | vals := []cty.Value{} | ||
1027 | |||
1028 | it := collVal.ElementIterator() | ||
1029 | |||
1030 | known := true | ||
1031 | for it.Next() { | ||
1032 | k, v := it.Element() | ||
1033 | if e.KeyVar != "" { | ||
1034 | childCtx.Variables[e.KeyVar] = k | ||
1035 | } | ||
1036 | childCtx.Variables[e.ValVar] = v | ||
1037 | |||
1038 | if e.CondExpr != nil { | ||
1039 | includeRaw, condDiags := e.CondExpr.Value(childCtx) | ||
1040 | diags = append(diags, condDiags...) | ||
1041 | if includeRaw.IsNull() { | ||
1042 | if known { | ||
1043 | diags = append(diags, &hcl.Diagnostic{ | ||
1044 | Severity: hcl.DiagError, | ||
1045 | Summary: "Condition is null", | ||
1046 | Detail: "The value of the 'if' clause must not be null.", | ||
1047 | Subject: e.CondExpr.Range().Ptr(), | ||
1048 | Context: &e.SrcRange, | ||
1049 | }) | ||
1050 | } | ||
1051 | known = false | ||
1052 | continue | ||
1053 | } | ||
1054 | if !includeRaw.IsKnown() { | ||
1055 | // We will eventually return DynamicVal, but we'll continue | ||
1056 | // iterating in case there are other diagnostics to gather | ||
1057 | // for later elements. | ||
1058 | known = false | ||
1059 | continue | ||
1060 | } | ||
1061 | |||
1062 | include, err := convert.Convert(includeRaw, cty.Bool) | ||
1063 | if err != nil { | ||
1064 | if known { | ||
1065 | diags = append(diags, &hcl.Diagnostic{ | ||
1066 | Severity: hcl.DiagError, | ||
1067 | Summary: "Invalid 'for' condition", | ||
1068 | Detail: fmt.Sprintf("The 'if' clause value is invalid: %s.", err.Error()), | ||
1069 | Subject: e.CondExpr.Range().Ptr(), | ||
1070 | Context: &e.SrcRange, | ||
1071 | }) | ||
1072 | } | ||
1073 | known = false | ||
1074 | continue | ||
1075 | } | ||
1076 | |||
1077 | if include.False() { | ||
1078 | // Skip this element | ||
1079 | continue | ||
1080 | } | ||
1081 | } | ||
1082 | |||
1083 | val, valDiags := e.ValExpr.Value(childCtx) | ||
1084 | diags = append(diags, valDiags...) | ||
1085 | vals = append(vals, val) | ||
1086 | } | ||
1087 | |||
1088 | if !known { | ||
1089 | return cty.DynamicVal, diags | ||
1090 | } | ||
1091 | |||
1092 | return cty.TupleVal(vals), diags | ||
1093 | } | ||
1094 | } | ||
1095 | |||
1096 | func (e *ForExpr) walkChildNodes(w internalWalkFunc) { | ||
1097 | e.CollExpr = w(e.CollExpr).(Expression) | ||
1098 | |||
1099 | scopeNames := map[string]struct{}{} | ||
1100 | if e.KeyVar != "" { | ||
1101 | scopeNames[e.KeyVar] = struct{}{} | ||
1102 | } | ||
1103 | if e.ValVar != "" { | ||
1104 | scopeNames[e.ValVar] = struct{}{} | ||
1105 | } | ||
1106 | |||
1107 | if e.KeyExpr != nil { | ||
1108 | w(ChildScope{ | ||
1109 | LocalNames: scopeNames, | ||
1110 | Expr: &e.KeyExpr, | ||
1111 | }) | ||
1112 | } | ||
1113 | w(ChildScope{ | ||
1114 | LocalNames: scopeNames, | ||
1115 | Expr: &e.ValExpr, | ||
1116 | }) | ||
1117 | if e.CondExpr != nil { | ||
1118 | w(ChildScope{ | ||
1119 | LocalNames: scopeNames, | ||
1120 | Expr: &e.CondExpr, | ||
1121 | }) | ||
1122 | } | ||
1123 | } | ||
1124 | |||
1125 | func (e *ForExpr) Range() hcl.Range { | ||
1126 | return e.SrcRange | ||
1127 | } | ||
1128 | |||
1129 | func (e *ForExpr) StartRange() hcl.Range { | ||
1130 | return e.OpenRange | ||
1131 | } | ||
1132 | |||
1133 | type SplatExpr struct { | ||
1134 | Source Expression | ||
1135 | Each Expression | ||
1136 | Item *AnonSymbolExpr | ||
1137 | |||
1138 | SrcRange hcl.Range | ||
1139 | MarkerRange hcl.Range | ||
1140 | } | ||
1141 | |||
1142 | func (e *SplatExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { | ||
1143 | sourceVal, diags := e.Source.Value(ctx) | ||
1144 | if diags.HasErrors() { | ||
1145 | // We'll evaluate our "Each" expression here just to see if it | ||
1146 | // produces any more diagnostics we can report. Since we're not | ||
1147 | // assigning a value to our AnonSymbolExpr here it will return | ||
1148 | // DynamicVal, which should short-circuit any use of it. | ||
1149 | _, itemDiags := e.Item.Value(ctx) | ||
1150 | diags = append(diags, itemDiags...) | ||
1151 | return cty.DynamicVal, diags | ||
1152 | } | ||
1153 | |||
1154 | if sourceVal.IsNull() { | ||
1155 | diags = append(diags, &hcl.Diagnostic{ | ||
1156 | Severity: hcl.DiagError, | ||
1157 | Summary: "Splat of null value", | ||
1158 | Detail: "Splat expressions (with the * symbol) cannot be applied to null values.", | ||
1159 | Subject: e.Source.Range().Ptr(), | ||
1160 | Context: hcl.RangeBetween(e.Source.Range(), e.MarkerRange).Ptr(), | ||
1161 | }) | ||
1162 | return cty.DynamicVal, diags | ||
1163 | } | ||
1164 | if !sourceVal.IsKnown() { | ||
1165 | return cty.DynamicVal, diags | ||
1166 | } | ||
1167 | |||
1168 | // A "special power" of splat expressions is that they can be applied | ||
1169 | // both to tuples/lists and to other values, and in the latter case | ||
1170 | // the value will be treated as an implicit single-value list. We'll | ||
1171 | // deal with that here first. | ||
1172 | if !(sourceVal.Type().IsTupleType() || sourceVal.Type().IsListType()) { | ||
1173 | sourceVal = cty.ListVal([]cty.Value{sourceVal}) | ||
1174 | } | ||
1175 | |||
1176 | vals := make([]cty.Value, 0, sourceVal.LengthInt()) | ||
1177 | it := sourceVal.ElementIterator() | ||
1178 | if ctx == nil { | ||
1179 | // we need a context to use our AnonSymbolExpr, so we'll just | ||
1180 | // make an empty one here to use as a placeholder. | ||
1181 | ctx = ctx.NewChild() | ||
1182 | } | ||
1183 | isKnown := true | ||
1184 | for it.Next() { | ||
1185 | _, sourceItem := it.Element() | ||
1186 | e.Item.setValue(ctx, sourceItem) | ||
1187 | newItem, itemDiags := e.Each.Value(ctx) | ||
1188 | diags = append(diags, itemDiags...) | ||
1189 | if itemDiags.HasErrors() { | ||
1190 | isKnown = false | ||
1191 | } | ||
1192 | vals = append(vals, newItem) | ||
1193 | } | ||
1194 | e.Item.clearValue(ctx) // clean up our temporary value | ||
1195 | |||
1196 | if !isKnown { | ||
1197 | return cty.DynamicVal, diags | ||
1198 | } | ||
1199 | |||
1200 | return cty.TupleVal(vals), diags | ||
1201 | } | ||
1202 | |||
1203 | func (e *SplatExpr) walkChildNodes(w internalWalkFunc) { | ||
1204 | e.Source = w(e.Source).(Expression) | ||
1205 | e.Each = w(e.Each).(Expression) | ||
1206 | } | ||
1207 | |||
1208 | func (e *SplatExpr) Range() hcl.Range { | ||
1209 | return e.SrcRange | ||
1210 | } | ||
1211 | |||
1212 | func (e *SplatExpr) StartRange() hcl.Range { | ||
1213 | return e.MarkerRange | ||
1214 | } | ||
1215 | |||
1216 | // AnonSymbolExpr is used as a placeholder for a value in an expression that | ||
1217 | // can be applied dynamically to any value at runtime. | ||
1218 | // | ||
1219 | // This is a rather odd, synthetic expression. It is used as part of the | ||
1220 | // representation of splat expressions as a placeholder for the current item | ||
1221 | // being visited in the splat evaluation. | ||
1222 | // | ||
1223 | // AnonSymbolExpr cannot be evaluated in isolation. If its Value is called | ||
1224 | // directly then cty.DynamicVal will be returned. Instead, it is evaluated | ||
1225 | // in terms of another node (i.e. a splat expression) which temporarily | ||
1226 | // assigns it a value. | ||
1227 | type AnonSymbolExpr struct { | ||
1228 | SrcRange hcl.Range | ||
1229 | values map[*hcl.EvalContext]cty.Value | ||
1230 | } | ||
1231 | |||
1232 | func (e *AnonSymbolExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { | ||
1233 | if ctx == nil { | ||
1234 | return cty.DynamicVal, nil | ||
1235 | } | ||
1236 | val, exists := e.values[ctx] | ||
1237 | if !exists { | ||
1238 | return cty.DynamicVal, nil | ||
1239 | } | ||
1240 | return val, nil | ||
1241 | } | ||
1242 | |||
1243 | // setValue sets a temporary local value for the expression when evaluated | ||
1244 | // in the given context, which must be non-nil. | ||
1245 | func (e *AnonSymbolExpr) setValue(ctx *hcl.EvalContext, val cty.Value) { | ||
1246 | if e.values == nil { | ||
1247 | e.values = make(map[*hcl.EvalContext]cty.Value) | ||
1248 | } | ||
1249 | if ctx == nil { | ||
1250 | panic("can't setValue for a nil EvalContext") | ||
1251 | } | ||
1252 | e.values[ctx] = val | ||
1253 | } | ||
1254 | |||
1255 | func (e *AnonSymbolExpr) clearValue(ctx *hcl.EvalContext) { | ||
1256 | if e.values == nil { | ||
1257 | return | ||
1258 | } | ||
1259 | if ctx == nil { | ||
1260 | panic("can't clearValue for a nil EvalContext") | ||
1261 | } | ||
1262 | delete(e.values, ctx) | ||
1263 | } | ||
1264 | |||
1265 | func (e *AnonSymbolExpr) walkChildNodes(w internalWalkFunc) { | ||
1266 | // AnonSymbolExpr is a leaf node in the tree | ||
1267 | } | ||
1268 | |||
1269 | func (e *AnonSymbolExpr) Range() hcl.Range { | ||
1270 | return e.SrcRange | ||
1271 | } | ||
1272 | |||
1273 | func (e *AnonSymbolExpr) StartRange() hcl.Range { | ||
1274 | return e.SrcRange | ||
1275 | } | ||