Skip to content

Commit

Permalink
Feature: Ignore comments in getLines() (#14)
Browse files Browse the repository at this point in the history
* Add go.mod

* Add basic unittest

* Add option to ignore comments in getLines()

* Extend test coverage
  • Loading branch information
martialblog authored Jul 31, 2023
1 parent f90e078 commit 45ae9f0
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 4 deletions.
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/ultraware/funlen

go 1.20
28 changes: 24 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@ const (
)

// Run runs this linter on the provided code
func Run(file *ast.File, fset *token.FileSet, lineLimit, stmtLimit int) []Message {
func Run(file *ast.File, fset *token.FileSet, lineLimit int, stmtLimit int, ignoreComments bool) []Message {
if lineLimit == 0 {
lineLimit = defaultLineLimit
}
if stmtLimit == 0 {
stmtLimit = defaultStmtLimit
}

cmap := ast.NewCommentMap(fset, file, file.Comments)

var msgs []Message
for _, f := range file.Decls {
decl, ok := f.(*ast.FuncDecl)
Expand All @@ -36,7 +38,7 @@ func Run(file *ast.File, fset *token.FileSet, lineLimit, stmtLimit int) []Messag
}

if lineLimit > 0 {
if lines := getLines(fset, decl); lines > lineLimit {
if lines := getLines(fset, decl, cmap.Filter(decl), ignoreComments); lines > lineLimit {
msgs = append(msgs, makeLineMessage(fset, decl.Name, lines, lineLimit))
}
}
Expand Down Expand Up @@ -65,8 +67,26 @@ func makeStmtMessage(fset *token.FileSet, funcInfo *ast.Ident, stmts, stmtLimit
}
}

func getLines(fset *token.FileSet, f *ast.FuncDecl) int { // nolint: interfacer
return fset.Position(f.End()).Line - fset.Position(f.Pos()).Line - 1
func getLines(fset *token.FileSet, f *ast.FuncDecl, cmap ast.CommentMap, ignoreComments bool) int { // nolint: interfacer
var lineCount int
var commentCount int

lineCount = fset.Position(f.End()).Line - fset.Position(f.Pos()).Line - 1

if !ignoreComments {
return lineCount
}

for _, c := range cmap.Comments() {
// If the CommenGroup's lines are inside the function
// count how many comments are in the CommentGroup
if (fset.Position(c.Pos()).Line > fset.Position(f.Pos()).Line) &&
(fset.Position(c.End()).Line < fset.Position(f.End()).Line) {
commentCount += len(c.List)
}
}

return lineCount - commentCount
}

func parseStmts(s []ast.Stmt) (total int) {
Expand Down
105 changes: 105 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package funlen

import (
"go/parser"
"go/token"
"strings"
"testing"
)

func TestRunTable(t *testing.T) {
testcases := map[string]struct {
input string
expected string
lineLimit int
stmtLimit int
}{
"too-many-statements": {
input: `package main
func main() {
print("Hello, world!")
print("Hello, world!")}`,
expected: "Function 'main' has too many statements (2 > 1)",
lineLimit: 1,
stmtLimit: 1,
},
"too-many-lines": {
input: `package main
import "fmt"
func main() {
print("main!")
print("is!")
print("too!")
print("long")}`,
expected: "Function 'main' is too long (3 > 1)",
lineLimit: 1,
stmtLimit: 10,
},
"too-many-statements-inline-func": {
input: `package main
func main() {
print("Hello, world!")
if true {
y := []int{1,2,3,4}
for k, v := range y {
f := func() { print("test") }
f()
}
}
print("Hello, world!")}`,
expected: "Function 'main' has too many statements (8 > 1)",
lineLimit: 1,
stmtLimit: 1,
},
}

for name, test := range testcases {
t.Run(name, func(t *testing.T) {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "", test.input, parser.ParseComments)

if err != nil {
t.Error("\nActual: ", err, "\n Did not expected error")
}

r := Run(f, fset, test.lineLimit, test.stmtLimit, false)
actual := r[0].Message

if !strings.Contains(actual, test.expected) {
t.Error("\nActual: ", actual, "\nExpected: ", test.expected)
}
})
}
}

func TestRunIgnoresComments(t *testing.T) {

input := `package main
func main() {
// Comment 1
// Comment 2
// Comment 3
print("Hello, world!")}
// Comment Doc
func unittest() {
// Comment 1
// Comment 2
print("Hello, world!")}
// Comment 3`

lineLimit := 2
stmtLimit := 2

fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "", input, parser.ParseComments)

if err != nil {
t.Error("\nActual: ", err, "\n Did not expected error")
}

r := Run(f, fset, lineLimit, stmtLimit, true)

if len(r) > 0 {
t.Error("\nActual: ", r, "\nExpected no lint errors")
}
}

0 comments on commit 45ae9f0

Please sign in to comment.