From 6bf2f29cdb26cedb150e9f7853d3cc2858316fd6 Mon Sep 17 00:00:00 2001 From: sdghchj Date: Fri, 25 Nov 2022 20:38:30 +0800 Subject: [PATCH 1/3] fix parsing bug affected by fmt --- formatter.go | 12 +++++++++-- formatter_test.go | 42 ++++++++++++++++++++++++++++++++++++ operation.go | 10 ++++++--- parser.go | 21 ++++++++++++++---- utils.go | 55 +++++++++++++++++++++++++++++++++++++++++++++++ utils_test.go | 38 ++++++++++++++++++++++++++++++++ 6 files changed, 169 insertions(+), 9 deletions(-) create mode 100644 utils.go create mode 100644 utils_test.go diff --git a/formatter.go b/formatter.go index 3badf8d37..511e3a822 100644 --- a/formatter.go +++ b/formatter.go @@ -145,8 +145,16 @@ func splitComment2(attr, body string) string { if specialTagForSplit[strings.ToLower(attr)] { for i := 0; i < len(body); i++ { if skipEnd, ok := skipChar[body[i]]; ok { - if skipLen := strings.IndexByte(body[i+1:], skipEnd); skipLen > 0 { - i += skipLen + skipStart, n := body[i], 1 + for i++; i < len(body); i++ { + if skipStart != skipEnd && body[i] == skipStart { + n++ + } else if body[i] == skipEnd { + n-- + if n == 0 { + break + } + } } } else if body[i] == ' ' || body[i] == '\t' { j := i diff --git a/formatter_test.go b/formatter_test.go index 76498eb5e..4c650a986 100644 --- a/formatter_test.go +++ b/formatter_test.go @@ -224,3 +224,45 @@ func Test_SyntaxError(t *testing.T) { _, err := NewFormatter().Format("invalid.go", contents) assert.Error(t, err) } + +func Test_splitComment2(t *testing.T) { + type args struct { + attr string + body string + } + tests := []struct { + name string + args args + want string + }{ + { + "test_splitComment2_1", + args{ + attr: "@param", + body: " data body web.GenericBodyMulti[[]types.Post, [][]types.Post]", + }, + "\tdata\tbody\tweb.GenericBodyMulti[[]types.Post, [][]types.Post]", + }, + { + "test_splitComment2_2", + args{ + attr: "@param", + body: ` some_id path int true "Some ID" Format(int64)`, + }, + "\tsome_id\tpath\tint\ttrue\t\"Some ID\"\tFormat(int64)", + }, + { + "test_splitComment2_3", + args{ + attr: "@param", + body: ` @Param some_id body web.Pet true "Some ID"`, + }, + "\t@Param\tsome_id\tbody\tweb.Pet\ttrue\t\"Some ID\"", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, splitComment2(tt.args.attr, tt.args.body), "splitComment2(%v, %v)", tt.args.attr, tt.args.body) + }) + } +} diff --git a/operation.go b/operation.go index 9fed210c8..bd817ba7a 100644 --- a/operation.go +++ b/operation.go @@ -110,9 +110,13 @@ func (operation *Operation) ParseComment(comment string, astFile *ast.File) erro return nil } - attribute := strings.Fields(commentLine)[0] - lineRemainder, lowerAttribute := strings.TrimSpace(commentLine[len(attribute):]), strings.ToLower(attribute) - + fields := FieldsByAnySpace(commentLine, 2) + attribute := fields[0] + lowerAttribute := strings.ToLower(attribute) + var lineRemainder string + if len(fields) > 1 { + lineRemainder = fields[1] + } switch lowerAttribute { case descriptionAttr: operation.ParseDescriptionComment(lineRemainder) diff --git a/parser.go b/parser.go index 088ec5f09..3e600d2df 100644 --- a/parser.go +++ b/parser.go @@ -441,8 +441,14 @@ func parseGeneralAPIInfo(parser *Parser, comments []string) error { // parsing classic meta data model for line := 0; line < len(comments); line++ { commentLine := comments[line] - attribute := strings.Split(commentLine, " ")[0] - value := strings.TrimSpace(commentLine[len(attribute):]) + commentLine = strings.TrimSpace(commentLine) + if len(commentLine) == 0 { + continue + } + fields := FieldsByAnySpace(commentLine, 2) + + attribute := strings.ToLower(fields[0]) + value := fields[1] multilineBlock := false if previousAttribute == attribute { @@ -732,7 +738,11 @@ func (parser *Parser) ParseProduceComment(commentLine string) error { func isGeneralAPIComment(comments []string) bool { for _, commentLine := range comments { - attribute := strings.ToLower(strings.Split(commentLine, " ")[0]) + commentLine = strings.TrimSpace(commentLine) + if len(commentLine) == 0 { + continue + } + attribute := strings.ToLower(FieldsByAnySpace(commentLine, 2)[0]) switch attribute { // The @summary, @router, @success, @failure annotation belongs to Operation case summaryAttr, routerAttr, successAttr, failureAttr, responseAttr: @@ -1123,7 +1133,10 @@ func extractDeclarationDescription(commentGroups ...*ast.CommentGroup) string { for _, comment := range commentGroup.List { commentText := strings.TrimSpace(strings.TrimLeft(comment.Text, "/")) - attribute := strings.Split(commentText, " ")[0] + if len(commentText) == 0 { + continue + } + attribute := FieldsByAnySpace(commentText, 2)[0] if strings.ToLower(attribute) != descriptionAttr { if !isHandlingDescription { diff --git a/utils.go b/utils.go new file mode 100644 index 000000000..df31ff2e1 --- /dev/null +++ b/utils.go @@ -0,0 +1,55 @@ +package swag + +import "unicode" + +// FieldsFunc split a string s by a func splitter into max n parts +func FieldsFunc(s string, f func(rune2 rune) bool, n int) []string { + // A span is used to record a slice of s of the form s[start:end]. + // The start index is inclusive and the end index is exclusive. + type span struct { + start int + end int + } + spans := make([]span, 0, 32) + + // Find the field start and end indices. + // Doing this in a separate pass (rather than slicing the string s + // and collecting the result substrings right away) is significantly + // more efficient, possibly due to cache effects. + start := -1 // valid span start if >= 0 + for end, rune := range s { + if f(rune) { + if start >= 0 { + spans = append(spans, span{start, end}) + // Set start to a negative value. + // Note: using -1 here consistently and reproducibly + // slows down this code by a several percent on amd64. + start = ^start + } + } else { + if start < 0 { + start = end + if n > 0 && len(spans)+1 >= n { + break + } + } + } + } + + // Last field might end at EOF. + if start >= 0 { + spans = append(spans, span{start, len(s)}) + } + + // Create strings from recorded field indices. + a := make([]string, len(spans)) + for i, span := range spans { + a[i] = s[span.start:span.end] + } + return a +} + +// FieldsByAnySpace split a string s by any space character into max n parts +func FieldsByAnySpace(s string, n int) []string { + return FieldsFunc(s, unicode.IsSpace, n) +} diff --git a/utils_test.go b/utils_test.go new file mode 100644 index 000000000..1c4d9953a --- /dev/null +++ b/utils_test.go @@ -0,0 +1,38 @@ +package swag + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestFieldsByAnySpace(t *testing.T) { + type args struct { + s string + n int + } + tests := []struct { + name string + args args + want []string + }{ + {"test1", + args{ + " aa bb cc dd ff", + 2, + }, + []string{"aa", "bb\tcc dd \t\tff"}, + }, + {"test2", + args{ + ` aa "bb cc dd ff"`, + 2, + }, + []string{"aa", `"bb cc dd ff"`}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, FieldsByAnySpace(tt.args.s, tt.args.n), "FieldsByAnySpace(%v, %v)", tt.args.s, tt.args.n) + }) + } +} From 0263940021e939e1bbea8aeeb0ca889653a8d38c Mon Sep 17 00:00:00 2001 From: sdghchj Date: Fri, 25 Nov 2022 21:14:23 +0800 Subject: [PATCH 2/3] fix tests --- parser.go | 5 ++++- testdata/enums/expected.json | 14 +++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/parser.go b/parser.go index 3e600d2df..a749ef279 100644 --- a/parser.go +++ b/parser.go @@ -448,7 +448,10 @@ func parseGeneralAPIInfo(parser *Parser, comments []string) error { fields := FieldsByAnySpace(commentLine, 2) attribute := strings.ToLower(fields[0]) - value := fields[1] + var value string + if len(fields) > 1 { + value = fields[1] + } multilineBlock := false if previousAttribute == attribute { diff --git a/testdata/enums/expected.json b/testdata/enums/expected.json index 1110f6cfa..916f116ea 100644 --- a/testdata/enums/expected.json +++ b/testdata/enums/expected.json @@ -1,7 +1,19 @@ { "swagger": "2.0", "info": { - "contact": {} + "description": "This is a sample server.", + "title": "Swagger Example API", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "API Support", + "url": "http://www.swagger.io/support", + "email": "support@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "1.0" }, "basePath": "/v2", "paths": { From 9a9c6c5dfd67289df98c7e831f78ae5701fa32c8 Mon Sep 17 00:00:00 2001 From: sdghchj Date: Fri, 25 Nov 2022 21:23:27 +0800 Subject: [PATCH 3/3] fix tests --- parser.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser.go b/parser.go index a749ef279..a6c9051dd 100644 --- a/parser.go +++ b/parser.go @@ -447,7 +447,7 @@ func parseGeneralAPIInfo(parser *Parser, comments []string) error { } fields := FieldsByAnySpace(commentLine, 2) - attribute := strings.ToLower(fields[0]) + attribute := fields[0] var value string if len(fields) > 1 { value = fields[1]