uber-go / dig
1
// Copyright (c) 2019 Uber Technologies, Inc.
2
//
3
// Permission is hereby granted, free of charge, to any person obtaining a copy
4
// of this software and associated documentation files (the "Software"), to deal
5
// in the Software without restriction, including without limitation the rights
6
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
// copies of the Software, and to permit persons to whom the Software is
8
// furnished to do so, subject to the following conditions:
9
//
10
// The above copyright notice and this permission notice shall be included in
11
// all copies or substantial portions of the Software.
12
//
13
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
// THE SOFTWARE.
20

21
package dig
22

23
import (
24
	"errors"
25
	"fmt"
26
	"io"
27
	"reflect"
28
	"sort"
29

30
	"go.uber.org/dig/internal/digreflect"
31
	"go.uber.org/dig/internal/dot"
32
)
33

34
// Errors which know their underlying cause should implement this interface to
35
// be compatible with RootCause.
36
//
37
// We use unexported methods because we don't want dig-internal causes to be
38
// confused with the cause of the user-provided errors. For example, if we
39
// used Unwrap(), then user-provided methods would also be unwrapped by
40
// RootCause. We want RootCause to eliminate the dig error chain only.
41
type causer interface {
42
	fmt.Formatter
43

44
	// Returns the next error in the chain.
45
	cause() error
46

47
	// Writes the message or context for this error in the chain.
48
	//
49
	// verb is either %v or %+v.
50
	writeMessage(w io.Writer, verb string)
51
}
52

53
// Implements fmt.Formatter for errors that implement causer.
54
//
55
// This Format method supports %v and %+v. In the %v form, the error is
56
// printed on one line. In the %+v form, the error is split across multiple
57
// lines on each error in the error chain.
58
func formatCauser(c causer, w fmt.State, v rune) {
59 2
	multiline := w.Flag('+') && v == 'v'
60 2
	verb := "%v"
61 2
	if multiline {
62 2
		verb = "%+v"
63
	}
64

65
	// "context: " or "context:\n"
66 2
	c.writeMessage(w, verb)
67 2
	io.WriteString(w, ":")
68 2
	if multiline {
69 2
		io.WriteString(w, "\n")
70 2
	} else {
71 2
		io.WriteString(w, " ")
72
	}
73

74 2
	fmt.Fprintf(w, verb, c.cause())
75
}
76

77
// RootCause returns the original error that caused the provided dig failure.
78
//
79
// RootCause may be used on errors returned by Invoke to get the original
80
// error returned by a constructor or invoked function.
81
func RootCause(err error) error {
82
	for {
83 2
		if e, ok := err.(causer); ok {
84 2
			err = e.cause()
85 2
		} else {
86 2
			return err
87
		}
88
	}
89
}
90

91
// errf is a version of fmt.Errorf with support for a chain of multiple
92
// formatted error messages.
93
//
94
// After msg, N arguments are consumed as formatting arguments for that
95
// message, where N is the number of % symbols in msg. Following that, another
96
// string may be added to become the next error in the chain. Each new error
97
// is the `cause()` for the prior error.
98
//
99
//   err := errf(
100
//     "could not process %v", thing,
101
//     "name %q is invalid", thing.Name,
102
//  )
103
//  fmt.Println(err)  // could not process Thing: name Foo is invalid
104
//  fmt.Println(RootCause(err))  // name Foo is invalid
105
//
106
// In place of a string, the last error can be another error, in which case it
107
// will be treated as the cause of the prior error chain.
108
//
109
//   errf(
110
//     "could not process %v", thing,
111
//     "date %q could not be parsed", thing.Date,
112
//     parseError,
113
//  )
114
func errf(msg string, args ...interface{}) error {
115
	// By implementing buildErrf as a closure rather than a standalone
116
	// function, we're able to ensure that it is called only from errf, or
117
	// from itself (recursively). By controlling these invocations in such
118
	// a tight space, we are able to easily verify manually that we
119
	// checked len(args) > 0 before making the call.
120 2
	var buildErrf func([]interface{}) error
121 2
	buildErrf = func(args []interface{}) error {
122 2
		arg, args := args[0], args[1:] // assume len(args) > 0
123 2
		if arg == nil {
124 2
			panic("It looks like you have found a bug in dig. " +
125 2
				"Please file an issue at https://github.com/uber-go/dig/issues/ " +
126 2
				"and provide the following message: " +
127 2
				"arg must not be nil")
128
		}
129

130 2
		switch v := arg.(type) {
131 2
		case string:
132 2
			need := numFmtArgs(v)
133 2
			if len(args) < need {
134 2
				panic(fmt.Sprintf(
135 2
					"It looks like you have found a bug in dig. "+
136 2
						"Please file an issue at https://github.com/uber-go/dig/issues/ "+
137 2
						"and provide the following message: "+
138 2
						"string %q needs %v arguments, got %v", v, need, len(args)))
139
			}
140

141 2
			msg := fmt.Sprintf(v, args[:need]...)
142 2
			args := args[need:]
143

144
			// If we don't have anything left to chain with, build the
145
			// final error.
146 2
			if len(args) == 0 {
147 2
				return errors.New(msg)
148
			}
149

150 2
			return wrappedError{
151 2
				msg: msg,
152 2
				err: buildErrf(args),
153
			}
154 2
		case error:
155 2
			if len(args) > 0 {
156 2
				panic(fmt.Sprintf(
157 2
					"It looks like you have found a bug in dig. "+
158 2
						"Please file an issue at https://github.com/uber-go/dig/issues/ "+
159 2
						"and provide the following message: "+
160 2
						"error must be the last element but got %v", args))
161
			}
162

163 2
			return v
164

165 2
		default:
166 2
			panic(fmt.Sprintf(
167 2
				"It looks like you have found a bug in dig. "+
168 2
					"Please file an issue at https://github.com/uber-go/dig/issues/ "+
169 2
					"and provide the following message: "+
170 2
					"unexpected errf-argument type %T", arg))
171
		}
172
	}
173

174
	// Prepend msg to the args list so that we can re-use the same
175
	// args processing logic. The msg is a string just for type-safety of
176
	// the first error.
177 2
	newArgs := make([]interface{}, len(args)+1)
178 2
	newArgs[0] = msg
179 2
	copy(newArgs[1:], args)
180 2
	return buildErrf(newArgs)
181
}
182

