]>
Commit | Line | Data |
---|---|---|
1 | package hclsyntax | |
2 | ||
3 | import ( | |
4 | "fmt" | |
5 | "sync" | |
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. | |
14 | type 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 | |
27 | var assertExprImplExpr hcl.Expression = Expression(nil) | |
28 | ||
29 | // LiteralValueExpr is an expression that just always returns a given value. | |
30 | type LiteralValueExpr struct { | |
31 | Val cty.Value | |
32 | SrcRange hcl.Range | |
33 | } | |
34 | ||
35 | func (e *LiteralValueExpr) walkChildNodes(w internalWalkFunc) { | |
36 | // Literal values have no child nodes | |
37 | } | |
38 | ||
39 | func (e *LiteralValueExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { | |
40 | return e.Val, nil | |
41 | } | |
42 | ||
43 | func (e *LiteralValueExpr) Range() hcl.Range { | |
44 | return e.SrcRange | |
45 | } | |
46 | ||
47 | func (e *LiteralValueExpr) StartRange() hcl.Range { | |
48 | return e.SrcRange | |
49 | } | |
50 | ||
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. | |
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. | |
98 | type ScopeTraversalExpr struct { | |
99 | Traversal hcl.Traversal | |
100 | SrcRange hcl.Range | |
101 | } | |
102 | ||
103 | func (e *ScopeTraversalExpr) walkChildNodes(w internalWalkFunc) { | |
104 | // Scope traversals have no child nodes | |
105 | } | |
106 | ||
107 | func (e *ScopeTraversalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { | |
108 | val, diags := e.Traversal.TraverseAbs(ctx) | |
109 | setDiagEvalContext(diags, e, ctx) | |
110 | return val, diags | |
111 | } | |
112 | ||
113 | func (e *ScopeTraversalExpr) Range() hcl.Range { | |
114 | return e.SrcRange | |
115 | } | |
116 | ||
117 | func (e *ScopeTraversalExpr) StartRange() hcl.Range { | |
118 | return e.SrcRange | |
119 | } | |
120 | ||
121 | // Implementation for hcl.AbsTraversalForExpr. | |
122 | func (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. | |
128 | type RelativeTraversalExpr struct { | |
129 | Source Expression | |
130 | Traversal hcl.Traversal | |
131 | SrcRange hcl.Range | |
132 | } | |
133 | ||
134 | func (e *RelativeTraversalExpr) walkChildNodes(w internalWalkFunc) { | |
135 | w(e.Source) | |
136 | } | |
137 | ||
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...) | |
143 | return ret, diags | |
144 | } | |
145 | ||
146 | func (e *RelativeTraversalExpr) Range() hcl.Range { | |
147 | return e.SrcRange | |
148 | } | |
149 | ||
150 | func (e *RelativeTraversalExpr) StartRange() hcl.Range { | |
151 | return e.SrcRange | |
152 | } | |
153 | ||
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() { | |
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. | |
170 | type 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 | ||
183 | func (e *FunctionCallExpr) walkChildNodes(w internalWalkFunc) { | |
184 | for _, arg := range e.Args { | |
185 | w(arg) | |
186 | } | |
187 | } | |
188 | ||
189 | func (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 | { | |
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, | |
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 | { | |
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, | |
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{ | |
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, | |
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{ | |
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, | |
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 | ), | |
318 | Subject: &e.CloseParenRange, | |
319 | Context: e.Range().Ptr(), | |
320 | Expression: e, | |
321 | EvalContext: ctx, | |
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 | ), | |
335 | Subject: args[len(params)].StartRange().Ptr(), | |
336 | Context: e.Range().Ptr(), | |
337 | Expression: e, | |
338 | EvalContext: ctx, | |
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 = ¶ms[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 | ), | |
368 | Subject: argExpr.StartRange().Ptr(), | |
369 | Context: e.Range().Ptr(), | |
370 | Expression: argExpr, | |
371 | EvalContext: ctx, | |
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 = ¶ms[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 | ), | |
407 | Subject: argExpr.StartRange().Ptr(), | |
408 | Context: e.Range().Ptr(), | |
409 | Expression: argExpr, | |
410 | EvalContext: ctx, | |
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 | ), | |
421 | Subject: e.StartRange().Ptr(), | |
422 | Context: e.Range().Ptr(), | |
423 | Expression: e, | |
424 | EvalContext: ctx, | |
425 | }) | |
426 | } | |
427 | ||
428 | return cty.DynamicVal, diags | |
429 | } | |
430 | ||
431 | return resultVal, diags | |
432 | } | |
433 | ||
434 | func (e *FunctionCallExpr) Range() hcl.Range { | |
435 | return hcl.RangeBetween(e.NameRange, e.CloseParenRange) | |
436 | } | |
437 | ||
438 | func (e *FunctionCallExpr) StartRange() hcl.Range { | |
439 | return hcl.RangeBetween(e.NameRange, e.OpenParenRange) | |
440 | } | |
441 | ||
442 | // Implementation for hcl.ExprCall. | |
443 | func (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 | ||
457 | type ConditionalExpr struct { | |
458 | Condition Expression | |
459 | TrueResult Expression | |
460 | FalseResult Expression | |
461 | ||
462 | SrcRange hcl.Range | |
463 | } | |
464 | ||
465 | func (e *ConditionalExpr) walkChildNodes(w internalWalkFunc) { | |
466 | w(e.Condition) | |
467 | w(e.TrueResult) | |
468 | w(e.FalseResult) | |
469 | } | |
470 | ||
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 | |
475 | ||
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 | ||
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.", | |
516 | trueResult.Type().FriendlyName(), falseResult.Type().FriendlyName(), | |
517 | ), | |
518 | Subject: hcl.RangeBetween(e.TrueResult.Range(), e.FalseResult.Range()).Ptr(), | |
519 | Context: &e.SrcRange, | |
520 | Expression: e, | |
521 | EvalContext: ctx, | |
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{ | |
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, | |
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{ | |
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, | |
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 | ), | |
571 | Subject: e.TrueResult.Range().Ptr(), | |
572 | Context: &e.SrcRange, | |
573 | Expression: e.TrueResult, | |
574 | EvalContext: ctx, | |
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 | ), | |
594 | Subject: e.FalseResult.Range().Ptr(), | |
595 | Context: &e.SrcRange, | |
596 | Expression: e.FalseResult, | |
597 | EvalContext: ctx, | |
598 | }) | |
599 | falseResult = cty.UnknownVal(resultType) | |
600 | } | |
601 | } | |
602 | return falseResult, diags | |
603 | } | |
604 | } | |
605 | ||
606 | func (e *ConditionalExpr) Range() hcl.Range { | |
607 | return e.SrcRange | |
608 | } | |
609 | ||
610 | func (e *ConditionalExpr) StartRange() hcl.Range { | |
611 | return e.Condition.StartRange() | |
612 | } | |
613 | ||
614 | type IndexExpr struct { | |
615 | Collection Expression | |
616 | Key Expression | |
617 | ||
618 | SrcRange hcl.Range | |
619 | OpenRange hcl.Range | |
620 | } | |
621 | ||
622 | func (e *IndexExpr) walkChildNodes(w internalWalkFunc) { | |
623 | w(e.Collection) | |
624 | w(e.Key) | |
625 | } | |
626 | ||
627 | func (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 | ||
634 | val, indexDiags := hcl.Index(coll, key, &e.SrcRange) | |
635 | setDiagEvalContext(indexDiags, e, ctx) | |
636 | diags = append(diags, indexDiags...) | |
637 | return val, diags | |
638 | } | |
639 | ||
640 | func (e *IndexExpr) Range() hcl.Range { | |
641 | return e.SrcRange | |
642 | } | |
643 | ||
644 | func (e *IndexExpr) StartRange() hcl.Range { | |
645 | return e.OpenRange | |
646 | } | |
647 | ||
648 | type TupleConsExpr struct { | |
649 | Exprs []Expression | |
650 | ||
651 | SrcRange hcl.Range | |
652 | OpenRange hcl.Range | |
653 | } | |
654 | ||
655 | func (e *TupleConsExpr) walkChildNodes(w internalWalkFunc) { | |
656 | for _, expr := range e.Exprs { | |
657 | w(expr) | |
658 | } | |
659 | } | |
660 | ||
661 | func (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 | ||
675 | func (e *TupleConsExpr) Range() hcl.Range { | |
676 | return e.SrcRange | |
677 | } | |
678 | ||
679 | func (e *TupleConsExpr) StartRange() hcl.Range { | |
680 | return e.OpenRange | |
681 | } | |
682 | ||
683 | // Implementation for hcl.ExprList | |
684 | func (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 | ||
692 | type ObjectConsExpr struct { | |
693 | Items []ObjectConsItem | |
694 | ||
695 | SrcRange hcl.Range | |
696 | OpenRange hcl.Range | |
697 | } | |
698 | ||
699 | type ObjectConsItem struct { | |
700 | KeyExpr Expression | |
701 | ValueExpr Expression | |
702 | } | |
703 | ||
704 | func (e *ObjectConsExpr) walkChildNodes(w internalWalkFunc) { | |
705 | for _, item := range e.Items { | |
706 | w(item.KeyExpr) | |
707 | w(item.ValueExpr) | |
708 | } | |
709 | } | |
710 | ||
711 | func (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{ | |
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, | |
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{ | |
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, | |
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 | ||
783 | func (e *ObjectConsExpr) Range() hcl.Range { | |
784 | return e.SrcRange | |
785 | } | |
786 | ||
787 | func (e *ObjectConsExpr) StartRange() hcl.Range { | |
788 | return e.OpenRange | |
789 | } | |
790 | ||
791 | // Implementation for hcl.ExprMap | |
792 | func (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. | |
806 | type ObjectConsKeyExpr struct { | |
807 | Wrapped Expression | |
808 | } | |
809 | ||
810 | func (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 | ||
819 | func (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() == "" { | |
823 | w(e.Wrapped) | |
824 | } | |
825 | } | |
826 | ||
827 | func (e *ObjectConsKeyExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { | |
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 | ||
848 | if ln := e.literalName(); ln != "" { | |
849 | return cty.StringVal(ln), nil | |
850 | } | |
851 | return e.Wrapped.Value(ctx) | |
852 | } | |
853 | ||
854 | func (e *ObjectConsKeyExpr) Range() hcl.Range { | |
855 | return e.Wrapped.Range() | |
856 | } | |
857 | ||
858 | func (e *ObjectConsKeyExpr) StartRange() hcl.Range { | |
859 | return e.Wrapped.StartRange() | |
860 | } | |
861 | ||
862 | // Implementation for hcl.AbsTraversalForExpr. | |
863 | func (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 | ||
873 | func (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...} | |
882 | type 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 | ||
899 | func (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{ | |
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, | |
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 | ), | |
928 | Subject: e.CollExpr.Range().Ptr(), | |
929 | Context: &e.SrcRange, | |
930 | Expression: e.CollExpr, | |
931 | EvalContext: ctx, | |
932 | }) | |
933 | return cty.DynamicVal, diags | |
934 | } | |
935 | if !collVal.IsKnown() { | |
936 | return cty.DynamicVal, diags | |
937 | } | |
938 | ||
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 { | |
944 | childCtx := ctx.NewChild() | |
945 | childCtx.Variables = map[string]cty.Value{} | |
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{ | |
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, | |
962 | }) | |
963 | return cty.DynamicVal, diags | |
964 | } | |
965 | _, err := convert.Convert(result, cty.Bool) | |
966 | if err != nil { | |
967 | diags = append(diags, &hcl.Diagnostic{ | |
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, | |
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() | |
998 | childCtx := ctx.NewChild() | |
999 | childCtx.Variables = map[string]cty.Value{} | |
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{ | |
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, | |
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{ | |
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, | |
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{ | |
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, | |
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{ | |
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, | |
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( | |
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.", | |
1103 | k, | |
1104 | ), | |
1105 | Subject: e.KeyExpr.Range().Ptr(), | |
1106 | Context: &e.SrcRange, | |
1107 | Expression: e.KeyExpr, | |
1108 | EvalContext: childCtx, | |
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() | |
1138 | childCtx := ctx.NewChild() | |
1139 | childCtx.Variables = map[string]cty.Value{} | |
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{ | |
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, | |
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{ | |
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, | |
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 | ||
1207 | func (e *ForExpr) walkChildNodes(w internalWalkFunc) { | |
1208 | w(e.CollExpr) | |
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, | |
1221 | Expr: e.KeyExpr, | |
1222 | }) | |
1223 | } | |
1224 | w(ChildScope{ | |
1225 | LocalNames: scopeNames, | |
1226 | Expr: e.ValExpr, | |
1227 | }) | |
1228 | if e.CondExpr != nil { | |
1229 | w(ChildScope{ | |
1230 | LocalNames: scopeNames, | |
1231 | Expr: e.CondExpr, | |
1232 | }) | |
1233 | } | |
1234 | } | |
1235 | ||
1236 | func (e *ForExpr) Range() hcl.Range { | |
1237 | return e.SrcRange | |
1238 | } | |
1239 | ||
1240 | func (e *ForExpr) StartRange() hcl.Range { | |
1241 | return e.OpenRange | |
1242 | } | |
1243 | ||
1244 | type SplatExpr struct { | |
1245 | Source Expression | |
1246 | Each Expression | |
1247 | Item *AnonSymbolExpr | |
1248 | ||
1249 | SrcRange hcl.Range | |
1250 | MarkerRange hcl.Range | |
1251 | } | |
1252 | ||
1253 | func (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 | ||
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 | ||
1279 | if sourceVal.IsNull() { | |
1280 | if autoUpgrade { | |
1281 | return cty.EmptyTupleVal, diags | |
1282 | } | |
1283 | diags = append(diags, &hcl.Diagnostic{ | |
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, | |
1291 | }) | |
1292 | return cty.DynamicVal, diags | |
1293 | } | |
1294 | ||
1295 | if autoUpgrade { | |
1296 | sourceVal = cty.TupleVal([]cty.Value{sourceVal}) | |
1297 | sourceTy = sourceVal.Type() | |
1298 | } | |
1299 | ||
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 | |
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 { | |
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 | |
1364 | } | |
1365 | ||
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 | } | |
1377 | } | |
1378 | ||
1379 | func (e *SplatExpr) walkChildNodes(w internalWalkFunc) { | |
1380 | w(e.Source) | |
1381 | w(e.Each) | |
1382 | } | |
1383 | ||
1384 | func (e *SplatExpr) Range() hcl.Range { | |
1385 | return e.SrcRange | |
1386 | } | |
1387 | ||
1388 | func (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. | |
1403 | type AnonSymbolExpr struct { | |
1404 | SrcRange hcl.Range | |
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 | |
1413 | } | |
1414 | ||
1415 | func (e *AnonSymbolExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { | |
1416 | if ctx == nil { | |
1417 | return cty.DynamicVal, nil | |
1418 | } | |
1419 | ||
1420 | e.valuesLock.RLock() | |
1421 | defer e.valuesLock.RUnlock() | |
1422 | ||
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. | |
1432 | func (e *AnonSymbolExpr) setValue(ctx *hcl.EvalContext, val cty.Value) { | |
1433 | e.valuesLock.Lock() | |
1434 | defer e.valuesLock.Unlock() | |
1435 | ||
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 | ||
1445 | func (e *AnonSymbolExpr) clearValue(ctx *hcl.EvalContext) { | |
1446 | e.valuesLock.Lock() | |
1447 | defer e.valuesLock.Unlock() | |
1448 | ||
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 | ||
1458 | func (e *AnonSymbolExpr) walkChildNodes(w internalWalkFunc) { | |
1459 | // AnonSymbolExpr is a leaf node in the tree | |
1460 | } | |
1461 | ||
1462 | func (e *AnonSymbolExpr) Range() hcl.Range { | |
1463 | return e.SrcRange | |
1464 | } | |
1465 | ||
1466 | func (e *AnonSymbolExpr) StartRange() hcl.Range { | |
1467 | return e.SrcRange | |
1468 | } |