1 // Package lint provides abstractions on top of go/analysis.
14 "golang.org/x/tools/go/analysis"
17 type Documentation struct {
25 func (doc *Documentation) String() string {
26 b := &strings.Builder{}
27 fmt.Fprintf(b, "%s\n\n", doc.Title)
29 fmt.Fprintf(b, "%s\n\n", doc.Text)
31 fmt.Fprint(b, "Available since\n ")
33 fmt.Fprint(b, "unreleased")
35 fmt.Fprintf(b, "%s", doc.Since)
38 fmt.Fprint(b, ", non-default")
41 if len(doc.Options) > 0 {
42 fmt.Fprintf(b, "\nOptions\n")
43 for _, opt := range doc.Options {
44 fmt.Fprintf(b, " %s", opt)
51 func newVersionFlag() flag.Getter {
52 tags := build.Default.ReleaseTags
53 v := tags[len(tags)-1][2:]
54 version := new(VersionFlag)
55 if err := version.Set(v); err != nil {
56 panic(fmt.Sprintf("internal error: %s", err))
63 func (v *VersionFlag) String() string {
64 return fmt.Sprintf("1.%d", *v)
67 func (v *VersionFlag) Set(s string) error {
69 return errors.New("invalid Go version")
72 return errors.New("invalid Go version")
75 return errors.New("invalid Go version")
77 i, err := strconv.Atoi(s[2:])
82 func (v *VersionFlag) Get() interface{} {
86 func InitializeAnalyzers(docs map[string]*Documentation, analyzers map[string]*analysis.Analyzer) map[string]*analysis.Analyzer {
87 out := make(map[string]*analysis.Analyzer, len(analyzers))
88 for k, v := range analyzers {
95 panic(fmt.Sprintf("missing documentation for check %s", k))
97 vc.Doc = fmt.Sprintf("%s\nOnline documentation\n https://staticcheck.io/docs/checks#%s", doc.String(), k)
98 if vc.Flags.Usage == nil {
99 fs := flag.NewFlagSet("", flag.PanicOnError)
100 fs.Var(newVersionFlag(), "go", "Target Go version")
107 // ExhaustiveTypeSwitch panics when called. It can be used to ensure
108 // that type switches are exhaustive.
109 func ExhaustiveTypeSwitch(v interface{}) {
110 panic(fmt.Sprintf("internal error: unhandled case %T", v))
113 // A directive is a comment of the form '//lint:<command>
114 // [arguments...]'. It represents instructions to the static analysis
116 type Directive struct {
119 Directive *ast.Comment
123 func parseDirective(s string) (cmd string, args []string) {
124 if !strings.HasPrefix(s, "//lint:") {
127 s = strings.TrimPrefix(s, "//lint:")
128 fields := strings.Split(s, " ")
129 return fields[0], fields[1:]
132 func ParseDirectives(files []*ast.File, fset *token.FileSet) []Directive {
134 for _, f := range files {
135 // OPT(dh): in our old code, we skip all the commentmap work if we
136 // couldn't find any directives, benchmark if that's actually
138 cm := ast.NewCommentMap(fset, f, f.Comments)
139 for node, cgs := range cm {
140 for _, cg := range cgs {
141 for _, c := range cg.List {
142 if !strings.HasPrefix(c.Text, "//lint:") {
145 cmd, args := parseDirective(c.Text)
152 dirs = append(dirs, d)