aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/mbilski
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2021-02-22 20:37:25 +0100
committerDmitry Vyukov <dvyukov@google.com>2021-02-22 21:02:12 +0100
commitfcc6d71be2c3ce7d9305c04fc2e87af554571bac (patch)
treeb01dbb3d1e2988e28ea158d2d543d603ec0b9569 /vendor/github.com/mbilski
parent8f23c528ad5a943b9ffec5dcaf332fd0f614006e (diff)
go.mod: update golangci-lint to v1.37
Diffstat (limited to 'vendor/github.com/mbilski')
-rw-r--r--vendor/github.com/mbilski/exhaustivestruct/LICENSE21
-rw-r--r--vendor/github.com/mbilski/exhaustivestruct/pkg/analyzer/analyzer.go187
2 files changed, 208 insertions, 0 deletions
diff --git a/vendor/github.com/mbilski/exhaustivestruct/LICENSE b/vendor/github.com/mbilski/exhaustivestruct/LICENSE
new file mode 100644
index 000000000..893eb73b9
--- /dev/null
+++ b/vendor/github.com/mbilski/exhaustivestruct/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 Mateusz Bilski
+
+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/mbilski/exhaustivestruct/pkg/analyzer/analyzer.go b/vendor/github.com/mbilski/exhaustivestruct/pkg/analyzer/analyzer.go
new file mode 100644
index 000000000..0dfb713c5
--- /dev/null
+++ b/vendor/github.com/mbilski/exhaustivestruct/pkg/analyzer/analyzer.go
@@ -0,0 +1,187 @@
+package analyzer
+
+import (
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/types"
+ "path"
+ "strings"
+
+ "golang.org/x/tools/go/analysis/passes/inspect"
+ "golang.org/x/tools/go/ast/inspector"
+
+ "golang.org/x/tools/go/analysis"
+)
+
+// Analyzer that checks if all struct's fields are initialized
+var Analyzer = &analysis.Analyzer{
+ Name: "exhaustivestruct",
+ Doc: "Checks if all struct's fields are initialized",
+ Run: run,
+ Requires: []*analysis.Analyzer{inspect.Analyzer},
+ Flags: newFlagSet(),
+}
+
+// StructPatternList is a comma separated list of expressions to match struct packages and names
+// The struct packages have the form example.com/package.ExampleStruct
+// The matching patterns can use matching syntax from https://pkg.go.dev/path#Match
+// If this list is empty, all structs are tested.
+var StructPatternList string
+
+func newFlagSet() flag.FlagSet {
+ fs := flag.NewFlagSet("", flag.PanicOnError)
+ fs.StringVar(&StructPatternList, "struct_patterns", "", "This is a comma separated list of expressions to match struct packages and names")
+ return *fs
+}
+
+func run(pass *analysis.Pass) (interface{}, error) {
+ splitFn := func(c rune) bool { return c == ',' }
+ inspector := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
+ structPatterns := strings.FieldsFunc(StructPatternList, splitFn)
+ // validate the pattern syntax
+ for _, pattern := range structPatterns {
+ _, err := path.Match(pattern, "")
+ if err != nil {
+ return nil, fmt.Errorf("invalid struct pattern %s: %w", pattern, err)
+ }
+ }
+
+ nodeFilter := []ast.Node{
+ (*ast.CompositeLit)(nil),
+ (*ast.ReturnStmt)(nil),
+ }
+
+ var returnStmt *ast.ReturnStmt
+
+ inspector.Preorder(nodeFilter, func(node ast.Node) {
+ var name string
+
+ compositeLit, ok := node.(*ast.CompositeLit)
+ if !ok {
+ // Keep track of the last return statement whilte iterating
+ retLit, ok := node.(*ast.ReturnStmt)
+ if ok {
+ returnStmt = retLit
+ }
+ return
+ }
+
+ i, ok := compositeLit.Type.(*ast.Ident)
+
+ if ok {
+ name = i.Name
+ } else {
+ s, ok := compositeLit.Type.(*ast.SelectorExpr)
+
+ if !ok {
+ return
+ }
+
+ name = s.Sel.Name
+ }
+
+ if compositeLit.Type == nil {
+ return
+ }
+
+ t := pass.TypesInfo.TypeOf(compositeLit.Type)
+
+ if t == nil {
+ return
+ }
+
+ if len(structPatterns) > 0 {
+ shouldLint := false
+ for _, pattern := range structPatterns {
+ // We check the patterns for vailidy ahead of time, so we don't need to check the error here
+ if match, _ := path.Match(pattern, t.String()); match {
+ shouldLint = true
+ break
+ }
+ }
+ if !shouldLint {
+ return
+ }
+ }
+
+ str, ok := t.Underlying().(*types.Struct)
+
+ if !ok {
+ return
+ }
+
+ // Don't report an error if:
+ // 1. This composite literal contains no fields and
+ // 2. It's in a return statement and
+ // 3. The return statement contains a non-nil error
+ if len(compositeLit.Elts) == 0 {
+ // Check if this composite is one of the results the last return statement
+ isInResults := false
+ if returnStmt != nil {
+ for _, result := range returnStmt.Results {
+ compareComposite, ok := result.(*ast.CompositeLit)
+ if ok {
+ if compareComposite == compositeLit {
+ isInResults = true
+ }
+ }
+ }
+ }
+ nonNilError := false
+ if isInResults {
+ // Check if any of the results has an error type and if that error is set to non-nil (if it's set to nil, the type would be "untyped nil")
+ for _, result := range returnStmt.Results {
+ if pass.TypesInfo.TypeOf(result).String() == "error" {
+ nonNilError = true
+ }
+ }
+ }
+
+ if nonNilError {
+ return
+ }
+ }
+
+ samePackage := strings.HasPrefix(t.String(), pass.Pkg.Path()+".")
+
+ missing := []string{}
+
+ for i := 0; i < str.NumFields(); i++ {
+ fieldName := str.Field(i).Name()
+ exists := false
+
+ if !samePackage && !str.Field(i).Exported() {
+ continue
+ }
+
+ for eIndex, e := range compositeLit.Elts {
+ if k, ok := e.(*ast.KeyValueExpr); ok {
+ if i, ok := k.Key.(*ast.Ident); ok {
+ if i.Name == fieldName {
+ exists = true
+ break
+ }
+ }
+ } else {
+ if eIndex == i {
+ exists = true
+ break
+ }
+ }
+ }
+
+ if !exists {
+ missing = append(missing, fieldName)
+ }
+ }
+
+ if len(missing) == 1 {
+ pass.Reportf(node.Pos(), "%s is missing in %s", missing[0], name)
+ } else if len(missing) > 1 {
+ pass.Reportf(node.Pos(), "%s are missing in %s", strings.Join(missing, ", "), name)
+ }
+ })
+
+ return nil, nil
+}