1
0
mirror of https://github.com/mgechev/revive.git synced 2024-11-24 08:32:22 +02:00
revive/rule/empty-lines.go
2022-07-14 15:15:42 +02:00

106 lines
2.4 KiB
Go

package rule
import (
"go/ast"
"go/token"
"github.com/mgechev/revive/lint"
)
// EmptyLinesRule lints empty lines in blocks.
type EmptyLinesRule struct{}
// Apply applies the rule to given file.
func (r *EmptyLinesRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
var failures []lint.Failure
onFailure := func(failure lint.Failure) {
failures = append(failures, failure)
}
w := lintEmptyLines{file, r.commentLines(file.CommentMap(), file), onFailure}
ast.Walk(w, file.AST)
return failures
}
// Name returns the rule name.
func (*EmptyLinesRule) Name() string {
return "empty-lines"
}
type lintEmptyLines struct {
file *lint.File
cmap map[int]struct{}
onFailure func(lint.Failure)
}
func (w lintEmptyLines) Visit(node ast.Node) ast.Visitor {
block, ok := node.(*ast.BlockStmt)
if !ok || len(block.List) == 0 {
return w
}
w.checkStart(block)
w.checkEnd(block)
return w
}
func (w lintEmptyLines) checkStart(block *ast.BlockStmt) {
blockStart := w.position(block.Lbrace)
firstNode := block.List[0]
firstStmt := w.position(firstNode.Pos())
firstBlockLineIsStmt := firstStmt.Line-(blockStart.Line+1) == 0
_, firstBlockLineIsComment := w.cmap[blockStart.Line+1]
if firstBlockLineIsStmt || firstBlockLineIsComment {
return
}
w.onFailure(lint.Failure{
Confidence: 1,
Node: block,
Category: "style",
Failure: "extra empty line at the start of a block",
})
}
func (w lintEmptyLines) checkEnd(block *ast.BlockStmt) {
blockEnd := w.position(block.Rbrace)
lastNode := block.List[len(block.List)-1]
lastStmt := w.position(lastNode.End())
lastBlockLineIsStmt := (blockEnd.Line-1)-lastStmt.Line == 0
_, lastBlockLineIsComment := w.cmap[blockEnd.Line-1]
if lastBlockLineIsStmt || lastBlockLineIsComment {
return
}
w.onFailure(lint.Failure{
Confidence: 1,
Node: block,
Category: "style",
Failure: "extra empty line at the end of a block",
})
}
func (w lintEmptyLines) position(pos token.Pos) token.Position {
return w.file.ToPosition(pos)
}
func (*EmptyLinesRule) commentLines(cmap ast.CommentMap, file *lint.File) map[int]struct{} {
result := map[int]struct{}{}
for _, comments := range cmap {
for _, comment := range comments {
start := file.ToPosition(comment.Pos())
end := file.ToPosition(comment.End())
for i := start.Line; i <= end.Line; i++ {
result[i] = struct{}{}
}
}
}
return result
}