Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

function-length.go 3.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. package rule
  2. import (
  3. "fmt"
  4. "go/ast"
  5. "reflect"
  6. "github.com/mgechev/revive/lint"
  7. )
  8. // FunctionLength lint.
  9. type FunctionLength struct{}
  10. // Apply applies the rule to given file.
  11. func (r *FunctionLength) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
  12. maxStmt, maxLines := r.parseArguments(arguments)
  13. var failures []lint.Failure
  14. walker := lintFuncLength{
  15. file: file,
  16. maxStmt: int(maxStmt),
  17. maxLines: int(maxLines),
  18. onFailure: func(failure lint.Failure) {
  19. failures = append(failures, failure)
  20. },
  21. }
  22. ast.Walk(walker, file.AST)
  23. return failures
  24. }
  25. // Name returns the rule name.
  26. func (r *FunctionLength) Name() string {
  27. return "function-length"
  28. }
  29. func (r *FunctionLength) parseArguments(arguments lint.Arguments) (maxStmt int64, maxLines int64) {
  30. if len(arguments) != 2 {
  31. panic(fmt.Sprintf(`invalid configuration for "function-length" rule, expected 2 arguments but got %d`, len(arguments)))
  32. }
  33. maxStmt, maxStmtOk := arguments[0].(int64)
  34. if !maxStmtOk {
  35. panic(fmt.Sprintf(`invalid configuration value for max statements in "function-length" rule; need int64 but got %T`, arguments[0]))
  36. }
  37. if maxStmt < 0 {
  38. panic(fmt.Sprintf(`the configuration value for max statements in "function-length" rule cannot be negative, got %d`, maxStmt))
  39. }
  40. maxLines, maxLinesOk := arguments[1].(int64)
  41. if !maxLinesOk {
  42. panic(fmt.Sprintf(`invalid configuration value for max lines in "function-length" rule; need int64 but got %T`, arguments[1]))
  43. }
  44. if maxLines < 0 {
  45. panic(fmt.Sprintf(`the configuration value for max statements in "function-length" rule cannot be negative, got %d`, maxLines))
  46. }
  47. return
  48. }
  49. type lintFuncLength struct {
  50. file *lint.File
  51. maxStmt int
  52. maxLines int
  53. onFailure func(lint.Failure)
  54. }
  55. func (w lintFuncLength) Visit(n ast.Node) ast.Visitor {
  56. node, ok := n.(*ast.FuncDecl)
  57. if !ok {
  58. return w
  59. }
  60. body := node.Body
  61. if body == nil || len(node.Body.List) == 0 {
  62. return nil
  63. }
  64. if w.maxStmt > 0 {
  65. stmtCount := w.countStmts(node.Body.List)
  66. if stmtCount > w.maxStmt {
  67. w.onFailure(lint.Failure{
  68. Confidence: 1,
  69. Failure: fmt.Sprintf("maximum number of statements per function exceeded; max %d but got %d", w.maxStmt, stmtCount),
  70. Node: node,
  71. })
  72. }
  73. }
  74. if w.maxLines > 0 {
  75. lineCount := w.countLines(node.Body)
  76. if lineCount > w.maxLines {
  77. w.onFailure(lint.Failure{
  78. Confidence: 1,
  79. Failure: fmt.Sprintf("maximum number of lines per function exceeded; max %d but got %d", w.maxLines, lineCount),
  80. Node: node,
  81. })
  82. }
  83. }
  84. return nil
  85. }
  86. func (w lintFuncLength) countLines(b *ast.BlockStmt) int {
  87. return w.file.ToPosition(b.End()).Line - w.file.ToPosition(b.Pos()).Line - 1
  88. }
  89. func (w lintFuncLength) countStmts(b []ast.Stmt) int {
  90. count := 0
  91. for _, s := range b {
  92. switch stmt := s.(type) {
  93. case *ast.BlockStmt:
  94. count += w.countStmts(stmt.List)
  95. case *ast.IfStmt:
  96. count += 1 + w.countBodyListStmts(stmt)
  97. if stmt.Else != nil {
  98. elseBody, ok := stmt.Else.(*ast.BlockStmt)
  99. if ok {
  100. count += w.countStmts(elseBody.List)
  101. }
  102. }
  103. case *ast.ForStmt, *ast.RangeStmt,
  104. *ast.SwitchStmt, *ast.TypeSwitchStmt, *ast.SelectStmt:
  105. count += 1 + w.countBodyListStmts(stmt)
  106. case *ast.CaseClause:
  107. count += w.countStmts(stmt.Body)
  108. case *ast.AssignStmt:
  109. count += 1 + w.countFuncLitStmts(stmt.Rhs[0])
  110. case *ast.GoStmt:
  111. count += 1 + w.countFuncLitStmts(stmt.Call.Fun)
  112. case *ast.DeferStmt:
  113. count += 1 + w.countFuncLitStmts(stmt.Call.Fun)
  114. default:
  115. count++
  116. }
  117. }
  118. return count
  119. }
  120. func (w lintFuncLength) countFuncLitStmts(stmt ast.Expr) int {
  121. if block, ok := stmt.(*ast.FuncLit); ok {
  122. return w.countStmts(block.Body.List)
  123. }
  124. return 0
  125. }
  126. func (w lintFuncLength) countBodyListStmts(t interface{}) int {
  127. i := reflect.ValueOf(t).Elem().FieldByName(`Body`).Elem().FieldByName(`List`).Interface()
  128. return w.countStmts(i.([]ast.Stmt))
  129. }