diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b225e27..8ca34c8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,7 +6,7 @@ on: - v* env: - GO_VERSION: "1.21" + GO_VERSION: "1.20" jobs: @@ -36,6 +36,6 @@ jobs: with: version: latest args: release --clean - workdir: cmd/go-factory-lint/ + workdir: cmd/gofactory/ env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.golangci.yml b/.golangci.yml index 38a2c4a..edfdfb9 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -8,7 +8,7 @@ linters-settings: - v - ok gci: - local-prefixes: github.com/maranqz/go-factory-lint + local-prefixes: github.com/maranqz/gofactory linters: enable-all: true @@ -36,6 +36,7 @@ issues: - path: lint_test linters: - varnamelen + - wrapcheck - linters: - lll source: "^(?: |\t)*// " @@ -45,4 +46,4 @@ issues: - linters: - godot - lll - source: "// ?TODO " \ No newline at end of file + source: "// ?TODO " diff --git a/Makefile b/Makefile index 330e555..ec722a8 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,17 @@ +.PHONY: fmt lint clean.test test test.clean + CVPKG=go list ./... | grep -v mocks | grep -v internal/ GO_TEST=go test `$(CVPKG)` -race COVERAGE_FILE="coverage.out" +all: fmt lint test install + +fmt: + go fmt ./... + +lint: + golangci-lint run --fix ./... + clean.test: go clean --testcache @@ -12,3 +22,6 @@ test.clean: clean.test test test.coverage: $(GO_TEST) -covermode=atomic -coverprofile=$(COVERAGE_FILE) + +install: + go install ./cmd/gofactory diff --git a/README.md b/README.md index 3d1cfb3..70d5d33 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,27 @@ # Factory linter -[![CI](https://github.com/maranqz/go-factory-lint/actions/workflows/ci.yml/badge.svg)](https://github.com/maranqz/go-factory-lint/actions/workflows/ci.yml) -[![Go Report Card](https://goreportcard.com/badge/github.com/maranqz/go-factory-lint)](https://goreportcard.com/report/github.com/maranqz/go-factory-lint?dummy=unused) +[![CI](https://github.com/maranqz/gofactory/actions/workflows/ci.yml/badge.svg)](https://github.com/maranqz/gofactory/actions/workflows/ci.yml) +[![Go Report Card](https://goreportcard.com/badge/github.com/maranqz/gofactory)](https://goreportcard.com/report/github.com/maranqz/gofactory?dummy=unused) [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE) -[![Coverage Status](https://coveralls.io/repos/github/maranqz/go-factory-lint/badge.svg?branch=main)](https://coveralls.io/github/maranqz/go-factory-lint?branch=main) +[![Coverage Status](https://coveralls.io/repos/github/maranqz/gofactory/badge.svg?branch=main)](https://coveralls.io/github/maranqz/gofactory?branch=main) The linter checks that the Structures are created by the Factory, and not directly. The checking helps to provide invariants without exclusion and helps avoid creating an invalid object. -## Use +## Usage -Installation +### Installation - go install github.com/maranqz/go-factory-lint/cmd/go-factory-lint@latest + go install github.com/maranqz/gofactory/cmd/gofactory@latest ### Options -- `--packageGlobs` - list of glob packages, which can create structures without factories inside the glob package. By default, all structures from another package should be created by factories, [tests](testdata/src/factory/packageGlobs). - - `onlyPackageGlobs` - use a factory to initiate a structure for glob packages only, [tests](testdata/src/factory/onlyPackageGlobs). +- `--packageGlobs` – list of glob packages, which can create structures without factories inside the glob package. +By default, all structures from another package should be created by factories, [tests](testdata/src/factory/packageGlobs). +- `--packageGlobsOnly` – use a factory to initiate a structure for glob packages only, +[tests](testdata/src/factory/packageGlobsOnly). Doesn't make sense without `--packageGlobs`. ## Example @@ -128,6 +130,7 @@ Linter doesn't catch some cases. 2. Local initialization, [example](testdata/src/factory/unimplemented/local/). 3. Named return. If you want to block that case, you can use [nonamedreturns](https://github.com/firefart/nonamedreturns) linter, [example](testdata/src/factory/unimplemented/named_return.go). 4. var declaration, `var initilized nested.Struct` gives structure without factory, [example](testdata/src/factory/unimplemented/var.go). + To block that case, you can use [gopublicfield](github.com/maranqz/gopublicfield) to prevent fill of structure fields. ## TODO diff --git a/cmd/go-factory-lint/main.go b/cmd/gofactory/main.go similarity index 52% rename from cmd/go-factory-lint/main.go rename to cmd/gofactory/main.go index 02af132..81bf9f3 100644 --- a/cmd/go-factory-lint/main.go +++ b/cmd/gofactory/main.go @@ -3,9 +3,9 @@ package main import ( "golang.org/x/tools/go/analysis/singlechecker" - "github.com/maranqz/go-factory-lint/v2" + "github.com/maranqz/gofactory" ) func main() { - singlechecker.Main(factory.NewAnalyzer()) + singlechecker.Main(gofactory.NewAnalyzer()) } diff --git a/factory.go b/factory.go index 07faa0a..45a9df6 100644 --- a/factory.go +++ b/factory.go @@ -1,61 +1,38 @@ -package factory +package gofactory import ( - "fmt" "go/ast" "go/types" "log" - "strings" "github.com/gobwas/glob" "golang.org/x/tools/go/analysis" - "golang.org/x/tools/go/analysis/passes/inspect" ) type config struct { - pkgGlobs stringsFlag + pkgGlobs globsFlag onlyPkgGlobs bool } -type stringsFlag []string - -func (s stringsFlag) String() string { - return strings.Join(s, ", ") -} - -func (s *stringsFlag) Set(value string) error { - *s = append(*s, value) - - return nil -} - -func (s stringsFlag) Value() []string { - res := make([]string, 0, len(s)) - - for _, str := range s { - res = append(res, strings.TrimSpace(str)) - } - - return res -} - const ( - packageGlobsDesc = "List of glob packages, which can create structures without factories inside the glob package" - onlyPkgGlobsDesc = "Use a factory to initiate a structure for glob packages only." + name = "gofactory" + doc = "Blocks the creation of structures directly, without a factory." + + packageGlobsDesc = "list of glob packages, which can create structures without factories inside the glob package" + onlyPkgGlobsDesc = "use a factory to initiate a structure for glob packages only" ) func NewAnalyzer() *analysis.Analyzer { analyzer := &analysis.Analyzer{ - Name: "gofactory", - Doc: "Blocks the creation of structures directly, without a factory.", - Requires: []*analysis.Analyzer{inspect.Analyzer}, + Name: name, + Doc: doc, } cfg := config{} analyzer.Flags.Var(&cfg.pkgGlobs, "packageGlobs", packageGlobsDesc) - analyzer.Flags.BoolVar(&cfg.onlyPkgGlobs, "onlyPackageGlobs", false, onlyPkgGlobsDesc) + analyzer.Flags.BoolVar(&cfg.onlyPkgGlobs, "packageGlobsOnly", false, onlyPkgGlobsDesc) analyzer.Run = run(&cfg) @@ -65,17 +42,14 @@ func NewAnalyzer() *analysis.Analyzer { func run(cfg *config) func(pass *analysis.Pass) (interface{}, error) { return func(pass *analysis.Pass) (interface{}, error) { var blockedStrategy blockedStrategy = newAnotherPkg() - if len(cfg.pkgGlobs) > 0 { + + pkgGlobs := cfg.pkgGlobs.Value() + if len(pkgGlobs) > 0 { defaultStrategy := blockedStrategy if cfg.onlyPkgGlobs { defaultStrategy = newNilPkg() } - pkgGlobs, err := compileGlobs(cfg.pkgGlobs.Value()) - if err != nil { - return nil, err - } - blockedStrategy = newBlockedPkgs( pkgGlobs, defaultStrategy, @@ -231,18 +205,14 @@ func (v *visitor) checkBrackets(expr ast.Expr, identObj types.Object) { func (v *visitor) report(node ast.Node, obj types.Object) { v.pass.Reportf( node.Pos(), - fmt.Sprintf(`Use factory for %s.%s`, obj.Pkg().Name(), obj.Name()), + "Use factory for %s.%s", obj.Pkg().Name(), obj.Name(), ) } func (v *visitor) unexpectedCode(node ast.Node) { - fset := v.pass.Fset - pos := fset.Position(node.Pos()) - - log.Printf("Unexpected code in %s:%d:%d, please report to the developer with example.\n", - fset.File(node.Pos()).Name(), - pos.Line, - pos.Column, + log.Printf("%s: unexpected code in %s, please report to the developer with example.\n", + name, + v.pass.Fset.Position(node.Pos()), ) } @@ -255,18 +225,3 @@ func containsMatchGlob(globs []glob.Glob, el string) bool { return false } - -func compileGlobs(globs []string) ([]glob.Glob, error) { - compiledGlobs := make([]glob.Glob, len(globs)) - - for idx, globString := range globs { - glob, err := glob.Compile(globString) - if err != nil { - return nil, fmt.Errorf("unable to compile globs %s: %w", glob, err) - } - - compiledGlobs[idx] = glob - } - - return compiledGlobs, nil -} diff --git a/globs_flag.go b/globs_flag.go new file mode 100644 index 0000000..fa7816a --- /dev/null +++ b/globs_flag.go @@ -0,0 +1,35 @@ +package gofactory + +import ( + "fmt" + "strings" + + "github.com/gobwas/glob" +) + +type globsFlag struct { + globsString []string + globs []glob.Glob +} + +func (g globsFlag) String() string { + return strings.Join(g.globsString, ", ") +} + +func (g *globsFlag) Set(globString string) error { + globString = strings.TrimSpace(globString) + + compiled, err := glob.Compile(globString) + if err != nil { + return fmt.Errorf("unable to compile globs %s: %w", globString, err) + } + + g.globsString = append(g.globsString, globString) + g.globs = append(g.globs, compiled) + + return nil +} + +func (g globsFlag) Value() []glob.Glob { + return g.globs +} diff --git a/go.mod b/go.mod index d51ed42..f388dbf 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/maranqz/go-factory-lint/v2 +module github.com/maranqz/gofactory go 1.20 diff --git a/lint_test.go b/lint_test.go index a83579e..4631554 100644 --- a/lint_test.go +++ b/lint_test.go @@ -1,4 +1,4 @@ -package factory_test +package gofactory_test import ( "path/filepath" @@ -7,7 +7,7 @@ import ( "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/analysistest" - "github.com/maranqz/go-factory-lint/v2" + "github.com/maranqz/gofactory" ) func TestLinterSuite(t *testing.T) { @@ -17,29 +17,25 @@ func TestLinterSuite(t *testing.T) { tests := map[string]struct { pkgs []string - prepare func(t *testing.T, a *analysis.Analyzer) + prepare func(t *testing.T, a *analysis.Analyzer) error }{ "simple": {pkgs: []string{"simple/..."}}, "casting": {pkgs: []string{"casting/..."}}, "generic": {pkgs: []string{"generic/..."}}, "packageGlobs": { pkgs: []string{"packageGlobs/..."}, - prepare: func(t *testing.T, a *analysis.Analyzer) { - if err := a.Flags.Set("packageGlobs", "factory/packageGlobs/blocked/**"); err != nil { - t.Fatal(err) - } + prepare: func(t *testing.T, a *analysis.Analyzer) error { + return a.Flags.Set("packageGlobs", "factory/packageGlobs/blocked/**") }, }, - "onlyPackageGlobs": { - pkgs: []string{"onlyPackageGlobs/main/..."}, - prepare: func(t *testing.T, a *analysis.Analyzer) { - if err := a.Flags.Set("packageGlobs", "factory/onlyPackageGlobs/blocked/**"); err != nil { - t.Fatal(err) + "packageGlobsOnly": { + pkgs: []string{"packageGlobsOnly/main/..."}, + prepare: func(t *testing.T, a *analysis.Analyzer) error { + if err := a.Flags.Set("packageGlobs", "factory/packageGlobsOnly/blocked/**"); err != nil { + return err } - if err := a.Flags.Set("onlyPackageGlobs", "true"); err != nil { - t.Fatal(err) - } + return a.Flags.Set("packageGlobsOnly", "true") }, }, } @@ -55,14 +51,15 @@ func TestLinterSuite(t *testing.T) { dirs = append(dirs, filepath.Join(testdata, "src", "factory", pkg)) } - analyzer := factory.NewAnalyzer() + analyzer := gofactory.NewAnalyzer() if tt.prepare != nil { - tt.prepare(t, analyzer) + if err := tt.prepare(t, analyzer); err != nil { + t.Fatal(err) + } } - analysistest.Run(t, testdata, - analyzer, dirs...) + analysistest.Run(t, testdata, analyzer, dirs...) }) } } diff --git a/strategy.go b/strategy.go index 9ab3436..0eb7a29 100644 --- a/strategy.go +++ b/strategy.go @@ -1,4 +1,4 @@ -package factory +package gofactory import ( "go/types" diff --git a/testdata/src/factory/packageGlobs/nested/nested.go b/testdata/src/factory/packageGlobs/nested/nested.go index a7f35b2..c9eda1c 100644 --- a/testdata/src/factory/packageGlobs/nested/nested.go +++ b/testdata/src/factory/packageGlobs/nested/nested.go @@ -1,7 +1,7 @@ package nested import ( - "factory/onlyPackageGlobs/blocked" + "factory/packageGlobsOnly/blocked" ) type Struct struct{} diff --git a/testdata/src/factory/onlyPackageGlobs/blocked/blocked.go b/testdata/src/factory/packageGlobsOnly/blocked/blocked.go similarity index 83% rename from testdata/src/factory/onlyPackageGlobs/blocked/blocked.go rename to testdata/src/factory/packageGlobsOnly/blocked/blocked.go index 693d2e4..9ce639a 100644 --- a/testdata/src/factory/onlyPackageGlobs/blocked/blocked.go +++ b/testdata/src/factory/packageGlobsOnly/blocked/blocked.go @@ -1,7 +1,7 @@ package blocked import ( - "factory/onlyPackageGlobs/blocked/blocked_nested" + "factory/packageGlobsOnly/blocked/blocked_nested" ) type Struct struct{} diff --git a/testdata/src/factory/onlyPackageGlobs/blocked/blocked_nested/blocked_nested.go b/testdata/src/factory/packageGlobsOnly/blocked/blocked_nested/blocked_nested.go similarity index 100% rename from testdata/src/factory/onlyPackageGlobs/blocked/blocked_nested/blocked_nested.go rename to testdata/src/factory/packageGlobsOnly/blocked/blocked_nested/blocked_nested.go diff --git a/testdata/src/factory/onlyPackageGlobs/main/main.go b/testdata/src/factory/packageGlobsOnly/main/main.go similarity index 89% rename from testdata/src/factory/onlyPackageGlobs/main/main.go rename to testdata/src/factory/packageGlobsOnly/main/main.go index ee5a225..36155f0 100644 --- a/testdata/src/factory/onlyPackageGlobs/main/main.go +++ b/testdata/src/factory/packageGlobsOnly/main/main.go @@ -1,9 +1,9 @@ package main import ( - "factory/onlyPackageGlobs/blocked" - "factory/onlyPackageGlobs/blocked/blocked_nested" - "factory/onlyPackageGlobs/nested" + "factory/packageGlobsOnly/blocked" + "factory/packageGlobsOnly/blocked/blocked_nested" + "factory/packageGlobsOnly/nested" ) type Struct struct{} diff --git a/testdata/src/factory/onlyPackageGlobs/nested/nested.go b/testdata/src/factory/packageGlobsOnly/nested/nested.go similarity index 91% rename from testdata/src/factory/onlyPackageGlobs/nested/nested.go rename to testdata/src/factory/packageGlobsOnly/nested/nested.go index ae66404..55e0a68 100644 --- a/testdata/src/factory/onlyPackageGlobs/nested/nested.go +++ b/testdata/src/factory/packageGlobsOnly/nested/nested.go @@ -1,7 +1,7 @@ package nested import ( - "factory/onlyPackageGlobs/blocked" + "factory/packageGlobsOnly/blocked" ) type Struct struct{}