-
Notifications
You must be signed in to change notification settings - Fork 1
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
0 parents
commit f1516df
Showing
7 changed files
with
1,297 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,113 @@ | ||
# identypo | ||
|
||
identypo is a Go static analysis tool to find typos in identifiers (functions, function calls, variables, constants, type declarations, packages, labels). It is built on top of [client9's misspell package](https://github.com/client9/misspell). | ||
|
||
## Installation | ||
|
||
go get -u github.com/alexkohler/identypo/cmd/identypo | ||
|
||
## Usage | ||
|
||
Similar to other Go static analysis tools (such as golint, go vet), identypo can be invoked with one or more filenames, directories, or packages named by its import path. Identypo also supports the `...` wildcard. By default, it will search for typos in every identifier (functions, function calls, variables, constants, type declarations, packages, labels). | ||
|
||
identypo [flags] files/directories/packages | ||
|
||
### Flags | ||
- **-tests** (default true) - Include test files in analysis | ||
- **-i** - Comma separated list of corrections to be ignored (for example, to stop corrections on "nto" and "creater", pass `-i="nto,creater"). This is a direct passthrough to the misspell package. | ||
- **-functions** - Find typos in function declarations only. | ||
- **-constants** - Find typos in constants only. | ||
- **-variables** - Find typos in variables only. | ||
- **-set_exit_status** (default false) - Set exit status to 1 if any issues are found. | ||
|
||
NOTE: by default, identypo will check for typos in every identifier (functions, function calls, variables, constants, type declarations, packages, labels). In this case, no flag needs specified. Due to a lack of frequency, there are currently no flags to find only type declarations, packages, or labels. | ||
|
||
## Example uses in popular Go repos | ||
|
||
Some selected examples from [Kubernetes](https://github.com/kubernetes/kubernetes): | ||
```Bash | ||
$ identypo ./... | ||
cmd/kubeadm/app/cmd/phases/kubeconfig_test.go:325 "Authorithy" should be Authority in SetupPkiDirWithCertificateAuthorithy | ||
cmd/kubeadm/app/util/apiclient/wait.go:51 "inital" should be initial in initalTimeout | ||
pkg/apis/certificates/types.go:125 "Committment" should be Commitment in UsageContentCommittment | ||
controller/nodeipam/ipam/cidrset/cidr_set.go:158 "Begining" should be Beginning in getBeginingAndEndIndices | ||
staging/src/k8s.io/apimachinery/pkg/conversion/converter_test.go:358 "Overriden" should be Overridden in TestConverter_WithConversionOverriden | ||
``` | ||
|
||
```Go | ||
// cmd/kubeadm/app/cmd/phases/kubeconfig_test.go:325 "Authorithy" should be Authority in SetupPkiDirWithCertificateAuthorithy | ||
pkidir := testutil.SetupPkiDirWithCertificateAuthorithy(t, tmpdir) | ||
|
||
// cmd/kubeadm/app/util/apiclient/wait.go:51 "inital" should be initial in initalTimeout | ||
WaitForHealthyKubelet(initalTimeout time.Duration, healthzEndpoint string) error | ||
|
||
// pkg/apis/certificates/types.go:125 "Committment" should be Commitment in UsageContentCommittment | ||
UsageContentCommittment KeyUsage = "content commitment" | ||
|
||
// controller/nodeipam/ipam/cidrset/cidr_set.go:158 "Begining" should be Beginning in getBeginingAndEndIndices | ||
func (s *CidrSet) getBeginingAndEndIndices(cidr *net.IPNet) (begin, end int, err error) { | ||
|
||
// staging/src/k8s.io/apimachinery/pkg/conversion/converter_test.go:358 "Overriden" should be Overridden in TestConverter_WithConversionOverriden | ||
func TestConverter_WithConversionOverriden(t *testing.T) { | ||
``` | ||
Some examples from the [Go standard library](https://github.com/golang/go) (utilizing the `-i` flag to suppress some non-isses): | ||
```Bash | ||
$ identypo -i="rela,nto,onot,alltime" ./... | ||
cmd/trace/goroutines.go:169 "dividened" should be dividend in dividened | ||
cmd/trace/goroutines.go:173 "dividened" should be dividend in dividened | ||
cmd/trace/goroutines.go:175 "dividened" should be dividend in dividened | ||
cmd/trace/goroutines.go:179 "dividened" should be dividend in dividened | ||
cmd/trace/annotations.go:1162 "dividened" should be dividend in dividened | ||
cmd/trace/annotations.go:1166 "dividened" should be dividend in dividened | ||
cmd/trace/annotations.go:1168 "dividened" should be dividend in dividened | ||
cmd/trace/annotations.go:1172 "dividened" should be dividend in dividened | ||
crypto/x509/verify.go:208 "Comparisions" should be Comparisons in MaxConstraintComparisions | ||
crypto/x509/verify.go:585 "Comparisions" should be Comparisons in MaxConstraintComparisions | ||
``` | ||
```Go | ||
// cmd/trace/annotations.go:1162-1172 dividened" should be dividend in dividened | ||
"percent": func(dividened, divisor int64) template.HTML { | ||
if divisor == 0 { | ||
return "" | ||
} | ||
return template.HTML(fmt.Sprintf("(%.1f%%)", float6(dividened)/float64(divisor)*100)) | ||
}, | ||
"barLen": func(dividened, divisor int64) template.HTML { | ||
if divisor == 0 { | ||
return "0" | ||
} | ||
return template.HTML(fmt.Sprintf("%.2f%%", float6(dividened)/float64(divisor)*100)) | ||
}, | ||
|
||
// crypto/x509/verify.go:208 "Comparisions" should be Comparisons in MaxConstraintComparisions | ||
type VerifyOptions struct { | ||
... | ||
Roots *CertPool // if nil, the system roots are used | ||
CurrentTime time.Time // if zero, the current time is used | ||
... | ||
MaxConstraintComparisions int | ||
} | ||
``` | ||
## Packages used | ||
- https://github.com/client9/misspell | ||
- https://github.com/fatih/camelcase | ||
## Contributing | ||
Please open an issue and/or a PR for any features/bugs. | ||
## Other static analysis tools | ||
If you've enjoyed identypo, take a look at my other static anaylsis tools! | ||
- [prealloc](https://github.com/alexkohler/prealloc) - Finds slice declarations that could potentially be preallocated. | ||
- [nakedret](https://github.com/alexkohler/nakedret) - Finds naked returns. | ||
- [unimport](https://github.com/alexkohler/unimport) - Finds unnecessary import aliases. |
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,51 @@ | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"go/build" | ||
"log" | ||
"os" | ||
|
||
"github.com/alexkohler/identypo" | ||
) | ||
|
||
func init() { | ||
build.Default.UseAllFiles = false | ||
} | ||
|
||
func usage() { | ||
log.Printf("Usage of %s:\n", os.Args[0]) | ||
log.Printf("\nidentypo[flags] # runs on package in current directory\n") | ||
log.Printf("\nidentypo [flags] [packages]\n") | ||
log.Printf("Flags:\n") | ||
flag.PrintDefaults() | ||
log.Printf("\nNOTE: by default, identypo will check for typos in every identifier (functions, function calls, variables, constants, type declarations, packages, labels). In this case, no flag needs specified.\n") | ||
} | ||
|
||
func main() { | ||
|
||
// Remove log timestamp | ||
log.SetFlags(0) | ||
|
||
ignores := flag.String("i", "", "ignore the following words requiring correction, comma separated (e.g. -i=\"nto,creater\")") | ||
includeTests := flag.Bool("tests", true, "include test (*_test.go) files") | ||
functionsOnly := flag.Bool("functions", false, "find typos in function declarations only") | ||
constantsOnly := flag.Bool("constants", false, "find typos in constants only") | ||
variablesOnly := flag.Bool("variables", false, "find typos in variables only") | ||
setExitStatus := flag.Bool("set_exit_status", false, "Set exit status to 1 if any issues are found") | ||
flag.Usage = usage | ||
flag.Parse() | ||
|
||
flags := identypo.Flags{ | ||
Ignores: *ignores, | ||
IncludeTests: *includeTests, | ||
FunctionsOnly: *functionsOnly, | ||
ConstantsOnly: *constantsOnly, | ||
VariablesOnly: *variablesOnly, | ||
SetExitStatus: *setExitStatus, | ||
} | ||
|
||
if err := identypo.CheckForIdentiferTypos(flag.Args(), flags); err != nil { | ||
log.Println(err) | ||
} | ||
} |
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,127 @@ | ||
package identypo | ||
|
||
import ( | ||
"fmt" | ||
"go/ast" | ||
"go/token" | ||
"log" | ||
"os" | ||
"strings" | ||
|
||
"github.com/client9/misspell" | ||
"github.com/fatih/camelcase" | ||
) | ||
|
||
// Flags contains configuration specific to identypo. | ||
// * Ignores - comma separated list of corrections to be ignored (for example, to stop corrections on "nto" and "creater", pass `-i="nto,creater"). This is a direct passthrough to the misspell package. | ||
// * IncludeTests - include test files in analysis | ||
// * FunctionsOnly - Find typos in function declarations only. | ||
// * ConstantsOnly - Find typos in constants only. | ||
// * VariablesOnly - Find typos in variables only. | ||
// * SetExitStatus - Set exit status to 1 if any issues are found. | ||
// Note: If FunctionsOnly, ConstantsOnly, and VariablesOnly are all false, every identifier will be searched for typos. | ||
// (functions, function calls, variables, constants, type declarations, packages, labels). | ||
type Flags struct { | ||
Ignores string | ||
IncludeTests bool | ||
FunctionsOnly, ConstantsOnly, VariablesOnly bool | ||
SetExitStatus bool | ||
} | ||
|
||
// CheckForIdentiferTypos takes a slice of file arguments (this could be file names, directories, or packages (with or without the ... wildcard). | ||
// Further configuration (such as words to ignore, whether or not to include tests, etc.) can be specified with the flags argument. Output is written | ||
// using the log.Printf function. This is currently not configurable. For redirection to a file/buffer, see the log.SetOutput() method. | ||
func CheckForIdentiferTypos(args []string, flags Flags) error { | ||
|
||
fset := token.NewFileSet() | ||
|
||
files, err := parseInput(args, fset, flags.IncludeTests) | ||
if err != nil { | ||
return fmt.Errorf("could not parse input %v", err) | ||
} | ||
|
||
return processIdentifiers(fset, files, flags) | ||
} | ||
|
||
func processIdentifiers(fset *token.FileSet, files []*ast.File, flags Flags) error { | ||
all := !flags.FunctionsOnly && !flags.ConstantsOnly && !flags.VariablesOnly | ||
|
||
retVis := &returnsVisitor{ | ||
f: fset, | ||
replacer: misspell.New(), | ||
} | ||
|
||
if len(flags.Ignores) > 0 { | ||
lci := strings.ToLower(flags.Ignores) | ||
retVis.replacer.RemoveRule(strings.Split(lci, ",")) | ||
} | ||
|
||
retVis.replacer.Compile() | ||
|
||
for _, f := range files { | ||
if f == nil { | ||
continue | ||
} | ||
ast.Walk(retVis, f) | ||
} | ||
|
||
exitStatus := 0 | ||
|
||
for _, ident := range retVis.identifiers { | ||
for _, word := range camelcase.Split(ident.Name) { | ||
v, d := retVis.replacer.Replace(word) | ||
if len(d) > 0 { | ||
exitStatus = 1 | ||
file := retVis.f.File(ident.Pos()) | ||
fileName := file.Name() | ||
line := file.Position(ident.Pos()).Line | ||
|
||
if all { | ||
// if we're including everything, no need to look at the kind of identifier we have | ||
log.Printf("%v:%v %q should be %v in %v\n", fileName, line, word, v, ident.Name) | ||
} else if ident.Obj != nil { | ||
switch ident.Obj.Kind { | ||
case ast.Fun: | ||
if !flags.FunctionsOnly { | ||
continue | ||
} | ||
case ast.Var: | ||
if !flags.VariablesOnly { | ||
continue | ||
} | ||
case ast.Con: | ||
if !flags.ConstantsOnly { | ||
continue | ||
} | ||
default: | ||
// labels, packages, etc. currently do not have individual flags and will be skipped | ||
continue | ||
} | ||
log.Printf("%v:%v %q should be %v in %v\n", fileName, line, word, v, ident.Name) | ||
} | ||
} | ||
} | ||
} | ||
|
||
if flags.SetExitStatus { | ||
os.Exit(exitStatus) | ||
} | ||
return nil | ||
} | ||
|
||
type returnsVisitor struct { | ||
f *token.FileSet | ||
identifiers []*ast.Ident | ||
replacer *misspell.Replacer | ||
} | ||
|
||
func (v *returnsVisitor) Visit(node ast.Node) ast.Visitor { | ||
funcDecl, ok := node.(*ast.Ident) | ||
if !ok { | ||
return v | ||
} | ||
|
||
v.identifiers = append(v.identifiers, funcDecl) | ||
|
||
return v | ||
} |
Oops, something went wrong.