aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/golangci/check/cmd/structcheck/structcheck.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/golangci/check/cmd/structcheck/structcheck.go')
-rw-r--r--vendor/github.com/golangci/check/cmd/structcheck/structcheck.go193
1 files changed, 193 insertions, 0 deletions
diff --git a/vendor/github.com/golangci/check/cmd/structcheck/structcheck.go b/vendor/github.com/golangci/check/cmd/structcheck/structcheck.go
new file mode 100644
index 000000000..5dc5f8380
--- /dev/null
+++ b/vendor/github.com/golangci/check/cmd/structcheck/structcheck.go
@@ -0,0 +1,193 @@
+// structcheck
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package structcheck
+
+import (
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/token"
+ "go/types"
+
+ "golang.org/x/tools/go/loader"
+)
+
+var (
+ assignmentsOnly = flag.Bool("structcheck.a", false, "Count assignments only")
+ loadTestFiles = flag.Bool("structcheck.t", false, "Load test files too")
+ buildTags = flag.String("structcheck.tags", "", "Build tags")
+)
+
+type visitor struct {
+ prog *loader.Program
+ pkg *loader.PackageInfo
+ m map[types.Type]map[string]int
+ skip map[types.Type]struct{}
+}
+
+func (v *visitor) decl(t types.Type, fieldName string) {
+ if _, ok := v.m[t]; !ok {
+ v.m[t] = make(map[string]int)
+ }
+ if _, ok := v.m[t][fieldName]; !ok {
+ v.m[t][fieldName] = 0
+ }
+}
+
+func (v *visitor) assignment(t types.Type, fieldName string) {
+ if _, ok := v.m[t]; !ok {
+ v.m[t] = make(map[string]int)
+ }
+ if _, ok := v.m[t][fieldName]; ok {
+ v.m[t][fieldName]++
+ } else {
+ v.m[t][fieldName] = 1
+ }
+}
+
+func (v *visitor) typeSpec(node *ast.TypeSpec) {
+ if strukt, ok := node.Type.(*ast.StructType); ok {
+ t := v.pkg.Info.Defs[node.Name].Type()
+ for _, f := range strukt.Fields.List {
+ if len(f.Names) > 0 {
+ fieldName := f.Names[0].Name
+ v.decl(t, fieldName)
+ }
+ }
+ }
+}
+
+func (v *visitor) typeAndFieldName(expr *ast.SelectorExpr) (types.Type, string, bool) {
+ selection := v.pkg.Info.Selections[expr]
+ if selection == nil {
+ return nil, "", false
+ }
+ recv := selection.Recv()
+ if ptr, ok := recv.(*types.Pointer); ok {
+ recv = ptr.Elem()
+ }
+ return recv, selection.Obj().Name(), true
+}
+
+func (v *visitor) assignStmt(node *ast.AssignStmt) {
+ for _, lhs := range node.Lhs {
+ var selector *ast.SelectorExpr
+ switch expr := lhs.(type) {
+ case *ast.SelectorExpr:
+ selector = expr
+ case *ast.IndexExpr:
+ if expr, ok := expr.X.(*ast.SelectorExpr); ok {
+ selector = expr
+ }
+ }
+ if selector != nil {
+ if t, fn, ok := v.typeAndFieldName(selector); ok {
+ v.assignment(t, fn)
+ }
+ }
+ }
+}
+
+func (v *visitor) compositeLiteral(node *ast.CompositeLit) {
+ t := v.pkg.Info.Types[node.Type].Type
+ for _, expr := range node.Elts {
+ if kv, ok := expr.(*ast.KeyValueExpr); ok {
+ if ident, ok := kv.Key.(*ast.Ident); ok {
+ v.assignment(t, ident.Name)
+ }
+ } else {
+ // Struct literal with positional values.
+ // All the fields are assigned.
+ v.skip[t] = struct{}{}
+ break
+ }
+ }
+}
+
+func (v *visitor) Visit(node ast.Node) ast.Visitor {
+ switch node := node.(type) {
+ case *ast.TypeSpec:
+ v.typeSpec(node)
+
+ case *ast.AssignStmt:
+ if *assignmentsOnly {
+ v.assignStmt(node)
+ }
+
+ case *ast.SelectorExpr:
+ if !*assignmentsOnly {
+ if t, fn, ok := v.typeAndFieldName(node); ok {
+ v.assignment(t, fn)
+ }
+ }
+
+ case *ast.CompositeLit:
+ v.compositeLiteral(node)
+ }
+
+ return v
+}
+
+type Issue struct {
+ Pos token.Position
+ Type string
+ FieldName string
+}
+
+func Run(program *loader.Program, reportExported bool) []Issue {
+ var issues []Issue
+ for _, pkg := range program.InitialPackages() {
+ visitor := &visitor{
+ m: make(map[types.Type]map[string]int),
+ skip: make(map[types.Type]struct{}),
+ prog: program,
+ pkg: pkg,
+ }
+ for _, f := range pkg.Files {
+ ast.Walk(visitor, f)
+ }
+
+ for t := range visitor.m {
+ if _, skip := visitor.skip[t]; skip {
+ continue
+ }
+ for fieldName, v := range visitor.m[t] {
+ if !reportExported && ast.IsExported(fieldName) {
+ continue
+ }
+ if v == 0 {
+ field, _, _ := types.LookupFieldOrMethod(t, false, pkg.Pkg, fieldName)
+ if field == nil {
+ fmt.Printf("%s: unknown field or method: %s.%s\n", pkg.Pkg.Path(), t, fieldName)
+ continue
+ }
+ if fieldName == "XMLName" {
+ if named, ok := field.Type().(*types.Named); ok && named.Obj().Pkg().Path() == "encoding/xml" {
+ continue
+ }
+ }
+ pos := program.Fset.Position(field.Pos())
+ issues = append(issues, Issue{
+ Pos: pos,
+ Type: types.TypeString(t, nil),
+ FieldName: fieldName,
+ })
+ }
+ }
+ }
+ }
+
+ return issues
+}