You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

empty-lines.go 2.2KB

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