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"
13 // Expression is the abstract type for nodes that behave as HCL expressions.
14 type Expression interface {
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.
21 Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics)
22 Variables() []hcl.Traversal
23 StartRange() hcl.Range
26 // Assert that Expression implements hcl.Expression
27 var assertExprImplExpr hcl.Expression = Expression(nil)
29 // LiteralValueExpr is an expression that just always returns a given value.
30 type LiteralValueExpr struct {
35 func (e *LiteralValueExpr) walkChildNodes(w internalWalkFunc) {
36 // Literal values have no child nodes
39 func (e *LiteralValueExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
43 func (e *LiteralValueExpr) Range() hcl.Range {
47 func (e *LiteralValueExpr) StartRange() hcl.Range {
51 // Implementation for hcl.AbsTraversalForExpr.
52 func (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.
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".
91 // No traversal is possible for any other value.
96 // ScopeTraversalExpr is an Expression that retrieves a value from the scope
98 type ScopeTraversalExpr struct {
99 Traversal hcl.Traversal
103 func (e *ScopeTraversalExpr) walkChildNodes(w internalWalkFunc) {
104 // Scope traversals have no child nodes
107 func (e *ScopeTraversalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
108 val, diags := e.Traversal.TraverseAbs(ctx)
109 setDiagEvalContext(diags, e, ctx)
113 func (e *ScopeTraversalExpr) Range() hcl.Range {
117 func (e *ScopeTraversalExpr) StartRange() hcl.Range {
121 // Implementation for hcl.AbsTraversalForExpr.
122 func (e *ScopeTraversalExpr) AsTraversal() hcl.Traversal {
126 // RelativeTraversalExpr is an Expression that retrieves a value from another
127 // value using a _relative_ traversal.
128 type RelativeTraversalExpr struct {
130 Traversal hcl.Traversal
134 func (e *RelativeTraversalExpr) walkChildNodes(w internalWalkFunc) {
138 func (e *RelativeTraversalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
139 src, diags := e.Source.Value(ctx)
140 ret, travDiags := e.Traversal.TraverseRel(src)
141 setDiagEvalContext(travDiags, e, ctx)
142 diags = append(diags, travDiags...)
146 func (e *RelativeTraversalExpr) Range() hcl.Range {
150 func (e *RelativeTraversalExpr) StartRange() hcl.Range {
154 // Implementation for hcl.AbsTraversalForExpr.
155 func (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() {
162 ret := make(hcl.Traversal, len(st)+len(e.Traversal))
164 copy(ret[len(st):], e.Traversal)
168 // FunctionCallExpr is an Expression that calls a function from the EvalContext
169 // and returns its result.
170 type FunctionCallExpr struct {
174 // If true, the final argument should be a tuple, list or set which will
175 // expand to be one argument per element.
179 OpenParenRange hcl.Range
180 CloseParenRange hcl.Range
183 func (e *FunctionCallExpr) walkChildNodes(w internalWalkFunc) {
184 for _, arg := range e.Args {
189 func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
190 var diags hcl.Diagnostics
192 var f function.Function
194 hasNonNilMap := false
197 if thisCtx.Functions == nil {
198 thisCtx = thisCtx.Parent()
202 f, exists = thisCtx.Functions[e.Name]
206 thisCtx = thisCtx.Parent()
211 return cty.DynamicVal, hcl.Diagnostics{
213 Severity: hcl.DiagError,
214 Summary: "Function calls not allowed",
215 Detail: "Functions may not be called here.",
216 Subject: e.Range().Ptr(),
223 avail := make([]string, 0, len(ctx.Functions))
224 for name := range ctx.Functions {
225 avail = append(avail, name)
227 suggestion := nameSuggestion(e.Name, avail)
228 if suggestion != "" {
229 suggestion = fmt.Sprintf(" Did you mean %q?", suggestion)
232 return cty.DynamicVal, hcl.Diagnostics{
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(),
246 varParam := f.VarParam()
251 // should never happen if the parser is behaving
252 panic("ExpandFinal set on function call with no arguments")
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
262 case expandVal.Type().IsTupleType() || expandVal.Type().IsListType() || expandVal.Type().IsSetType():
263 if expandVal.IsNull() {
264 diags = append(diags, &hcl.Diagnostic{
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,
273 return cty.DynamicVal, diags
275 if !expandVal.IsKnown() {
276 return cty.DynamicVal, diags
279 newArgs := make([]Expression, 0, (len(args)-1)+expandVal.LengthInt())
280 newArgs = append(newArgs, args[:len(args)-1]...)
281 it := expandVal.ElementIterator()
283 _, val := it.Element()
284 newArgs = append(newArgs, &LiteralValueExpr{
286 SrcRange: expandExpr.Range(),
291 diags = append(diags, &hcl.Diagnostic{
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,
300 return cty.DynamicVal, diags
304 if len(args) < len(params) {
305 missing := params[len(args)]
310 return cty.DynamicVal, hcl.Diagnostics{
312 Severity: hcl.DiagError,
313 Summary: "Not enough function arguments",
315 "Function %q expects%s %d argument(s). Missing value for %q.",
316 e.Name, qual, len(params), missing.Name,
318 Subject: &e.CloseParenRange,
319 Context: e.Range().Ptr(),
326 if varParam == nil && len(args) > len(params) {
327 return cty.DynamicVal, hcl.Diagnostics{
329 Severity: hcl.DiagError,
330 Summary: "Too many function arguments",
332 "Function %q expects only %d argument(s).",
335 Subject: args[len(params)].StartRange().Ptr(),
336 Context: e.Range().Ptr(),
343 argVals := make([]cty.Value, len(args))
345 for i, argExpr := range args {
346 var param *function.Parameter
353 val, argDiags := argExpr.Value(ctx)
354 if len(argDiags) > 0 {
355 diags = append(diags, argDiags...)
358 // Try to convert our value to the parameter type
359 val, err := convert.Convert(val, param.Type)
361 diags = append(diags, &hcl.Diagnostic{
362 Severity: hcl.DiagError,
363 Summary: "Invalid function argument",
365 "Invalid value for %q parameter: %s.",
368 Subject: argExpr.StartRange().Ptr(),
369 Context: e.Range().Ptr(),
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
382 return cty.DynamicVal, diags
385 resultVal, err := f.Call(argVals)
387 switch terr := err.(type) {
388 case function.ArgError:
390 var param *function.Parameter
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",
404 "Invalid value for %q parameter: %s.",
407 Subject: argExpr.StartRange().Ptr(),
408 Context: e.Range().Ptr(),
414 diags = append(diags, &hcl.Diagnostic{
415 Severity: hcl.DiagError,
416 Summary: "Error in function call",
418 "Call to function %q failed: %s.",
421 Subject: e.StartRange().Ptr(),
422 Context: e.Range().Ptr(),
428 return cty.DynamicVal, diags
431 return resultVal, diags
434 func (e *FunctionCallExpr) Range() hcl.Range {
435 return hcl.RangeBetween(e.NameRange, e.CloseParenRange)
438 func (e *FunctionCallExpr) StartRange() hcl.Range {
439 return hcl.RangeBetween(e.NameRange, e.OpenParenRange)
442 // Implementation for hcl.ExprCall.
443 func (e *FunctionCallExpr) ExprCall() *hcl.StaticCall {
444 ret := &hcl.StaticCall{
446 NameRange: e.NameRange,
447 Arguments: make([]hcl.Expression, len(e.Args)),
448 ArgsRange: hcl.RangeBetween(e.OpenParenRange, e.CloseParenRange),
450 // Need to convert our own Expression objects into hcl.Expression.
451 for i, arg := range e.Args {
452 ret.Arguments[i] = arg
457 type ConditionalExpr struct {
459 TrueResult Expression
460 FalseResult Expression
465 func (e *ConditionalExpr) walkChildNodes(w internalWalkFunc) {
471 func (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
476 // Try to find a type that both results can be converted to.
477 resultType, convs := convert.UnifyUnsafe([]cty.Type{trueResult.Type(), falseResult.Type()})
478 if resultType == cty.NilType {
479 return cty.DynamicVal, hcl.Diagnostics{
481 Severity: hcl.DiagError,
482 Summary: "Inconsistent conditional result types",
484 // FIXME: Need a helper function for showing natural-language type diffs,
485 // since this will generate some useless messages in some cases, like
486 // "These expressions are object and object respectively" if the
487 // object types don't exactly match.
488 "The true and false result expressions must have consistent types. The given expressions are %s and %s, respectively.",
489 trueResult.Type().FriendlyName(), falseResult.Type().FriendlyName(),
491 Subject: hcl.RangeBetween(e.TrueResult.Range(), e.FalseResult.Range()).Ptr(),
492 Context: &e.SrcRange,
499 condResult, condDiags := e.Condition.Value(ctx)
500 diags = append(diags, condDiags...)
501 if condResult.IsNull() {
502 diags = append(diags, &hcl.Diagnostic{
503 Severity: hcl.DiagError,
504 Summary: "Null condition",
505 Detail: "The condition value is null. Conditions must either be true or false.",
506 Subject: e.Condition.Range().Ptr(),
507 Context: &e.SrcRange,
508 Expression: e.Condition,
511 return cty.UnknownVal(resultType), diags
513 if !condResult.IsKnown() {
514 return cty.UnknownVal(resultType), diags
516 condResult, err := convert.Convert(condResult, cty.Bool)
518 diags = append(diags, &hcl.Diagnostic{
519 Severity: hcl.DiagError,
520 Summary: "Incorrect condition type",
521 Detail: fmt.Sprintf("The condition expression must be of type bool."),
522 Subject: e.Condition.Range().Ptr(),
523 Context: &e.SrcRange,
524 Expression: e.Condition,
527 return cty.UnknownVal(resultType), diags
530 if condResult.True() {
531 diags = append(diags, trueDiags...)
534 trueResult, err = convs[0](trueResult)
536 // Unsafe conversion failed with the concrete result value
537 diags = append(diags, &hcl.Diagnostic{
538 Severity: hcl.DiagError,
539 Summary: "Inconsistent conditional result types",
541 "The true result value has the wrong type: %s.",
544 Subject: e.TrueResult.Range().Ptr(),
545 Context: &e.SrcRange,
546 Expression: e.TrueResult,
549 trueResult = cty.UnknownVal(resultType)
552 return trueResult, diags
554 diags = append(diags, falseDiags...)
557 falseResult, err = convs[1](falseResult)
559 // Unsafe conversion failed with the concrete result value
560 diags = append(diags, &hcl.Diagnostic{
561 Severity: hcl.DiagError,
562 Summary: "Inconsistent conditional result types",
564 "The false result value has the wrong type: %s.",
567 Subject: e.FalseResult.Range().Ptr(),
568 Context: &e.SrcRange,
569 Expression: e.FalseResult,
572 falseResult = cty.UnknownVal(resultType)
575 return falseResult, diags
579 func (e *ConditionalExpr) Range() hcl.Range {
583 func (e *ConditionalExpr) StartRange() hcl.Range {
584 return e.Condition.StartRange()
587 type IndexExpr struct {
588 Collection Expression
595 func (e *IndexExpr) walkChildNodes(w internalWalkFunc) {
600 func (e *IndexExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
601 var diags hcl.Diagnostics
602 coll, collDiags := e.Collection.Value(ctx)
603 key, keyDiags := e.Key.Value(ctx)
604 diags = append(diags, collDiags...)
605 diags = append(diags, keyDiags...)
607 val, indexDiags := hcl.Index(coll, key, &e.SrcRange)
608 setDiagEvalContext(indexDiags, e, ctx)
609 diags = append(diags, indexDiags...)
613 func (e *IndexExpr) Range() hcl.Range {
617 func (e *IndexExpr) StartRange() hcl.Range {
621 type TupleConsExpr struct {
628 func (e *TupleConsExpr) walkChildNodes(w internalWalkFunc) {
629 for _, expr := range e.Exprs {
634 func (e *TupleConsExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
636 var diags hcl.Diagnostics
638 vals = make([]cty.Value, len(e.Exprs))
639 for i, expr := range e.Exprs {
640 val, valDiags := expr.Value(ctx)
642 diags = append(diags, valDiags...)
645 return cty.TupleVal(vals), diags
648 func (e *TupleConsExpr) Range() hcl.Range {
652 func (e *TupleConsExpr) StartRange() hcl.Range {
656 // Implementation for hcl.ExprList
657 func (e *TupleConsExpr) ExprList() []hcl.Expression {
658 ret := make([]hcl.Expression, len(e.Exprs))
659 for i, expr := range e.Exprs {
665 type ObjectConsExpr struct {
666 Items []ObjectConsItem
672 type ObjectConsItem struct {
677 func (e *ObjectConsExpr) walkChildNodes(w internalWalkFunc) {
678 for _, item := range e.Items {
684 func (e *ObjectConsExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
685 var vals map[string]cty.Value
686 var diags hcl.Diagnostics
688 // This will get set to true if we fail to produce any of our keys,
689 // either because they are actually unknown or if the evaluation produces
690 // errors. In all of these case we must return DynamicPseudoType because
691 // we're unable to know the full set of keys our object has, and thus
692 // we can't produce a complete value of the intended type.
694 // We still evaluate all of the item keys and values to make sure that we
695 // get as complete as possible a set of diagnostics.
698 vals = make(map[string]cty.Value, len(e.Items))
699 for _, item := range e.Items {
700 key, keyDiags := item.KeyExpr.Value(ctx)
701 diags = append(diags, keyDiags...)
703 val, valDiags := item.ValueExpr.Value(ctx)
704 diags = append(diags, valDiags...)
706 if keyDiags.HasErrors() {
712 diags = append(diags, &hcl.Diagnostic{
713 Severity: hcl.DiagError,
714 Summary: "Null value as key",
715 Detail: "Can't use a null value as a key.",
716 Subject: item.ValueExpr.Range().Ptr(),
717 Expression: item.KeyExpr,
725 key, err = convert.Convert(key, cty.String)
727 diags = append(diags, &hcl.Diagnostic{
728 Severity: hcl.DiagError,
729 Summary: "Incorrect key type",
730 Detail: fmt.Sprintf("Can't use this value as a key: %s.", err.Error()),
731 Subject: item.KeyExpr.Range().Ptr(),
732 Expression: item.KeyExpr,
744 keyStr := key.AsString()
750 return cty.DynamicVal, diags
753 return cty.ObjectVal(vals), diags
756 func (e *ObjectConsExpr) Range() hcl.Range {
760 func (e *ObjectConsExpr) StartRange() hcl.Range {
764 // Implementation for hcl.ExprMap
765 func (e *ObjectConsExpr) ExprMap() []hcl.KeyValuePair {
766 ret := make([]hcl.KeyValuePair, len(e.Items))
767 for i, item := range e.Items {
768 ret[i] = hcl.KeyValuePair{
770 Value: item.ValueExpr,
776 // ObjectConsKeyExpr is a special wrapper used only for ObjectConsExpr keys,
777 // which deals with the special case that a naked identifier in that position
778 // must be interpreted as a literal string rather than evaluated directly.
779 type ObjectConsKeyExpr struct {
783 func (e *ObjectConsKeyExpr) literalName() string {
784 // This is our logic for deciding whether to behave like a literal string.
785 // We lean on our AbsTraversalForExpr implementation here, which already
786 // deals with some awkward cases like the expression being the result
787 // of the keywords "null", "true" and "false" which we'd want to interpret
789 return hcl.ExprAsKeyword(e.Wrapped)
792 func (e *ObjectConsKeyExpr) walkChildNodes(w internalWalkFunc) {
793 // We only treat our wrapped expression as a real expression if we're
794 // not going to interpret it as a literal.
795 if e.literalName() == "" {
800 func (e *ObjectConsKeyExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
801 // Because we accept a naked identifier as a literal key rather than a
802 // reference, it's confusing to accept a traversal containing periods
803 // here since we can't tell if the user intends to create a key with
804 // periods or actually reference something. To avoid confusing downstream
805 // errors we'll just prohibit a naked multi-step traversal here and
806 // require the user to state their intent more clearly.
807 // (This is handled at evaluation time rather than parse time because
808 // an application using static analysis _can_ accept a naked multi-step
809 // traversal here, if desired.)
810 if travExpr, isTraversal := e.Wrapped.(*ScopeTraversalExpr); isTraversal && len(travExpr.Traversal) > 1 {
811 var diags hcl.Diagnostics
812 diags = append(diags, &hcl.Diagnostic{
813 Severity: hcl.DiagError,
814 Summary: "Ambiguous attribute key",
815 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.",
816 Subject: e.Range().Ptr(),
818 return cty.DynamicVal, diags
821 if ln := e.literalName(); ln != "" {
822 return cty.StringVal(ln), nil
824 return e.Wrapped.Value(ctx)
827 func (e *ObjectConsKeyExpr) Range() hcl.Range {
828 return e.Wrapped.Range()
831 func (e *ObjectConsKeyExpr) StartRange() hcl.Range {
832 return e.Wrapped.StartRange()
835 // Implementation for hcl.AbsTraversalForExpr.
836 func (e *ObjectConsKeyExpr) AsTraversal() hcl.Traversal {
837 // We can produce a traversal only if our wrappee can.
838 st, diags := hcl.AbsTraversalForExpr(e.Wrapped)
839 if diags.HasErrors() {
846 func (e *ObjectConsKeyExpr) UnwrapExpression() Expression {
850 // ForExpr represents iteration constructs:
852 // tuple = [for i, v in list: upper(v) if i > 2]
853 // object = {for k, v in map: k => upper(v)}
854 // object_of_tuples = {for v in list: v.key: v...}
855 type ForExpr struct {
856 KeyVar string // empty if ignoring the key
861 KeyExpr Expression // nil when producing a tuple
863 CondExpr Expression // null if no "if" clause is present
865 Group bool // set if the ellipsis is used on the value in an object for
872 func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
873 var diags hcl.Diagnostics
875 collVal, collDiags := e.CollExpr.Value(ctx)
876 diags = append(diags, collDiags...)
878 if collVal.IsNull() {
879 diags = append(diags, &hcl.Diagnostic{
880 Severity: hcl.DiagError,
881 Summary: "Iteration over null value",
882 Detail: "A null value cannot be used as the collection in a 'for' expression.",
883 Subject: e.CollExpr.Range().Ptr(),
884 Context: &e.SrcRange,
885 Expression: e.CollExpr,
888 return cty.DynamicVal, diags
890 if collVal.Type() == cty.DynamicPseudoType {
891 return cty.DynamicVal, diags
893 if !collVal.CanIterateElements() {
894 diags = append(diags, &hcl.Diagnostic{
895 Severity: hcl.DiagError,
896 Summary: "Iteration over non-iterable value",
898 "A value of type %s cannot be used as the collection in a 'for' expression.",
899 collVal.Type().FriendlyName(),
901 Subject: e.CollExpr.Range().Ptr(),
902 Context: &e.SrcRange,
903 Expression: e.CollExpr,
906 return cty.DynamicVal, diags
908 if !collVal.IsKnown() {
909 return cty.DynamicVal, diags
912 // Before we start we'll do an early check to see if any CondExpr we've
913 // been given is of the wrong type. This isn't 100% reliable (it may
914 // be DynamicVal until real values are given) but it should catch some
915 // straightforward cases and prevent a barrage of repeated errors.
916 if e.CondExpr != nil {
917 childCtx := ctx.NewChild()
918 childCtx.Variables = map[string]cty.Value{}
920 childCtx.Variables[e.KeyVar] = cty.DynamicVal
922 childCtx.Variables[e.ValVar] = cty.DynamicVal
924 result, condDiags := e.CondExpr.Value(childCtx)
925 diags = append(diags, condDiags...)
927 diags = append(diags, &hcl.Diagnostic{
928 Severity: hcl.DiagError,
929 Summary: "Condition is null",
930 Detail: "The value of the 'if' clause must not be null.",
931 Subject: e.CondExpr.Range().Ptr(),
932 Context: &e.SrcRange,
933 Expression: e.CondExpr,
936 return cty.DynamicVal, diags
938 _, err := convert.Convert(result, cty.Bool)
940 diags = append(diags, &hcl.Diagnostic{
941 Severity: hcl.DiagError,
942 Summary: "Invalid 'for' condition",
943 Detail: fmt.Sprintf("The 'if' clause value is invalid: %s.", err.Error()),
944 Subject: e.CondExpr.Range().Ptr(),
945 Context: &e.SrcRange,
946 Expression: e.CondExpr,
949 return cty.DynamicVal, diags
951 if condDiags.HasErrors() {
952 return cty.DynamicVal, diags
956 if e.KeyExpr != nil {
957 // Producing an object
958 var vals map[string]cty.Value
959 var groupVals map[string][]cty.Value
961 groupVals = map[string][]cty.Value{}
963 vals = map[string]cty.Value{}
966 it := collVal.ElementIterator()
971 childCtx := ctx.NewChild()
972 childCtx.Variables = map[string]cty.Value{}
974 childCtx.Variables[e.KeyVar] = k
976 childCtx.Variables[e.ValVar] = v
978 if e.CondExpr != nil {
979 includeRaw, condDiags := e.CondExpr.Value(childCtx)
980 diags = append(diags, condDiags...)
981 if includeRaw.IsNull() {
983 diags = append(diags, &hcl.Diagnostic{
984 Severity: hcl.DiagError,
985 Summary: "Invalid 'for' condition",
986 Detail: "The value of the 'if' clause must not be null.",
987 Subject: e.CondExpr.Range().Ptr(),
988 Context: &e.SrcRange,
989 Expression: e.CondExpr,
990 EvalContext: childCtx,
996 include, err := convert.Convert(includeRaw, cty.Bool)
999 diags = append(diags, &hcl.Diagnostic{
1000 Severity: hcl.DiagError,
1001 Summary: "Invalid 'for' condition",
1002 Detail: fmt.Sprintf("The 'if' clause value is invalid: %s.", err.Error()),
1003 Subject: e.CondExpr.Range().Ptr(),
1004 Context: &e.SrcRange,
1005 Expression: e.CondExpr,
1006 EvalContext: childCtx,
1012 if !include.IsKnown() {
1017 if include.False() {
1018 // Skip this element
1023 keyRaw, keyDiags := e.KeyExpr.Value(childCtx)
1024 diags = append(diags, keyDiags...)
1025 if keyRaw.IsNull() {
1027 diags = append(diags, &hcl.Diagnostic{
1028 Severity: hcl.DiagError,
1029 Summary: "Invalid object key",
1030 Detail: "Key expression in 'for' expression must not produce a null value.",
1031 Subject: e.KeyExpr.Range().Ptr(),
1032 Context: &e.SrcRange,
1033 Expression: e.KeyExpr,
1034 EvalContext: childCtx,
1040 if !keyRaw.IsKnown() {
1045 key, err := convert.Convert(keyRaw, cty.String)
1048 diags = append(diags, &hcl.Diagnostic{
1049 Severity: hcl.DiagError,
1050 Summary: "Invalid object key",
1051 Detail: fmt.Sprintf("The key expression produced an invalid result: %s.", err.Error()),
1052 Subject: e.KeyExpr.Range().Ptr(),
1053 Context: &e.SrcRange,
1054 Expression: e.KeyExpr,
1055 EvalContext: childCtx,
1062 val, valDiags := e.ValExpr.Value(childCtx)
1063 diags = append(diags, valDiags...)
1067 groupVals[k] = append(groupVals[k], val)
1070 if _, exists := vals[k]; exists {
1071 diags = append(diags, &hcl.Diagnostic{
1072 Severity: hcl.DiagError,
1073 Summary: "Duplicate object key",
1074 Detail: fmt.Sprintf(
1075 "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.",
1078 Subject: e.KeyExpr.Range().Ptr(),
1079 Context: &e.SrcRange,
1080 Expression: e.KeyExpr,
1081 EvalContext: childCtx,
1084 vals[key.AsString()] = val
1090 return cty.DynamicVal, diags
1094 vals = map[string]cty.Value{}
1095 for k, gvs := range groupVals {
1096 vals[k] = cty.TupleVal(gvs)
1100 return cty.ObjectVal(vals), diags
1103 // Producing a tuple
1104 vals := []cty.Value{}
1106 it := collVal.ElementIterator()
1110 k, v := it.Element()
1111 childCtx := ctx.NewChild()
1112 childCtx.Variables = map[string]cty.Value{}
1114 childCtx.Variables[e.KeyVar] = k
1116 childCtx.Variables[e.ValVar] = v
1118 if e.CondExpr != nil {
1119 includeRaw, condDiags := e.CondExpr.Value(childCtx)
1120 diags = append(diags, condDiags...)
1121 if includeRaw.IsNull() {
1123 diags = append(diags, &hcl.Diagnostic{
1124 Severity: hcl.DiagError,
1125 Summary: "Invalid 'for' condition",
1126 Detail: "The value of the 'if' clause must not be null.",
1127 Subject: e.CondExpr.Range().Ptr(),
1128 Context: &e.SrcRange,
1129 Expression: e.CondExpr,
1130 EvalContext: childCtx,
1136 if !includeRaw.IsKnown() {
1137 // We will eventually return DynamicVal, but we'll continue
1138 // iterating in case there are other diagnostics to gather
1139 // for later elements.
1144 include, err := convert.Convert(includeRaw, cty.Bool)
1147 diags = append(diags, &hcl.Diagnostic{
1148 Severity: hcl.DiagError,
1149 Summary: "Invalid 'for' condition",
1150 Detail: fmt.Sprintf("The 'if' clause value is invalid: %s.", err.Error()),
1151 Subject: e.CondExpr.Range().Ptr(),
1152 Context: &e.SrcRange,
1153 Expression: e.CondExpr,
1154 EvalContext: childCtx,
1161 if include.False() {
1162 // Skip this element
1167 val, valDiags := e.ValExpr.Value(childCtx)
1168 diags = append(diags, valDiags...)
1169 vals = append(vals, val)
1173 return cty.DynamicVal, diags
1176 return cty.TupleVal(vals), diags
1180 func (e *ForExpr) walkChildNodes(w internalWalkFunc) {
1183 scopeNames := map[string]struct{}{}
1185 scopeNames[e.KeyVar] = struct{}{}
1188 scopeNames[e.ValVar] = struct{}{}
1191 if e.KeyExpr != nil {
1193 LocalNames: scopeNames,
1198 LocalNames: scopeNames,
1201 if e.CondExpr != nil {
1203 LocalNames: scopeNames,
1209 func (e *ForExpr) Range() hcl.Range {
1213 func (e *ForExpr) StartRange() hcl.Range {
1217 type SplatExpr struct {
1220 Item *AnonSymbolExpr
1223 MarkerRange hcl.Range
1226 func (e *SplatExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
1227 sourceVal, diags := e.Source.Value(ctx)
1228 if diags.HasErrors() {
1229 // We'll evaluate our "Each" expression here just to see if it
1230 // produces any more diagnostics we can report. Since we're not
1231 // assigning a value to our AnonSymbolExpr here it will return
1232 // DynamicVal, which should short-circuit any use of it.
1233 _, itemDiags := e.Item.Value(ctx)
1234 diags = append(diags, itemDiags...)
1235 return cty.DynamicVal, diags
1238 sourceTy := sourceVal.Type()
1239 if sourceTy == cty.DynamicPseudoType {
1240 // If we don't even know the _type_ of our source value yet then
1241 // we'll need to defer all processing, since we can't decide our
1242 // result type either.
1243 return cty.DynamicVal, diags
1246 // A "special power" of splat expressions is that they can be applied
1247 // both to tuples/lists and to other values, and in the latter case
1248 // the value will be treated as an implicit single-item tuple, or as
1249 // an empty tuple if the value is null.
1250 autoUpgrade := !(sourceTy.IsTupleType() || sourceTy.IsListType() || sourceTy.IsSetType())
1252 if sourceVal.IsNull() {
1254 return cty.EmptyTupleVal, diags
1256 diags = append(diags, &hcl.Diagnostic{
1257 Severity: hcl.DiagError,
1258 Summary: "Splat of null value",
1259 Detail: "Splat expressions (with the * symbol) cannot be applied to null sequences.",
1260 Subject: e.Source.Range().Ptr(),
1261 Context: hcl.RangeBetween(e.Source.Range(), e.MarkerRange).Ptr(),
1262 Expression: e.Source,
1265 return cty.DynamicVal, diags
1269 sourceVal = cty.TupleVal([]cty.Value{sourceVal})
1270 sourceTy = sourceVal.Type()
1273 // We'll compute our result type lazily if we need it. In the normal case
1274 // it's inferred automatically from the value we construct.
1275 resultTy := func() (cty.Type, hcl.Diagnostics) {
1276 chiCtx := ctx.NewChild()
1277 var diags hcl.Diagnostics
1279 case sourceTy.IsListType() || sourceTy.IsSetType():
1280 ety := sourceTy.ElementType()
1281 e.Item.setValue(chiCtx, cty.UnknownVal(ety))
1282 val, itemDiags := e.Each.Value(chiCtx)
1283 diags = append(diags, itemDiags...)
1284 e.Item.clearValue(chiCtx) // clean up our temporary value
1285 return cty.List(val.Type()), diags
1286 case sourceTy.IsTupleType():
1287 etys := sourceTy.TupleElementTypes()
1288 resultTys := make([]cty.Type, 0, len(etys))
1289 for _, ety := range etys {
1290 e.Item.setValue(chiCtx, cty.UnknownVal(ety))
1291 val, itemDiags := e.Each.Value(chiCtx)
1292 diags = append(diags, itemDiags...)
1293 e.Item.clearValue(chiCtx) // clean up our temporary value
1294 resultTys = append(resultTys, val.Type())
1296 return cty.Tuple(resultTys), diags
1298 // Should never happen because of our promotion to list above.
1299 return cty.DynamicPseudoType, diags
1303 if !sourceVal.IsKnown() {
1304 // We can't produce a known result in this case, but we'll still
1305 // indicate what the result type would be, allowing any downstream type
1306 // checking to proceed.
1307 ty, tyDiags := resultTy()
1308 diags = append(diags, tyDiags...)
1309 return cty.UnknownVal(ty), diags
1312 vals := make([]cty.Value, 0, sourceVal.LengthInt())
1313 it := sourceVal.ElementIterator()
1315 // we need a context to use our AnonSymbolExpr, so we'll just
1316 // make an empty one here to use as a placeholder.
1317 ctx = ctx.NewChild()
1321 _, sourceItem := it.Element()
1322 e.Item.setValue(ctx, sourceItem)
1323 newItem, itemDiags := e.Each.Value(ctx)
1324 diags = append(diags, itemDiags...)
1325 if itemDiags.HasErrors() {
1328 vals = append(vals, newItem)
1330 e.Item.clearValue(ctx) // clean up our temporary value
1333 // We'll ingore the resultTy diagnostics in this case since they
1334 // will just be the same errors we saw while iterating above.
1336 return cty.UnknownVal(ty), diags
1340 case sourceTy.IsListType() || sourceTy.IsSetType():
1342 ty, tyDiags := resultTy()
1343 diags = append(diags, tyDiags...)
1344 return cty.ListValEmpty(ty.ElementType()), diags
1346 return cty.ListVal(vals), diags
1348 return cty.TupleVal(vals), diags
1352 func (e *SplatExpr) walkChildNodes(w internalWalkFunc) {
1357 func (e *SplatExpr) Range() hcl.Range {
1361 func (e *SplatExpr) StartRange() hcl.Range {
1362 return e.MarkerRange
1365 // AnonSymbolExpr is used as a placeholder for a value in an expression that
1366 // can be applied dynamically to any value at runtime.
1368 // This is a rather odd, synthetic expression. It is used as part of the
1369 // representation of splat expressions as a placeholder for the current item
1370 // being visited in the splat evaluation.
1372 // AnonSymbolExpr cannot be evaluated in isolation. If its Value is called
1373 // directly then cty.DynamicVal will be returned. Instead, it is evaluated
1374 // in terms of another node (i.e. a splat expression) which temporarily
1375 // assigns it a value.
1376 type AnonSymbolExpr struct {
1379 // values and its associated lock are used to isolate concurrent
1380 // evaluations of a symbol from one another. It is the calling application's
1381 // responsibility to ensure that the same splat expression is not evalauted
1382 // concurrently within the _same_ EvalContext, but it is fine and safe to
1383 // do cuncurrent evaluations with distinct EvalContexts.
1384 values map[*hcl.EvalContext]cty.Value
1385 valuesLock sync.RWMutex
1388 func (e *AnonSymbolExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
1390 return cty.DynamicVal, nil
1393 e.valuesLock.RLock()
1394 defer e.valuesLock.RUnlock()
1396 val, exists := e.values[ctx]
1398 return cty.DynamicVal, nil
1403 // setValue sets a temporary local value for the expression when evaluated
1404 // in the given context, which must be non-nil.
1405 func (e *AnonSymbolExpr) setValue(ctx *hcl.EvalContext, val cty.Value) {
1407 defer e.valuesLock.Unlock()
1409 if e.values == nil {
1410 e.values = make(map[*hcl.EvalContext]cty.Value)
1413 panic("can't setValue for a nil EvalContext")
1418 func (e *AnonSymbolExpr) clearValue(ctx *hcl.EvalContext) {
1420 defer e.valuesLock.Unlock()
1422 if e.values == nil {
1426 panic("can't clearValue for a nil EvalContext")
1428 delete(e.values, ctx)
1431 func (e *AnonSymbolExpr) walkChildNodes(w internalWalkFunc) {
1432 // AnonSymbolExpr is a leaf node in the tree
1435 func (e *AnonSymbolExpr) Range() hcl.Range {
1439 func (e *AnonSymbolExpr) StartRange() hcl.Range {