diff --git a/getters.go b/getters.go index c3beef8..02b8ace 100644 --- a/getters.go +++ b/getters.go @@ -1,6 +1,7 @@ package godot import ( + "errors" "fmt" "go/ast" "go/token" @@ -9,6 +10,8 @@ import ( "strings" ) +var errEmptyInput = errors.New("empty input") + // specialReplacer is a replacer for some types of special lines in comments, // which shouldn't be checked. For example, if comment ends with a block of // code it should not necessarily have a period at the end. @@ -21,6 +24,10 @@ type parsedFile struct { } func newParsedFile(file *ast.File, fset *token.FileSet) (*parsedFile, error) { + if file == nil || fset == nil || len(file.Comments) == 0 { + return nil, errEmptyInput + } + pf := parsedFile{ fset: fset, file: file, @@ -48,10 +55,6 @@ func newParsedFile(file *ast.File, fset *token.FileSet) (*parsedFile, error) { // getComments extracts comments from a file. func (pf *parsedFile) getComments(scope Scope, exclude []*regexp.Regexp) []comment { - if len(pf.file.Comments) == 0 { - return nil - } - var comments []comment decl := pf.getDeclarationComments(exclude) switch scope { diff --git a/godot.go b/godot.go index 703f846..526a5f7 100644 --- a/godot.go +++ b/godot.go @@ -44,6 +44,9 @@ type comment struct { // Run runs this linter on the provided code. func Run(file *ast.File, fset *token.FileSet, settings Settings) ([]Issue, error) { pf, err := newParsedFile(file, fset) + if err == errEmptyInput { + return nil, nil + } if err != nil { return nil, fmt.Errorf("parse input file: %v", err) } diff --git a/godot_test.go b/godot_test.go index 2e6afd7..6bbdea6 100644 --- a/godot_test.go +++ b/godot_test.go @@ -14,15 +14,42 @@ import ( var testExclude = []string{"^ ?@"} func TestRun(t *testing.T) { + t.Run("empty input", func(t *testing.T) { + issues, err := Run(nil, nil, Settings{}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if len(issues) > 0 { + t.Fatal("Unexpected issues") + } + }) + + t.Run("no comments", func(t *testing.T) { + testFile := filepath.Join("testdata", "nocomments", "main.go") + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, testFile, nil, parser.ParseComments) + if err != nil { + t.Fatalf("Failed to parse input file: %v", err) + } + + issues, err := Run(f, fset, Settings{}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if len(issues) > 0 { + t.Fatal("Unexpected issues") + } + }) + testFile := filepath.Join("testdata", "check", "main.go") fset := token.NewFileSet() - f, err := parser.ParseFile(fset, testFile, nil, parser.ParseComments) + file, err := parser.ParseFile(fset, testFile, nil, parser.ParseComments) if err != nil { t.Fatalf("Failed to parse input file: %v", err) } // Test invalid regexp - _, err = Run(f, fset, Settings{ + _, err = Run(file, fset, Settings{ Scope: DeclScope, Exclude: []string{"["}, Period: true, @@ -67,7 +94,7 @@ func TestRun(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { var expected int - for _, c := range f.Comments { + for _, c := range file.Comments { if strings.Contains(c.Text(), "[PASS]") { continue } @@ -77,7 +104,7 @@ func TestRun(t *testing.T) { } } } - issues, err := Run(f, fset, Settings{ + issues, err := Run(file, fset, Settings{ Scope: tt.scope, Exclude: testExclude, Period: true, @@ -95,28 +122,18 @@ func TestRun(t *testing.T) { } func TestFix(t *testing.T) { - testFile := filepath.Join("testdata", "check", "main.go") - fset := token.NewFileSet() - file, err := parser.ParseFile(fset, testFile, nil, parser.ParseComments) - if err != nil { - t.Fatalf("Failed to parse file %s: %v", testFile, err) - } - content, err := ioutil.ReadFile(testFile) // nolint: gosec - if err != nil { - t.Fatalf("Failed to read test file %s: %v", testFile, err) - } - t.Run("file not found", func(t *testing.T) { - path := filepath.Join("testdata", "not-exists.go") - _, err := Fix(path, nil, nil, Settings{}) + testFile := filepath.Join("testdata", "not-exists.go") + _, err := Fix(testFile, nil, nil, Settings{}) if err == nil { t.Fatal("Expected error, got nil") } }) t.Run("empty file", func(t *testing.T) { - path := filepath.Join("testdata", "empty.go") - fixed, err := Fix(path, nil, nil, Settings{}) + testFile := filepath.Join("testdata", "empty", "main.go") + + fixed, err := Fix(testFile, nil, nil, Settings{}) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -125,6 +142,47 @@ func TestFix(t *testing.T) { } }) + t.Run("no comments", func(t *testing.T) { + testFile := filepath.Join("testdata", "nocomments", "main.go") + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, testFile, nil, parser.ParseComments) + if err != nil { + t.Fatalf("Failed to parse input file: %v", err) + } + content, err := ioutil.ReadFile(testFile) + if err != nil { + t.Fatalf("Failed to read input file: %v", err) + } + + fixed, err := Fix(testFile, f, fset, Settings{}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + assertEqualContent(t, string(content), string(fixed)) + }) + + testFile := filepath.Join("testdata", "check", "main.go") + fset := token.NewFileSet() + file, err := parser.ParseFile(fset, testFile, nil, parser.ParseComments) + if err != nil { + t.Fatalf("Failed to parse file %s: %v", testFile, err) + } + content, err := ioutil.ReadFile(testFile) // nolint: gosec + if err != nil { + t.Fatalf("Failed to read test file %s: %v", testFile, err) + } + + // Test invalid regexp + _, err = Fix(testFile, file, fset, Settings{ + Scope: DeclScope, + Exclude: []string{"["}, + Period: true, + Capital: true, + }) + if err == nil { + t.Fatalf("Expected error, got nil on regexp parsing") + } + t.Run("scope: decl", func(t *testing.T) { expected := strings.ReplaceAll(string(content), "[PERIOD_DECL]", "[PERIOD_DECL].") expected = strings.ReplaceAll(expected, "non-capital-decl", "Non-capital-decl") @@ -184,6 +242,46 @@ func TestFix(t *testing.T) { } func TestReplace(t *testing.T) { + t.Run("file not found", func(t *testing.T) { + path := filepath.Join("testdata", "not-exists.go") + err := Replace(path, nil, nil, Settings{}) + if err == nil { + t.Fatal("Expected error, got nil") + } + }) + + t.Run("empty file", func(t *testing.T) { + testFile := filepath.Join("testdata", "empty", "main.go") + + err := Replace(testFile, nil, nil, Settings{}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + }) + + t.Run("no comments", func(t *testing.T) { + testFile := filepath.Join("testdata", "nocomments", "main.go") + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, testFile, nil, parser.ParseComments) + if err != nil { + t.Fatalf("Failed to parse input file: %v", err) + } + content, err := ioutil.ReadFile(testFile) + if err != nil { + t.Fatalf("Failed to read input file: %v", err) + } + + err = Replace(testFile, f, fset, Settings{}) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + fixed, err := ioutil.ReadFile(testFile) + if err != nil { + t.Fatalf("Failed to read fixed file: %v", err) + } + assertEqualContent(t, string(content), string(fixed)) + }) + testFile := filepath.Join("testdata", "check", "main.go") fset := token.NewFileSet() file, err := parser.ParseFile(fset, testFile, nil, parser.ParseComments) @@ -200,13 +298,16 @@ func TestReplace(t *testing.T) { t.Fatalf("Failed to read test file %s: %v", testFile, err) } - t.Run("file not found", func(t *testing.T) { - path := filepath.Join("testdata", "not-exists.go") - err := Replace(path, nil, nil, Settings{}) - if err == nil { - t.Fatal("Expected error, got nil") - } + // Test invalid regexp + err = Replace(testFile, file, fset, Settings{ + Scope: DeclScope, + Exclude: []string{"["}, + Period: true, + Capital: true, }) + if err == nil { + t.Fatalf("Expected error, got nil on regexp parsing") + } t.Run("scope: decl", func(t *testing.T) { defer func() { diff --git a/testdata/empty.go b/testdata/empty/main.go similarity index 100% rename from testdata/empty.go rename to testdata/empty/main.go diff --git a/testdata/nocomments/main.go b/testdata/nocomments/main.go new file mode 100644 index 0000000..24963e5 --- /dev/null +++ b/testdata/nocomments/main.go @@ -0,0 +1,5 @@ +package main + +func main() { + println("Hello, world!") +}