183
// Returns the number of formatting arguments in the provided string. Does not
184
// count escaped % symbols, specifically the string "%%".
185
//
186
//   fmt.Println(numFmtArgs("rate: %d%%"))  // 1
187
func numFmtArgs(s string) int {
188 2
	var (
189 2
		count   int
190 2
		percent bool // saw %
191 2
	)
192
	for _, c := range s {
193 2
		if percent && c != '%' {
194
			// Counts only if it's not a %%.
195 2
			count++
196
		}
197

198
		// Next iteration should consider % only if the current %
199
		// stands alone.
200 2
		percent = !percent && c == '%'
201
	}
202 2
	return count
203
}
204

205
type wrappedError struct {
206
	err error
207
	msg string
208
}
209

210
var _ causer = wrappedError{}
211

212
func (e wrappedError) cause() error {
213 2
	return e.err
214
}
215

216
func (e wrappedError) writeMessage(w io.Writer, _ string) {
217 2
	io.WriteString(w, e.msg)
218
}
219

220 2
func (e wrappedError) Error() string { return fmt.Sprint(e) }
221
func (e wrappedError) Format(w fmt.State, c rune) {
222 2
	formatCauser(e, w, c)
223
}
224

225
// errProvide is returned when a constructor could not be Provided into the
226
// container.
227
type errProvide struct {
228
	Func   *digreflect.Func
229
	Reason error
230
}
231

232
var _ causer = errProvide{}
233

234
func (e errProvide) cause() error {
235 2
	return e.Reason
236
}
237

238
func (e errProvide) writeMessage(w io.Writer, verb string) {
239 2
	fmt.Fprintf(w, "cannot provide function "+verb, e.Func)
240
}
241

242 2
func (e errProvide) Error() string { return fmt.Sprint(e) }
243
func (e errProvide) Format(w fmt.State, c rune) {
244 2
	formatCauser(e, w, c)
245
}
246

247
// errConstructorFailed is returned when a user-provided constructor failed
248
// with a non-nil error.
249
type errConstructorFailed struct {
250
	Func   *digreflect.Func
251
	Reason error
252
}
253

254
var _ causer = errConstructorFailed{}
255

256
func (e errConstructorFailed) cause() error {
257 2
	return e.Reason
258
}
259

260
func (e errConstructorFailed) writeMessage(w io.Writer, verb string) {
261 2
	fmt.Fprintf(w, "received non-nil error from function "+verb, e.Func)
262
}
263

264 2
func (e errConstructorFailed) Error() string { return fmt.Sprint(e) }
265
func (e errConstructorFailed) Format(w fmt.State, c rune) {
266 2
	formatCauser(e, w, c)
267
}
268

269
// errArgumentsFailed is returned when a function could not be run because one
270
// of its dependencies failed to build for any reason.
271
type errArgumentsFailed struct {
272
	Func   *digreflect.Func
273
	Reason error
274
}
275

