support deadcode

This commit is contained in:
golangci 2018-05-06 14:51:06 +03:00
parent 062caa018c
commit b3f856af80
14 changed files with 259 additions and 15 deletions

8
Gopkg.lock generated
View File

@ -58,6 +58,12 @@
revision = "c34cdb4725f4c3844d095133c6e40e448b86589b"
version = "v1.1.1"
[[projects]]
branch = "master"
name = "github.com/golangci/go-misc"
packages = ["deadcode"]
revision = "a82b63c685e730fbc1efbade9ce6316ac85cceb7"
[[projects]]
branch = "master"
name = "github.com/golangci/gofmt"
@ -231,6 +237,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "c4187e819ccca1b06537adb888a052a895c6236f90accd827314c12b2fd1ce3e"
inputs-digest = "fd69e23afecb188e33d824ca597d116e47e913418eb1f0842ccb4f9bb44c7c7c"
solver-name = "gps-cdcl"
solver-version = 1

35
pkg/golinters/deadcode.go Normal file
View File

@ -0,0 +1,35 @@
package golinters
import (
"context"
"fmt"
deadcodeAPI "github.com/golangci/go-misc/deadcode"
"github.com/golangci/golangci-lint/pkg/config"
"github.com/golangci/golangci-lint/pkg/result"
"github.com/golangci/golangci-shared/pkg/executors"
)
type deadcode struct{}
func (deadcode) Name() string {
return "deadcode"
}
func (d deadcode) Run(ctx context.Context, exec executors.Executor, cfg *config.Run) (*result.Result, error) {
issues, err := deadcodeAPI.Run(cfg.Paths.MixedPaths(), true) // TODO: configure need of tests
if err != nil {
return nil, err
}
res := &result.Result{}
for _, i := range issues {
res.Issues = append(res.Issues, result.Issue{
File: i.Pos.Filename,
LineNumber: i.Pos.Line,
Text: fmt.Sprintf("%s is unused", formatCode(i.UnusedIdentName, cfg)),
FromLinter: d.Name(),
})
}
return res, nil
}

View File

@ -36,9 +36,11 @@ func GetAllSupportedLinterConfigs() []LinterConfig {
return []LinterConfig{
enabledByDefault(govet{}, "Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string"),
enabledByDefault(errcheck{}, "Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases"),
enabledByDefault(golint{}, "Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes."),
disabledByDefault(gofmt{}, "Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification."),
disabledByDefault(gofmt{useGoimports: true}, "Goimports does everything that gofmt does. Additionally it checks unused imports."),
enabledByDefault(golint{}, "Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes"),
enabledByDefault(deadcode{}, "Finds unused code"),
disabledByDefault(gofmt{}, "Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification"),
disabledByDefault(gofmt{useGoimports: true}, "Goimports does everything that gofmt does. Additionally it checks unused imports"),
}
}

20
pkg/golinters/testdata/deadcode.go vendored Normal file
View File

@ -0,0 +1,20 @@
package testdata
var y int
var unused int // ERROR "`unused` is unused"
func f(x int) {
}
func g(x int) { // ERROR "`g` is unused"
}
func H(x int) {
}
func init() {
f(y)
}
var _ int

View File

