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