2018-10-11 23:52:46 +02:00
|
|
|
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.
|
2022-07-14 15:15:42 +02:00
|
|
|
func (r *EmptyLinesRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
|
2018-10-11 23:52:46 +02:00
|
|
|
var failures []lint.Failure
|
|
|
|
|
|
|
|
onFailure := func(failure lint.Failure) {
|
|
|
|
failures = append(failures, failure)
|
|
|
|
}
|
|
|
|
|
2022-07-14 15:15:42 +02:00
|
|
|
w := lintEmptyLines{file, r.commentLines(file.CommentMap(), file), onFailure}
|
2018-10-11 23:52:46 +02:00
|
|
|
ast.Walk(w, file.AST)
|
|
|
|
return failures
|
|
|
|
}
|
|
|
|
|
|
|
|
// Name returns the rule name.
|
2022-04-10 11:55:13 +02:00
|
|
|
func (*EmptyLinesRule) Name() string {
|
2018-10-11 23:52:46 +02:00
|
|
|
return "empty-lines"
|
|
|
|
}
|
|
|
|
|
|
|
|
type lintEmptyLines struct {
|
|
|
|
file *lint.File
|
2022-07-14 15:15:42 +02:00
|
|
|
cmap map[int]struct{}
|
2018-10-11 23:52:46 +02:00
|
|
|
onFailure func(lint.Failure)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w lintEmptyLines) Visit(node ast.Node) ast.Visitor {
|
|
|
|
block, ok := node.(*ast.BlockStmt)
|
2022-07-14 15:15:42 +02:00
|
|
|
if !ok || len(block.List) == 0 {
|
2018-10-11 23:52:46 +02:00
|
|
|
return w
|
|
|
|
}
|
|
|
|
|
|
|
|
w.checkStart(block)
|
|
|
|
w.checkEnd(block)
|
|
|
|
|
|
|
|
return w
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w lintEmptyLines) checkStart(block *ast.BlockStmt) {
|
2022-07-14 15:15:42 +02:00
|
|
|
blockStart := w.position(block.Lbrace)
|
2018-10-11 23:52:46 +02:00
|
|
|
firstNode := block.List[0]
|
2022-07-14 15:15:42 +02:00
|
|
|
firstStmt := w.position(firstNode.Pos())
|
2018-10-11 23:52:46 +02:00
|
|
|
|
2022-07-14 15:15:42 +02:00
|
|
|
firstBlockLineIsStmt := firstStmt.Line-(blockStart.Line+1) == 0
|
|
|
|
_, firstBlockLineIsComment := w.cmap[blockStart.Line+1]
|
|
|
|
if firstBlockLineIsStmt || firstBlockLineIsComment {
|
2018-10-11 23:52:46 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-07-14 15:15:42 +02:00
|
|
|
w.onFailure(lint.Failure{
|
|
|
|
Confidence: 1,
|
|
|
|
Node: block,
|
|
|
|
Category: "style",
|
|
|
|
Failure: "extra empty line at the start of a block",
|
|
|
|
})
|
2018-10-11 23:52:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (w lintEmptyLines) checkEnd(block *ast.BlockStmt) {
|
2022-07-14 15:15:42 +02:00
|
|
|
blockEnd := w.position(block.Rbrace)
|
2018-10-11 23:52:46 +02:00
|
|
|
lastNode := block.List[len(block.List)-1]
|
2022-07-14 15:15:42 +02:00
|
|
|
lastStmt := w.position(lastNode.End())
|
2018-10-11 23:52:46 +02:00
|
|
|
|
2022-07-14 15:15:42 +02:00
|
|
|
lastBlockLineIsStmt := (blockEnd.Line-1)-lastStmt.Line == 0
|
|
|
|
_, lastBlockLineIsComment := w.cmap[blockEnd.Line-1]
|
|
|
|
if lastBlockLineIsStmt || lastBlockLineIsComment {
|
2018-10-11 23:52:46 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-07-14 15:15:42 +02:00
|
|
|
w.onFailure(lint.Failure{
|
|
|
|
Confidence: 1,
|
|
|
|
Node: block,
|
|
|
|
Category: "style",
|
|
|
|
Failure: "extra empty line at the end of a block",
|
|
|
|
})
|
2018-10-11 23:52:46 +02:00
|
|
|
}
|
|
|
|
|
2022-07-14 15:15:42 +02:00
|
|
|
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{}{}
|
2018-10-11 23:52:46 +02:00
|
|
|
|
2022-07-14 15:15:42 +02:00
|
|
|
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{}{}
|
|
|
|
}
|
2018-10-13 18:47:29 +02:00
|
|
|
}
|
2018-10-11 23:52:46 +02:00
|
|
|
}
|
|
|
|
|
2022-07-14 15:15:42 +02:00
|
|
|
return result
|
2018-10-11 23:52:46 +02:00
|
|
|
}
|