276
var _ causer = errArgumentsFailed{}
277

278
func (e errArgumentsFailed) cause() error {
279 2
	return e.Reason
280
}
281

282
func (e errArgumentsFailed) writeMessage(w io.Writer, verb string) {
283 2
	fmt.Fprintf(w, "could not build arguments for function "+verb, e.Func)
284
}
285

286 2
func (e errArgumentsFailed) Error() string { return fmt.Sprint(e) }
287
func (e errArgumentsFailed) Format(w fmt.State, c rune) {
288 2
	formatCauser(e, w, c)
289
}
290

291
// errMissingDependencies is returned when the dependencies of a function are
292
// not available in the container.
293
type errMissingDependencies struct {
294
	Func   *digreflect.Func
295
	Reason error
296
}
297

298
var _ causer = errMissingDependencies{}
299

300
func (e errMissingDependencies) cause() error {
301 2
	return e.Reason
302
}
303

304
func (e errMissingDependencies) writeMessage(w io.Writer, verb string) {
305 2
	fmt.Fprintf(w, "missing dependencies for function "+verb, e.Func)
306
}
307

308 2
func (e errMissingDependencies) Error() string { return fmt.Sprint(e) }
309
func (e errMissingDependencies) Format(w fmt.State, c rune) {
310 2
	formatCauser(e, w, c)
311
}
312

313
// errParamSingleFailed is returned when a paramSingle could not be built.
314
type errParamSingleFailed struct {
315
	Key    key
316
	Reason error
317
	CtorID dot.CtorID
318
}
319

320
var _ causer = errParamSingleFailed{}
321

322
func (e errParamSingleFailed) cause() error {
323 2
	return e.Reason
324
}
325

326
func (e errParamSingleFailed) writeMessage(w io.Writer, _ string) {
327 2
	fmt.Fprintf(w, "failed to build %v", e.Key)
328
}
329

330 2
func (e errParamSingleFailed) Error() string { return fmt.Sprint(e) }
331
func (e errParamSingleFailed) Format(w fmt.State, c rune) {
332 2
	formatCauser(e, w, c)
333
}
334

335
func (e errParamSingleFailed) updateGraph(g *dot.Graph) {
336
	failed := &dot.Result{
337 2
		Node: &dot.Node{
338 2
			Name:  e.Key.name,
339 2
			Group: e.Key.group,
340 2
			Type:  e.Key.t,
341 2
		},
342
	}
343 2
	g.FailNodes([]*dot.Result{failed}, e.CtorID)
344
}
345

346
// errParamGroupFailed is returned when a value group cannot be built because
347
// any of the values in the group failed to build.
348
type errParamGroupFailed struct {
349
	Key    key
350
	Reason error
351
	CtorID dot.CtorID
352
}
353

354
var _ causer = errParamGroupFailed{}
355

356
func (e errParamGroupFailed) cause() error {
357 2
	return e.Reason
358
}
359

360
func (e errParamGroupFailed) writeMessage(w io.Writer, _ string) {
361 2
	fmt.Fprintf(w, "could not build value group %v", e.Key)
362
}
363

364 2
func (e errParamGroupFailed) Error() string { return fmt.Sprint(e) }
365
func (e errParamGroupFailed) Format(w fmt.State, c rune) {
366 2
	formatCauser(e, w, c)
367
}
368

369
func (e errParamGroupFailed) updateGraph(g *dot.Graph) {
370 2
	g.FailGroupNodes(e.Key.group, e.Key.t, e.CtorID)
371
}
372

373
// missingType holds information about a type that was missing in the
374
// container.
375
type missingType struct {
376
	Key key // item that was missing
377

378
	// If non-empty, we will include suggestions for what the user may have
379
	// meant.
380
	suggestions []key
381
}
382

