]>
Commit | Line | Data |
---|---|---|
15c0b25d AP |
1 | package hcl |
2 | ||
3 | import ( | |
4 | "fmt" | |
5 | ) | |
6 | ||
7 | // DiagnosticSeverity represents the severity of a diagnostic. | |
8 | type DiagnosticSeverity int | |
9 | ||
10 | const ( | |
11 | // DiagInvalid is the invalid zero value of DiagnosticSeverity | |
12 | DiagInvalid DiagnosticSeverity = iota | |
13 | ||
14 | // DiagError indicates that the problem reported by a diagnostic prevents | |
15 | // further progress in parsing and/or evaluating the subject. | |
16 | DiagError | |
17 | ||
18 | // DiagWarning indicates that the problem reported by a diagnostic warrants | |
19 | // user attention but does not prevent further progress. It is most | |
20 | // commonly used for showing deprecation notices. | |
21 | DiagWarning | |
22 | ) | |
23 | ||
24 | // Diagnostic represents information to be presented to a user about an | |
25 | // error or anomoly in parsing or evaluating configuration. | |
26 | type Diagnostic struct { | |
27 | Severity DiagnosticSeverity | |
28 | ||
107c1cdb | 29 | // Summary and Detail contain the English-language description of the |
15c0b25d AP |
30 | // problem. Summary is a terse description of the general problem and |
31 | // detail is a more elaborate, often-multi-sentence description of | |
32 | // the probem and what might be done to solve it. | |
33 | Summary string | |
34 | Detail string | |
107c1cdb ND |
35 | |
36 | // Subject and Context are both source ranges relating to the diagnostic. | |
37 | // | |
38 | // Subject is a tight range referring to exactly the construct that | |
39 | // is problematic, while Context is an optional broader range (which should | |
40 | // fully contain Subject) that ought to be shown around Subject when | |
41 | // generating isolated source-code snippets in diagnostic messages. | |
42 | // If Context is nil, the Subject is also the Context. | |
43 | // | |
44 | // Some diagnostics have no source ranges at all. If Context is set then | |
45 | // Subject should always also be set. | |
15c0b25d AP |
46 | Subject *Range |
47 | Context *Range | |
107c1cdb ND |
48 | |
49 | // For diagnostics that occur when evaluating an expression, Expression | |
50 | // may refer to that expression and EvalContext may point to the | |
51 | // EvalContext that was active when evaluating it. This may allow for the | |
52 | // inclusion of additional useful information when rendering a diagnostic | |
53 | // message to the user. | |
54 | // | |
55 | // It is not always possible to select a single EvalContext for a | |
56 | // diagnostic, and so in some cases this field may be nil even when an | |
57 | // expression causes a problem. | |
58 | // | |
59 | // EvalContexts form a tree, so the given EvalContext may refer to a parent | |
60 | // which in turn refers to another parent, etc. For a full picture of all | |
61 | // of the active variables and functions the caller must walk up this | |
62 | // chain, preferring definitions that are "closer" to the expression in | |
63 | // case of colliding names. | |
64 | Expression Expression | |
65 | EvalContext *EvalContext | |
15c0b25d AP |
66 | } |
67 | ||
68 | // Diagnostics is a list of Diagnostic instances. | |
69 | type Diagnostics []*Diagnostic | |
70 | ||
71 | // error implementation, so that diagnostics can be returned via APIs | |
72 | // that normally deal in vanilla Go errors. | |
73 | // | |
74 | // This presents only minimal context about the error, for compatibility | |
75 | // with usual expectations about how errors will present as strings. | |
76 | func (d *Diagnostic) Error() string { | |
77 | return fmt.Sprintf("%s: %s; %s", d.Subject, d.Summary, d.Detail) | |
78 | } | |
79 | ||
80 | // error implementation, so that sets of diagnostics can be returned via | |
81 | // APIs that normally deal in vanilla Go errors. | |
82 | func (d Diagnostics) Error() string { | |
83 | count := len(d) | |
84 | switch { | |
85 | case count == 0: | |
86 | return "no diagnostics" | |
87 | case count == 1: | |
88 | return d[0].Error() | |
89 | default: | |
90 | return fmt.Sprintf("%s, and %d other diagnostic(s)", d[0].Error(), count-1) | |
91 | } | |
92 | } | |
93 | ||
94 | // Append appends a new error to a Diagnostics and return the whole Diagnostics. | |
95 | // | |
96 | // This is provided as a convenience for returning from a function that | |
97 | // collects and then returns a set of diagnostics: | |
98 | // | |
99 | // return nil, diags.Append(&hcl.Diagnostic{ ... }) | |
100 | // | |
101 | // Note that this modifies the array underlying the diagnostics slice, so | |
102 | // must be used carefully within a single codepath. It is incorrect (and rude) | |
103 | // to extend a diagnostics created by a different subsystem. | |
104 | func (d Diagnostics) Append(diag *Diagnostic) Diagnostics { | |
105 | return append(d, diag) | |
106 | } | |
107 | ||
108 | // Extend concatenates the given Diagnostics with the receiver and returns | |
109 | // the whole new Diagnostics. | |
110 | // | |
111 | // This is similar to Append but accepts multiple diagnostics to add. It has | |
112 | // all the same caveats and constraints. | |
113 | func (d Diagnostics) Extend(diags Diagnostics) Diagnostics { | |
114 | return append(d, diags...) | |
115 | } | |
116 | ||
117 | // HasErrors returns true if the receiver contains any diagnostics of | |
118 | // severity DiagError. | |
119 | func (d Diagnostics) HasErrors() bool { | |
120 | for _, diag := range d { | |
121 | if diag.Severity == DiagError { | |
122 | return true | |
123 | } | |
124 | } | |
125 | return false | |
126 | } | |
127 | ||
107c1cdb ND |
128 | func (d Diagnostics) Errs() []error { |
129 | var errs []error | |
130 | for _, diag := range d { | |
131 | if diag.Severity == DiagError { | |
132 | errs = append(errs, diag) | |
133 | } | |
134 | } | |
135 | ||
136 | return errs | |
137 | } | |
138 | ||
15c0b25d AP |
139 | // A DiagnosticWriter emits diagnostics somehow. |
140 | type DiagnosticWriter interface { | |
141 | WriteDiagnostic(*Diagnostic) error | |
142 | WriteDiagnostics(Diagnostics) error | |
143 | } |