diff options
| author | Taras Madan <tarasmadan@google.com> | 2025-01-22 16:07:17 +0100 |
|---|---|---|
| committer | Taras Madan <tarasmadan@google.com> | 2025-01-23 10:42:36 +0000 |
| commit | 7b4377ad9d8a7205416df8d6217ef2b010f89481 (patch) | |
| tree | e6fec4fd12ff807a16d847923f501075bf71d16c /vendor/github.com/tomarrell | |
| parent | 475a4c203afb8b7d3af51c4fd32bb170ff32a45e (diff) | |
vendor: delete
Diffstat (limited to 'vendor/github.com/tomarrell')
| -rw-r--r-- | vendor/github.com/tomarrell/wrapcheck/v2/LICENSE | 21 | ||||
| -rw-r--r-- | vendor/github.com/tomarrell/wrapcheck/v2/wrapcheck/wrapcheck.go | 463 |
2 files changed, 0 insertions, 484 deletions
diff --git a/vendor/github.com/tomarrell/wrapcheck/v2/LICENSE b/vendor/github.com/tomarrell/wrapcheck/v2/LICENSE deleted file mode 100644 index b5d9d30d3..000000000 --- a/vendor/github.com/tomarrell/wrapcheck/v2/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2020 Tom Arrell - -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. diff --git a/vendor/github.com/tomarrell/wrapcheck/v2/wrapcheck/wrapcheck.go b/vendor/github.com/tomarrell/wrapcheck/v2/wrapcheck/wrapcheck.go deleted file mode 100644 index 2c1b2d20b..000000000 --- a/vendor/github.com/tomarrell/wrapcheck/v2/wrapcheck/wrapcheck.go +++ /dev/null @@ -1,463 +0,0 @@ -package wrapcheck - -import ( - "fmt" - "go/ast" - "go/token" - "go/types" - "regexp" - "strings" - - "github.com/gobwas/glob" - "golang.org/x/tools/go/analysis" -) - -var DefaultIgnoreSigs = []string{ - ".Errorf(", - "errors.New(", - "errors.Unwrap(", - "errors.Join(", - ".Wrap(", - ".Wrapf(", - ".WithMessage(", - ".WithMessagef(", - ".WithStack(", -} - -// WrapcheckConfig is the set of configuration values which configure the -// behaviour of the linter. -type WrapcheckConfig struct { - // IgnoreSigs defines a list of substrings which if contained within the - // signature of the function call returning the error, will be ignored. This - // allows you to specify functions that wrapcheck will not report as - // unwrapped. - // - // For example, an ignoreSig of `[]string{"errors.New("}` will ignore errors - // returned from the stdlib package error's function: - // - // `func errors.New(message string) error` - // - // Due to the signature containing the substring `errors.New(`. - // - // Note: Setting this value will intentionally override the default ignored - // sigs. To achieve the same behaviour as default, you should add the default - // list to your config. - IgnoreSigs []string `mapstructure:"ignoreSigs" yaml:"ignoreSigs"` - - // IgnoreSigRegexps defines a list of regular expressions which if matched - // to the signature of the function call returning the error, will be ignored. This - // allows you to specify functions that wrapcheck will not report as - // unwrapped. - // - // For example, an ignoreSigRegexp of `[]string{"\.New.*Err\("}`` will ignore errors - // returned from any signature whose method name starts with "New" and ends with "Err" - // due to the signature matching the regular expression `\.New.*Err\(`. - // - // Note that this is similar to the ignoreSigs configuration, but provides - // slightly more flexibility in defining rules by which signatures will be - // ignored. - IgnoreSigRegexps []string `mapstructure:"ignoreSigRegexps" yaml:"ignoreSigRegexps"` - - // IgnorePackageGlobs defines a list of globs which, if matching the package - // of the function returning the error, will ignore the error when doing - // wrapcheck analysis. - // - // This is useful for broadly ignoring packages and subpackages from wrapcheck - // analysis. For example, to ignore all errors from all packages and - // subpackages of "encoding" you may include the configuration: - // - // -- .wrapcheck.yaml - // ignorePackageGlobs: - // - encoding/* - IgnorePackageGlobs []string `mapstructure:"ignorePackageGlobs" yaml:"ignorePackageGlobs"` - - // IgnoreInterfaceRegexps defines a list of regular expressions which, if matched - // to a underlying interface name, will ignore unwrapped errors returned from a - // function whose call is defined on the given interface. - // - // For example, an ignoreInterfaceRegexps of `[]string{"Transac(tor|tion)"}` will ignore errors - // returned from any function whose call is defined on a interface named 'Transactor' - // or 'Transaction' due to the name matching the regular expression `Transac(tor|tion)`. - IgnoreInterfaceRegexps []string `mapstructure:"ignoreInterfaceRegexps" yaml:"ignoreInterfaceRegexps"` -} - -func NewDefaultConfig() WrapcheckConfig { - return WrapcheckConfig{ - IgnoreSigs: DefaultIgnoreSigs, - IgnoreSigRegexps: []string{}, - IgnorePackageGlobs: []string{}, - IgnoreInterfaceRegexps: []string{}, - } -} - -func NewAnalyzer(cfg WrapcheckConfig) *analysis.Analyzer { - return &analysis.Analyzer{ - Name: "wrapcheck", - Doc: "Checks that errors returned from external packages are wrapped", - Run: run(cfg), - } -} - -func run(cfg WrapcheckConfig) func(*analysis.Pass) (interface{}, error) { - // Precompile the regexps, report the error - var ( - ignoreSigRegexp []*regexp.Regexp - ignoreInterfaceRegexps []*regexp.Regexp - ignorePackageGlobs []glob.Glob - err error - ) - - ignoreSigRegexp, err = compileRegexps(cfg.IgnoreSigRegexps) - if err == nil { - ignoreInterfaceRegexps, err = compileRegexps(cfg.IgnoreInterfaceRegexps) - } - if err == nil { - ignorePackageGlobs, err = compileGlobs(cfg.IgnorePackageGlobs) - } - - return func(pass *analysis.Pass) (interface{}, error) { - if err != nil { - return nil, err - } - - for _, file := range pass.Files { - // Keep track of parents so that can can traverse upwards to check for - // FuncDecls and FuncLits. - var parents []ast.Node - - ast.Inspect(file, func(n ast.Node) bool { - if n == nil { - // Pop, since we're done with this node and its children. - parents = parents[:len(parents)-1] - } else { - // Push this node on the stack, since its children will be visited - // next. - parents = append(parents, n) - } - - ret, ok := n.(*ast.ReturnStmt) - if !ok { - return true - } - - if len(ret.Results) < 1 { - return true - } - - // Iterate over the values to be returned looking for errors - for _, expr := range ret.Results { - // Check if the return expression is a function call, if it is, we need - // to handle it by checking the return params of the function. - retFn, ok := expr.(*ast.CallExpr) - if ok { - // If you go up, and the parent is a FuncLit, then don't report an - // error as you are in an anonymous function. If you are inside a - // FuncDecl, then continue as normal. - for i := len(parents) - 1; i > 0; i-- { - if _, ok := parents[i].(*ast.FuncLit); ok { - return true - } else if _, ok := parents[i].(*ast.FuncDecl); ok { - break - } - } - - // If the return type of the function is a single error. This will not - // match an error within multiple return values, for that, the below - // tuple check is required. - - if isError(pass.TypesInfo.TypeOf(expr)) { - reportUnwrapped(pass, retFn, retFn.Pos(), cfg, ignoreSigRegexp, ignoreInterfaceRegexps, ignorePackageGlobs) - return true - } - - // Check if one of the return values from the function is an error - tup, ok := pass.TypesInfo.TypeOf(expr).(*types.Tuple) - if !ok { - return true - } - - // Iterate over the return values of the function looking for error - // types - for i := 0; i < tup.Len(); i++ { - v := tup.At(i) - if v == nil { - return true - } - if isError(v.Type()) { - reportUnwrapped(pass, retFn, expr.Pos(), cfg, ignoreSigRegexp, ignoreInterfaceRegexps, ignorePackageGlobs) - return true - } - } - } - - if !isError(pass.TypesInfo.TypeOf(expr)) { - continue - } - - ident, ok := expr.(*ast.Ident) - if !ok { - return true - } - - var call *ast.CallExpr - - // Attempt to find the most recent short assign - if shortAss := prevErrAssign(pass, file, ident); shortAss != nil { - call, ok = shortAss.Rhs[0].(*ast.CallExpr) - if !ok { - return true - } - } else if isUnresolved(file, ident) { - // TODO Check if the identifier is unresolved, and try to resolve it in - // another file. - return true - } else { - // Check for ValueSpec nodes in order to locate a possible var - // declaration. - if ident.Obj == nil { - return true - } - - vSpec, ok := ident.Obj.Decl.(*ast.ValueSpec) - if !ok { - // We couldn't find a short or var assign for this error return. - // This is an error. Where did this identifier come from? Possibly a - // function param. - // - // TODO decide how to handle this case, whether to follow function - // param back, or assert wrapping at call site. - - return true - } - - if len(vSpec.Values) < 1 { - return true - } - - call, ok = vSpec.Values[0].(*ast.CallExpr) - if !ok { - return true - } - } - - // Make sure there is a call identified as producing the error being - // returned, otherwise just bail - if call == nil { - return true - } - - reportUnwrapped(pass, call, ident.NamePos, cfg, ignoreSigRegexp, ignoreInterfaceRegexps, ignorePackageGlobs) - } - - return true - }) - } - - return nil, nil - } -} - -// Report unwrapped takes a call expression and an identifier and reports -// if the call is unwrapped. -func reportUnwrapped( - pass *analysis.Pass, - call *ast.CallExpr, - tokenPos token.Pos, - cfg WrapcheckConfig, - regexpsSig []*regexp.Regexp, - regexpsInter []*regexp.Regexp, - pkgGlobs []glob.Glob, -) { - - sel, ok := call.Fun.(*ast.SelectorExpr) - if !ok { - return - } - - // Check for ignored signatures - fnSig := pass.TypesInfo.ObjectOf(sel.Sel).String() - if contains(cfg.IgnoreSigs, fnSig) { - return - } else if containsMatch(regexpsSig, fnSig) { - return - } - - // Check if the underlying type of the "x" in x.y.z is an interface, as - // errors returned from exported interface types should be wrapped, unless - // ignored as per `ignoreInterfaceRegexps` - if sel.Sel.IsExported() && isInterface(pass, sel) { - pkgPath := pass.TypesInfo.ObjectOf(sel.Sel).Pkg().Path() - name := types.TypeString(pass.TypesInfo.TypeOf(sel.X), func(p *types.Package) string { return p.Name() }) - if !containsMatch(regexpsInter, name) && !containsMatchGlob(pkgGlobs, pkgPath) { - pass.Reportf(tokenPos, "error returned from interface method should be wrapped: sig: %s", fnSig) - return - } - } - - // Check whether the function being called comes from another package, - // as functions called across package boundaries which returns errors - // should be wrapped - if isFromOtherPkg(pass, sel, pkgGlobs) { - pass.Reportf(tokenPos, "error returned from external package is unwrapped: sig: %s", fnSig) - return - } -} - -// isInterface returns whether the function call is one defined on an interface. -func isInterface(pass *analysis.Pass, sel *ast.SelectorExpr) bool { - _, ok := pass.TypesInfo.TypeOf(sel.X).Underlying().(*types.Interface) - - return ok -} - -// isFromotherPkg returns whether the function is defined in the package -// currently under analysis or is considered external. It will ignore packages -// defined in config.IgnorePackageGlobs. -func isFromOtherPkg(pass *analysis.Pass, sel *ast.SelectorExpr, pkgGlobs []glob.Glob) bool { - // The package of the function that we are calling which returns the error - fn := pass.TypesInfo.ObjectOf(sel.Sel) - if containsMatchGlob(pkgGlobs, fn.Pkg().Path()) { - return false - } - - // If it's not a package name, then we should check the selector to make sure - // that it's an identifier from the same package - if pass.Pkg.Path() == fn.Pkg().Path() { - return false - } - - return true -} - -// prevErrAssign traverses the AST of a file looking for the most recent -// assignment to an error declaration which is specified by the returnIdent -// identifier. -// -// This only returns short form assignments and reassignments, e.g. `:=` and -// `=`. This does not include `var` statements. This function will return nil if -// the only declaration is a `var` (aka ValueSpec) declaration. -func prevErrAssign(pass *analysis.Pass, file *ast.File, returnIdent *ast.Ident) *ast.AssignStmt { - // A slice containing all the assignments which contain an identifier - // referring to the source declaration of the error. This is to catch - // cases where err is defined once, and then reassigned multiple times - // within the same block. In these cases, we should check the method of - // the most recent call. - var assigns []*ast.AssignStmt - - // Find all assignments which have the same declaration - ast.Inspect(file, func(n ast.Node) bool { - if ass, ok := n.(*ast.AssignStmt); ok { - for _, expr := range ass.Lhs { - if !isError(pass.TypesInfo.TypeOf(expr)) { - continue - } - - if assIdent, ok := expr.(*ast.Ident); ok { - if assIdent.Obj == nil || returnIdent.Obj == nil { - // If we can't find the Obj for one of the identifiers, just skip - // it. - return true - } else if assIdent.Obj.Decl == returnIdent.Obj.Decl { - assigns = append(assigns, ass) - } - } - } - } - - return true - }) - - // Iterate through the assignments, comparing the token positions to - // find the assignment that directly precedes the return position - var mostRecentAssign *ast.AssignStmt - - for _, ass := range assigns { - if ass.Pos() > returnIdent.Pos() { - break - } - - mostRecentAssign = ass - } - - return mostRecentAssign -} - -func contains(slice []string, el string) bool { - for _, s := range slice { - if strings.Contains(el, s) { - return true - } - } - - return false -} - -func containsMatch(regexps []*regexp.Regexp, el string) bool { - for _, re := range regexps { - if re.MatchString(el) { - return true - } - } - - return false -} - -func containsMatchGlob(globs []glob.Glob, el string) bool { - for _, g := range globs { - if g.Match(el) { - return true - } - } - - return false -} - -// isError returns whether or not the provided type interface is an error -func isError(typ types.Type) bool { - if typ == nil { - return false - } - - return typ.String() == "error" -} - -func isUnresolved(file *ast.File, ident *ast.Ident) bool { - for _, unresolvedIdent := range file.Unresolved { - if unresolvedIdent.Pos() == ident.Pos() { - return true - } - } - - return false -} - -// compileRegexps compiles a set of regular expressions returning them for use, -// or the first encountered error due to an invalid expression. -func compileRegexps(regexps []string) ([]*regexp.Regexp, error) { - compiledRegexps := make([]*regexp.Regexp, len(regexps)) - for idx, reg := range regexps { - re, err := regexp.Compile(reg) - if err != nil { - return nil, fmt.Errorf("unable to compile regexp %s: %v\n", reg, err) - } - - compiledRegexps[idx] = re - } - - return compiledRegexps, nil -} - -// compileGlobs compiles a set of globs, returning them for use, -// or the first encountered error due to an invalid expression. -func compileGlobs(globs []string) ([]glob.Glob, error) { - compiledGlobs := make([]glob.Glob, len(globs)) - for idx, globString := range globs { - glob, err := glob.Compile(globString) - if err != nil { - return nil, fmt.Errorf("unable to compile globs %s: %v\n", glob, err) - } - - compiledGlobs[idx] = glob - } - return compiledGlobs, nil -} |
