| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534 |
- // Copyright (c) 2019 Uber Technologies, Inc.
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- package dig
- import (
- "errors"
- "fmt"
- "io"
- "reflect"
- "sort"
- "go.uber.org/dig/internal/digreflect"
- "go.uber.org/dig/internal/dot"
- )
- // Errors which know their underlying cause should implement this interface to
- // be compatible with RootCause.
- //
- // We use unexported methods because we don't want dig-internal causes to be
- // confused with the cause of the user-provided errors. For example, if we
- // used Unwrap(), then user-provided methods would also be unwrapped by
- // RootCause. We want RootCause to eliminate the dig error chain only.
- type causer interface {
- fmt.Formatter
- // Returns the next error in the chain.
- cause() error
- // Writes the message or context for this error in the chain.
- //
- // verb is either %v or %+v.
- writeMessage(w io.Writer, verb string)
- }
- // Implements fmt.Formatter for errors that implement causer.
- //
- // This Format method supports %v and %+v. In the %v form, the error is
- // printed on one line. In the %+v form, the error is split across multiple
- // lines on each error in the error chain.
- func formatCauser(c causer, w fmt.State, v rune) {
- multiline := w.Flag('+') && v == 'v'
- verb := "%v"
- if multiline {
- verb = "%+v"
- }
- // "context: " or "context:\n"
- c.writeMessage(w, verb)
- io.WriteString(w, ":")
- if multiline {
- io.WriteString(w, "\n")
- } else {
- io.WriteString(w, " ")
- }
- fmt.Fprintf(w, verb, c.cause())
- }
- // RootCause returns the original error that caused the provided dig failure.
- //
- // RootCause may be used on errors returned by Invoke to get the original
- // error returned by a constructor or invoked function.
- func RootCause(err error) error {
- for {
- if e, ok := err.(causer); ok {
- err = e.cause()
- } else {
- return err
- }
- }
- }
- // errf is a version of fmt.Errorf with support for a chain of multiple
- // formatted error messages.
- //
- // After msg, N arguments are consumed as formatting arguments for that
- // message, where N is the number of % symbols in msg. Following that, another
- // string may be added to become the next error in the chain. Each new error
- // is the `cause()` for the prior error.
- //
- // err := errf(
- // "could not process %v", thing,
- // "name %q is invalid", thing.Name,
- // )
- // fmt.Println(err) // could not process Thing: name Foo is invalid
- // fmt.Println(RootCause(err)) // name Foo is invalid
- //
- // In place of a string, the last error can be another error, in which case it
- // will be treated as the cause of the prior error chain.
- //
- // errf(
- // "could not process %v", thing,
- // "date %q could not be parsed", thing.Date,
- // parseError,
- // )
- func errf(msg string, args ...interface{}) error {
- // By implementing buildErrf as a closure rather than a standalone
- // function, we're able to ensure that it is called only from errf, or
- // from itself (recursively). By controlling these invocations in such
- // a tight space, we are able to easily verify manually that we
- // checked len(args) > 0 before making the call.
- var buildErrf func([]interface{}) error
- buildErrf = func(args []interface{}) error {
- arg, args := args[0], args[1:] // assume len(args) > 0
- if arg == nil {
- panic("It looks like you have found a bug in dig. " +
- "Please file an issue at https://github.com/uber-go/dig/issues/ " +
- "and provide the following message: " +
- "arg must not be nil")
- }
- switch v := arg.(type) {
- case string:
- need := numFmtArgs(v)
- if len(args) < need {
- panic(fmt.Sprintf(
- "It looks like you have found a bug in dig. "+
- "Please file an issue at https://github.com/uber-go/dig/issues/ "+
- "and provide the following message: "+
- "string %q needs %v arguments, got %v", v, need, len(args)))
- }
- msg := fmt.Sprintf(v, args[:need]...)
- args := args[need:]
- // If we don't have anything left to chain with, build the
- // final error.
- if len(args) == 0 {
- return errors.New(msg)
- }
- return wrappedError{
- msg: msg,
- err: buildErrf(args),
- }
- case error:
- if len(args) > 0 {
- panic(fmt.Sprintf(
- "It looks like you have found a bug in dig. "+
- "Please file an issue at https://github.com/uber-go/dig/issues/ "+
- "and provide the following message: "+
- "error must be the last element but got %v", args))
- }
- return v
- default:
- panic(fmt.Sprintf(
- "It looks like you have found a bug in dig. "+
- "Please file an issue at https://github.com/uber-go/dig/issues/ "+
- "and provide the following message: "+
- "unexpected errf-argument type %T", arg))
- }
- }
- // Prepend msg to the args list so that we can re-use the same
- // args processing logic. The msg is a string just for type-safety of
- // the first error.
- newArgs := make([]interface{}, len(args)+1)
- newArgs[0] = msg
- copy(newArgs[1:], args)
- return buildErrf(newArgs)
- }
- // Returns the number of formatting arguments in the provided string. Does not
- // count escaped % symbols, specifically the string "%%".
- //
- // fmt.Println(numFmtArgs("rate: %d%%")) // 1
- func numFmtArgs(s string) int {
- var (
- count int
- percent bool // saw %
- )
- for _, c := range s {
- if percent && c != '%' {
- // Counts only if it's not a %%.
- count++
- }
- // Next iteration should consider % only if the current %
- // stands alone.
- percent = !percent && c == '%'
- }
- return count
- }
- type wrappedError struct {
- err error
- msg string
- }
- var _ causer = wrappedError{}
- func (e wrappedError) cause() error {
- return e.err
- }
- func (e wrappedError) writeMessage(w io.Writer, _ string) {
- io.WriteString(w, e.msg)
- }
- func (e wrappedError) Error() string { return fmt.Sprint(e) }
- func (e wrappedError) Format(w fmt.State, c rune) {
- formatCauser(e, w, c)
- }
- // errProvide is returned when a constructor could not be Provided into the
- // container.
- type errProvide struct {
- Func *digreflect.Func
- Reason error
- }
- var _ causer = errProvide{}
- func (e errProvide) cause() error {
- return e.Reason
- }
- func (e errProvide) writeMessage(w io.Writer, verb string) {
- fmt.Fprintf(w, "cannot provide function "+verb, e.Func)
- }
- func (e errProvide) Error() string { return fmt.Sprint(e) }
- func (e errProvide) Format(w fmt.State, c rune) {
- formatCauser(e, w, c)
- }
- // errConstructorFailed is returned when a user-provided constructor failed
- // with a non-nil error.
- type errConstructorFailed struct {
- Func *digreflect.Func
- Reason error
- }
- var _ causer = errConstructorFailed{}
- func (e errConstructorFailed) cause() error {
- return e.Reason
- }
- func (e errConstructorFailed) writeMessage(w io.Writer, verb string) {
- fmt.Fprintf(w, "received non-nil error from function "+verb, e.Func)
- }
- func (e errConstructorFailed) Error() string { return fmt.Sprint(e) }
- func (e errConstructorFailed) Format(w fmt.State, c rune) {
- formatCauser(e, w, c)
- }
- // errArgumentsFailed is returned when a function could not be run because one
- // of its dependencies failed to build for any reason.
- type errArgumentsFailed struct {
- Func *digreflect.Func
- Reason error
- }
- var _ causer = errArgumentsFailed{}
- func (e errArgumentsFailed) cause() error {
- return e.Reason
- }
- func (e errArgumentsFailed) writeMessage(w io.Writer, verb string) {
- fmt.Fprintf(w, "could not build arguments for function "+verb, e.Func)
- }
- func (e errArgumentsFailed) Error() string { return fmt.Sprint(e) }
- func (e errArgumentsFailed) Format(w fmt.State, c rune) {
- formatCauser(e, w, c)
- }
- // errMissingDependencies is returned when the dependencies of a function are
- // not available in the container.
- type errMissingDependencies struct {
- Func *digreflect.Func
- Reason error
- }
- var _ causer = errMissingDependencies{}
- func (e errMissingDependencies) cause() error {
- return e.Reason
- }
- func (e errMissingDependencies) writeMessage(w io.Writer, verb string) {
- fmt.Fprintf(w, "missing dependencies for function "+verb, e.Func)
- }
- func (e errMissingDependencies) Error() string { return fmt.Sprint(e) }
- func (e errMissingDependencies) Format(w fmt.State, c rune) {
- formatCauser(e, w, c)
- }
- // errParamSingleFailed is returned when a paramSingle could not be built.
- type errParamSingleFailed struct {
- Key key
- Reason error
- CtorID dot.CtorID
- }
- var _ causer = errParamSingleFailed{}
- func (e errParamSingleFailed) cause() error {
- return e.Reason
- }
- func (e errParamSingleFailed) writeMessage(w io.Writer, _ string) {
- fmt.Fprintf(w, "failed to build %v", e.Key)
- }
- func (e errParamSingleFailed) Error() string { return fmt.Sprint(e) }
- func (e errParamSingleFailed) Format(w fmt.State, c rune) {
- formatCauser(e, w, c)
- }
- func (e errParamSingleFailed) updateGraph(g *dot.Graph) {
- failed := &dot.Result{
- Node: &dot.Node{
- Name: e.Key.name,
- Group: e.Key.group,
- Type: e.Key.t,
- },
- }
- g.FailNodes([]*dot.Result{failed}, e.CtorID)
- }
- // errParamGroupFailed is returned when a value group cannot be built because
- // any of the values in the group failed to build.
- type errParamGroupFailed struct {
- Key key
- Reason error
- CtorID dot.CtorID
- }
- var _ causer = errParamGroupFailed{}
- func (e errParamGroupFailed) cause() error {
- return e.Reason
- }
- func (e errParamGroupFailed) writeMessage(w io.Writer, _ string) {
- fmt.Fprintf(w, "could not build value group %v", e.Key)
- }
- func (e errParamGroupFailed) Error() string { return fmt.Sprint(e) }
- func (e errParamGroupFailed) Format(w fmt.State, c rune) {
- formatCauser(e, w, c)
- }
- func (e errParamGroupFailed) updateGraph(g *dot.Graph) {
- g.FailGroupNodes(e.Key.group, e.Key.t, e.CtorID)
- }
- // missingType holds information about a type that was missing in the
- // container.
- type missingType struct {
- Key key // item that was missing
- // If non-empty, we will include suggestions for what the user may have
- // meant.
- suggestions []key
- }
- // Format prints a string representation of missingType.
- //
- // With %v, it prints a short representation ideal for an itemized list.
- //
- // io.Writer
- // io.Writer: did you mean *bytes.Buffer?
- // io.Writer: did you mean *bytes.Buffer, or *os.File?
- //
- // With %+v, it prints a longer representation ideal for standalone output.
- //
- // io.Writer: did you mean to Provide it?
- // io.Writer: did you mean to use *bytes.Buffer?
- // io.Writer: did you mean to use one of *bytes.Buffer, or *os.File?
- func (mt missingType) Format(w fmt.State, v rune) {
- plusV := w.Flag('+') && v == 'v'
- fmt.Fprint(w, mt.Key)
- switch len(mt.suggestions) {
- case 0:
- if plusV {
- io.WriteString(w, " (did you mean to Provide it?)")
- }
- case 1:
- sug := mt.suggestions[0]
- if plusV {
- fmt.Fprintf(w, " (did you mean to use %v?)", sug)
- } else {
- fmt.Fprintf(w, " (did you mean %v?)", sug)
- }
- default:
- if plusV {
- io.WriteString(w, " (did you mean to use one of ")
- } else {
- io.WriteString(w, " (did you mean ")
- }
- lastIdx := len(mt.suggestions) - 1
- for i, sug := range mt.suggestions {
- if i > 0 {
- io.WriteString(w, ", ")
- if i == lastIdx {
- io.WriteString(w, "or ")
- }
- }
- fmt.Fprint(w, sug)
- }
- io.WriteString(w, "?)")
- }
- }
- // errMissingType is returned when one or more values that were expected in
- // the container were not available.
- //
- // Multiple instances of this error may be merged together by appending them.
- type errMissingTypes []missingType // inv: len > 0
- func newErrMissingTypes(c containerStore, k key) errMissingTypes {
- // Possible types we will look for in the container. We will always look
- // for pointers to the requested type and some extras on a per-Kind basis.
- suggestions := []reflect.Type{reflect.PtrTo(k.t)}
- if k.t.Kind() == reflect.Ptr {
- // The user requested a pointer but maybe we have a value.
- suggestions = append(suggestions, k.t.Elem())
- }
- knownTypes := c.knownTypes()
- if k.t.Kind() == reflect.Interface {
- // Maybe we have an implementation of the interface.
- for _, t := range knownTypes {
- if t.Implements(k.t) {
- suggestions = append(suggestions, t)
- }
- }
- } else {
- // Maybe we have an interface that this type implements.
- for _, t := range knownTypes {
- if t.Kind() == reflect.Interface {
- if k.t.Implements(t) {
- suggestions = append(suggestions, t)
- }
- }
- }
- }
- // range through c.providers is non-deterministic. Let's sort the list of
- // suggestions.
- sort.Sort(byTypeName(suggestions))
- mt := missingType{Key: k}
- for _, t := range suggestions {
- if len(c.getValueProviders(k.name, t)) > 0 {
- k.t = t
- mt.suggestions = append(mt.suggestions, k)
- }
- }
- return errMissingTypes{mt}
- }
- func (e errMissingTypes) Error() string {
- return fmt.Sprint(e)
- }
- func (e errMissingTypes) Format(w fmt.State, v rune) {
- multiline := w.Flag('+') && v == 'v'
- if len(e) == 1 {
- io.WriteString(w, "missing type:")
- } else {
- io.WriteString(w, "missing types:")
- }
- if !multiline {
- // With %v, we need a space between : since the error
- // won't be on a new line.
- io.WriteString(w, " ")
- }
- for i, mt := range e {
- if multiline {
- io.WriteString(w, "\n\t- ")
- } else if i > 0 {
- io.WriteString(w, "; ")
- }
- if multiline {
- fmt.Fprintf(w, "%+v", mt)
- } else {
- fmt.Fprintf(w, "%v", mt)
- }
- }
- }
- func (e errMissingTypes) updateGraph(g *dot.Graph) {
- missing := make([]*dot.Result, len(e))
- for i, mt := range e {
- missing[i] = &dot.Result{
- Node: &dot.Node{
- Name: mt.Key.name,
- Group: mt.Key.group,
- Type: mt.Key.t,
- },
- }
- }
- g.AddMissingNodes(missing)
- }
- type errVisualizer interface {
- updateGraph(*dot.Graph)
- }
|