gitee.com/wgliang/goreporter@v0.0.0-20180902115603-df1b20f7c5d0/linters/simpler/lint/lintutil/util.go (about) 1 // Copyright (c) 2013 The Go Authors. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file or at 5 // https://developers.google.com/open-source/licenses/bsd. 6 7 // Package lintutil provides helpers for writing linter command lines. 8 package lintutil // import "github.com/360EntSecGroup-Skylar/goreporter/linters/simpler/lint/lintutil" 9 10 import ( 11 "errors" 12 "flag" 13 "fmt" 14 "go/build" 15 "go/parser" 16 "go/token" 17 "log" 18 "os" 19 "path/filepath" 20 "strconv" 21 "strings" 22 23 "github.com/360EntSecGroup-Skylar/goreporter/linters/simpler/lint" 24 25 "github.com/kisielk/gotool" 26 "golang.org/x/tools/go/loader" 27 ) 28 29 func usage(name string, flags *flag.FlagSet) func() { 30 return func() { 31 fmt.Fprintf(os.Stderr, "Usage of %s:\n", name) 32 fmt.Fprintf(os.Stderr, "\t%s [flags] # runs on package in current directory\n", name) 33 fmt.Fprintf(os.Stderr, "\t%s [flags] packages\n", name) 34 fmt.Fprintf(os.Stderr, "\t%s [flags] directory\n", name) 35 fmt.Fprintf(os.Stderr, "\t%s [flags] files... # must be a single package\n", name) 36 fmt.Fprintf(os.Stderr, "Flags:\n") 37 flags.PrintDefaults() 38 } 39 } 40 41 type runner struct { 42 checker lint.Checker 43 tags []string 44 ignores []lint.Ignore 45 version int 46 } 47 48 func (runner runner) resolveRelative(importPaths []string) (goFiles bool, err error) { 49 if len(importPaths) == 0 { 50 return false, nil 51 } 52 if strings.HasSuffix(importPaths[0], ".go") { 53 // User is specifying a package in terms of .go files, don't resolve 54 return true, nil 55 } 56 wd, err := os.Getwd() 57 if err != nil { 58 return false, err 59 } 60 ctx := build.Default 61 ctx.BuildTags = runner.tags 62 for i, path := range importPaths { 63 bpkg, err := ctx.Import(path, wd, build.FindOnly) 64 if err != nil { 65 return false, fmt.Errorf("can't load package %q: %v", path, err) 66 } 67 importPaths[i] = bpkg.ImportPath 68 } 69 return false, nil 70 } 71 72 func parseIgnore(s string) ([]lint.Ignore, error) { 73 var out []lint.Ignore 74 if len(s) == 0 { 75 return nil, nil 76 } 77 for _, part := range strings.Fields(s) { 78 p := strings.Split(part, ":") 79 if len(p) != 2 { 80 return nil, errors.New("malformed ignore string") 81 } 82 path := p[0] 83 checks := strings.Split(p[1], ",") 84 out = append(out, lint.Ignore{Pattern: path, Checks: checks}) 85 } 86 return out, nil 87 } 88 89 type versionFlag int 90 91 func (v *versionFlag) String() string { 92 return fmt.Sprintf("1.%d", *v) 93 } 94 95 func (v *versionFlag) Set(s string) error { 96 if len(s) < 3 { 97 return errors.New("invalid Go version") 98 } 99 if s[0] != '1' { 100 return errors.New("invalid Go version") 101 } 102 if s[1] != '.' { 103 return errors.New("invalid Go version") 104 } 105 i, err := strconv.Atoi(s[2:]) 106 *v = versionFlag(i) 107 return err 108 } 109 110 func (v *versionFlag) Get() interface{} { 111 return int(*v) 112 } 113 114 func FlagSet(name string) *flag.FlagSet { 115 flags := flag.NewFlagSet("", flag.ExitOnError) 116 flags.Usage = usage(name, flags) 117 flags.Float64("min_confidence", 0, "Deprecated; use -ignore instead") 118 flags.String("tags", "", "List of `build tags`") 119 flags.String("ignore", "", "Space separated list of checks to ignore, in the following format: 'import/path/file.go:Check1,Check2,...' Both the import path and file name sections support globbing, e.g. 'os/exec/*_test.go'") 120 flags.Bool("tests", true, "Include tests") 121 122 tags := build.Default.ReleaseTags 123 v := tags[len(tags)-1][2:] 124 version := new(versionFlag) 125 if err := version.Set(v); err != nil { 126 panic(fmt.Sprintf("internal error: %s", err)) 127 } 128 129 flags.Var(version, "go", "Target Go `version` in the format '1.x'") 130 return flags 131 } 132 133 func ProcessFlagSet(c lint.Checker, fs *flag.FlagSet) (results []string) { 134 tags := fs.Lookup("tags").Value.(flag.Getter).Get().(string) 135 ignore := fs.Lookup("ignore").Value.(flag.Getter).Get().(string) 136 tests := fs.Lookup("tests").Value.(flag.Getter).Get().(bool) 137 version := fs.Lookup("go").Value.(flag.Getter).Get().(int) 138 139 ps, lprog, err := Lint(c, fs.Args(), &Options{ 140 Tags: strings.Fields(tags), 141 LintTests: tests, 142 Ignores: ignore, 143 GoVersion: version, 144 }) 145 if err != nil { 146 l := log.New(os.Stderr, "", log.LstdFlags) 147 l.Println(err) 148 } 149 150 for _, p := range ps { 151 pos := lprog.Fset.Position(p.Position) 152 results = append(results, fmt.Sprintf("%v: %s", relativePositionString(pos), p.Text)) 153 } 154 return 155 } 156 157 type Options struct { 158 Tags []string 159 LintTests bool 160 Ignores string 161 GoVersion int 162 } 163 164 func Lint(c lint.Checker, pkgs []string, opt *Options) ([]lint.Problem, *loader.Program, error) { 165 // TODO(dh): Instead of returning the loader.Program, we should 166 // store token.Position instead of token.Pos in lint.Problem. 167 if opt == nil { 168 opt = &Options{} 169 } 170 ignores, err := parseIgnore(opt.Ignores) 171 if err != nil { 172 return nil, nil, err 173 } 174 runner := &runner{ 175 checker: c, 176 tags: opt.Tags, 177 ignores: ignores, 178 version: opt.GoVersion, 179 } 180 paths := gotool.ImportPaths(pkgs) 181 goFiles, err := runner.resolveRelative(paths) 182 if err != nil { 183 return nil, nil, err 184 } 185 ctx := build.Default 186 ctx.BuildTags = runner.tags 187 conf := &loader.Config{ 188 Build: &ctx, 189 ParserMode: parser.ParseComments, 190 ImportPkgs: map[string]bool{}, 191 } 192 if goFiles { 193 conf.CreateFromFilenames("adhoc", paths...) 194 } else { 195 for _, path := range paths { 196 conf.ImportPkgs[path] = opt.LintTests 197 } 198 } 199 lprog, err := conf.Load() 200 if err != nil { 201 return nil, nil, err 202 } 203 return runner.lint(lprog), lprog, nil 204 } 205 206 func shortPath(path string) string { 207 cwd, err := os.Getwd() 208 if err != nil { 209 return path 210 } 211 if rel, err := filepath.Rel(cwd, path); err == nil && len(rel) < len(path) { 212 return rel 213 } 214 return path 215 } 216 217 func relativePositionString(pos token.Position) string { 218 s := shortPath(pos.Filename) 219 if pos.IsValid() { 220 if s != "" { 221 s += ":" 222 } 223 s += fmt.Sprintf("%d:%d", pos.Line, pos.Column) 224 } 225 if s == "" { 226 s = "-" 227 } 228 return s 229 } 230 231 func ProcessArgs(name string, c lint.Checker, args []string) { 232 flags := FlagSet(name) 233 flags.Parse(args) 234 235 ProcessFlagSet(c, flags) 236 } 237 238 func (runner *runner) lint(lprog *loader.Program) []lint.Problem { 239 l := &lint.Linter{ 240 Checker: runner.checker, 241 Ignores: runner.ignores, 242 GoVersion: runner.version, 243 } 244 return l.Lint(lprog) 245 }