@ -5,15 +5,15 @@ import (
"os"
)
func retErr() error {
func RetErr() error {
return nil
}
func missedErrorCheck() {
retErr() // ERROR "Error return value of `retErr` is not checked"
func MissedErrorCheck() {
RetErr() // ERROR "Error return value of `RetErr` is not checked"
}
func ignoreCloseMissingErrHandling() error {
func IgnoreCloseMissingErrHandling() error {
f, err := os.Open("t.go")
if err != nil {
return err
@ -23,7 +23,7 @@ func ignoreCloseMissingErrHandling() error {
return nil
}
func ignoreCloseInDeferMissingErrHandling() {
func IgnoreCloseInDeferMissingErrHandling() {
resp, err := http.Get("http://example.com/")
if err != nil {
panic(err)

View File

@ -2,7 +2,7 @@ package testdata
import "fmt"
func gofmtNotSimplified() {
func GofmtNotSimplified() {
var x []string
fmt.Print(x[1:len(x)]) // ERROR "File is not gofmt-ed with -s"
}

View File

@ -5,7 +5,7 @@ import (
"github.com/golangci/golangci-lint/pkg/config"
)
func bar() {
func Bar() {
fmt.Print("x")
_ = config.Config{}
}

View File

@ -1,6 +1,6 @@
package testdata
var go_lint string // ERROR "don't use underscores in Go names; var go_lint should be goLint"
var Go_lint string // ERROR "don't use underscores in Go names; var Go_lint should be GoLint"
func ExportedFuncWithNoComment() {
}

View File

@ -2,11 +2,11 @@ package testdata
import "os"
func govet() error {
func Govet() error {
return &os.PathError{"first", "path", os.ErrNotExist} // ERROR "os.PathError composite literal uses unkeyed fields"
}
func govetShadow(f *os.File, buf []byte) (err error) {
func GovetShadow(f *os.File, buf []byte) (err error) {
if f != nil {
_, err := f.Read(buf) // ERROR "declaration of .err. shadows declaration at testdata/govet.go:9"
if err != nil {

View File

@ -27,6 +27,16 @@ type lintRes struct {
res *result.Result
}
func runLinter(ctx context.Context, linter Linter, exec executors.Executor, cfg *config.Run) (res *result.Result, err error) {
defer func() {
if panicData := recover(); panicData != nil {
err = fmt.Errorf("panic occured: %s", panicData)
}
}()
res, err = linter.Run(ctx, exec, cfg)
return
}
func runLinters(ctx context.Context, wg *sync.WaitGroup, tasksCh chan Linter, lintResultsCh chan lintRes, exec executors.Executor, cfg *config.Config) {
for i := 0; i < cfg.Common.Concurrency; i++ {
go func() {
@ -47,7 +57,7 @@ func runLinters(ctx context.Context, wg *sync.WaitGroup, tasksCh chan Linter, li
if !ok {
return
}
res, lerr := linter.Run(ctx, exec, &cfg.Run)
res, lerr := runLinter(ctx, linter, exec, &cfg.Run)
lintResultsCh <- lintRes{
linter: linter,
err: lerr,

27
vendor/github.com/golangci/go-misc/LICENSE generated vendored Normal file
View File

@ -0,0 +1,27 @@
Copyright (c) 2012 Rémy Oudompheng. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* The name of Rémy Oudompheng may not be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

18
vendor/github.com/golangci/go-misc/deadcode/README.md generated vendored Normal file
View File

@ -0,0 +1,18 @@
# deadcode
`deadcode` is a very simple utility which detects unused declarations in a Go package.
## Usage
```
deadcode [-test] [packages]
-test Include test files
packages A list of packages using the same conventions as the go tool
```
## Limitations
* Self-referential unused code is not currently reported
* A single package can be tested at a time
* Unused methods are not reported

125
vendor/github.com/golangci/go-misc/deadcode/deadcode.go generated vendored Normal file
View File

@ -0,0 +1,125 @@
package deadcode
import (
"fmt"
"go/ast"
"go/token"
"go/types"
"os"
"path/filepath"
"sort"
"golang.org/x/tools/go/loader"
)
var exitCode int
var (
withTestFiles bool
)
type Issue struct {
Pos token.Position
UnusedIdentName string
}
func Run(paths []string, processTestFiles bool) ([]Issue, error) {
ctx := &Context{
withTests: processTestFiles,
}
ctx.Load(paths...)
report := ctx.Process()
var issues []Issue
for _, obj := range report {
issues = append(issues, Issue{
Pos: ctx.Config.Fset.Position(obj.Pos()),
UnusedIdentName: obj.Name(),
})
}
return issues, nil
}
func fatalf(format string, args ...interface{}) {
panic(fmt.Errorf(format, args...))
}
type Context struct {
cwd string
withTests bool
loader.Config
}
func (ctx *Context) Load(args ...string) {
ctx.Config.FromArgs(args, ctx.withTests)
}
// error formats the error to standard error, adding program
// identification and a newline
func (ctx *Context) errorf(pos token.Pos, format string, args ...interface{}) {
if ctx.cwd == "" {
ctx.cwd, _ = os.Getwd()
}
p := ctx.Config.Fset.Position(pos)
f, err := filepath.Rel(ctx.cwd, p.Filename)
if err == nil {
p.Filename = f
}
fmt.Fprintf(os.Stderr, p.String()+": "+format+"\n", args...)
exitCode = 2
}
func (ctx *Context) Process() []types.Object {
prog, err := ctx.Config.Load()
if err != nil {
fatalf("cannot load packages: %s", err)
}
var allUnused []types.Object
for _, pkg := range prog.Imported {
unused := doPackage(prog, pkg)
allUnused = append(allUnused, unused...)
}
for _, pkg := range prog.Created {
unused := doPackage(prog, pkg)
allUnused = append(allUnused, unused...)
}
sort.Sort(objects(allUnused))
return allUnused
}
func doPackage(prog *loader.Program, pkg *loader.PackageInfo) []types.Object {
used := make(map[types.Object]bool)
for _, file := range pkg.Files {
ast.Inspect(file, func(n ast.Node) bool {
id, ok := n.(*ast.Ident)
if !ok {
return true
}
obj := pkg.Info.Uses[id]
if obj != nil {
used[obj] = true
}
return false
})
}
global := pkg.Pkg.Scope()
var unused []types.Object
for _, name := range global.Names() {
if pkg.Pkg.Name() == "main" && name == "main" {
continue
}
obj := global.Lookup(name)
if !used[obj] && (pkg.Pkg.Name() == "main" || !ast.IsExported(name)) {
unused = append(unused, obj)
}
}
return unused
}
type objects []types.Object
func (s objects) Len() int { return len(s) }
func (s objects) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s objects) Less(i, j int) bool { return s[i].Pos() < s[j].Pos() }

View File

@ -0,0 +1 @@
../vdeck/vdeck.js