Skip to content

Commit

Permalink
Revert "Merge pull request kisielk#148 from suzmue/typecheck-with-gop…
Browse files Browse the repository at this point in the history
…ackages"

This reverts commit 355fb1c, reversing
changes made to e96dacd.
  • Loading branch information
dominikh committed Aug 7, 2018
1 parent 355fb1c commit 1787c4b
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 204 deletions.
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ sudo: false

matrix:
include:
- go: "1.6"
- go: "1.7"
- go: "1.8"
- go: "1.9"
- go: "1.10"
- go: "tip"
Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ errcheck is a program for checking for unchecked errors in go programs.

go get -u github.com/kisielk/errcheck

errcheck requires Go 1.9 or newer and depends on the package go/packages from the golang.org/x/tools repository.
errcheck requires Go 1.6 or newer and depends on the package go/loader from the golang.org/x/tools repository.

## Use

Expand Down Expand Up @@ -94,9 +94,12 @@ no arguments.

## Cgo

Currently errcheck is unable to check packages that import "C" due to limitations in the importer when used with versions earlier than Go 1.11.
Currently errcheck is unable to check packages that `import "C"` due to limitations
in the importer.

However, you can use errcheck on packages that depend on those which use cgo. In order for this to work you need to go install the cgo dependencies before running errcheck on the dependent packages.
However, you can use errcheck on packages that depend on those which use cgo. In
order for this to work you need to `go install` the cgo dependencies before running
errcheck on the dependent packages.

See https://github.com/kisielk/errcheck/issues/16 for more details.

Expand Down
7 changes: 5 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
module github.com/kisielk/errcheck
module "github.com/kisielk/errcheck"

require golang.org/x/tools v0.0.0-20180803180156-3c07937fe18c
require (
"github.com/kisielk/gotool" v1.0.0
"golang.org/x/tools" v0.0.0-20180221164845-07fd8470d635
)
86 changes: 44 additions & 42 deletions internal/errcheck/errcheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"go/ast"
"go/build"
"go/token"
"go/types"
"os"
Expand All @@ -16,7 +17,8 @@ import (
"strings"
"sync"

"golang.org/x/tools/go/packages"
"go/parser"
"golang.org/x/tools/go/loader"
)

var errorType *types.Interface
Expand Down Expand Up @@ -163,19 +165,28 @@ func (c *Checker) logf(msg string, args ...interface{}) {
}
}

