github.com/songshiyun/revive@v1.1.5-0.20220323112655-f8433a19b3c5/rule/empty-lines.go (about) 1 package rule 2 3 import ( 4 "go/ast" 5 "go/token" 6 7 "github.com/songshiyun/revive/lint" 8 ) 9 10 // EmptyLinesRule lints empty lines in blocks. 11 type EmptyLinesRule struct{} 12 13 // Apply applies the rule to given file. 14 func (r *EmptyLinesRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { 15 var failures []lint.Failure 16 17 onFailure := func(failure lint.Failure) { 18 failures = append(failures, failure) 19 } 20 21 w := lintEmptyLines{file, file.CommentMap(), onFailure} 22 ast.Walk(w, file.AST) 23 return failures 24 } 25 26 // Name returns the rule name. 27 func (r *EmptyLinesRule) Name() string { 28 return "empty-lines" 29 } 30 31 type lintEmptyLines struct { 32 file *lint.File 33 cmap ast.CommentMap 34 onFailure func(lint.Failure) 35 } 36 37 func (w lintEmptyLines) Visit(node ast.Node) ast.Visitor { 38 block, ok := node.(*ast.BlockStmt) 39 if !ok { 40 return w 41 } 42 43 w.checkStart(block) 44 w.checkEnd(block) 45 46 return w 47 } 48 49 func (w lintEmptyLines) checkStart(block *ast.BlockStmt) { 50 if len(block.List) == 0 { 51 return 52 } 53 54 start := w.position(block.Lbrace) 55 firstNode := block.List[0] 56 57 if w.commentBetween(start, firstNode) { 58 return 59 } 60 61 first := w.position(firstNode.Pos()) 62 if first.Line-start.Line > 1 { 63 w.onFailure(lint.Failure{ 64 Confidence: 1, 65 Node: block, 66 Category: "style", 67 Failure: "extra empty line at the start of a block", 68 }) 69 } 70 } 71 72 func (w lintEmptyLines) checkEnd(block *ast.BlockStmt) { 73 if len(block.List) < 1 { 74 return 75 } 76 77 end := w.position(block.Rbrace) 78 lastNode := block.List[len(block.List)-1] 79 80 if w.commentBetween(end, lastNode) { 81 return 82 } 83 84 last := w.position(lastNode.End()) 85 if end.Line-last.Line > 1 { 86 w.onFailure(lint.Failure{ 87 Confidence: 1, 88 Node: lastNode, 89 Category: "style", 90 Failure: "extra empty line at the end of a block", 91 }) 92 } 93 } 94 95 func (w lintEmptyLines) commentBetween(position token.Position, node ast.Node) bool { 96 comments := w.cmap.Filter(node).Comments() 97 if len(comments) == 0 { 98 return false 99 } 100 101 for _, comment := range comments { 102 start, end := w.position(comment.Pos()), w.position(comment.End()) 103 if start.Line-position.Line == 1 || position.Line-end.Line == 1 { 104 return true 105 } 106 } 107 108 return false 109 } 110 111 func (w lintEmptyLines) position(pos token.Pos) token.Position { 112 return w.file.ToPosition(pos) 113 }