144 lines
4.7 KiB
Go
144 lines
4.7 KiB
Go
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 anomaly 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 problem 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
|
|
}
|