Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix parsing bug affected by fmt #1398

Merged
merged 3 commits into from Nov 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 10 additions & 2 deletions formatter.go
Expand Up @@ -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
Expand Down
42 changes: 42 additions & 0 deletions formatter_test.go
Expand Up @@ -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)
})
}
}
10 changes: 7 additions & 3 deletions operation.go
Expand Up @@ -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)
Expand Down
24 changes: 20 additions & 4 deletions parser.go
Expand Up @@ -441,8 +441,17 @@ 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 := fields[0]
var value string
if len(fields) > 1 {
value = fields[1]
}

multilineBlock := false
if previousAttribute == attribute {
Expand Down Expand Up @@ -732,7 +741,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:
Expand Down Expand Up @@ -1123,7 +1136,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 {
Expand Down
14 changes: 13 additions & 1 deletion 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": {
Expand Down
55 changes: 55 additions & 0 deletions 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)
}
38 changes: 38 additions & 0 deletions 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)
})
}
}