1
0
mirror of https://github.com/mgechev/revive.git synced 2025-07-03 00:26:51 +02:00
Files
revive/rule/file_length_limit.go

132 lines
3.2 KiB
Go
Raw Normal View History

package rule
import (
"bufio"
"bytes"
"fmt"
"go/ast"
"go/token"
"strings"
"github.com/mgechev/revive/lint"
)
// FileLengthLimitRule lints the number of lines in a file.
type FileLengthLimitRule struct {
// max is the maximum number of lines allowed in a file. 0 means the rule is disabled.
max int
// skipComments indicates whether to skip comment lines when counting lines.
skipComments bool
// skipBlankLines indicates whether to skip blank lines when counting lines.
skipBlankLines bool
}
// Apply applies the rule to given file.
func (r *FileLengthLimitRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
if r.max <= 0 {
// when max is negative or 0 the rule is disabled
return nil
}
all := 0
blank := 0
scanner := bufio.NewScanner(bytes.NewReader(file.Content()))
for scanner.Scan() {
all++
if len(bytes.TrimSpace(scanner.Bytes())) == 0 {
blank++
}
}
if err := scanner.Err(); err != nil {
return newInternalFailureError(err)
}
lines := all
if r.skipComments {
lines -= countCommentLines(file.AST.Comments)
}
if r.skipBlankLines {
lines -= blank
}
if lines <= r.max {
return nil
}
return []lint.Failure{
{
Category: lint.FailureCategoryCodeStyle,
Confidence: 1,
Position: lint.FailurePosition{
Start: token.Position{
Filename: file.Name,
Line: all,
},
},
Failure: fmt.Sprintf("file length is %d lines, which exceeds the limit of %d", lines, r.max),
},
}
}
// Configure validates the rule configuration, and configures the rule accordingly.
//
// Configuration implements the [lint.ConfigurableRule] interface.
func (r *FileLengthLimitRule) Configure(arguments lint.Arguments) error {
if len(arguments) < 1 {
return nil // use default
}
argKV, ok := arguments[0].(map[string]any)
if !ok {
return fmt.Errorf(`invalid argument to the "file-length-limit" rule. Expecting a k,v map, got %T`, arguments[0])
}
for k, v := range argKV {
switch {
case isRuleOption(k, "max"):
maxLines, ok := v.(int64)
if !ok || maxLines < 0 {
return fmt.Errorf(`invalid configuration value for max lines in "file-length-limit" rule; need positive int64 but got %T`, v)
}
r.max = int(maxLines)
case isRuleOption(k, "skipComments"):
skipComments, ok := v.(bool)
if !ok {
return fmt.Errorf(`invalid configuration value for skip comments in "file-length-limit" rule; need bool but got %T`, v)
}
r.skipComments = skipComments
case isRuleOption(k, "skipBlankLines"):
skipBlankLines, ok := v.(bool)
if !ok {
return fmt.Errorf(`invalid configuration value for skip blank lines in "file-length-limit" rule; need bool but got %T`, v)
}
r.skipBlankLines = skipBlankLines
}
}
return nil
}
// Name returns the rule name.
func (*FileLengthLimitRule) Name() string {
return "file-length-limit"
}
func countCommentLines(comments []*ast.CommentGroup) int {
count := 0
for _, cg := range comments {
for _, comment := range cg.List {
if len(comment.Text) < 2 {
continue
}
switch comment.Text[1] {
case '/': // single-line comment
count++
case '*': // multi-line comment
count += strings.Count(comment.Text, "\n") + 1
}
}
}
return count
}