aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/tfdiags
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/tfdiags')
-rw-r--r--vendor/github.com/hashicorp/terraform/tfdiags/config_traversals.go68
-rw-r--r--vendor/github.com/hashicorp/terraform/tfdiags/contextual.go372
-rw-r--r--vendor/github.com/hashicorp/terraform/tfdiags/diagnostic.go14
-rw-r--r--vendor/github.com/hashicorp/terraform/tfdiags/diagnostic_base.go31
-rw-r--r--vendor/github.com/hashicorp/terraform/tfdiags/diagnostics.go149
-rw-r--r--vendor/github.com/hashicorp/terraform/tfdiags/error.go7
-rw-r--r--vendor/github.com/hashicorp/terraform/tfdiags/hcl.go10
-rw-r--r--vendor/github.com/hashicorp/terraform/tfdiags/rpc_friendly.go6
-rw-r--r--vendor/github.com/hashicorp/terraform/tfdiags/severity_string.go8
-rw-r--r--vendor/github.com/hashicorp/terraform/tfdiags/simple_warning.go7
-rw-r--r--vendor/github.com/hashicorp/terraform/tfdiags/sourceless.go13
11 files changed, 683 insertions, 2 deletions
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/config_traversals.go b/vendor/github.com/hashicorp/terraform/tfdiags/config_traversals.go
new file mode 100644
index 0000000..8e41f46
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/tfdiags/config_traversals.go
@@ -0,0 +1,68 @@
1package tfdiags
2
3import (
4 "bytes"
5 "fmt"
6 "strconv"
7
8 "github.com/zclconf/go-cty/cty"
9)
10
11// FormatCtyPath is a helper function to produce a user-friendly string
12// representation of a cty.Path. The result uses a syntax similar to the
13// HCL expression language in the hope of it being familiar to users.
14func FormatCtyPath(path cty.Path) string {
15 var buf bytes.Buffer
16 for _, step := range path {
17 switch ts := step.(type) {
18 case cty.GetAttrStep:
19 fmt.Fprintf(&buf, ".%s", ts.Name)
20 case cty.IndexStep:
21 buf.WriteByte('[')
22 key := ts.Key
23 keyTy := key.Type()
24 switch {
25 case key.IsNull():
26 buf.WriteString("null")
27 case !key.IsKnown():
28 buf.WriteString("(not yet known)")
29 case keyTy == cty.Number:
30 bf := key.AsBigFloat()
31 buf.WriteString(bf.Text('g', -1))
32 case keyTy == cty.String:
33 buf.WriteString(strconv.Quote(key.AsString()))
34 default:
35 buf.WriteString("...")
36 }
37 buf.WriteByte(']')
38 }
39 }
40 return buf.String()
41}
42
43// FormatError is a helper function to produce a user-friendly string
44// representation of certain special error types that we might want to
45// include in diagnostic messages.
46//
47// This currently has special behavior only for cty.PathError, where a
48// non-empty path is rendered in a HCL-like syntax as context.
49func FormatError(err error) string {
50 perr, ok := err.(cty.PathError)
51 if !ok || len(perr.Path) == 0 {
52 return err.Error()
53 }
54
55 return fmt.Sprintf("%s: %s", FormatCtyPath(perr.Path), perr.Error())
56}
57
58// FormatErrorPrefixed is like FormatError except that it presents any path
59// information after the given prefix string, which is assumed to contain
60// an HCL syntax representation of the value that errors are relative to.
61func FormatErrorPrefixed(err error, prefix string) string {
62 perr, ok := err.(cty.PathError)
63 if !ok || len(perr.Path) == 0 {
64 return fmt.Sprintf("%s: %s", prefix, err.Error())
65 }
66
67 return fmt.Sprintf("%s%s: %s", prefix, FormatCtyPath(perr.Path), perr.Error())
68}
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/contextual.go b/vendor/github.com/hashicorp/terraform/tfdiags/contextual.go
new file mode 100644
index 0000000..25b2140
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/tfdiags/contextual.go
@@ -0,0 +1,372 @@
1package tfdiags
2
3import (
4 "github.com/hashicorp/hcl2/hcl"
5 "github.com/zclconf/go-cty/cty"
6 "github.com/zclconf/go-cty/cty/gocty"
7)
8
9// The "contextual" family of diagnostics are designed to allow separating
10// the detection of a problem from placing that problem in context. For
11// example, some code that is validating an object extracted from configuration
12// may not have access to the configuration that generated it, but can still
13// report problems within that object which the caller can then place in
14// context by calling IsConfigBody on the returned diagnostics.
15//
16// When contextual diagnostics are used, the documentation for a method must
17// be very explicit about what context is implied for any diagnostics returned,
18// to help ensure the expected result.
19
20// contextualFromConfig is an interface type implemented by diagnostic types
21// that can elaborate themselves when given information about the configuration
22// body they are embedded in.
23//
24// Usually this entails extracting source location information in order to
25// populate the "Subject" range.
26type contextualFromConfigBody interface {
27 ElaborateFromConfigBody(hcl.Body) Diagnostic
28}
29
30// InConfigBody returns a copy of the receiver with any config-contextual
31// diagnostics elaborated in the context of the given body.
32func (d Diagnostics) InConfigBody(body hcl.Body) Diagnostics {
33 if len(d) == 0 {
34 return nil
35 }
36
37 ret := make(Diagnostics, len(d))
38 for i, srcDiag := range d {
39 if cd, isCD := srcDiag.(contextualFromConfigBody); isCD {
40 ret[i] = cd.ElaborateFromConfigBody(body)
41 } else {
42 ret[i] = srcDiag
43 }
44 }
45
46 return ret
47}
48
49// AttributeValue returns a diagnostic about an attribute value in an implied current
50// configuration context. This should be returned only from functions whose
51// interface specifies a clear configuration context that this will be
52// resolved in.
53//
54// The given path is relative to the implied configuration context. To describe
55// a top-level attribute, it should be a single-element cty.Path with a
56// cty.GetAttrStep. It's assumed that the path is returning into a structure
57// that would be produced by our conventions in the configschema package; it
58// may return unexpected results for structures that can't be represented by
59// configschema.
60//
61// Since mapping attribute paths back onto configuration is an imprecise
62// operation (e.g. dynamic block generation may cause the same block to be
63// evaluated multiple times) the diagnostic detail should include the attribute
64// name and other context required to help the user understand what is being
65// referenced in case the identified source range is not unique.
66//
67// The returned attribute will not have source location information until
68// context is applied to the containing diagnostics using diags.InConfigBody.
69// After context is applied, the source location is the value assigned to the
70// named attribute, or the containing body's "missing item range" if no
71// value is present.
72func AttributeValue(severity Severity, summary, detail string, attrPath cty.Path) Diagnostic {
73 return &attributeDiagnostic{
74 diagnosticBase: diagnosticBase{
75 severity: severity,
76 summary: summary,
77 detail: detail,
78 },
79 attrPath: attrPath,
80 }
81}
82
83// GetAttribute extracts an attribute cty.Path from a diagnostic if it contains
84// one. Normally this is not accessed directly, and instead the config body is
85// added to the Diagnostic to create a more complete message for the user. In
86// some cases however, we may want to know just the name of the attribute that
87// generated the Diagnostic message.
88// This returns a nil cty.Path if it does not exist in the Diagnostic.
89func GetAttribute(d Diagnostic) cty.Path {
90 if d, ok := d.(*attributeDiagnostic); ok {
91 return d.attrPath
92 }
93 return nil
94}
95
96type attributeDiagnostic struct {
97 diagnosticBase
98 attrPath cty.Path
99 subject *SourceRange // populated only after ElaborateFromConfigBody
100}
101
102// ElaborateFromConfigBody finds the most accurate possible source location
103// for a diagnostic's attribute path within the given body.
104//
105// Backing out from a path back to a source location is not always entirely
106// possible because we lose some information in the decoding process, so
107// if an exact position cannot be found then the returned diagnostic will
108// refer to a position somewhere within the containing body, which is assumed
109// to be better than no location at all.
110//
111// If possible it is generally better to report an error at a layer where
112// source location information is still available, for more accuracy. This
113// is not always possible due to system architecture, so this serves as a
114// "best effort" fallback behavior for such situations.
115func (d *attributeDiagnostic) ElaborateFromConfigBody(body hcl.Body) Diagnostic {
116 if len(d.attrPath) < 1 {
117 // Should never happen, but we'll allow it rather than crashing.
118 return d
119 }
120
121 if d.subject != nil {
122 // Don't modify an already-elaborated diagnostic.
123 return d
124 }
125
126 ret := *d
127
128 // This function will often end up re-decoding values that were already
129 // decoded by an earlier step. This is non-ideal but is architecturally
130 // more convenient than arranging for source location information to be
131 // propagated to every place in Terraform, and this happens only in the
132 // presence of errors where performance isn't a concern.
133
134 traverse := d.attrPath[:]
135 final := d.attrPath[len(d.attrPath)-1]
136
137 // Index should never be the first step
138 // as indexing of top blocks (such as resources & data sources)
139 // is handled elsewhere
140 if _, isIdxStep := traverse[0].(cty.IndexStep); isIdxStep {
141 subject := SourceRangeFromHCL(body.MissingItemRange())
142 ret.subject = &subject
143 return &ret
144 }
145
146 // Process index separately
147 idxStep, hasIdx := final.(cty.IndexStep)
148 if hasIdx {
149 final = d.attrPath[len(d.attrPath)-2]
150 traverse = d.attrPath[:len(d.attrPath)-1]
151 }
152
153 // If we have more than one step after removing index
154 // then we'll first try to traverse to a child body
155 // corresponding to the requested path.
156 if len(traverse) > 1 {
157 body = traversePathSteps(traverse, body)
158 }
159
160 // Default is to indicate a missing item in the deepest body we reached
161 // while traversing.
162 subject := SourceRangeFromHCL(body.MissingItemRange())
163 ret.subject = &subject
164
165 // Once we get here, "final" should be a GetAttr step that maps to an
166 // attribute in our current body.
167 finalStep, isAttr := final.(cty.GetAttrStep)
168 if !isAttr {
169 return &ret
170 }
171
172 content, _, contentDiags := body.PartialContent(&hcl.BodySchema{
173 Attributes: []hcl.AttributeSchema{
174 {
175 Name: finalStep.Name,
176 Required: true,
177 },
178 },
179 })
180 if contentDiags.HasErrors() {
181 return &ret
182 }
183
184 if attr, ok := content.Attributes[finalStep.Name]; ok {
185 hclRange := attr.Expr.Range()
186 if hasIdx {
187 // Try to be more precise by finding index range
188 hclRange = hclRangeFromIndexStepAndAttribute(idxStep, attr)
189 }
190 subject = SourceRangeFromHCL(hclRange)
191 ret.subject = &subject
192 }
193
194 return &ret
195}
196
197func traversePathSteps(traverse []cty.PathStep, body hcl.Body) hcl.Body {
198 for i := 0; i < len(traverse); i++ {
199 step := traverse[i]
200
201 switch tStep := step.(type) {
202 case cty.GetAttrStep:
203
204 var next cty.PathStep
205 if i < (len(traverse) - 1) {
206 next = traverse[i+1]
207 }
208
209 // Will be indexing into our result here?
210 var indexType cty.Type
211 var indexVal cty.Value
212 if nextIndex, ok := next.(cty.IndexStep); ok {
213 indexVal = nextIndex.Key
214 indexType = indexVal.Type()
215 i++ // skip over the index on subsequent iterations
216 }
217
218 var blockLabelNames []string
219 if indexType == cty.String {
220 // Map traversal means we expect one label for the key.
221 blockLabelNames = []string{"key"}
222 }
223
224 // For intermediate steps we expect to be referring to a child
225 // block, so we'll attempt decoding under that assumption.
226 content, _, contentDiags := body.PartialContent(&hcl.BodySchema{
227 Blocks: []hcl.BlockHeaderSchema{
228 {
229 Type: tStep.Name,
230 LabelNames: blockLabelNames,
231 },
232 },
233 })
234 if contentDiags.HasErrors() {
235 return body
236 }
237 filtered := make([]*hcl.Block, 0, len(content.Blocks))
238 for _, block := range content.Blocks {
239 if block.Type == tStep.Name {
240 filtered = append(filtered, block)
241 }
242 }
243 if len(filtered) == 0 {
244 // Step doesn't refer to a block
245 continue
246 }
247
248 switch indexType {
249 case cty.NilType: // no index at all
250 if len(filtered) != 1 {
251 return body
252 }
253 body = filtered[0].Body
254 case cty.Number:
255 var idx int
256 err := gocty.FromCtyValue(indexVal, &idx)
257 if err != nil || idx >= len(filtered) {
258 return body
259 }
260 body = filtered[idx].Body
261 case cty.String:
262 key := indexVal.AsString()
263 var block *hcl.Block
264 for _, candidate := range filtered {
265 if candidate.Labels[0] == key {
266 block = candidate
267 break
268 }
269 }
270 if block == nil {
271 // No block with this key, so we'll just indicate a
272 // missing item in the containing block.
273 return body
274 }
275 body = block.Body
276 default:
277 // Should never happen, because only string and numeric indices
278 // are supported by cty collections.
279 return body
280 }
281
282 default:
283 // For any other kind of step, we'll just return our current body
284 // as the subject and accept that this is a little inaccurate.
285 return body
286 }
287 }
288 return body
289}
290
291func hclRangeFromIndexStepAndAttribute(idxStep cty.IndexStep, attr *hcl.Attribute) hcl.Range {
292 switch idxStep.Key.Type() {
293 case cty.Number:
294 var idx int
295 err := gocty.FromCtyValue(idxStep.Key, &idx)
296 items, diags := hcl.ExprList(attr.Expr)
297 if diags.HasErrors() {
298 return attr.Expr.Range()
299 }
300 if err != nil || idx >= len(items) {
301 return attr.NameRange
302 }
303 return items[idx].Range()
304 case cty.String:
305 pairs, diags := hcl.ExprMap(attr.Expr)
306 if diags.HasErrors() {
307 return attr.Expr.Range()
308 }
309 stepKey := idxStep.Key.AsString()
310 for _, kvPair := range pairs {
311 key, err := kvPair.Key.Value(nil)
312 if err != nil {
313 return attr.Expr.Range()
314 }
315 if key.AsString() == stepKey {
316 startRng := kvPair.Value.StartRange()
317 return startRng
318 }
319 }
320 return attr.NameRange
321 }
322 return attr.Expr.Range()
323}
324
325func (d *attributeDiagnostic) Source() Source {
326 return Source{
327 Subject: d.subject,
328 }
329}
330
331// WholeContainingBody returns a diagnostic about the body that is an implied
332// current configuration context. This should be returned only from
333// functions whose interface specifies a clear configuration context that this
334// will be resolved in.
335//
336// The returned attribute will not have source location information until
337// context is applied to the containing diagnostics using diags.InConfigBody.
338// After context is applied, the source location is currently the missing item
339// range of the body. In future, this may change to some other suitable
340// part of the containing body.
341func WholeContainingBody(severity Severity, summary, detail string) Diagnostic {
342 return &wholeBodyDiagnostic{
343 diagnosticBase: diagnosticBase{
344 severity: severity,
345 summary: summary,
346 detail: detail,
347 },
348 }
349}
350
351type wholeBodyDiagnostic struct {
352 diagnosticBase
353 subject *SourceRange // populated only after ElaborateFromConfigBody
354}
355
356func (d *wholeBodyDiagnostic) ElaborateFromConfigBody(body hcl.Body) Diagnostic {
357 if d.subject != nil {
358 // Don't modify an already-elaborated diagnostic.
359 return d
360 }
361
362 ret := *d
363 rng := SourceRangeFromHCL(body.MissingItemRange())
364 ret.subject = &rng
365 return &ret
366}
367
368func (d *wholeBodyDiagnostic) Source() Source {
369 return Source{
370 Subject: d.subject,
371 }
372}
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/diagnostic.go b/vendor/github.com/hashicorp/terraform/tfdiags/diagnostic.go
index 2c23f76..c91ba9a 100644
--- a/vendor/github.com/hashicorp/terraform/tfdiags/diagnostic.go
+++ b/vendor/github.com/hashicorp/terraform/tfdiags/diagnostic.go
@@ -1,9 +1,18 @@
1package tfdiags 1package tfdiags
2 2
3import (
4 "github.com/hashicorp/hcl2/hcl"
5)
6
3type Diagnostic interface { 7type Diagnostic interface {
4 Severity() Severity 8 Severity() Severity
5 Description() Description 9 Description() Description
6 Source() Source 10 Source() Source
11
12 // FromExpr returns the expression-related context for the diagnostic, if
13 // available. Returns nil if the diagnostic is not related to an
14 // expression evaluation.
15 FromExpr() *FromExpr
7} 16}
8 17
9type Severity rune 18type Severity rune
@@ -24,3 +33,8 @@ type Source struct {
24 Subject *SourceRange 33 Subject *SourceRange
25 Context *SourceRange 34 Context *SourceRange
26} 35}
36
37type FromExpr struct {
38 Expression hcl.Expression
39 EvalContext *hcl.EvalContext
40}
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/diagnostic_base.go b/vendor/github.com/hashicorp/terraform/tfdiags/diagnostic_base.go
new file mode 100644
index 0000000..50bf9d8
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/tfdiags/diagnostic_base.go
@@ -0,0 +1,31 @@
1package tfdiags
2
3// diagnosticBase can be embedded in other diagnostic structs to get
4// default implementations of Severity and Description. This type also
5// has default implementations of Source and FromExpr that return no source
6// location or expression-related information, so embedders should generally
7// override those method to return more useful results where possible.
8type diagnosticBase struct {
9 severity Severity
10 summary string
11 detail string
12}
13
14func (d diagnosticBase) Severity() Severity {
15 return d.severity
16}
17
18func (d diagnosticBase) Description() Description {
19 return Description{
20 Summary: d.summary,
21 Detail: d.detail,
22 }
23}
24
25func (d diagnosticBase) Source() Source {
26 return Source{}
27}
28
29func (d diagnosticBase) FromExpr() *FromExpr {
30 return nil
31}
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/diagnostics.go b/vendor/github.com/hashicorp/terraform/tfdiags/diagnostics.go
index 667ba80..465b230 100644
--- a/vendor/github.com/hashicorp/terraform/tfdiags/diagnostics.go
+++ b/vendor/github.com/hashicorp/terraform/tfdiags/diagnostics.go
@@ -3,6 +3,9 @@ package tfdiags
3import ( 3import (
4 "bytes" 4 "bytes"
5 "fmt" 5 "fmt"
6 "path/filepath"
7 "sort"
8 "strings"
6 9
7 "github.com/hashicorp/errwrap" 10 "github.com/hashicorp/errwrap"
8 multierror "github.com/hashicorp/go-multierror" 11 multierror "github.com/hashicorp/go-multierror"
@@ -54,6 +57,8 @@ func (diags Diagnostics) Append(new ...interface{}) Diagnostics {
54 diags = append(diags, ti...) // flatten 57 diags = append(diags, ti...) // flatten
55 case diagnosticsAsError: 58 case diagnosticsAsError:
56 diags = diags.Append(ti.Diagnostics) // unwrap 59 diags = diags.Append(ti.Diagnostics) // unwrap
60 case NonFatalError:
61 diags = diags.Append(ti.Diagnostics) // unwrap
57 case hcl.Diagnostics: 62 case hcl.Diagnostics:
58 for _, hclDiag := range ti { 63 for _, hclDiag := range ti {
59 diags = append(diags, hclDiagnostic{hclDiag}) 64 diags = append(diags, hclDiagnostic{hclDiag})
@@ -136,6 +141,54 @@ func (diags Diagnostics) Err() error {
136 return diagnosticsAsError{diags} 141 return diagnosticsAsError{diags}
137} 142}
138 143
144// ErrWithWarnings is similar to Err except that it will also return a non-nil
145// error if the receiver contains only warnings.
146//
147// In the warnings-only situation, the result is guaranteed to be of dynamic
148// type NonFatalError, allowing diagnostics-aware callers to type-assert
149// and unwrap it, treating it as non-fatal.
150//
151// This should be used only in contexts where the caller is able to recognize
152// and handle NonFatalError. For normal callers that expect a lack of errors
153// to be signaled by nil, use just Diagnostics.Err.
154func (diags Diagnostics) ErrWithWarnings() error {
155 if len(diags) == 0 {
156 return nil
157 }
158 if diags.HasErrors() {
159 return diags.Err()
160 }
161 return NonFatalError{diags}
162}
163
164// NonFatalErr is similar to Err except that it always returns either nil
165// (if there are no diagnostics at all) or NonFatalError.
166//
167// This allows diagnostics to be returned over an error return channel while
168// being explicit that the diagnostics should not halt processing.
169//
170// This should be used only in contexts where the caller is able to recognize
171// and handle NonFatalError. For normal callers that expect a lack of errors
172// to be signaled by nil, use just Diagnostics.Err.
173func (diags Diagnostics) NonFatalErr() error {
174 if len(diags) == 0 {
175 return nil
176 }
177 return NonFatalError{diags}
178}
179
180// Sort applies an ordering to the diagnostics in the receiver in-place.
181//
182// The ordering is: warnings before errors, sourceless before sourced,
183// short source paths before long source paths, and then ordering by
184// position within each file.
185//
186// Diagnostics that do not differ by any of these sortable characteristics
187// will remain in the same relative order after this method returns.
188func (diags Diagnostics) Sort() {
189 sort.Stable(sortDiagnostics(diags))
190}
191
139type diagnosticsAsError struct { 192type diagnosticsAsError struct {
140 Diagnostics 193 Diagnostics
141} 194}
@@ -179,3 +232,99 @@ func (dae diagnosticsAsError) WrappedErrors() []error {
179 } 232 }
180 return errs 233 return errs
181} 234}
235
236// NonFatalError is a special error type, returned by
237// Diagnostics.ErrWithWarnings and Diagnostics.NonFatalErr,
238// that indicates that the wrapped diagnostics should be treated as non-fatal.
239// Callers can conditionally type-assert an error to this type in order to
240// detect the non-fatal scenario and handle it in a different way.
241type NonFatalError struct {
242 Diagnostics
243}
244
245func (woe NonFatalError) Error() string {
246 diags := woe.Diagnostics
247 switch {
248 case len(diags) == 0:
249 // should never happen, since we don't create this wrapper if
250 // there are no diagnostics in the list.
251 return "no errors or warnings"
252 case len(diags) == 1:
253 desc := diags[0].Description()
254 if desc.Detail == "" {
255 return desc.Summary
256 }
257 return fmt.Sprintf("%s: %s", desc.Summary, desc.Detail)
258 default:
259 var ret bytes.Buffer
260 if diags.HasErrors() {
261 fmt.Fprintf(&ret, "%d problems:\n", len(diags))
262 } else {
263 fmt.Fprintf(&ret, "%d warnings:\n", len(diags))
264 }
265 for _, diag := range woe.Diagnostics {
266 desc := diag.Description()
267 if desc.Detail == "" {
268 fmt.Fprintf(&ret, "\n- %s", desc.Summary)
269 } else {
270 fmt.Fprintf(&ret, "\n- %s: %s", desc.Summary, desc.Detail)
271 }
272 }
273 return ret.String()
274 }
275}
276
277// sortDiagnostics is an implementation of sort.Interface
278type sortDiagnostics []Diagnostic
279
280var _ sort.Interface = sortDiagnostics(nil)
281
282func (sd sortDiagnostics) Len() int {
283 return len(sd)
284}
285
286func (sd sortDiagnostics) Less(i, j int) bool {
287 iD, jD := sd[i], sd[j]
288 iSev, jSev := iD.Severity(), jD.Severity()
289 iSrc, jSrc := iD.Source(), jD.Source()
290
291 switch {
292
293 case iSev != jSev:
294 return iSev == Warning
295
296 case (iSrc.Subject == nil) != (jSrc.Subject == nil):
297 return iSrc.Subject == nil
298
299 case iSrc.Subject != nil && *iSrc.Subject != *jSrc.Subject:
300 iSubj := iSrc.Subject
301 jSubj := jSrc.Subject
302 switch {
303 case iSubj.Filename != jSubj.Filename:
304 // Path with fewer segments goes first if they are different lengths
305 sep := string(filepath.Separator)
306 iCount := strings.Count(iSubj.Filename, sep)
307 jCount := strings.Count(jSubj.Filename, sep)
308 if iCount != jCount {
309 return iCount < jCount
310 }
311 return iSubj.Filename < jSubj.Filename
312 case iSubj.Start.Byte != jSubj.Start.Byte:
313 return iSubj.Start.Byte < jSubj.Start.Byte
314 case iSubj.End.Byte != jSubj.End.Byte:
315 return iSubj.End.Byte < jSubj.End.Byte
316 }
317 fallthrough
318
319 default:
320 // The remaining properties do not have a defined ordering, so
321 // we'll leave it unspecified. Since we use sort.Stable in
322 // the caller of this, the ordering of remaining items will
323 // be preserved.
324 return false
325 }
326}
327
328func (sd sortDiagnostics) Swap(i, j int) {
329 sd[i], sd[j] = sd[j], sd[i]
330}
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/error.go b/vendor/github.com/hashicorp/terraform/tfdiags/error.go
index 35edc30..13f7a71 100644
--- a/vendor/github.com/hashicorp/terraform/tfdiags/error.go
+++ b/vendor/github.com/hashicorp/terraform/tfdiags/error.go
@@ -13,7 +13,7 @@ func (e nativeError) Severity() Severity {
13 13
14func (e nativeError) Description() Description { 14func (e nativeError) Description() Description {
15 return Description{ 15 return Description{
16 Summary: e.err.Error(), 16 Summary: FormatError(e.err),
17 } 17 }
18} 18}
19 19
@@ -21,3 +21,8 @@ func (e nativeError) Source() Source {
21 // No source information available for a native error 21 // No source information available for a native error
22 return Source{} 22 return Source{}
23} 23}
24
25func (e nativeError) FromExpr() *FromExpr {
26 // Native errors are not expression-related
27 return nil
28}
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/hcl.go b/vendor/github.com/hashicorp/terraform/tfdiags/hcl.go
index 24851f4..f9aec41 100644
--- a/vendor/github.com/hashicorp/terraform/tfdiags/hcl.go
+++ b/vendor/github.com/hashicorp/terraform/tfdiags/hcl.go
@@ -40,6 +40,16 @@ func (d hclDiagnostic) Source() Source {
40 return ret 40 return ret
41} 41}
42 42
43func (d hclDiagnostic) FromExpr() *FromExpr {
44 if d.diag.Expression == nil || d.diag.EvalContext == nil {
45 return nil
46 }
47 return &FromExpr{
48 Expression: d.diag.Expression,
49 EvalContext: d.diag.EvalContext,
50 }
51}
52
43// SourceRangeFromHCL constructs a SourceRange from the corresponding range 53// SourceRangeFromHCL constructs a SourceRange from the corresponding range
44// type within the HCL package. 54// type within the HCL package.
45func SourceRangeFromHCL(hclRange hcl.Range) SourceRange { 55func SourceRangeFromHCL(hclRange hcl.Range) SourceRange {
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/rpc_friendly.go b/vendor/github.com/hashicorp/terraform/tfdiags/rpc_friendly.go
index 6cc95cc..485063b 100644
--- a/vendor/github.com/hashicorp/terraform/tfdiags/rpc_friendly.go
+++ b/vendor/github.com/hashicorp/terraform/tfdiags/rpc_friendly.go
@@ -48,6 +48,12 @@ func (d *rpcFriendlyDiag) Source() Source {
48 } 48 }
49} 49}
50 50
51func (d rpcFriendlyDiag) FromExpr() *FromExpr {
52 // RPC-friendly diagnostics cannot preserve expression information because
53 // expressions themselves are not RPC-friendly.
54 return nil
55}
56
51func init() { 57func init() {
52 gob.Register((*rpcFriendlyDiag)(nil)) 58 gob.Register((*rpcFriendlyDiag)(nil))
53} 59}
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/severity_string.go b/vendor/github.com/hashicorp/terraform/tfdiags/severity_string.go
index 0b1249b..78a7210 100644
--- a/vendor/github.com/hashicorp/terraform/tfdiags/severity_string.go
+++ b/vendor/github.com/hashicorp/terraform/tfdiags/severity_string.go
@@ -4,6 +4,14 @@ package tfdiags
4 4
5import "strconv" 5import "strconv"
6 6
7func _() {
8 // An "invalid array index" compiler error signifies that the constant values have changed.
9 // Re-run the stringer command to generate them again.
10 var x [1]struct{}
11 _ = x[Error-69]
12 _ = x[Warning-87]
13}
14
7const ( 15const (
8 _Severity_name_0 = "Error" 16 _Severity_name_0 = "Error"
9 _Severity_name_1 = "Warning" 17 _Severity_name_1 = "Warning"
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/simple_warning.go b/vendor/github.com/hashicorp/terraform/tfdiags/simple_warning.go
index fb3ac98..b0f1ecd 100644
--- a/vendor/github.com/hashicorp/terraform/tfdiags/simple_warning.go
+++ b/vendor/github.com/hashicorp/terraform/tfdiags/simple_warning.go
@@ -20,6 +20,11 @@ func (e simpleWarning) Description() Description {
20} 20}
21 21
22func (e simpleWarning) Source() Source { 22func (e simpleWarning) Source() Source {
23 // No source information available for a native error 23 // No source information available for a simple warning
24 return Source{} 24 return Source{}
25} 25}
26
27func (e simpleWarning) FromExpr() *FromExpr {
28 // Simple warnings are not expression-related
29 return nil
30}
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/sourceless.go b/vendor/github.com/hashicorp/terraform/tfdiags/sourceless.go
new file mode 100644
index 0000000..eaa2737
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/tfdiags/sourceless.go
@@ -0,0 +1,13 @@
1package tfdiags
2
3// Sourceless creates and returns a diagnostic with no source location
4// information. This is generally used for operational-type errors that are
5// caused by or relate to the environment where Terraform is running rather
6// than to the provided configuration.
7func Sourceless(severity Severity, summary, detail string) Diagnostic {
8 return diagnosticBase{
9 severity: severity,
10 summary: summary,
11 detail: detail,
12 }
13}