diff --git a/mockgen/internal/tests/ignore/interfaces.go b/mockgen/internal/tests/ignore/interfaces.go new file mode 100644 index 0000000..8ea17b5 --- /dev/null +++ b/mockgen/internal/tests/ignore/interfaces.go @@ -0,0 +1,21 @@ +package ignore + +//go:generate mockgen -source=interfaces.go -destination=mock.go -package=ignore + +//gomock:ignore +type IgnoreMe interface{} + +//gomock:ignore +type ( + IgnoreMe2 interface{} +) + +type ( + //gomock:ignore + IgnoreMe3 interface{} +) + +// GenerateMockForMe some interface +type GenerateMockForMe interface { + B() bool +} diff --git a/mockgen/internal/tests/ignore/mock.go b/mockgen/internal/tests/ignore/mock.go new file mode 100644 index 0000000..65bcc31 --- /dev/null +++ b/mockgen/internal/tests/ignore/mock.go @@ -0,0 +1,52 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./internal/tests/ignore/interfaces.go +// +// Generated by this command: +// +// /Users/tulzke/Library/Caches/JetBrains/GoLand2023.1/tmp/GoLand/___1go_build_go_uber_org_mock_mockgen -source=./internal/tests/ignore/interfaces.go -destination=./internal/tests/ignore/mock.go -package=ignore +// +// Package ignore is a generated GoMock package. +package ignore + +import ( + reflect "reflect" + + gomock "go.uber.org/mock/gomock" +) + +// MockGenerateMockForMe is a mock of GenerateMockForMe interface. +type MockGenerateMockForMe struct { + ctrl *gomock.Controller + recorder *MockGenerateMockForMeMockRecorder +} + +// MockGenerateMockForMeMockRecorder is the mock recorder for MockGenerateMockForMe. +type MockGenerateMockForMeMockRecorder struct { + mock *MockGenerateMockForMe +} + +// NewMockGenerateMockForMe creates a new mock instance. +func NewMockGenerateMockForMe(ctrl *gomock.Controller) *MockGenerateMockForMe { + mock := &MockGenerateMockForMe{ctrl: ctrl} + mock.recorder = &MockGenerateMockForMeMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockGenerateMockForMe) EXPECT() *MockGenerateMockForMeMockRecorder { + return m.recorder +} + +// B mocks base method. +func (m *MockGenerateMockForMe) B() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "B") + ret0, _ := ret[0].(bool) + return ret0 +} + +// B indicates an expected call of B. +func (mr *MockGenerateMockForMeMockRecorder) B() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "B", reflect.TypeOf((*MockGenerateMockForMe)(nil).B)) +} diff --git a/mockgen/parse.go b/mockgen/parse.go index 8379fa3..ab02d65 100644 --- a/mockgen/parse.go +++ b/mockgen/parse.go @@ -48,7 +48,7 @@ func sourceMode(source string) (*model.Package, error) { } fs := token.NewFileSet() - file, err := parser.ParseFile(fs, source, nil, 0) + file, err := parser.ParseFile(fs, source, nil, parser.ParseComments) if err != nil { return nil, fmt.Errorf("failed parsing source file %v: %v", source, err) } @@ -221,8 +221,14 @@ func (p *fileParser) parseFile(importPath string, file *ast.File) (*model.Packag } } + ic := newIgnoreChecker(p.fileSet, file) + var is []*model.Interface for ni := range iterInterfaces(file) { + if ic.isIgnore(ni.name.String()) { + continue + } + i, err := p.parseInterface(ni.name.String(), importPath, ni) if err != nil { return nil, err @@ -257,7 +263,13 @@ func (p *fileParser) parsePackage(path string) (*fileParser, error) { for _, pkg := range pkgs { file := ast.MergePackageFiles(pkg, ast.FilterFuncDuplicates|ast.FilterUnassociatedComments|ast.FilterImportDuplicates) + ic := newIgnoreChecker(p.fileSet, file) + for ni := range iterInterfaces(file) { + if ic.isIgnore(ni.name.String()) { + continue + } + newP.importedInterfaces.Set(path, ni.name.Name, ni) } imports, _ := importsOfFile(file) @@ -660,6 +672,52 @@ func (p *fileParser) parseArrayLength(expr ast.Expr) (string, error) { } } +const ( + ignoreComment = "gomock:ignore" +) + +type ignoreChecker struct { + ignoreNames map[string]struct{} +} + +func newIgnoreChecker(fileSet *token.FileSet, file *ast.File) ignoreChecker { + ignoreTypeNames := make(map[string]struct{}, len(file.Decls)) + commentsMap := ast.NewCommentMap(fileSet, file, file.Comments) + + for _, decl := range file.Decls { + gd, ok := decl.(*ast.GenDecl) + if !ok || gd.Tok != token.TYPE { + continue + } + + ignoreAllSpecs := hasIgnoreComments(commentsMap[decl]) + for _, spec := range gd.Specs { + ts, ok := spec.(*ast.TypeSpec) + if !ok { + continue + } + + if ignoreAllSpecs { + ignoreTypeNames[ts.Name.String()] = struct{}{} + continue + } + + if hasIgnoreComments(commentsMap[spec]) { + ignoreTypeNames[ts.Name.Name] = struct{}{} + } + } + } + + return ignoreChecker{ignoreNames: ignoreTypeNames} +} + +// isIgnore returns true if such an interface needs to be ignored +func (l ignoreChecker) isIgnore(name string) bool { + _, ok := l.ignoreNames[name] + + return ok +} + // importsOfFile returns a map of package name to import path // of the imports in file. func importsOfFile(file *ast.File) (normalImports map[string]importedPackage, dotImports []string) { @@ -766,6 +824,19 @@ func isVariadic(f *ast.FuncType) bool { return ok } +// hasIgnoreComments returns true if the comment contains ignoreComment +func hasIgnoreComments(commentsGroups []*ast.CommentGroup) bool { + for _, commentGroup := range commentsGroups { + for _, comment := range commentGroup.List { + if strings.Contains(comment.Text, ignoreComment) { + return true + } + } + } + + return false +} + // packageNameOfDir get package import path via dir func packageNameOfDir(srcDir string) (string, error) { files, err := os.ReadDir(srcDir)