Skip to content

Commit

Permalink
fix parsing bug affected by fmt (#1398)
Browse files Browse the repository at this point in the history
* fix parsing bug affected by fmt
  • Loading branch information
sdghchj committed Nov 26, 2022
1 parent 80d5221 commit 30684a2
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 10 deletions.
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)
})
}
}

0 comments on commit 30684a2

Please sign in to comment.