summaryrefslogtreecommitdiff
path: root/diagnostic.go
diff options
context:
space:
mode:
Diffstat (limited to 'diagnostic.go')
-rw-r--r--diagnostic.go143
1 files changed, 143 insertions, 0 deletions
diff --git a/diagnostic.go b/diagnostic.go
new file mode 100644
index 0000000..c320961
--- /dev/null
+++ b/diagnostic.go
@@ -0,0 +1,143 @@
+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
+}