| package hcl |
| |
| import ( |
| "fmt" |
| ) |
| |
| // DiagnosticSeverity represents the severity of a diagnostic. |
| type DiagnosticSeverity int |
| |
| const ( |
| // DiagInvalid is the invalid zero value of DiagnosticSeverity |
| DiagInvalid DiagnosticSeverity = iota |
| |
| // DiagError indicates that the problem reported by a diagnostic prevents |
| // further progress in parsing and/or evaluating the subject. |
| DiagError |
| |
| // DiagWarning indicates that the problem reported by a diagnostic warrants |
| // user attention but does not prevent further progress. It is most |
| // commonly used for showing deprecation notices. |
| DiagWarning |
| ) |
| |
| // Diagnostic represents information to be presented to a user about an |
| // error or anomoly in parsing or evaluating configuration. |
| type Diagnostic struct { |
| Severity DiagnosticSeverity |
| |
| // Summary and Detail contain the English-language description of the |
| // problem. Summary is a terse description of the general problem and |
| // detail is a more elaborate, often-multi-sentence description of |
| // the probem and what might be done to solve it. |
| Summary string |
| Detail string |
| |
| // Subject and Context are both source ranges relating to the diagnostic. |
| // |
| // Subject is a tight range referring to exactly the construct that |
| // is problematic, while Context is an optional broader range (which should |
| // fully contain Subject) that ought to be shown around Subject when |
| // generating isolated source-code snippets in diagnostic messages. |
| // If Context is nil, the Subject is also the Context. |
| // |
| // Some diagnostics have no source ranges at all. If Context is set then |
| // Subject should always also be set. |
| Subject *Range |
| Context *Range |
| |
| // For diagnostics that occur when evaluating an expression, Expression |
| // may refer to that expression and EvalContext may point to the |
| // EvalContext that was active when evaluating it. This may allow for the |
| // inclusion of additional useful information when rendering a diagnostic |
| // message to the user. |
| // |
| // It is not always possible to select a single EvalContext for a |
| // diagnostic, and so in some cases this field may be nil even when an |
| // expression causes a problem. |
| // |
| // EvalContexts form a tree, so the given EvalContext may refer to a parent |
| // which in turn refers to another parent, etc. For a full picture of all |
| // of the active variables and functions the caller must walk up this |
| // chain, preferring definitions that are "closer" to the expression in |
| // case of colliding names. |
| Expression Expression |
| EvalContext *EvalContext |
| } |
| |
| // Diagnostics is a list of Diagnostic instances. |
| type Diagnostics []*Diagnostic |
| |
| // error implementation, so that diagnostics can be returned via APIs |
| // that normally deal in vanilla Go errors. |
| // |
| // This presents only minimal context about the error, for compatibility |
| // with usual expectations about how errors will present as strings. |
| func (d *Diagnostic) Error() string { |
| return fmt.Sprintf("%s: %s; %s", d.Subject, d.Summary, d.Detail) |
| } |
| |
| // error implementation, so that sets of diagnostics can be returned via |
| // APIs that normally deal in vanilla Go errors. |
| func (d Diagnostics) Error() string { |
| count := len(d) |
| switch { |
| case count == 0: |
| return "no diagnostics" |
| case count == 1: |
| return d[0].Error() |
| default: |
| return fmt.Sprintf("%s, and %d other diagnostic(s)", d[0].Error(), count-1) |
| } |
| } |
| |
| // Append appends a new error to a Diagnostics and return the whole Diagnostics. |
| // |
| // This is provided as a convenience for returning from a function that |
| // collects and then returns a set of diagnostics: |
| // |
| // return nil, diags.Append(&hcl.Diagnostic{ ... }) |
| // |
| // Note that this modifies the array underlying the diagnostics slice, so |
| // must be used carefully within a single codepath. It is incorrect (and rude) |
| // to extend a diagnostics created by a different subsystem. |
| func (d Diagnostics) Append(diag *Diagnostic) Diagnostics { |
| return append(d, diag) |
| } |
| |
| // Extend concatenates the given Diagnostics with the receiver and returns |
| // the whole new Diagnostics. |
| // |
| // This is similar to Append but accepts multiple diagnostics to add. It has |
| // all the same caveats and constraints. |
| func (d Diagnostics) Extend(diags Diagnostics) Diagnostics { |
| return append(d, diags...) |
| } |
| |
| // HasErrors returns true if the receiver contains any diagnostics of |
| // severity DiagError. |
| func (d Diagnostics) HasErrors() bool { |
| for _, diag := range d { |
| if diag.Severity == DiagError { |
| return true |
| } |
| } |
| return false |
| } |
| |
| func (d Diagnostics) Errs() []error { |
| var errs []error |
| for _, diag := range d { |
| if diag.Severity == DiagError { |
| errs = append(errs, diag) |
| } |
| } |
| |
| return errs |
| } |
| |
| // A DiagnosticWriter emits diagnostics somehow. |
| type DiagnosticWriter interface { |
| WriteDiagnostic(*Diagnostic) error |
| WriteDiagnostics(Diagnostics) error |
| } |