forked from mewbak/check
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
opennota
committed
Jul 18, 2014
0 parents
commit 54dcd80
Showing
7 changed files
with
1,115 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
language: go | ||
|
||
go: | ||
- 1.1 | ||
- 1.2 | ||
- 1.3 | ||
- tip | ||
|
||
install: | ||
- go get github.com/opennota/check | ||
- go build . | ||
|
||
script: | ||
- go test -v | ||
|
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
check [](https://travis-ci.org/opennota/check) | ||
======= | ||
|
||
A set of utilities for checking Go sources. | ||
|
||
## Installation | ||
|
||
$ go get github.com/opennota/check/cmd/defercheck | ||
$ go get github.com/opennota/check/cmd/structcheck | ||
$ go get github.com/opennota/check/cmd/varcheck | ||
|
||
## Usage | ||
|
||
Find repeating `defer`s. | ||
|
||
``` | ||
$ defercheck go/parser | ||
Repeating defer p.closeScope() inside function parseSwitchStmt | ||
``` | ||
|
||
Find unused struct fields. | ||
|
||
``` | ||
$ structcheck --help | ||
Usage of structcheck: | ||
-a=false: Count assignments only | ||
-n=1: Minimum use count | ||
$ structcheck fmt | ||
pp.n | ||
ssave.argLimit | ||
ssave.limit | ||
ssave.maxWid | ||
ssave.nlIsEnd | ||
ssave.nlIsSpace | ||
``` | ||
|
||
Find unused global variables and constants. | ||
|
||
``` | ||
$ varcheck --help | ||
Usage of varcheck: | ||
-e=false: Report exported variables and constants | ||
$ varcheck image/jpeg | ||
unzig | ||
quantIndexLuminance | ||
quantIndexChrominance | ||
huffIndexChrominanceDC | ||
dcTable | ||
maxV | ||
maxH | ||
acTable | ||
huffIndexLuminanceAC | ||
huffIndexChrominanceAC | ||
huffIndexLuminanceDC | ||
``` | ||
|
||
## Known limitations | ||
|
||
structcheck doesn't handle embedded structs yet. | ||
|
||
## License | ||
|
||
GNU LGPL v3+ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"go/ast" | ||
"os" | ||
|
||
"github.com/opennota/check" | ||
) | ||
|
||
type visitor struct { | ||
m map[*ast.Object][]string | ||
funcName string | ||
} | ||
|
||
var exitStatus int | ||
|
||
func (v *visitor) Visit(node ast.Node) ast.Visitor { | ||
switch node := node.(type) { | ||
case *ast.FuncDecl: | ||
v.funcName = node.Name.Name | ||
v.m = make(map[*ast.Object][]string) | ||
|
||
case *ast.DeferStmt: | ||
if sel, ok := node.Call.Fun.(*ast.SelectorExpr); ok { | ||
if ident, ok := sel.X.(*ast.Ident); ok { | ||
if selectors, ok := v.m[ident.Obj]; !ok { | ||
v.m[ident.Obj] = []string{sel.Sel.Name} | ||
} else { | ||
found := false | ||
for _, selname := range selectors { | ||
if selname == sel.Sel.Name { | ||
fmt.Printf("Repeating defer %s.%s() inside function %s\n", | ||
ident.Name, selname, v.funcName) | ||
found = true | ||
exitStatus = 1 | ||
break | ||
} | ||
} | ||
if !found { | ||
v.m[ident.Obj] = append(selectors, sel.Sel.Name) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
return v | ||
} | ||
|
||
func main() { | ||
flag.Parse() | ||
pkgPath := "." | ||
if len(flag.Args()) > 0 { | ||
pkgPath = flag.Arg(0) | ||
} | ||
visitor := &visitor{} | ||
_, astFiles := check.ASTFilesForPackage(pkgPath) | ||
for _, f := range astFiles { | ||
ast.Walk(visitor, f) | ||
} | ||
os.Exit(exitStatus) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"go/ast" | ||
"log" | ||
"os" | ||
|
||
_ "code.google.com/p/go.tools/go/gcimporter" | ||
"code.google.com/p/go.tools/go/types" | ||
"github.com/opennota/check" | ||
) | ||
|
||
var ( | ||
assignmentsOnly = flag.Bool("a", false, "Count assignments only") | ||
minimumUseCount = flag.Int("n", 1, "Minimum use count") | ||
) | ||
|
||
type visitor struct { | ||
pkg *types.Package | ||
info types.Info | ||
m map[string]map[string]int | ||
} | ||
|
||
func (v *visitor) decl(structName, fieldName string) { | ||
if _, ok := v.m[structName]; !ok { | ||
v.m[structName] = make(map[string]int) | ||
} | ||
if _, ok := v.m[structName][fieldName]; !ok { | ||
v.m[structName][fieldName] = 0 | ||
} | ||
} | ||
|
||
func (v *visitor) assignment(structName, fieldName string) { | ||
if _, ok := v.m[structName]; !ok { | ||
v.m[structName] = make(map[string]int) | ||
} | ||
if _, ok := v.m[structName][fieldName]; ok { | ||
v.m[structName][fieldName]++ | ||
} else { | ||
v.m[structName][fieldName] = 1 | ||
} | ||
} | ||
|
||
func (v *visitor) typeSpec(node *ast.TypeSpec) { | ||
if strukt, ok := node.Type.(*ast.StructType); ok { | ||
structName := node.Name.Name | ||
for _, f := range strukt.Fields.List { | ||
if len(f.Names) > 0 { | ||
fieldName := f.Names[0].Name | ||
v.decl(structName, fieldName) | ||
} | ||
} | ||
} | ||
} | ||
|
||
func (v *visitor) names(expr *ast.SelectorExpr) (string, string, bool) { | ||
selection := v.info.Selections[expr] | ||
if selection == nil { | ||
return "", "", false | ||
} | ||
recv := selection.Recv() | ||
if ptr, ok := recv.(*types.Pointer); ok { | ||
recv = ptr.Elem() | ||
} | ||
return types.TypeString(v.pkg, recv), selection.Obj().Name(), true | ||
} | ||
|
||
func (v *visitor) assignStmt(node *ast.AssignStmt) { | ||
for _, lhs := range node.Lhs { | ||
var selector *ast.SelectorExpr | ||
switch expr := lhs.(type) { | ||
case *ast.SelectorExpr: | ||
selector = expr | ||
case *ast.IndexExpr: | ||
if expr, ok := expr.X.(*ast.SelectorExpr); ok { | ||
selector = expr | ||
} | ||
} | ||
if selector != nil { | ||
if sn, fn, ok := v.names(selector); ok { | ||
v.assignment(sn, fn) | ||
} | ||
} | ||
} | ||
} | ||
|
||
func (v *visitor) compositeLiteral(node *ast.CompositeLit) { | ||
t := v.info.Types[node.Type] | ||
for _, expr := range node.Elts { | ||
if kv, ok := expr.(*ast.KeyValueExpr); ok { // no support for positional field values yet | ||
if ident, ok := kv.Key.(*ast.Ident); ok { | ||
v.assignment(types.TypeString(v.pkg, t.Type), ident.Name) | ||
} | ||
} | ||
} | ||
} | ||
|
||
func (v *visitor) Visit(node ast.Node) ast.Visitor { | ||
switch node := node.(type) { | ||
case *ast.TypeSpec: | ||
v.typeSpec(node) | ||
|
||
case *ast.AssignStmt: | ||
if *assignmentsOnly { | ||
v.assignStmt(node) | ||
} | ||
|
||
case *ast.SelectorExpr: | ||
if !*assignmentsOnly { | ||
if sn, fn, ok := v.names(node); ok { | ||
v.assignment(sn, fn) | ||
} | ||
} | ||
|
||
case *ast.CompositeLit: | ||
v.compositeLiteral(node) | ||
} | ||
|
||
return v | ||
} | ||
|
||
func main() { | ||
flag.Parse() | ||
pkgPath := "." | ||
if len(flag.Args()) > 0 { | ||
pkgPath = flag.Arg(0) | ||
} | ||
visitor := &visitor{ | ||
info: types.Info{ | ||
Types: make(map[ast.Expr]types.TypeAndValue), | ||
Selections: make(map[*ast.SelectorExpr]*types.Selection), | ||
}, | ||
|
||
m: make(map[string]map[string]int), | ||
} | ||
fset, astFiles := check.ASTFilesForPackage(pkgPath) | ||
config := types.Config{} | ||
var err error | ||
visitor.pkg, err = config.Check(pkgPath, fset, astFiles, &visitor.info) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
for _, f := range astFiles { | ||
ast.Walk(visitor, f) | ||
} | ||
exitStatus := 0 | ||
for structName := range visitor.m { | ||
for fieldName, v := range visitor.m[structName] { | ||
if v < *minimumUseCount { | ||
fmt.Printf("%s.%s\n", structName, fieldName) | ||
exitStatus = 1 | ||
} | ||
} | ||
} | ||
os.Exit(exitStatus) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"go/ast" | ||
"os" | ||
|
||
"github.com/opennota/check" | ||
) | ||
|
||
var ( | ||
reportExported = flag.Bool("e", false, "Report exported variables and constants") | ||
) | ||
|
||
type visitor struct { | ||
m map[*ast.Object]int | ||
insideFunc bool | ||
} | ||
|
||
func (v *visitor) decl(obj *ast.Object) { | ||
if _, ok := v.m[obj]; !ok { | ||
v.m[obj] = 0 | ||
} | ||
} | ||
|
||
func (v *visitor) use(obj *ast.Object) { | ||
if _, ok := v.m[obj]; ok { | ||
v.m[obj]++ | ||
} else { | ||
v.m[obj] = 1 | ||
} | ||
} | ||
|
||
func (v *visitor) Visit(node ast.Node) ast.Visitor { | ||
switch node := node.(type) { | ||
case *ast.Ident: | ||
v.use(node.Obj) | ||
|
||
case *ast.ValueSpec: | ||
if !v.insideFunc { | ||
for _, ident := range node.Names { | ||
if ident.Name != "_" { | ||
v.decl(ident.Obj) | ||
} | ||
} | ||
} | ||
for _, val := range node.Values { | ||
ast.Walk(v, val) | ||
} | ||
return nil | ||
|
||
case *ast.FuncDecl: | ||
if node.Body != nil { | ||
v.insideFunc = true | ||
ast.Walk(v, node.Body) | ||
v.insideFunc = false | ||
} | ||
|
||
return nil | ||
} | ||
|
||
return v | ||
} | ||
|
||
func main() { | ||
flag.Parse() | ||
pkgPath := "." | ||
if len(flag.Args()) > 0 { | ||
pkgPath = flag.Arg(0) | ||
} | ||
visitor := &visitor{m: make(map[*ast.Object]int)} | ||
_, astFiles := check.ASTFilesForPackage(pkgPath) | ||
for _, f := range astFiles { | ||
ast.Walk(visitor, f) | ||
} | ||
exitStatus := 0 | ||
for obj, useCount := range visitor.m { | ||
if useCount == 0 && (*reportExported || !check.IsExported(obj.Name)) { | ||
fmt.Println(obj.Name) | ||
exitStatus = 1 | ||
} | ||
} | ||
os.Exit(exitStatus) | ||
} |
Oops, something went wrong.