]>
Commit | Line | Data |
---|---|---|
107c1cdb ND |
1 | package lang |
2 | ||
3 | import ( | |
4 | "github.com/hashicorp/hcl2/hcl" | |
5 | "github.com/hashicorp/terraform/addrs" | |
6 | "github.com/hashicorp/terraform/configs/configschema" | |
7 | "github.com/hashicorp/terraform/lang/blocktoattr" | |
8 | "github.com/hashicorp/terraform/tfdiags" | |
9 | ) | |
10 | ||
11 | // References finds all of the references in the given set of traversals, | |
12 | // returning diagnostics if any of the traversals cannot be interpreted as a | |
13 | // reference. | |
14 | // | |
15 | // This function does not do any de-duplication of references, since references | |
16 | // have source location information embedded in them and so any invalid | |
17 | // references that are duplicated should have errors reported for each | |
18 | // occurence. | |
19 | // | |
20 | // If the returned diagnostics contains errors then the result may be | |
21 | // incomplete or invalid. Otherwise, the returned slice has one reference per | |
22 | // given traversal, though it is not guaranteed that the references will | |
23 | // appear in the same order as the given traversals. | |
24 | func References(traversals []hcl.Traversal) ([]*addrs.Reference, tfdiags.Diagnostics) { | |
25 | if len(traversals) == 0 { | |
26 | return nil, nil | |
27 | } | |
28 | ||
29 | var diags tfdiags.Diagnostics | |
30 | refs := make([]*addrs.Reference, 0, len(traversals)) | |
31 | ||
32 | for _, traversal := range traversals { | |
33 | ref, refDiags := addrs.ParseRef(traversal) | |
34 | diags = diags.Append(refDiags) | |
35 | if ref == nil { | |
36 | continue | |
37 | } | |
38 | refs = append(refs, ref) | |
39 | } | |
40 | ||
41 | return refs, diags | |
42 | } | |
43 | ||
44 | // ReferencesInBlock is a helper wrapper around References that first searches | |
45 | // the given body for traversals, before converting those traversals to | |
46 | // references. | |
47 | // | |
48 | // A block schema must be provided so that this function can determine where in | |
49 | // the body variables are expected. | |
50 | func ReferencesInBlock(body hcl.Body, schema *configschema.Block) ([]*addrs.Reference, tfdiags.Diagnostics) { | |
51 | if body == nil { | |
52 | return nil, nil | |
53 | } | |
54 | ||
55 | // We use blocktoattr.ExpandedVariables instead of hcldec.Variables or | |
56 | // dynblock.VariablesHCLDec here because when we evaluate a block we'll | |
57 | // first apply the dynamic block extension and _then_ the blocktoattr | |
58 | // transform, and so blocktoattr.ExpandedVariables takes into account | |
59 | // both of those transforms when it analyzes the body to ensure we find | |
60 | // all of the references as if they'd already moved into their final | |
61 | // locations, even though we can't expand dynamic blocks yet until we | |
62 | // already know which variables are required. | |
63 | // | |
64 | // The set of cases we want to detect here is covered by the tests for | |
65 | // the plan graph builder in the main 'terraform' package, since it's | |
66 | // in a better position to test this due to having mock providers etc | |
67 | // available. | |
68 | traversals := blocktoattr.ExpandedVariables(body, schema) | |
69 | return References(traversals) | |
70 | } | |
71 | ||
72 | // ReferencesInExpr is a helper wrapper around References that first searches | |
73 | // the given expression for traversals, before converting those traversals | |
74 | // to references. | |
75 | func ReferencesInExpr(expr hcl.Expression) ([]*addrs.Reference, tfdiags.Diagnostics) { | |
76 | if expr == nil { | |
77 | return nil, nil | |
78 | } | |
79 | traversals := expr.Variables() | |
80 | return References(traversals) | |
81 | } |