diff --git a/parser.go b/parser.go index 7a0b59e64..ea9e66524 100644 --- a/parser.go +++ b/parser.go @@ -1,6 +1,7 @@ package swag import ( + "bufio" "context" "encoding/json" "errors" @@ -385,6 +386,47 @@ func (parser *Parser) skipPackageByPrefix(pkgpath string) bool { return true } +func parseAnnotations(packageDir, searchDir string) error { + // Combine the packageDir and searchDir to locate the files + files, err := filepath.Glob(filepath.Join(packageDir, searchDir, "*.go")) + if err != nil { + return fmt.Errorf("error finding files in directory %s: %w", searchDir, err) + } + + for _, file := range files { + f, err := os.Open(file) + if err != nil { + return fmt.Errorf("error opening file %s: %w", file, err) + } + defer f.Close() + + scanner := bufio.NewScanner(f) + previousLineWasAnnotation := false + + for scanner.Scan() { + line := scanner.Text() + trimmed := strings.TrimSpace(line) + + if strings.HasPrefix(trimmed, "//") { + // This is an annotation + if previousLineWasAnnotation && len(trimmed) == 2 { + // The previous line was an annotation, and this line is a newline + return fmt.Errorf("newline detected between annotations in file %s, which is not allowed", file) + } + previousLineWasAnnotation = true + } else { + previousLineWasAnnotation = false + } + } + + if err := scanner.Err(); err != nil { + return fmt.Errorf("error reading annotations in file %s: %w", file, err) + } + } + + return nil +} + // ParseAPIMultiSearchDir is like ParseAPI but for multiple search dirs. func (parser *Parser) ParseAPIMultiSearchDir(searchDirs []string, mainAPIFile string, parseDepth int) error { for _, searchDir := range searchDirs { @@ -399,6 +441,10 @@ func (parser *Parser) ParseAPIMultiSearchDir(searchDirs []string, mainAPIFile st if err != nil { return err } + + if err := parseAnnotations(packageDir, searchDir); err != nil { + return err + } } absMainAPIFilePath, err := filepath.Abs(filepath.Join(searchDirs[0], mainAPIFile)) diff --git a/parser_test.go b/parser_test.go index 777d411f7..e6736a642 100644 --- a/parser_test.go +++ b/parser_test.go @@ -1,9 +1,11 @@ package swag import ( + "bufio" "bytes" "encoding/json" "errors" + "fmt" "go/ast" goparser "go/parser" "go/token" @@ -4373,6 +4375,55 @@ func Test(){ assert.NotNil(t, val2.Get) } +// Test function for parser +func TestParser_ParseAnnotations(t *testing.T) { + t.Run("NoNewlineBetweenAnnotations", func(t *testing.T) { + content := `// @annotation1 +// @annotation2` + err := testParseAnnotations(content) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + }) + + t.Run("NewlineBetweenAnnotations", func(t *testing.T) { + content := `// @annotation1 + +// @annotation2` + err := testParseAnnotations(content) + if err == nil { + t.Fatalf("expected an error due to newline between annotations, but got nil") + } + }) +} + +// Helper function to simulate annotation parsing and detect newline issues +func testParseAnnotations(content string) error { + scanner := bufio.NewScanner(strings.NewReader(content)) + previousLineWasAnnotation := false + + for scanner.Scan() { + line := scanner.Text() + trimmed := strings.TrimSpace(line) + + if strings.HasPrefix(trimmed, "//") { + // Detected an annotation + if previousLineWasAnnotation && len(trimmed) == 2 { + // Newline detected between annotations + return fmt.Errorf("newline detected between annotations, which is not allowed") + } + previousLineWasAnnotation = true + } else if trimmed == "" && previousLineWasAnnotation { + // Newline following an annotation + return fmt.Errorf("newline detected between annotations") + } else { + previousLineWasAnnotation = false + } + } + + return scanner.Err() +} + func TestParser_EmbeddedStructAsOtherAliasGoListNested(t *testing.T) { t.Parallel()