1
0
mirror of https://github.com/mgechev/revive.git synced 2025-03-19 21:07:46 +02:00

rule.exported: add feature to disable checking on const,method,function, variable (#1047)

Co-authored-by: chavacava <salvadorcavadini+github@gmail.com>
This commit is contained in:
Marcin Federowicz 2024-10-02 10:46:52 +02:00 committed by GitHub
parent ca2a32b087
commit ff7b0adb4c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 136 additions and 34 deletions

View File

@ -475,12 +475,17 @@ Available flags are:
* _disableStutteringCheck_ disables checking for method names that stutter with the package name (i.e. avoid failure messages of the form _type name will be used as x.XY by other packages, and that stutters; consider calling this Y_) * _disableStutteringCheck_ disables checking for method names that stutter with the package name (i.e. avoid failure messages of the form _type name will be used as x.XY by other packages, and that stutters; consider calling this Y_)
* _sayRepetitiveInsteadOfStutters_ replaces the use of the term _stutters_ by _repetitive_ in failure messages * _sayRepetitiveInsteadOfStutters_ replaces the use of the term _stutters_ by _repetitive_ in failure messages
* _checkPublicInterface_ enabled checking public method definitions in public interface types * _checkPublicInterface_ enabled checking public method definitions in public interface types
* _disableChecksOnConstants_ disable all checks on constant declarations
* _disableChecksOnFunctions_ disable all checks on function declarations
* _disableChecksOnMethods_ disable all checks on method declarations
* _disableChecksOnTypes_ disable all checks on type declarations
* _disableChecksOnVariables_ disable all checks on variable declarations
Example: Example:
```toml ```toml
[rule.exported] [rule.exported]
arguments = ["checkPrivateReceivers", "disableStutteringCheck", "checkPublicInterface"] arguments = ["checkPrivateReceivers", "disableStutteringCheck", "checkPublicInterface", "disableChecksOnFunctions"]
``` ```
## file-header ## file-header

View File

@ -13,40 +13,92 @@ import (
"github.com/mgechev/revive/lint" "github.com/mgechev/revive/lint"
) )
// disabledChecks store ignored warnings types
type disabledChecks struct {
Const bool
Function bool
Method bool
PrivateReceivers bool
PublicInterfaces bool
Stuttering bool
Type bool
Var bool
}
const checkNamePrivateReceivers = "privateReceivers"
const checkNamePublicInterfaces = "publicInterfaces"
const checkNameStuttering = "stuttering"
// isDisabled returns true if the given check is disabled, false otherwise
func (dc *disabledChecks) isDisabled(checkName string) bool {
switch checkName {
case "var":
return dc.Var
case "const":
return dc.Const
case "function":
return dc.Function
case "method":
return dc.Method
case checkNamePrivateReceivers:
return dc.PrivateReceivers
case checkNamePublicInterfaces:
return dc.PublicInterfaces
case checkNameStuttering:
return dc.Stuttering
case "type":
return dc.Type
default:
return false
}
}
// ExportedRule lints given else constructs. // ExportedRule lints given else constructs.
type ExportedRule struct { type ExportedRule struct {
configured bool configured bool
checkPrivateReceivers bool stuttersMsg string
disableStutteringCheck bool disabledChecks disabledChecks
checkPublicInterface bool
stuttersMsg string
sync.Mutex sync.Mutex
} }
func (r *ExportedRule) configure(arguments lint.Arguments) { func (r *ExportedRule) configure(arguments lint.Arguments) {
r.Lock() r.Lock()
defer r.Unlock() defer r.Unlock()
if !r.configured { if r.configured {
r.stuttersMsg = "stutters" return
for _, flag := range arguments { }
flagStr, ok := flag.(string) r.configured = true
if !ok {
panic(fmt.Sprintf("Invalid argument for the %s rule: expecting a string, got %T", r.Name(), flag)) r.disabledChecks = disabledChecks{PrivateReceivers: true, PublicInterfaces: true}
} r.stuttersMsg = "stutters"
switch flagStr { for _, flag := range arguments {
switch flag := flag.(type) {
case string:
switch flag {
case "checkPrivateReceivers": case "checkPrivateReceivers":
r.checkPrivateReceivers = true r.disabledChecks.PrivateReceivers = false
case "disableStutteringCheck": case "disableStutteringCheck":
r.disableStutteringCheck = true r.disabledChecks.Stuttering = true
case "sayRepetitiveInsteadOfStutters": case "sayRepetitiveInsteadOfStutters":
r.stuttersMsg = "is repetitive" r.stuttersMsg = "is repetitive"
case "checkPublicInterface": case "checkPublicInterface":
r.checkPublicInterface = true r.disabledChecks.PublicInterfaces = false
case "disableChecksOnConstants":
r.disabledChecks.Const = true
case "disableChecksOnFunctions":
r.disabledChecks.Function = true
case "disableChecksOnMethods":
r.disabledChecks.Method = true
case "disableChecksOnTypes":
r.disabledChecks.Type = true
case "disableChecksOnVariables":
r.disabledChecks.Var = true
default: default:
panic(fmt.Sprintf("Unknown configuration flag %s for %s rule", flagStr, r.Name())) panic(fmt.Sprintf("Unknown configuration flag %s for %s rule", flag, r.Name()))
} }
default:
panic(fmt.Sprintf("Invalid argument for the %s rule: expecting a string, got %T", r.Name(), flag))
} }
r.configured = true
} }
} }
@ -68,10 +120,8 @@ func (r *ExportedRule) Apply(file *lint.File, args lint.Arguments) []lint.Failur
failures = append(failures, failure) failures = append(failures, failure)
}, },
genDeclMissingComments: make(map[*ast.GenDecl]bool), genDeclMissingComments: make(map[*ast.GenDecl]bool),
checkPrivateReceivers: r.checkPrivateReceivers,
disableStutteringCheck: r.disableStutteringCheck,
checkPublicInterface: r.checkPublicInterface,
stuttersMsg: r.stuttersMsg, stuttersMsg: r.stuttersMsg,
disabledChecks: r.disabledChecks,
} }
ast.Walk(&walker, fileAst) ast.Walk(&walker, fileAst)
@ -90,30 +140,30 @@ type lintExported struct {
lastGen *ast.GenDecl lastGen *ast.GenDecl
genDeclMissingComments map[*ast.GenDecl]bool genDeclMissingComments map[*ast.GenDecl]bool
onFailure func(lint.Failure) onFailure func(lint.Failure)
checkPrivateReceivers bool
disableStutteringCheck bool
checkPublicInterface bool
stuttersMsg string stuttersMsg string
disabledChecks disabledChecks
} }
func (w *lintExported) lintFuncDoc(fn *ast.FuncDecl) { func (w *lintExported) lintFuncDoc(fn *ast.FuncDecl) {
if !ast.IsExported(fn.Name.Name) { if !ast.IsExported(fn.Name.Name) {
// func is unexported return // func is unexported, nothing to do
return
} }
kind := "function" kind := "function"
name := fn.Name.Name name := fn.Name.Name
if fn.Recv != nil && len(fn.Recv.List) > 0 { isMethod := fn.Recv != nil && len(fn.Recv.List) > 0
// method if isMethod {
kind = "method" kind = "method"
recv := typeparams.ReceiverType(fn) recv := typeparams.ReceiverType(fn)
if !w.checkPrivateReceivers && !ast.IsExported(recv) {
// receiver is unexported if !ast.IsExported(recv) && w.disabledChecks.PrivateReceivers {
return return
} }
if commonMethods[name] { if commonMethods[name] {
return return
} }
switch name { switch name {
case "Len", "Less", "Swap": case "Len", "Less", "Swap":
sortables := w.file.Pkg.Sortable() sortables := w.file.Pkg.Sortable()
@ -123,6 +173,11 @@ func (w *lintExported) lintFuncDoc(fn *ast.FuncDecl) {
} }
name = recv + "." + name name = recv + "." + name
} }
if w.disabledChecks.isDisabled(kind) {
return
}
if fn.Doc == nil { if fn.Doc == nil {
w.onFailure(lint.Failure{ w.onFailure(lint.Failure{
Node: fn, Node: fn,
@ -132,6 +187,7 @@ func (w *lintExported) lintFuncDoc(fn *ast.FuncDecl) {
}) })
return return
} }
s := normalizeText(fn.Doc.Text()) s := normalizeText(fn.Doc.Text())
prefix := fn.Name.Name + " " prefix := fn.Name.Name + " "
if !strings.HasPrefix(s, prefix) { if !strings.HasPrefix(s, prefix) {
@ -145,7 +201,7 @@ func (w *lintExported) lintFuncDoc(fn *ast.FuncDecl) {
} }
func (w *lintExported) checkStutter(id *ast.Ident, thing string) { func (w *lintExported) checkStutter(id *ast.Ident, thing string) {
if w.disableStutteringCheck { if w.disabledChecks.Stuttering {
return return
} }
@ -179,9 +235,14 @@ func (w *lintExported) checkStutter(id *ast.Ident, thing string) {
} }
func (w *lintExported) lintTypeDoc(t *ast.TypeSpec, doc *ast.CommentGroup) { func (w *lintExported) lintTypeDoc(t *ast.TypeSpec, doc *ast.CommentGroup) {
if w.disabledChecks.isDisabled("type") {
return
}
if !ast.IsExported(t.Name.Name) { if !ast.IsExported(t.Name.Name) {
return return
} }
if doc == nil { if doc == nil {
w.onFailure(lint.Failure{ w.onFailure(lint.Failure{
Node: t, Node: t,
@ -203,18 +264,19 @@ func (w *lintExported) lintTypeDoc(t *ast.TypeSpec, doc *ast.CommentGroup) {
break break
} }
} }
// if comment starts with name of type and has some text after - it's ok // if comment starts with name of type and has some text after - it's ok
expectedPrefix := t.Name.Name + " " expectedPrefix := t.Name.Name + " "
if strings.HasPrefix(s, expectedPrefix) { if strings.HasPrefix(s, expectedPrefix) {
return return
} }
w.onFailure(lint.Failure{ w.onFailure(lint.Failure{
Node: doc, Node: doc,
Confidence: 1, Confidence: 1,
Category: "comments", Category: "comments",
Failure: fmt.Sprintf(`comment on exported type %v should be of the form "%s..." (with optional leading article)`, t.Name, expectedPrefix), Failure: fmt.Sprintf(`comment on exported type %v should be of the form "%s..." (with optional leading article)`, t.Name, expectedPrefix),
}) })
} }
func (w *lintExported) lintValueSpecDoc(vs *ast.ValueSpec, gd *ast.GenDecl, genDeclMissingComments map[*ast.GenDecl]bool) { func (w *lintExported) lintValueSpecDoc(vs *ast.ValueSpec, gd *ast.GenDecl, genDeclMissingComments map[*ast.GenDecl]bool) {
@ -223,6 +285,10 @@ func (w *lintExported) lintValueSpecDoc(vs *ast.ValueSpec, gd *ast.GenDecl, genD
kind = "const" kind = "const"
} }
if w.disabledChecks.isDisabled(kind) {
return
}
if len(vs.Names) > 1 { if len(vs.Names) > 1 {
// Check that none are exported except for the first. // Check that none are exported except for the first.
for _, n := range vs.Names[1:] { for _, n := range vs.Names[1:] {
@ -324,7 +390,7 @@ func (w *lintExported) Visit(n ast.Node) ast.Visitor {
w.lintTypeDoc(v, doc) w.lintTypeDoc(v, doc)
w.checkStutter(v.Name, "type") w.checkStutter(v.Name, "type")
if w.checkPublicInterface { if !w.disabledChecks.PublicInterfaces {
if iface, ok := v.Type.(*ast.InterfaceType); ok { if iface, ok := v.Type.(*ast.InterfaceType); ok {
if ast.IsExported(v.Name.Name) { if ast.IsExported(v.Name.Name) {
w.doCheckPublicInterface(v.Name.Name, iface) w.doCheckPublicInterface(v.Name.Name, iface)

View File

@ -30,3 +30,9 @@ func TestCheckPublicInterfaceOption(t *testing.T) {
testRule(t, "exported-issue-1002", &rule.ExportedRule{}, &lint.RuleConfig{Arguments: args}) testRule(t, "exported-issue-1002", &rule.ExportedRule{}, &lint.RuleConfig{Arguments: args})
} }
func TestCheckDisablingOnDeclarationTypes(t *testing.T) {
args := []any{"disableChecksOnConstants", "disableChecksOnFunctions", "disableChecksOnMethods", "disableChecksOnTypes", "disableChecksOnVariables"}
testRule(t, "exported-issue-1045", &rule.ExportedRule{}, &lint.RuleConfig{Arguments: args})
}

25
testdata/exported-issue-1045.go vendored Normal file
View File

@ -0,0 +1,25 @@
// Package golint comment
package golint
// path separator defined by os.Separator.
const FilePath = "xyz"
// Rewrite string to remove non-standard path characters
func UnicodeSanitize(s string) string {}
// Tags returns a slice of tags. The order is the original tag order unless it
// was changed.
func (t *Tags) Keys() []string {}
// A value which may be passed to the which parameter for Getitimer and
type ItimerWhich int
/*
// PropertyBag
*/
// Rectangle An area within an image.
type Rectangle struct {}