mirror of
https://github.com/mgechev/revive.git
synced 2025-11-23 22:04:49 +02:00
adds comments-density rule (#979)
This commit is contained in:
@@ -539,6 +539,7 @@ List of all available rules. The rules ported from `golint` are left unchanged a
|
||||
| [`enforce-slice-style`](./RULES_DESCRIPTIONS.md#enforce-slice-style) | string (defaults to "any") | Enforces consistent usage of `make([]type, 0)` or `[]type{}` for slice initialization. Does not affect `make(map[type]type, non_zero_len, or_non_zero_cap)` constructions. | no | no |
|
||||
| [`enforce-repeated-arg-type-style`](./RULES_DESCRIPTIONS.md#enforce-repeated-arg-type-style) | string (defaults to "any") | Enforces consistent style for repeated argument and/or return value types. | no | no |
|
||||
| [`max-control-nesting`](./RULES_DESCRIPTIONS.md#max-control-nesting) | int (defaults to 5) | Sets restriction for maximum nesting of control structures. | no | no |
|
||||
| [`comments-density`](./RULES_DESCRIPTIONS.md#comments-density) | int (defaults to 0) | Enforces a minumum comment / code relation | no | no |
|
||||
|
||||
|
||||
## Configurable rules
|
||||
|
||||
@@ -14,6 +14,7 @@ List of all available rules.
|
||||
- [call-to-gc](#call-to-gc)
|
||||
- [cognitive-complexity](#cognitive-complexity)
|
||||
- [comment-spacings](#comment-spacings)
|
||||
- [comments-density](#comment-spacings)
|
||||
- [confusing-naming](#confusing-naming)
|
||||
- [confusing-results](#confusing-results)
|
||||
- [constant-logical-expr](#constant-logical-expr)
|
||||
@@ -193,6 +194,19 @@ Example:
|
||||
[rule.comment-spacings]
|
||||
arguments =["mypragma:", "+optional"]
|
||||
```
|
||||
## comments-density
|
||||
|
||||
_Description_: Spots files not respecting a minimum value for the [_comments lines density_](https://docs.sonarsource.com/sonarqube/latest/user-guide/metric-definitions/) metric = _comment lines / (lines of code + comment lines) * 100_
|
||||
|
||||
_Configuration_: (int) the minimum expected comments lines density.
|
||||
|
||||
Example:
|
||||
|
||||
```toml
|
||||
[rule.comments-density]
|
||||
arguments =[15]
|
||||
```
|
||||
|
||||
## confusing-naming
|
||||
|
||||
_Description_: Methods or fields of `struct` that have names different only by capitalization could be confusing.
|
||||
|
||||
@@ -95,6 +95,7 @@ var allRules = append([]lint.Rule{
|
||||
&rule.EnforceRepeatedArgTypeStyleRule{},
|
||||
&rule.EnforceSliceStyleRule{},
|
||||
&rule.MaxControlNestingRule{},
|
||||
&rule.CommentsDensityRule{},
|
||||
}, defaultRules...)
|
||||
|
||||
var allFormatters = []lint.Formatter{
|
||||
|
||||
95
rule/comments-density.go
Normal file
95
rule/comments-density.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/mgechev/revive/lint"
|
||||
)
|
||||
|
||||
// CommentsDensityRule lints given else constructs.
|
||||
type CommentsDensityRule struct {
|
||||
minumumCommentsDensity int64
|
||||
configured bool
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
const defaultMinimumCommentsPercentage = 0
|
||||
|
||||
func (r *CommentsDensityRule) configure(arguments lint.Arguments) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
if r.configured {
|
||||
return
|
||||
}
|
||||
|
||||
r.configured = true
|
||||
|
||||
if len(arguments) < 1 {
|
||||
r.minumumCommentsDensity = defaultMinimumCommentsPercentage
|
||||
return
|
||||
}
|
||||
|
||||
var ok bool
|
||||
r.minumumCommentsDensity, ok = arguments[0].(int64)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("invalid argument for %q rule: argument should be an int, got %T", r.Name(), arguments[0]))
|
||||
}
|
||||
}
|
||||
|
||||
// Apply applies the rule to given file.
|
||||
func (r *CommentsDensityRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
||||
r.configure(arguments)
|
||||
|
||||
commentsLines := countDocLines(file.AST.Comments)
|
||||
statementsCount := countStatements(file.AST)
|
||||
density := (float32(commentsLines) / float32(statementsCount+commentsLines)) * 100
|
||||
|
||||
if density < float32(r.minumumCommentsDensity) {
|
||||
return []lint.Failure{
|
||||
{
|
||||
Node: file.AST,
|
||||
Confidence: 1,
|
||||
Failure: fmt.Sprintf("the file has a comment density of %2.f%% (%d comment lines for %d code lines) but expected a minimum of %d%%", density, commentsLines, statementsCount, r.minumumCommentsDensity),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Name returns the rule name.
|
||||
func (*CommentsDensityRule) Name() string {
|
||||
return "comments-density"
|
||||
}
|
||||
|
||||
// countStatements counts the number of program statements in the given AST.
|
||||
func countStatements(node ast.Node) int {
|
||||
counter := 0
|
||||
|
||||
ast.Inspect(node, func(n ast.Node) bool {
|
||||
switch n.(type) {
|
||||
case *ast.ExprStmt, *ast.AssignStmt, *ast.ReturnStmt, *ast.GoStmt, *ast.DeferStmt,
|
||||
*ast.BranchStmt, *ast.IfStmt, *ast.SwitchStmt, *ast.TypeSwitchStmt,
|
||||
*ast.SelectStmt, *ast.ForStmt, *ast.RangeStmt, *ast.CaseClause, *ast.CommClause,
|
||||
*ast.DeclStmt, *ast.FuncDecl:
|
||||
counter++
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return counter
|
||||
}
|
||||
|
||||
func countDocLines(comments []*ast.CommentGroup) int {
|
||||
acc := 0
|
||||
for _, c := range comments {
|
||||
lines := strings.Split(c.Text(), "\n")
|
||||
acc += len(lines) - 1
|
||||
}
|
||||
|
||||
return acc
|
||||
}
|
||||
20
test/comments-density_test.go
Normal file
20
test/comments-density_test.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mgechev/revive/lint"
|
||||
"github.com/mgechev/revive/rule"
|
||||
)
|
||||
|
||||
func TestCommentsDensity(t *testing.T) {
|
||||
testRule(t, "comments-density-1", &rule.CommentsDensityRule{}, &lint.RuleConfig{
|
||||
Arguments: []any{int64(60)},
|
||||
})
|
||||
|
||||
testRule(t, "comments-density-2", &rule.CommentsDensityRule{}, &lint.RuleConfig{
|
||||
Arguments: []any{int64(90)},
|
||||
})
|
||||
|
||||
testRule(t, "comments-density-3", &rule.CommentsDensityRule{}, &lint.RuleConfig{})
|
||||
}
|
||||
9
testdata/comments-density-1.go
vendored
Normal file
9
testdata/comments-density-1.go
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
package fixtures // MATCH /the file has a comment density of 57% (4 comment lines for 3 code lines) but expected a minimum of 60%/
|
||||
|
||||
// func contains banned characters Ω // authorized banned chars in comment
|
||||
func cd1() error {
|
||||
// the var
|
||||
var charσhid string
|
||||
/* the return */
|
||||
return nil
|
||||
}
|
||||
34
testdata/comments-density-2.go
vendored
Normal file
34
testdata/comments-density-2.go
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
package fixtures // MATCH /the file has a comment density of 19% (5 comment lines for 21 code lines) but expected a minimum of 90%/
|
||||
|
||||
// datarace is function
|
||||
func datarace() (r int, c char) {
|
||||
for _, p := range []int{1, 2} {
|
||||
go func() {
|
||||
print(r)
|
||||
print(p)
|
||||
}()
|
||||
for i, p1 := range []int{1, 2} {
|
||||
a := p1
|
||||
go func() {
|
||||
print(r)
|
||||
print(p)
|
||||
print(p1)
|
||||
print(a)
|
||||
print(i)
|
||||
}()
|
||||
print(i)
|
||||
print(p)
|
||||
go func() {
|
||||
_ = c
|
||||
}()
|
||||
}
|
||||
print(p1)
|
||||
}
|
||||
/* Goroutines
|
||||
are
|
||||
awesome */
|
||||
go func() {
|
||||
print(r)
|
||||
}()
|
||||
print(r)
|
||||
}
|
||||
34
testdata/comments-density-3.go
vendored
Normal file
34
testdata/comments-density-3.go
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
package fixtures
|
||||
|
||||
// datarace is function
|
||||
func datarace() (r int, c char) {
|
||||
for _, p := range []int{1, 2} {
|
||||
go func() {
|
||||
print(r)
|
||||
print(p)
|
||||
}()
|
||||
for i, p1 := range []int{1, 2} {
|
||||
a := p1
|
||||
go func() {
|
||||
print(r)
|
||||
print(p)
|
||||
print(p1)
|
||||
print(a)
|
||||
print(i)
|
||||
}()
|
||||
print(i)
|
||||
print(p)
|
||||
go func() {
|
||||
_ = c
|
||||
}()
|
||||
}
|
||||
print(p1)
|
||||
}
|
||||
/* Goroutines
|
||||
are
|
||||
awesome */
|
||||
go func() {
|
||||
print(r)
|
||||
}()
|
||||
print(r)
|
||||
}
|
||||
Reference in New Issue
Block a user