]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/configs/configschema/validate_traversal.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / configs / configschema / validate_traversal.go
1 package configschema
2
3 import (
4 "fmt"
5 "sort"
6
7 "github.com/hashicorp/hcl2/hcl"
8 "github.com/hashicorp/hcl2/hcl/hclsyntax"
9 "github.com/zclconf/go-cty/cty"
10
11 "github.com/hashicorp/terraform/helper/didyoumean"
12 "github.com/hashicorp/terraform/tfdiags"
13 )
14
15 // StaticValidateTraversal checks whether the given traversal (which must be
16 // relative) refers to a construct in the receiving schema, returning error
17 // diagnostics if any problems are found.
18 //
19 // This method is "optimistic" in that it will not return errors for possible
20 // problems that cannot be detected statically. It is possible that an
21 // traversal which passed static validation will still fail when evaluated.
22 func (b *Block) StaticValidateTraversal(traversal hcl.Traversal) tfdiags.Diagnostics {
23 if !traversal.IsRelative() {
24 panic("StaticValidateTraversal on absolute traversal")
25 }
26 if len(traversal) == 0 {
27 return nil
28 }
29
30 var diags tfdiags.Diagnostics
31
32 next := traversal[0]
33 after := traversal[1:]
34
35 var name string
36 switch step := next.(type) {
37 case hcl.TraverseAttr:
38 name = step.Name
39 case hcl.TraverseIndex:
40 // No other traversal step types are allowed directly at a block.
41 // If it looks like the user was trying to use index syntax to
42 // access an attribute then we'll produce a specialized message.
43 key := step.Key
44 if key.Type() == cty.String && key.IsKnown() && !key.IsNull() {
45 maybeName := key.AsString()
46 if hclsyntax.ValidIdentifier(maybeName) {
47 diags = diags.Append(&hcl.Diagnostic{
48 Severity: hcl.DiagError,
49 Summary: `Invalid index operation`,
50 Detail: fmt.Sprintf(`Only attribute access is allowed here. Did you mean to access attribute %q using the dot operator?`, maybeName),
51 Subject: &step.SrcRange,
52 })
53 return diags
54 }
55 }
56 // If it looks like some other kind of index then we'll use a generic error.
57 diags = diags.Append(&hcl.Diagnostic{
58 Severity: hcl.DiagError,
59 Summary: `Invalid index operation`,
60 Detail: `Only attribute access is allowed here, using the dot operator.`,
61 Subject: &step.SrcRange,
62 })
63 return diags
64 default:
65 // No other traversal types should appear in a normal valid traversal,
66 // but we'll handle this with a generic error anyway to be robust.
67 diags = diags.Append(&hcl.Diagnostic{
68 Severity: hcl.DiagError,
69 Summary: `Invalid operation`,
70 Detail: `Only attribute access is allowed here, using the dot operator.`,
71 Subject: next.SourceRange().Ptr(),
72 })
73 return diags
74 }
75
76 if attrS, exists := b.Attributes[name]; exists {
77 // For attribute validation we will just apply the rest of the
78 // traversal to an unknown value of the attribute type and pass
79 // through HCL's own errors, since we don't want to replicate all of
80 // HCL's type checking rules here.
81 val := cty.UnknownVal(attrS.Type)
82 _, hclDiags := after.TraverseRel(val)
83 diags = diags.Append(hclDiags)
84 return diags
85 }
86
87 if blockS, exists := b.BlockTypes[name]; exists {
88 moreDiags := blockS.staticValidateTraversal(name, after)
89 diags = diags.Append(moreDiags)
90 return diags
91 }
92
93 // If we get here then the name isn't valid at all. We'll collect up
94 // all of the names that _are_ valid to use as suggestions.
95 var suggestions []string
96 for name := range b.Attributes {
97 suggestions = append(suggestions, name)
98 }
99 for name := range b.BlockTypes {
100 suggestions = append(suggestions, name)
101 }
102 sort.Strings(suggestions)
103 suggestion := didyoumean.NameSuggestion(name, suggestions)
104 if suggestion != "" {
105 suggestion = fmt.Sprintf(" Did you mean %q?", suggestion)
106 }
107 diags = diags.Append(&hcl.Diagnostic{
108 Severity: hcl.DiagError,
109 Summary: `Unsupported attribute`,
110 Detail: fmt.Sprintf(`This object has no argument, nested block, or exported attribute named %q.%s`, name, suggestion),
111 Subject: next.SourceRange().Ptr(),
112 })
113
114 return diags
115 }
116
117 func (b *NestedBlock) staticValidateTraversal(typeName string, traversal hcl.Traversal) tfdiags.Diagnostics {
118 if b.Nesting == NestingSingle || b.Nesting == NestingGroup {
119 // Single blocks are easy: just pass right through.
120 return b.Block.StaticValidateTraversal(traversal)
121 }
122
123 if len(traversal) == 0 {
124 // It's always valid to access a nested block's attribute directly.
125 return nil
126 }
127
128 var diags tfdiags.Diagnostics
129 next := traversal[0]
130 after := traversal[1:]
131
132 switch b.Nesting {
133
134 case NestingSet:
135 // Can't traverse into a set at all, since it does not have any keys
136 // to index with.
137 diags = diags.Append(&hcl.Diagnostic{
138 Severity: hcl.DiagError,
139 Summary: `Cannot index a set value`,
140 Detail: fmt.Sprintf(`Block type %q is represented by a set of objects, and set elements do not have addressable keys. To find elements matching specific criteria, use a "for" expression with an "if" clause.`, typeName),
141 Subject: next.SourceRange().Ptr(),
142 })
143 return diags
144
145 case NestingList:
146 if _, ok := next.(hcl.TraverseIndex); ok {
147 moreDiags := b.Block.StaticValidateTraversal(after)
148 diags = diags.Append(moreDiags)
149 } else {
150 diags = diags.Append(&hcl.Diagnostic{
151 Severity: hcl.DiagError,
152 Summary: `Invalid operation`,
153 Detail: fmt.Sprintf(`Block type %q is represented by a list of objects, so it must be indexed using a numeric key, like .%s[0].`, typeName, typeName),
154 Subject: next.SourceRange().Ptr(),
155 })
156 }
157 return diags
158
159 case NestingMap:
160 // Both attribute and index steps are valid for maps, so we'll just
161 // pass through here and let normal evaluation catch an
162 // incorrectly-typed index key later, if present.
163 moreDiags := b.Block.StaticValidateTraversal(after)
164 diags = diags.Append(moreDiags)
165 return diags
166
167 default:
168 // Invalid nesting type is just ignored. It's checked by
169 // InternalValidate. (Note that we handled NestingSingle separately
170 // back at the start of this function.)
171 return nil
172 }
173 }