Skip to content

Commit 7a8145b

Browse files
authored
chore: relinted codebase (#3)
* Exported API reduced to what go-swagger is calling: all other exported symbols are now unexported * carried out all simple relinting actions * modernized statements (use iterators, ...) * used nolint where appropriate for now, pending future deeper refactoring actions * refactored deeply nested structures and complex functions (including test code) * refactored code duplication * relaxed a few linting constraints (complexity, maintainability index) No change in semantics, no change in test assertions Signed-off-by: Frédéric BIDON <fredbi@yahoo.com>
1 parent e31d271 commit 7a8145b

File tree

43 files changed

+2345
-2315
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+2345
-2315
lines changed

.golangci.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,16 @@ linters:
2626
min-len: 2
2727
min-occurrences: 3
2828
cyclop:
29-
max-complexity: 20
29+
max-complexity: 30
3030
gocyclo:
31-
min-complexity: 20
31+
min-complexity: 30
3232
exhaustive:
3333
default-signifies-exhaustive: true
3434
default-case-required: true
3535
lll:
3636
line-length: 180
37+
maintidx:
38+
under: 15
3739
exclusions:
3840
generated: lax
3941
presets:

application.go

Lines changed: 122 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,27 @@ import (
1010
"go/types"
1111
"log"
1212
"os"
13+
"regexp"
1314
"strings"
1415

1516
"github.com/go-openapi/spec"
16-
"github.com/go-openapi/swag"
17+
"github.com/go-openapi/swag/conv"
1718

1819
"golang.org/x/tools/go/packages"
1920
)
2021

2122
const pkgLoadMode = packages.NeedName | packages.NeedFiles | packages.NeedImports | packages.NeedDeps | packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo
2223

2324
func safeConvert(str string) bool {
24-
b, err := swag.ConvertBool(str)
25+
b, err := conv.ConvertBool(str)
2526
if err != nil {
2627
return false
2728
}
2829
return b
2930
}
3031

3132
// Debug is true when process is run with DEBUG=1 env var.
32-
var Debug = safeConvert(os.Getenv("DEBUG"))
33+
var Debug = safeConvert(os.Getenv("DEBUG")) //nolint:gochecknoglobals // package-level configuration from environment
3334

3435
type node uint32
3536

@@ -286,49 +287,51 @@ func (d *entityDecl) HasParameterAnnotation() bool {
286287
}
287288

288289
func (s *scanCtx) FindDecl(pkgPath, name string) (*entityDecl, bool) {
289-
if pkg, ok := s.app.AllPackages[pkgPath]; ok {
290-
for _, file := range pkg.Syntax {
291-
for _, d := range file.Decls {
292-
gd, ok := d.(*ast.GenDecl)
290+
pkg, ok := s.app.AllPackages[pkgPath]
291+
if !ok {
292+
return nil, false
293+
}
294+
295+
for _, file := range pkg.Syntax {
296+
for _, d := range file.Decls {
297+
gd, ok := d.(*ast.GenDecl)
298+
if !ok {
299+
continue
300+
}
301+
302+
for _, sp := range gd.Specs {
303+
ts, ok := sp.(*ast.TypeSpec)
304+
if !ok || ts.Name.Name != name {
305+
continue
306+
}
307+
308+
def, ok := pkg.TypesInfo.Defs[ts.Name]
293309
if !ok {
310+
debugLogf("couldn't find type info for %s", ts.Name)
294311
continue
295312
}
296313

297-
for _, sp := range gd.Specs {
298-
if ts, ok := sp.(*ast.TypeSpec); ok && ts.Name.Name == name {
299-
def, ok := pkg.TypesInfo.Defs[ts.Name]
300-
if !ok {
301-
debugLogf("couldn't find type info for %s", ts.Name)
302-
303-
continue
304-
}
305-
306-
nt, isNamed := def.Type().(*types.Named)
307-
at, isAliased := def.Type().(*types.Alias)
308-
if !isNamed && !isAliased {
309-
debugLogf("%s is not a named or an aliased type but a %T", ts.Name, def.Type())
310-
311-
continue
312-
}
313-
314-
comments := ts.Doc // type ( /* doc */ Foo struct{} )
315-
if comments == nil {
316-
comments = gd.Doc // /* doc */ type ( Foo struct{} )
317-
}
318-
319-
decl := &entityDecl{
320-
Comments: comments,
321-
Type: nt,
322-
Alias: at,
323-
Ident: ts.Name,
324-
Spec: ts,
325-
File: file,
326-
Pkg: pkg,
327-
}
328-
329-
return decl, true
330-
}
314+
nt, isNamed := def.Type().(*types.Named)
315+
at, isAliased := def.Type().(*types.Alias)
316+
if !isNamed && !isAliased {
317+
debugLogf("%s is not a named or an aliased type but a %T", ts.Name, def.Type())
318+
continue
331319
}
320+
321+
comments := ts.Doc // type ( /* doc */ Foo struct{} )
322+
if comments == nil {
323+
comments = gd.Doc // /* doc */ type ( Foo struct{} )
324+
}
325+
326+
return &entityDecl{
327+
Comments: comments,
328+
Type: nt,
329+
Alias: at,
330+
Ident: ts.Name,
331+
Spec: ts,
332+
File: file,
333+
Pkg: pkg,
334+
}, true
332335
}
333336
}
334337
}
@@ -612,64 +615,72 @@ func (a *typeIndex) processPackage(pkg *packages.Package) error {
612615
}
613616

614617
for _, file := range pkg.Syntax {
615-
n, err := a.detectNodes(file)
616-
if err != nil {
618+
if err := a.processFile(pkg, file); err != nil {
617619
return err
618620
}
621+
}
619622

620-
if n&metaNode != 0 {
621-
a.Meta = append(a.Meta, metaSection{Comments: file.Doc})
622-
}
623+
return nil
624+
}
623625

624-
if n&operationNode != 0 {
625-
for _, cmts := range file.Comments {
626-
pp := parsePathAnnotation(rxOperation, cmts.List)
627-
if pp.Method == "" {
628-
continue // not a valid operation
629-
}
630-
if !shouldAcceptTag(pp.Tags, a.includeTags, a.excludeTags) {
631-
debugLogf("operation %s %s is ignored due to tag rules", pp.Method, pp.Path)
632-
continue
633-
}
634-
a.Operations = append(a.Operations, pp)
635-
}
636-
}
626+
func (a *typeIndex) processFile(pkg *packages.Package, file *ast.File) error {
627+
n, err := a.detectNodes(file)
628+
if err != nil {
629+
return err
630+
}
637631

638-
if n&routeNode != 0 {
639-
for _, cmts := range file.Comments {
640-
pp := parsePathAnnotation(rxRoute, cmts.List)
641-
if pp.Method == "" {
642-
continue // not a valid operation
643-
}
644-
if !shouldAcceptTag(pp.Tags, a.includeTags, a.excludeTags) {
645-
debugLogf("operation %s %s is ignored due to tag rules", pp.Method, pp.Path)
646-
continue
647-
}
648-
a.Routes = append(a.Routes, pp)
649-
}
632+
if n&metaNode != 0 {
633+
a.Meta = append(a.Meta, metaSection{Comments: file.Doc})
634+
}
635+
636+
if n&operationNode != 0 {
637+
a.Operations = a.collectPathAnnotations(rxOperation, file.Comments, a.Operations)
638+
}
639+
640+
if n&routeNode != 0 {
641+
a.Routes = a.collectPathAnnotations(rxRoute, file.Comments, a.Routes)
642+
}
643+
644+
a.processFileDecls(pkg, file, n)
645+
646+
return nil
647+
}
648+
649+
func (a *typeIndex) collectPathAnnotations(rx *regexp.Regexp, comments []*ast.CommentGroup, dst []parsedPathContent) []parsedPathContent {
650+
for _, cmts := range comments {
651+
pp := parsePathAnnotation(rx, cmts.List)
652+
if pp.Method == "" {
653+
continue
650654
}
655+
if !shouldAcceptTag(pp.Tags, a.includeTags, a.excludeTags) {
656+
debugLogf("operation %s %s is ignored due to tag rules", pp.Method, pp.Path)
657+
continue
658+
}
659+
dst = append(dst, pp)
660+
}
661+
return dst
662+
}
651663

652-
for _, dt := range file.Decls {
653-
switch fd := dt.(type) {
654-
case *ast.BadDecl:
664+
func (a *typeIndex) processFileDecls(pkg *packages.Package, file *ast.File, n node) {
665+
for _, dt := range file.Decls {
666+
switch fd := dt.(type) {
667+
case *ast.BadDecl:
668+
continue
669+
case *ast.FuncDecl:
670+
if fd.Body == nil {
655671
continue
656-
case *ast.FuncDecl:
657-
if fd.Body == nil {
658-
continue
659-
}
660-
for _, stmt := range fd.Body.List {
661-
if dstm, ok := stmt.(*ast.DeclStmt); ok {
662-
if gd, isGD := dstm.Decl.(*ast.GenDecl); isGD {
663-
a.processDecl(pkg, file, n, gd)
664-
}
672+
}
673+
for _, stmt := range fd.Body.List {
674+
if dstm, ok := stmt.(*ast.DeclStmt); ok {
675+
if gd, isGD := dstm.Decl.(*ast.GenDecl); isGD {
676+
a.processDecl(pkg, file, n, gd)
665677
}
666678
}
667-
case *ast.GenDecl:
668-
a.processDecl(pkg, file, n, fd)
669679
}
680+
case *ast.GenDecl:
681+
a.processDecl(pkg, file, n, fd)
670682
}
671683
}
672-
return nil
673684
}
674685

675686
func (a *typeIndex) processDecl(pkg *packages.Package, file *ast.File, n node, gd *ast.GenDecl) {
@@ -748,10 +759,23 @@ func (a *typeIndex) walkImports(pkg *packages.Package) error {
748759
return nil
749760
}
750761

762+
func checkStructConflict(seenStruct *string, annotation string, text string) error {
763+
if *seenStruct != "" && *seenStruct != annotation {
764+
return fmt.Errorf("classifier: already annotated as %s, can't also be %q - %s: %w", *seenStruct, annotation, text, ErrCodeScan)
765+
}
766+
*seenStruct = annotation
767+
return nil
768+
}
769+
770+
// detectNodes scans all comment groups in a file and returns a bitmask of
771+
// detected swagger annotation types. Node types like route, operation, and
772+
// meta accumulate freely across comment groups. Struct-level annotations
773+
// (model, parameters, response) are mutually exclusive within a single
774+
// comment group — mixing them is an error.
751775
func (a *typeIndex) detectNodes(file *ast.File) (node, error) {
752776
var n node
753777
for _, comments := range file.Comments {
754-
var seenStruct string
778+
var seenStruct string // tracks the struct annotation for this comment group
755779
for _, cline := range comments.List {
756780
if cline == nil {
757781
continue
@@ -764,7 +788,7 @@ func (a *typeIndex) detectNodes(file *ast.File) (node, error) {
764788
}
765789

766790
matches := rxSwaggerAnnotation.FindStringSubmatch(cline.Text)
767-
if len(matches) < 2 {
791+
if len(matches) < minAnnotationMatch {
768792
continue
769793
}
770794

@@ -775,41 +799,36 @@ func (a *typeIndex) detectNodes(file *ast.File) (node, error) {
775799
n |= operationNode
776800
case "model":
777801
n |= modelNode
778-
if seenStruct == "" || seenStruct == matches[1] {
779-
seenStruct = matches[1]
780-
} else {
781-
return 0, fmt.Errorf("classifier: already annotated as %s, can't also be %q - %s", seenStruct, matches[1], cline.Text)
802+
if err := checkStructConflict(&seenStruct, matches[1], cline.Text); err != nil {
803+
return 0, err
782804
}
783805
case "meta":
784806
n |= metaNode
785807
case "parameters":
786808
n |= parametersNode
787-
if seenStruct == "" || seenStruct == matches[1] {
788-
seenStruct = matches[1]
789-
} else {
790-
return 0, fmt.Errorf("classifier: already annotated as %s, can't also be %q - %s", seenStruct, matches[1], cline.Text)
809+
if err := checkStructConflict(&seenStruct, matches[1], cline.Text); err != nil {
810+
return 0, err
791811
}
792812
case "response":
793813
n |= responseNode
794-
if seenStruct == "" || seenStruct == matches[1] {
795-
seenStruct = matches[1]
796-
} else {
797-
return 0, fmt.Errorf("classifier: already annotated as %s, can't also be %q - %s", seenStruct, matches[1], cline.Text)
814+
if err := checkStructConflict(&seenStruct, matches[1], cline.Text); err != nil {
815+
return 0, err
798816
}
799-
case "strfmt", "name", "discriminated", "file", "enum", "default", "alias", "type":
817+
case "strfmt", paramNameKey, "discriminated", "file", "enum", "default", "alias", "type":
800818
// TODO: perhaps collect these and pass along to avoid lookups later on
801819
case "allOf":
802820
case "ignore":
803821
default:
804-
return 0, fmt.Errorf("classifier: unknown swagger annotation %q", matches[1])
822+
return 0, fmt.Errorf("classifier: unknown swagger annotation %q: %w", matches[1], ErrCodeScan)
805823
}
806824
}
807825
}
826+
808827
return n, nil
809828
}
810829

811830
func debugLogf(format string, args ...any) {
812831
if Debug {
813-
_ = log.Output(2, fmt.Sprintf(format, args...))
832+
_ = log.Output(logCallerDepth, fmt.Sprintf(format, args...))
814833
}
815834
}

application_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,16 @@ import (
1919
)
2020

2121
var (
22-
petstoreCtx *scanCtx
23-
classificationCtx *scanCtx
22+
petstoreCtx *scanCtx //nolint:gochecknoglobals // test package cache shared across test functions
23+
classificationCtx *scanCtx //nolint:gochecknoglobals // test package cache shared across test functions
2424
)
2525

2626
var (
27-
enableSpecOutput bool
28-
enableDebug bool
27+
enableSpecOutput bool //nolint:gochecknoglobals // test flag registered in init
28+
enableDebug bool //nolint:gochecknoglobals // test flag registered in init
2929
)
3030

31-
func init() {
31+
func init() { //nolint:gochecknoinits // registers test flags before TestMain
3232
flag.BoolVar(&enableSpecOutput, "enable-spec-output", false, "enable spec gen test to write output to a file")
3333
flag.BoolVar(&enableDebug, "enable-debug", false, "enable debug output in tests")
3434
}
@@ -108,19 +108,19 @@ func loadPetstorePkgsCtx(t *testing.T) *scanCtx {
108108
return petstoreCtx
109109
}
110110

111-
func loadClassificationPkgsCtx(t *testing.T, extra ...string) *scanCtx {
111+
func loadClassificationPkgsCtx(t *testing.T) *scanCtx {
112112
t.Helper()
113113

114114
if classificationCtx != nil {
115115
return classificationCtx
116116
}
117117

118118
sctx, err := newScanCtx(&Options{
119-
Packages: append([]string{
119+
Packages: []string{
120120
"./goparsing/classification",
121121
"./goparsing/classification/models",
122122
"./goparsing/classification/operations",
123-
}, extra...),
123+
},
124124
WorkDir: "fixtures",
125125
})
126126
require.NoError(t, err)

0 commit comments

Comments
 (0)