-
Notifications
You must be signed in to change notification settings - Fork 3
/
noticeme.go
118 lines (104 loc) · 3.06 KB
/
noticeme.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Copyright 2020 Molecula Corp.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package noticeme
import (
"errors"
"go/ast"
"go/types"
"strings"
"sync"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/ast/inspector"
)
const Doc = `check for unused values by type
Invoke with -types <typelist>, with comma-separated types. Names are
checked against the name with no qualifier, just a package name, and the
full import path. Reports any expression statements that have one or more
of the given types.
`
var Analyzer = &analysis.Analyzer{
Name: "noticeme",
Doc: Doc,
Requires: []*analysis.Analyzer{inspect.Analyzer},
Run: noticeme,
}
var importantTypes string
func init() {
Analyzer.Flags.StringVar(&importantTypes, "types", "", "specify important types")
}
type typeList []string
func typeNameOnly(pkg *types.Package) string {
return ""
}
func packageNameOnly(pkg *types.Package) string {
return pkg.Name()
}
// matchImportance returns the first string in the list it found which
// matches the last component of the name of the given type, or of a type
// within it if it's a tuple.
func (tl typeList) matchImportance(t types.Type) (bool, string) {
if tuple, ok := t.(*types.Tuple); ok {
for i := 0; i < tuple.Len(); i++ {
subType := tuple.At(i).Type()
important, why := tl.matchImportance(subType)
if important {
return important, why
}
}
}
names := []string{
types.TypeString(t, typeNameOnly),
types.TypeString(t, packageNameOnly),
types.TypeString(t, nil),
}
for _, w := range tl {
for _, name := range names {
if name == w {
return true, name
}
}
}
return false, ""
}
var parseImportant sync.Once
var importantList typeList
var relevantTypes = map[types.Type]string{}
func noticeme(pass *analysis.Pass) (_ interface{}, err error) {
parseImportant.Do(func() {
if importantTypes == "" {
err = errors.New("you must specify which types you care about (-types)")
return
}
importantList = strings.Split(importantTypes, ",")
})
if err != nil {
return nil, err
}
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
nodeFilter := []ast.Node{(*ast.ExprStmt)(nil)}
inspect.Preorder(nodeFilter, func(n ast.Node) {
expr := n.(*ast.ExprStmt).X
exprType := pass.TypesInfo.Types[expr].Type
relevant, ok := relevantTypes[exprType]
if !ok {
_, relevant = importantList.matchImportance(exprType)
relevantTypes[exprType] = relevant
}
if relevant != "" {
pass.Reportf(n.Pos(), "unused value of type %s", relevant)
}
})
return nil, nil
}