383
// Format prints a string representation of missingType.
384
//
385
// With %v, it prints a short representation ideal for an itemized list.
386
//
387
//   io.Writer
388
//   io.Writer: did you mean *bytes.Buffer?
389
//   io.Writer: did you mean *bytes.Buffer, or *os.File?
390
//
391
// With %+v, it prints a longer representation ideal for standalone output.
392
//
393
//   io.Writer: did you mean to Provide it?
394
//   io.Writer: did you mean to use *bytes.Buffer?
395
//   io.Writer: did you mean to use one of *bytes.Buffer, or *os.File?
396
func (mt missingType) Format(w fmt.State, v rune) {
397 2
	plusV := w.Flag('+') && v == 'v'
398

399 2
	fmt.Fprint(w, mt.Key)
400 2
	switch len(mt.suggestions) {
401 2
	case 0:
402 2
		if plusV {
403 2
			io.WriteString(w, " (did you mean to Provide it?)")
404
		}
405 2
	case 1:
406 2
		sug := mt.suggestions[0]
407 2
		if plusV {
408 2
			fmt.Fprintf(w, " (did you mean to use %v?)", sug)
409 2
		} else {
410 2
			fmt.Fprintf(w, " (did you mean %v?)", sug)
411
		}
412 2
	default:
413 2
		if plusV {
414 2
			io.WriteString(w, " (did you mean to use one of ")
415 2
		} else {
416 2
			io.WriteString(w, " (did you mean ")
417
		}
418

419 2
		lastIdx := len(mt.suggestions) - 1
420
		for i, sug := range mt.suggestions {
421 2
			if i > 0 {
422 2
				io.WriteString(w, ", ")
423 2
				if i == lastIdx {
424 2
					io.WriteString(w, "or ")
425
				}
426
			}
427 2
			fmt.Fprint(w, sug)
428
		}
429 2
		io.WriteString(w, "?)")
430
	}
431
}
432

433
// errMissingType is returned when one or more values that were expected in
434
// the container were not available.
435
//
436
// Multiple instances of this error may be merged together by appending them.
437
type errMissingTypes []missingType // inv: len > 0
438

439
func newErrMissingTypes(c containerStore, k key) errMissingTypes {
440
	// Possible types we will look for in the container. We will always look
441
	// for pointers to the requested type and some extras on a per-Kind basis.
442 2
	suggestions := []reflect.Type{reflect.PtrTo(k.t)}
443

444 2
	if k.t.Kind() == reflect.Ptr {
445
		// The user requested a pointer but maybe we have a value.
446 2
		suggestions = append(suggestions, k.t.Elem())
447
	}
448

449 2
	knownTypes := c.knownTypes()
450 2
	if k.t.Kind() == reflect.Interface {
451
		// Maybe we have an implementation of the interface.
452
		for _, t := range knownTypes {
453 2
			if t.Implements(k.t) {
454 2
				suggestions = append(suggestions, t)
455
			}
456
		}
457 2
	} else {
458
		// Maybe we have an interface that this type implements.
459
		for _, t := range knownTypes {
460 2
			if t.Kind() == reflect.Interface {
461 2
				if k.t.Implements(t) {
462 2
					suggestions = append(suggestions, t)
463
				}
464
			}
465
		}
466
	}
467

468
	// range through c.providers is non-deterministic. Let's sort the list of
469
	// suggestions.
470 2
	sort.Sort(byTypeName(suggestions))
471

472 2
	mt := missingType{Key: k}
473
	for _, t := range suggestions {
474 2
		if len(c.getValueProviders(k.name, t)) > 0 {
475 2
			k.t = t
476 2
			mt.suggestions = append(mt.suggestions, k)
477
		}
478
	}
479

480 2
	return errMissingTypes{mt}
481
}
482

483
func (e errMissingTypes) Error() string {
484 2
	return fmt.Sprint(e)
485
}
486

487
func (e errMissingTypes) Format(w fmt.State, v rune) {
488 2
	multiline := w.Flag('+') && v == 'v'
489

490 2
	if len(e) == 1 {
491 2
		io.WriteString(w, "missing type:")
492 2
	} else {
493 2
		io.WriteString(w, "missing types:")
494
	}
495

496 2
	if !multiline {
497
		// With %v, we need a space between : since the error
498
		// won't be on a new line.
499 2
		io.WriteString(w, " ")
500
	}
501

502
	for i, mt := range e {
503 2
		if multiline {
504 2
			io.WriteString(w, "\n\t- ")
505 2
		} else if i > 0 {
506 2
			io.WriteString(w, "; ")
507
		}
508

509 2
		if multiline {
510 2
			fmt.Fprintf(w, "%+v", mt)
511 2
		} else {
512 2
			fmt.Fprintf(w, "%v", mt)
513
		}
514
	}
515
}
516

517
func (e errMissingTypes) updateGraph(g *dot.Graph) {
518 2
	missing := make([]*dot.Result, len(e))
519

520
	for i, mt := range e {
521 2
		missing[i] = &dot.Result{
522 2
			Node: &dot.Node{
523 2
				Name:  mt.Key.name,
524 2
				Group: mt.Key.group,
525 2
				Type:  mt.Key.t,
526 2
			},
527
		}
528
	}
529 2
	g.AddMissingNodes(missing)
530
}
531

532
type errVisualizer interface {
533
	updateGraph(*dot.Graph)
534
}

Read our documentation on viewing source code .

Loading