From 6bc0d21bc8f63e3754816eb551cbe58982a6c7ab Mon Sep 17 00:00:00 2001 From: Abhinav Gupta Date: Wed, 9 Mar 2022 15:11:42 -0800 Subject: [PATCH 1/3] doc: Clarify Supply/Replace behavior with interface values (#857) Use of `fx.Replace` or `fx.Supply` with interface values can lead to unexpected behavior. `fx.Supply((io.Reader)(r))` will use the type of the implementation `r`, not `io.Reader`. This clarifies the documentation for both APIs to make this explicit. Refs GO-1253 --- replace.go | 44 +++++++++++++++++++++++++++++++++++++++++--- supply.go | 24 ++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/replace.go b/replace.go index f7fa865cb..cd0a13ab7 100644 --- a/replace.go +++ b/replace.go @@ -28,14 +28,52 @@ import ( "go.uber.org/fx/internal/fxreflect" ) -// Replace provides instantiated values for graph modification. Similar to -// what fx.Supply is to fx.Provide, values provided by fx.Replace behaves -// similarly to values produced by decorators specified with fx.Decorate. +// Replace provides instantiated values for graph modification as if +// they had been provided using a decorator with fx.Decorate. +// The most specific type of each value (as determined by reflection) is used. // // Refer to the documentation on fx.Decorate to see how graph modifications // work with fx.Module. // +// This serves a purpose similar to what fx.Supply does for fx.Provide. +// +// For example, given, +// +// var log *zap.Logger = ... +// +// The following two forms are equivalent. +// +// fx.Replace(log) +// +// fx.Decorate( +// func() *zap.Logger { +// return log +// }, +// ) +// // Replace panics if a value (or annotation target) is an untyped nil or an error. +// +// Replace Caveats +// +// As mentioned above, Replace uses the most specific type of the provided +// value. For interface values, this refers to the type of the implementation, +// not the interface. So if you try to replace an io.Writer, fx.Replace will +// use the type of the implementation. +// +// var stderr io.Writer = os.Stderr +// fx.Replace(stderr) +// +// Is equivalent to, +// +// fx.Decorate(func() *os.File { return os.Stderr }) +// +// This is typically NOT what you intended. To replace the io.Writer in the +// container with the value above, we need to use the fx.Annotate function with +// the fx.As annotation. +// +// fx.Replace( +// fx.Annotate(os.Stderr, fx.As(new(io.Writer))) +// ) func Replace(values ...interface{}) Option { decorators := make([]interface{}, len(values)) // one function per value types := make([]reflect.Type, len(values)) diff --git a/supply.go b/supply.go index 5813833cf..5e573ac16 100644 --- a/supply.go +++ b/supply.go @@ -32,6 +32,8 @@ import ( // they had been provided using a constructor that simply returns them. // The most specific type of each value (as determined by reflection) is used. // +// This serves a purpose similar to what fx.Replace does for fx.Decorate. +// // For example, given: // // type ( @@ -53,6 +55,28 @@ import ( // ) // // Supply panics if a value (or annotation target) is an untyped nil or an error. +// +// Supply Caveats +// +// As mentioned above, Supply uses the most specific type of the provided +// value. For interface values, this refers to the type of the implementation, +// not the interface. So if you supply an http.Handler, fx.Supply will use the +// type of the implementation. +// +// var handler http.Handler = http.HandlerFunc(f) +// fx.Supply(handler) +// +// Is equivalent to, +// +// fx.Provide(func() http.HandlerFunc { return f }) +// +// This is typically NOT what you intended. To supply the handler above as an +// http.Handler, we need to use the fx.Annotate function with the fx.As +// annotation. +// +// fx.Supply( +// fx.Annotate(handler, fx.As(new(http.Handler))), +// ) func Supply(values ...interface{}) Option { constructors := make([]interface{}, len(values)) // one function per value types := make([]reflect.Type, len(values)) From 65e6e7f133d44a62cb6348ab9729067d99599dd1 Mon Sep 17 00:00:00 2001 From: Abhinav Gupta Date: Wed, 9 Mar 2022 16:40:41 -0800 Subject: [PATCH 2/3] doc: Expand and organize Decorate documentation (#858) This elaborates on the documentation for fx.Decorate with more examples, and organizes it into two sections: - Decorator functions, enumerating the various signatures a decorator function can have - Decorator scope, explaining how decorator changes are scoped Added demonstrative examples for everything. In the future, we can turn some of these into example tests. Refs GO-1254 --- decorate.go | 126 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 108 insertions(+), 18 deletions(-) diff --git a/decorate.go b/decorate.go index 1e863d391..a241a3cbb 100644 --- a/decorate.go +++ b/decorate.go @@ -29,41 +29,82 @@ import ( ) // Decorate specifies one or more decorator functions to an Fx application. -// Decorator functions let users augment objects in the graph. They can take in -// zero or more dependencies that must be provided to the application with fx.Provide, -// and produce one or more values that can be used by other invoked values. // -// An example decorator is the following function which accepts a value, augments that value, -// and returns the replacement value. +// Decorator functions +// +// Decorator functions let users augment objects in the graph. +// They can take in zero or more dependencies that must be provided to the +// application with fx.Provide, and produce one or more values that can be used +// by other fx.Provide and fx.Invoke calls. // // fx.Decorate(func(log *zap.Logger) *zap.Logger { // return log.Named("myapp") // }) +// fx.Invoke(func(log *zap.Logger) { +// log.Info("hello") +// // Output: +// // {"level": "info","logger":"myapp","msg":"hello"} +// }) // -// The following decorator accepts multiple dependencies from the graph, augments and returns -// one of them. +// The following decorator accepts multiple dependencies from the graph, +// augments and returns one of them. // // fx.Decorate(func(log *zap.Logger, cfg *Config) *zap.Logger { // return log.Named(cfg.Name) // }) // -// Similar to fx.Provide, functions passed to fx.Decorate may optionally return an error -// as their last result. If a decorator returns a non-nil error, it will halt application startup. +// Similar to fx.Provide, functions passed to fx.Decorate may optionally return +// an error as their last result. +// If a decorator returns a non-nil error, it will halt application startup. +// +// fx.Decorate(func(conn *sql.DB, cfg *Config) (*sql.DB, error) { +// if err := conn.Ping(); err != nil { +// return sql.Open("driver-name", cfg.FallbackDB) +// } +// return conn, nil +// }) +// +// Decorators support both, fx.In and fx.Out structs, similar to fx.Provide and +// fx.Invoke. +// +// type Params struct { +// fx.In +// +// Client usersvc.Client `name:"readOnly"` +// } // -// All modifications in the object graph due to a decorator are scoped to the fx.Module it was -// specified from. Decorations specified in the top-level fx.New call apply across the application. +// type Result struct { +// fx.Out +// +// Client usersvc.Client `name:"readOnly"` +// } // -// Decorators can be annotated using fx.Annotate, but not with fx.Annotated. Refer to documentation -// on fx.Annotate() to learn how to use it for annotating functions. +// fx.Decorate(func(p Params) Result { +// ... +// }) // -// Decorators support fx.In and fx.Out structs, similar to fx.Provide and fx.Invoke. +// Decorators can be annotated with the fx.Annotate function, but not with the +// fx.Annotated type. Refer to documentation on fx.Annotate() to learn how to +// use it for annotating functions. +// +// fx.Decorate( +// fx.Annotate( +// func(client usersvc.Client) usersvc.Client { +// // ... +// }, +// fx.ParamTags(`name:"readOnly"`), +// fx.ResultTags(`name:"readOnly"`), +// ), +// ) // -// Decorators support value groups as well. For example, the following code shows a decorator -// which takes in a value group using fx.In struct, and returns another value group. +// Decorators support augmenting, filtering, or replacing value groups. +// To decorate a value group, expect the entire value group slice and produce +// the new slice. // // type HandlerParam struct { // fx.In // +// Log *zap.Logger // Handlers []Handler `group:"server" // } // @@ -73,11 +114,60 @@ import ( // Handlers []Handler `group:"server" // } // +// fx.Decorate(func(p HandlerParam) HandlerResult { +// var r HandlerResult +// for _, handler := range p.Handlers { +// r.Handlers = append(r.Handlers, wrapWithLogger(p.Log, handler)) +// } +// return r +// }), +// +// Decorator scope +// +// Modifications made to the Fx graph with fx.Decorate are scoped to the +// deepest fx.Module inside which the decorator was specified. +// +// fx.Module("mymodule", +// fx.Decorate(func(log *zap.Logger) *zap.Logger { +// return log.Named("myapp") +// }), +// fx.Invoke(func(log *zap.Logger) { +// log.Info("decorated logger") +// // Output: +// // {"level": "info","logger":"myapp","msg":"decorated logger"} +// }), +// ), +// fx.Invoke(func(log *zap.Logger) { +// log.Info("plain logger") +// // Output: +// // {"level": "info","msg":"plain logger"} +// }), +// +// Decorations specified in the top-level fx.New call apply across the +// application and chain with module-specific decorators. +// // fx.New( // // ... -// fx.Decorate(func(p HandlerParam) HandlerResult { -// // ... +// fx.Decorate(func(log *zap.Logger) *zap.Logger { +// return log.With(zap.Field("service", "myservice")) // }), +// // ... +// fx.Invoke(func(log *zap.Logger) { +// log.Info("outer decorator") +// // Output: +// // {"level": "info","service":"myservice","msg":"outer decorator"} +// }), +// // ... +// fx.Module("mymodule", +// fx.Decorate(func(log *zap.Logger) *zap.Logger { +// return log.Named("myapp") +// }), +// fx.Invoke(func(log *zap.Logger) { +// log.Info("inner decorator") +// // Output: +// // {"level": "info","logger":"myapp","service":"myservice","msg":"inner decorator"} +// }), +// ), // ) func Decorate(decorators ...interface{}) Option { return decorateOption{ From 24f6f50ab9b4a8c9e0d96c703f1344b0d62221cb Mon Sep 17 00:00:00 2001 From: Abhinav Gupta Date: Tue, 15 Mar 2022 14:36:47 -0700 Subject: [PATCH 3/3] CI: run against Go 1.17/1.18 (#862) We only support the most recent two Go minor releases. staticcheck had to be upgraded to master fro this to work. --- .github/workflows/go.yml | 6 ++--- go.mod | 9 +++++++- tools/go.mod | 15 +++++++++---- tools/go.sum | 47 +++++++++++++++++++++------------------- 4 files changed, 47 insertions(+), 30 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 7f3fe1eee..1c210bcd8 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -14,9 +14,9 @@ jobs: strategy: matrix: os: ["ubuntu-latest", "windows-latest"] - go: ["1.15.x", "1.16.x", "1.17.x"] + go: ["1.17.x", "1.18.x"] include: - - go: 1.17.x + - go: 1.18.x os: "ubuntu-latest" latest: true @@ -28,7 +28,7 @@ jobs: - name: Checkout code uses: actions/checkout@v2 - + - name: Load cached dependencies uses: actions/cache@v1 with: diff --git a/go.mod b/go.mod index 7150a6465..337d60423 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module go.uber.org/fx -go 1.13 +go 1.17 require ( github.com/benbjohnson/clock v1.3.0 @@ -11,3 +11,10 @@ require ( go.uber.org/zap v1.16.0 golang.org/x/sys v0.0.0-20210903071746-97244b99971b ) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + go.uber.org/atomic v1.6.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect +) diff --git a/tools/go.mod b/tools/go.mod index 2116fae69..d15484e0a 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -1,10 +1,17 @@ module go.uber.org/fx/tools -go 1.16 +go 1.17 require ( golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 - golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect - golang.org/x/tools v0.1.4 - honnef.co/go/tools v0.2.0 + golang.org/x/tools v0.1.10 + honnef.co/go/tools v0.3.0-0.dev.0.20220306074811-23e1086441d2 +) + +require ( + github.com/BurntSushi/toml v1.0.0 // indirect + golang.org/x/exp/typeparams v0.0.0-20220314205449-43aec2f8a4e7 // indirect + golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect + golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect ) diff --git a/tools/go.sum b/tools/go.sum index 73da2377d..2cc761072 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -1,44 +1,47 @@ -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU= +github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/exp/typeparams v0.0.0-20220314205449-43aec2f8a4e7 h1:mzVEuUhnztMdHb426A1o6cjz5ha6YV3t6zFTBXqSon4= +golang.org/x/exp/typeparams v0.0.0-20220314205449-43aec2f8a4e7/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 h1:A9i04dxx7Cribqbs8jf3FQLogkL/CV2YN7hj9KWJCkc= +golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.4 h1:cVngSRcfgyZCzys3KYOpCFa+4dqX/Oub9tAq00ttGVs= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -honnef.co/go/tools v0.2.0 h1:ws8AfbgTX3oIczLPNPCu5166oBg9ST2vNs0rcht+mDE= -honnef.co/go/tools v0.2.0/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= +honnef.co/go/tools v0.3.0-0.dev.0.20220306074811-23e1086441d2 h1:utiSabORbG/JeX7MlmKMdmsjwom2+v8zmdb6SoBe4UY= +honnef.co/go/tools v0.3.0-0.dev.0.20220306074811-23e1086441d2/go.mod h1:dZI0HmIvwDMW8owtLBJxTHoeX48yuF5p5pDy3y73jGU=