// loadPackages is used for testing.
var loadPackages = func(cfg *packages.Config, paths ...string) ([]*packages.Package, error) {
return packages.Load(cfg, paths...)
}
func (c *Checker) load(paths ...string) (*loader.Program, error) {
ctx := build.Default
for _, tag := range c.Tags {
ctx.BuildTags = append(ctx.BuildTags, tag)
}
loadcfg := loader.Config{
Build: &ctx,
}

func (c *Checker) load(paths ...string) ([]*packages.Package, error) {
cfg := &packages.Config{
Mode: packages.LoadAllSyntax,
Tests: !c.WithoutTests,
Flags: []string{fmt.Sprintf("-tags=%s", strings.Join(c.Tags, " "))},
Error: func(error) {}, // don't print type check errors
if c.WithoutGeneratedCode {
loadcfg.ParserMode = parser.ParseComments
}

rest, err := loadcfg.FromArgs(paths, !c.WithoutTests)
if err != nil {
return nil, fmt.Errorf("could not parse arguments: %s", err)
}
if len(rest) > 0 {
return nil, fmt.Errorf("unhandled extra arguments: %v", rest)
}
return loadPackages(cfg, paths...)

return loadcfg.Load()
}

var generatedCodeRegexp = regexp.MustCompile("^// Code generated .* DO NOT EDIT\\.$")
Expand All @@ -198,28 +209,27 @@ func (c *Checker) shouldSkipFile(file *ast.File) bool {

// CheckPackages checks packages for errors.
func (c *Checker) CheckPackages(paths ...string) error {
pkgs, err := c.load(paths...)
program, err := c.load(paths...)
if err != nil {
return err
}
// Check for errors in the initial packages.
for _, pkg := range pkgs {
if len(pkg.Errors) > 0 {
return fmt.Errorf("errors while loading package %s: %v", pkg.ID, pkg.Errors)
}
return fmt.Errorf("could not type check: %s", err)
}

var wg sync.WaitGroup
u := &UncheckedErrors{}
for _, pkg := range pkgs {
for _, pkgInfo := range program.InitialPackages() {
if pkgInfo.Pkg.Path() == "unsafe" { // not a real package
continue
}

wg.Add(1)

go func(pkg *packages.Package) {
go func(pkgInfo *loader.PackageInfo) {
defer wg.Done()
c.logf("Checking %s", pkg.Types.Path())
c.logf("Checking %s", pkgInfo.Pkg.Path())

v := &visitor{
pkg: pkg,
prog: program,
pkg: pkgInfo,
ignore: c.Ignore,
blank: c.Blank,
asserts: c.Asserts,
Expand All @@ -228,36 +238,28 @@ func (c *Checker) CheckPackages(paths ...string) error {
errors: []UncheckedError{},
}

for _, astFile := range v.pkg.Syntax {
for _, astFile := range v.pkg.Files {
if c.shouldSkipFile(astFile) {
continue
}
ast.Walk(v, astFile)
}
u.Append(v.errors...)
}(pkg)
}(pkgInfo)
}

wg.Wait()
if u.Len() > 0 {
// Sort unchecked errors and remove duplicates. Duplicates may occur when a file
// containing an unchecked error belongs to > 1 package.
sort.Sort(byName{u})
uniq := u.Errors[:0] // compact in-place
for i, err := range u.Errors {
if i == 0 || err != u.Errors[i-1] {
uniq = append(uniq, err)
}
}
u.Errors = uniq
return u
}
return nil
}

// visitor implements the errcheck algorithm
type visitor struct {
pkg *packages.Package
prog *loader.Program
pkg *loader.PackageInfo
ignore map[string]*regexp.Regexp
blank bool
asserts bool
Expand All @@ -282,7 +284,7 @@ func (v *visitor) selectorAndFunc(call *ast.CallExpr) (*ast.SelectorExpr, *types
return nil, nil, false
}

fn, ok := v.pkg.TypesInfo.ObjectOf(sel.Sel).(*types.Func)
fn, ok := v.pkg.ObjectOf(sel.Sel).(*types.Func)
if !ok {
// Shouldn't happen, but be paranoid
return nil, nil, false
Expand Down Expand Up @@ -338,7 +340,7 @@ func (v *visitor) namesForExcludeCheck(call *ast.CallExpr) []string {

// This will be missing for functions without a receiver (like fmt.Printf),
// so just fall back to the the function's fullName in that case.
selection, ok := v.pkg.TypesInfo.Selections[sel]
selection, ok := v.pkg.Selections[sel]
if !ok {
return []string{name}
}
Expand Down Expand Up @@ -399,7 +401,7 @@ func (v *visitor) ignoreCall(call *ast.CallExpr) bool {
return true
}

if obj := v.pkg.TypesInfo.Uses[id]; obj != nil {
if obj := v.pkg.Uses[id]; obj != nil {
if pkg := obj.Pkg(); pkg != nil {
if re, ok := v.ignore[pkg.Path()]; ok {
return re.MatchString(id.Name)
Expand Down Expand Up @@ -433,7 +435,7 @@ func nonVendoredPkgPath(pkgPath string) (string, bool) {
// len(s) == number of return types of call
// s[i] == true iff return type at position i from left is an error type
func (v *visitor) errorsByArg(call *ast.CallExpr) []bool {
switch t := v.pkg.TypesInfo.Types[call].Type.(type) {
switch t := v.pkg.Types[call].Type.(type) {
case *types.Named:
// Single return
return []bool{isErrorType(t)}
Expand Down Expand Up @@ -475,15 +477,15 @@ func (v *visitor) callReturnsError(call *ast.CallExpr) bool {
// isRecover returns true if the given CallExpr is a call to the built-in recover() function.
func (v *visitor) isRecover(call *ast.CallExpr) bool {
if fun, ok := call.Fun.(*ast.Ident); ok {
if _, ok := v.pkg.TypesInfo.Uses[fun].(*types.Builtin); ok {
if _, ok := v.pkg.Uses[fun].(*types.Builtin); ok {
return fun.Name == "recover"
}
}
return false
}

func (v *visitor) addErrorAtPosition(position token.Pos, call *ast.CallExpr) {
pos := v.pkg.Fset.Position(position)
pos := v.prog.Fset.Position(position)
lines, ok := v.lines[pos.Filename]
if !ok {
lines = readfile(pos.Filename)
Expand Down
Loading

0 comments on commit 1787c4b

Please sign in to comment.