2018-01-21 18:04:41 -08:00
package rule
2017-11-26 19:19:41 -08:00
import (
"fmt"
"go/ast"
"go/token"
"strings"
"unicode"
"unicode/utf8"
2022-06-18 19:47:53 +03:00
"github.com/mgechev/revive/internal/typeparams"
2018-01-24 15:44:03 -08:00
"github.com/mgechev/revive/lint"
2017-11-26 19:19:41 -08:00
)
2025-05-27 08:44:24 +03:00
// disabledChecks store ignored warnings types.
2024-10-02 10:46:52 +02:00
type disabledChecks struct {
Const bool
Function bool
Method bool
PrivateReceivers bool
PublicInterfaces bool
2025-04-07 20:59:55 +02:00
RepetitiveNames bool
2024-10-02 10:46:52 +02:00
Type bool
Var bool
}
2024-12-13 21:38:46 +01:00
const (
checkNamePrivateReceivers = "privateReceivers"
checkNamePublicInterfaces = "publicInterfaces"
checkNameStuttering = "stuttering"
)
2024-10-02 10:46:52 +02:00
2025-05-27 08:44:24 +03:00
// isDisabled returns true if the given check is disabled, false otherwise.
2024-10-02 10:46:52 +02:00
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 :
2025-04-07 20:59:55 +02:00
return dc . RepetitiveNames
2024-10-02 10:46:52 +02:00
case "type" :
return dc . Type
default :
return false
}
}
2024-11-19 23:58:14 +02:00
var commonMethods = map [ string ] bool {
"Error" : true ,
"Read" : true ,
"ServeHTTP" : true ,
"String" : true ,
"Write" : true ,
"Unwrap" : true ,
}
2024-12-01 17:44:41 +02:00
// ExportedRule lints naming and commenting conventions on exported symbols.
2021-10-17 20:34:48 +02:00
type ExportedRule struct {
2025-04-07 20:59:55 +02:00
isRepetitiveMsg string
disabledChecks disabledChecks
2021-10-17 20:34:48 +02:00
}
2017-11-26 19:19:41 -08:00
2024-12-13 21:38:46 +01:00
// Configure validates the rule configuration, and configures the rule accordingly.
//
2025-04-07 20:59:55 +02:00
// Configure makes the rule implement the [lint.ConfigurableRule] interface.
2024-12-13 21:38:46 +01:00
func ( r * ExportedRule ) Configure ( arguments lint . Arguments ) error {
2024-10-02 10:46:52 +02:00
r . disabledChecks = disabledChecks { PrivateReceivers : true , PublicInterfaces : true }
2025-04-07 20:59:55 +02:00
r . isRepetitiveMsg = "stutters"
2024-10-02 10:46:52 +02:00
for _ , flag := range arguments {
switch flag := flag . ( type ) {
case string :
2025-03-28 01:34:20 -07:00
switch {
case isRuleOption ( flag , "checkPrivateReceivers" ) :
2024-10-02 10:46:52 +02:00
r . disabledChecks . PrivateReceivers = false
2025-03-28 01:34:20 -07:00
case isRuleOption ( flag , "disableStutteringCheck" ) :
2025-04-07 20:59:55 +02:00
r . disabledChecks . RepetitiveNames = true
2025-03-28 01:34:20 -07:00
case isRuleOption ( flag , "sayRepetitiveInsteadOfStutters" ) :
2025-04-07 20:59:55 +02:00
r . isRepetitiveMsg = "is repetitive"
2025-03-28 01:34:20 -07:00
case isRuleOption ( flag , "checkPublicInterface" ) :
2024-10-02 10:46:52 +02:00
r . disabledChecks . PublicInterfaces = false
2025-03-28 01:34:20 -07:00
case isRuleOption ( flag , "disableChecksOnConstants" ) :
2024-10-02 10:46:52 +02:00
r . disabledChecks . Const = true
2025-03-28 01:34:20 -07:00
case isRuleOption ( flag , "disableChecksOnFunctions" ) :
2024-10-02 10:46:52 +02:00
r . disabledChecks . Function = true
2025-03-28 01:34:20 -07:00
case isRuleOption ( flag , "disableChecksOnMethods" ) :
2024-10-02 10:46:52 +02:00
r . disabledChecks . Method = true
2025-03-28 01:34:20 -07:00
case isRuleOption ( flag , "disableChecksOnTypes" ) :
2024-10-02 10:46:52 +02:00
r . disabledChecks . Type = true
2025-03-28 01:34:20 -07:00
case isRuleOption ( flag , "disableChecksOnVariables" ) :
2024-10-02 10:46:52 +02:00
r . disabledChecks . Var = true
2024-07-30 13:36:03 +05:00
default :
2024-12-11 19:35:58 +01:00
return fmt . Errorf ( "unknown configuration flag %s for %s rule" , flag , r . Name ( ) )
2024-07-30 13:36:03 +05:00
}
2024-10-02 10:46:52 +02:00
default :
2024-12-11 19:35:58 +01:00
return fmt . Errorf ( "invalid argument for the %s rule: expecting a string, got %T" , r . Name ( ) , flag )
2021-10-17 20:34:48 +02:00
}
2021-08-26 17:36:24 +02:00
}
2024-12-11 19:35:58 +01:00
return nil
2022-04-10 09:06:59 +02:00
}
// Apply applies the rule to given file.
2024-12-13 21:38:46 +01:00
func ( r * ExportedRule ) Apply ( file * lint . File , _ lint . Arguments ) [ ] lint . Failure {
2025-06-16 15:23:47 +03:00
if ! file . IsImportable ( ) {
return nil
2022-04-10 09:06:59 +02:00
}
2021-08-26 17:36:24 +02:00
2025-06-16 15:23:47 +03:00
var failures [ ] lint . Failure
2017-11-26 19:19:41 -08:00
walker := lintExported {
2025-04-07 20:59:55 +02:00
file : file ,
2018-01-24 15:44:03 -08:00
onFailure : func ( failure lint . Failure ) {
2017-11-26 19:19:41 -08:00
failures = append ( failures , failure )
} ,
2024-11-16 19:05:30 +02:00
genDeclMissingComments : map [ * ast . GenDecl ] bool { } ,
2025-04-07 20:59:55 +02:00
isRepetitiveMsg : r . isRepetitiveMsg ,
2024-10-02 10:46:52 +02:00
disabledChecks : r . disabledChecks ,
2017-11-26 19:19:41 -08:00
}
2025-04-07 20:59:55 +02:00
ast . Walk ( & walker , file . AST )
2017-11-26 19:19:41 -08:00
return failures
}
// Name returns the rule name.
2022-04-10 11:55:13 +02:00
func ( * ExportedRule ) Name ( ) string {
2018-01-27 17:01:18 -08:00
return "exported"
2017-11-26 19:19:41 -08:00
}
type lintExported struct {
2018-01-24 15:44:03 -08:00
file * lint . File
2025-04-07 20:59:55 +02:00
lastGenDecl * ast . GenDecl // the last visited general declaration in the AST
2017-11-26 19:19:41 -08:00
genDeclMissingComments map [ * ast . GenDecl ] bool
2018-01-24 15:44:03 -08:00
onFailure func ( lint . Failure )
2025-04-07 20:59:55 +02:00
isRepetitiveMsg string
2024-10-02 10:46:52 +02:00
disabledChecks disabledChecks
2017-11-26 19:19:41 -08:00
}
func ( w * lintExported ) lintFuncDoc ( fn * ast . FuncDecl ) {
if ! ast . IsExported ( fn . Name . Name ) {
2024-10-02 10:46:52 +02:00
return // func is unexported, nothing to do
2017-11-26 19:19:41 -08:00
}
2024-10-02 10:46:52 +02:00
2017-11-26 19:19:41 -08:00
kind := "function"
name := fn . Name . Name
2025-04-07 20:59:55 +02:00
if isMethod := fn . Recv != nil && len ( fn . Recv . List ) > 0 ; isMethod {
if ! w . mustCheckMethod ( fn ) {
2017-11-26 19:19:41 -08:00
return
}
2024-10-02 10:46:52 +02:00
2025-04-07 20:59:55 +02:00
kind = "method"
recv := typeparams . ReceiverType ( fn )
2017-11-26 19:19:41 -08:00
name = recv + "." + name
}
2024-10-02 10:46:52 +02:00
if w . disabledChecks . isDisabled ( kind ) {
return
}
2025-07-22 10:47:37 +02:00
status := w . checkGoDocStatus ( fn . Doc , fn . Name . Name )
switch status {
case exportedGoDocStatusOK :
return // comment is fine
case exportedGoDocStatusMissing :
w . addFailuref ( fn , status . Confidence ( ) , lint . FailureCategoryComments ,
2025-04-07 20:59:55 +02:00
"exported %s %s should have comment or be unexported" , kind , name ,
)
2017-11-26 19:19:41 -08:00
return
}
2024-10-02 10:46:52 +02:00
2025-07-22 10:47:37 +02:00
firstCommentLine := w . firstCommentLine ( fn . Doc )
w . addFailuref ( fn . Doc , status . Confidence ( ) , lint . FailureCategoryComments ,
` comment on exported %s %s should be of the form "%s ..."%s ` , kind , name , fn . Name . Name , status . CorrectionHint ( firstCommentLine ) ,
)
}
func ( * lintExported ) hasPrefixInsensitive ( s , prefix string ) bool {
return strings . HasPrefix ( strings . ToLower ( s ) , strings . ToLower ( prefix ) )
}
func ( * lintExported ) stripFirstRune ( s string ) string {
// Decode the first rune to handle multi-byte characters.
firstRune , size := utf8 . DecodeRuneInString ( s )
if firstRune == utf8 . RuneError {
return s // no valid first rune found
2017-11-26 19:19:41 -08:00
}
2025-07-22 10:47:37 +02:00
// Return the string without the first rune.
return s [ size : ]
2017-11-26 19:19:41 -08:00
}
2025-04-07 20:59:55 +02:00
func ( w * lintExported ) checkRepetitiveNames ( id * ast . Ident , thing string ) {
if w . disabledChecks . RepetitiveNames {
2021-08-26 17:36:24 +02:00
return
}
2025-04-07 20:59:55 +02:00
pkg , name := w . file . AST . Name . Name , id . Name
2017-11-26 19:19:41 -08:00
if ! ast . IsExported ( name ) {
// unexported name
return
}
2025-04-07 20:59:55 +02:00
// A name is repetitive if the package name is a strict prefix
2017-11-26 19:19:41 -08:00
// and the next character of the name starts a new word.
if len ( name ) <= len ( pkg ) {
2025-04-07 20:59:55 +02:00
// name is too short to be a repetition.
2017-11-26 19:19:41 -08:00
// This permits the name to be the same as the package name.
return
}
if ! strings . EqualFold ( pkg , name [ : len ( pkg ) ] ) {
return
}
// We can assume the name is well-formed UTF-8.
// If the next rune after the package name is uppercase or an underscore
2025-04-07 20:59:55 +02:00
// the it's starting a new word and thus this name is repetitive.
2017-11-26 19:19:41 -08:00
rem := name [ len ( pkg ) : ]
if next , _ := utf8 . DecodeRuneInString ( rem ) ; next == '_' || unicode . IsUpper ( next ) {
2025-04-07 20:59:55 +02:00
w . addFailuref ( id , 0.8 , lint . FailureCategoryNaming ,
"%s name will be used as %s.%s by other packages, and that %s; consider calling this %s" , thing , pkg , name , w . isRepetitiveMsg , rem ,
)
2017-11-26 19:19:41 -08:00
}
}
2025-04-07 20:59:55 +02:00
var articles = [ ... ] string { "A" , "An" , "The" , "This" }
func ( w * lintExported ) lintTypeDoc ( t * ast . TypeSpec , doc * ast . CommentGroup , firstCommentLine string ) {
2024-10-02 10:46:52 +02:00
if w . disabledChecks . isDisabled ( "type" ) {
return
}
2025-04-07 20:59:55 +02:00
typeName := t . Name . Name
if ! ast . IsExported ( typeName ) {
2017-11-26 19:19:41 -08:00
return
}
2024-10-02 10:46:52 +02:00
2025-04-07 20:59:55 +02:00
if firstCommentLine == "" {
w . addFailuref ( t , 1 , lint . FailureCategoryComments ,
"exported type %v should have comment or be unexported" , t . Name ,
)
2017-11-26 19:19:41 -08:00
return
}
2025-07-22 10:47:37 +02:00
expectedPrefix := typeName
2017-11-26 19:19:41 -08:00
for _ , a := range articles {
2025-04-07 20:59:55 +02:00
if typeName == a {
2018-07-08 08:14:15 +10:00
continue
}
2024-11-19 16:43:04 +02:00
var found bool
2025-04-07 20:59:55 +02:00
if firstCommentLine , found = strings . CutPrefix ( firstCommentLine , a + " " ) ; found {
2025-07-22 10:47:37 +02:00
expectedPrefix = a + " " + typeName
2017-11-26 19:19:41 -08:00
break
}
}
2024-10-02 10:46:52 +02:00
2025-07-22 10:47:37 +02:00
status := w . checkGoDocStatus ( doc , expectedPrefix )
if status == exportedGoDocStatusOK {
2024-07-30 13:36:03 +05:00
return
2017-11-26 19:19:41 -08:00
}
2025-07-22 10:47:37 +02:00
w . addFailuref ( doc , status . Confidence ( ) , lint . FailureCategoryComments ,
` comment on exported type %v should be of the form "%s ..." (with optional leading article)%s ` , t . Name , typeName , status . CorrectionHint ( firstCommentLine ) ,
2025-04-07 20:59:55 +02:00
)
2017-11-26 19:19:41 -08:00
}
2025-05-27 08:44:24 +03:00
// checkValueNames returns true if names check, false otherwise.
2025-04-07 20:59:55 +02:00
func ( w * lintExported ) checkValueNames ( names [ ] * ast . Ident , nodeToBlame ast . Node , kind string ) bool {
// Check that none are exported except for the first.
if len ( names ) < 2 {
return true // nothing to check
}
for _ , n := range names [ 1 : ] {
if ast . IsExported ( n . Name ) {
w . addFailuref ( nodeToBlame , 1 , lint . FailureCategoryComments ,
"exported %s %s should have its own declaration" , kind , n . Name ,
)
return false
}
}
return true
}
2025-07-22 10:47:37 +02:00
2017-11-26 19:19:41 -08:00
func ( w * lintExported ) lintValueSpecDoc ( vs * ast . ValueSpec , gd * ast . GenDecl , genDeclMissingComments map [ * ast . GenDecl ] bool ) {
kind := "var"
if gd . Tok == token . CONST {
kind = "const"
}
2024-10-02 10:46:52 +02:00
if w . disabledChecks . isDisabled ( kind ) {
return
}
2025-04-07 20:59:55 +02:00
if ! w . checkValueNames ( vs . Names , vs , kind ) {
return
2017-11-26 19:19:41 -08:00
}
// Only one name.
name := vs . Names [ 0 ] . Name
if ! ast . IsExported ( name ) {
return
}
2025-07-22 10:47:37 +02:00
vsFirstCommentLine := w . firstCommentLine ( vs . Doc )
gdFirstCommentLine := w . firstCommentLine ( gd . Doc )
2025-04-07 20:59:55 +02:00
if vsFirstCommentLine == "" && gdFirstCommentLine == "" {
2017-11-26 19:19:41 -08:00
if genDeclMissingComments [ gd ] {
return
}
block := ""
if kind == "const" && gd . Lparen . IsValid ( ) {
block = " (or a comment on this block)"
}
2025-04-07 20:59:55 +02:00
w . addFailuref ( vs , 1 , lint . FailureCategoryComments ,
"exported %s %s should have comment%s or be unexported" , kind , name , block ,
)
2017-11-26 19:19:41 -08:00
genDeclMissingComments [ gd ] = true
return
}
2025-04-07 20:59:55 +02:00
2017-11-26 19:19:41 -08:00
// If this GenDecl has parens and a comment, we don't check its comment form.
2025-04-07 20:59:55 +02:00
if gdFirstCommentLine != "" && gd . Lparen . IsValid ( ) {
2017-11-26 19:19:41 -08:00
return
}
2025-04-07 20:59:55 +02:00
2017-11-26 19:19:41 -08:00
// The relevant text to check will be on either vs.Doc or gd.Doc.
// Use vs.Doc preferentially.
2022-06-28 17:14:26 +02:00
var doc * ast . CommentGroup
switch {
2025-04-07 20:59:55 +02:00
case vsFirstCommentLine != "" :
2022-06-28 17:14:26 +02:00
doc = vs . Doc
2025-04-07 20:59:55 +02:00
case vsFirstCommentLine != "" && gdFirstCommentLine == "" :
2022-06-28 17:14:26 +02:00
doc = vs . Comment
default :
2017-11-26 19:19:41 -08:00
doc = gd . Doc
}
2025-07-22 10:47:37 +02:00
firstCommentLine := w . firstCommentLine ( doc )
2022-06-28 17:14:26 +02:00
2025-07-22 10:47:37 +02:00
status := w . checkGoDocStatus ( doc , name )
if status == exportedGoDocStatusOK {
return
}
w . addFailuref ( doc , status . Confidence ( ) , lint . FailureCategoryComments ,
` comment on exported %s %s should be of the form "%s ..."%s ` , kind , name , name , status . CorrectionHint ( firstCommentLine ) ,
)
}
type exportedGoDocStatus int
const (
exportedGoDocStatusOK exportedGoDocStatus = iota
exportedGoDocStatusMissing
exportedGoDocStatusCaseMismatch
exportedGoDocStatusFirstLetterMismatch
exportedGoDocStatusUnexpected
)
func ( gds exportedGoDocStatus ) Confidence ( ) float64 {
if gds == exportedGoDocStatusUnexpected {
return 0.8
2017-11-26 19:19:41 -08:00
}
2025-07-22 10:47:37 +02:00
return 1
}
func ( gds exportedGoDocStatus ) CorrectionHint ( firstCommentLine string ) string {
firstWord := strings . Split ( firstCommentLine , " " ) [ 0 ]
switch gds {
case exportedGoDocStatusCaseMismatch :
return ` by using its correct casing, not " ` + firstWord + ` ..." `
case exportedGoDocStatusFirstLetterMismatch :
return ` to match its exported status, not " ` + firstWord + ` ..." `
}
return ""
}
func ( w * lintExported ) checkGoDocStatus ( comment * ast . CommentGroup , name string ) exportedGoDocStatus {
firstCommentLine := w . firstCommentLine ( comment )
if firstCommentLine == "" {
return exportedGoDocStatusMissing
}
name = strings . TrimSpace ( name )
// Make sure the expected prefix has a space at the end.
expectedPrefix := name + " "
if strings . HasPrefix ( firstCommentLine , expectedPrefix ) {
return exportedGoDocStatusOK
}
if ! w . hasPrefixInsensitive ( firstCommentLine , expectedPrefix ) {
return exportedGoDocStatusUnexpected
}
if strings . HasPrefix ( w . stripFirstRune ( firstCommentLine ) , w . stripFirstRune ( expectedPrefix ) ) {
// Only the first character differs, such as "sendJSON" became "SendJSON".
// so we consider the scope has changed.
return exportedGoDocStatusFirstLetterMismatch
}
return exportedGoDocStatusCaseMismatch
2017-11-26 19:19:41 -08:00
}
2025-04-07 20:59:55 +02:00
// firstCommentLine yields the first line of interest in comment group or "" if there is nothing of interest.
// An "interesting line" is a comment line that is neither a directive (e.g. //go:...) or a deprecation comment
// (lines from the first line with a prefix // Deprecated: to the end of the comment group)
// Empty or spaces-only lines are discarded.
2025-07-22 10:47:37 +02:00
func ( lintExported ) firstCommentLine ( comment * ast . CommentGroup ) ( result string ) {
2025-01-27 07:12:16 +01:00
if comment == nil {
2025-04-07 20:59:55 +02:00
return ""
2025-01-27 07:12:16 +01:00
}
2025-04-07 20:59:55 +02:00
commentWithoutDirectives := comment . Text ( ) // removes directives from the comment block
lines := strings . Split ( commentWithoutDirectives , "\n" )
for _ , line := range lines {
line := strings . TrimSpace ( line )
if line == "" {
continue // ignore empty lines
}
if strings . HasPrefix ( line , "Deprecated: " ) {
break // ignore deprecation comment line and the subsequent lines of the original comment
}
2025-04-06 13:59:04 +03:00
2025-04-07 20:59:55 +02:00
result = line
break // first non-directive/non-empty/non-deprecation comment line found
}
2025-01-27 07:12:16 +01:00
2025-04-07 20:59:55 +02:00
return result
2019-09-10 19:26:47 +02:00
}
2017-11-26 19:19:41 -08:00
func ( w * lintExported ) Visit ( n ast . Node ) ast . Visitor {
switch v := n . ( type ) {
case * ast . GenDecl :
2025-04-07 20:59:55 +02:00
switch v . Tok {
case token . IMPORT :
2017-11-26 19:19:41 -08:00
return nil
2025-04-07 20:59:55 +02:00
case token . CONST , token . TYPE , token . VAR :
w . lastGenDecl = v
2017-11-26 19:19:41 -08:00
}
return w
case * ast . FuncDecl :
w . lintFuncDoc ( v )
if v . Recv == nil {
2025-04-07 20:59:55 +02:00
// Only check for repetitive names on functions, not methods.
2017-11-26 19:19:41 -08:00
// Method names are not used package-qualified.
2025-04-07 20:59:55 +02:00
w . checkRepetitiveNames ( v . Name , "func" )
2017-11-26 19:19:41 -08:00
}
// Don't proceed inside funcs.
return nil
case * ast . TypeSpec :
// inside a GenDecl, which usually has the doc
doc := v . Doc
2025-04-07 20:59:55 +02:00
2025-07-22 10:47:37 +02:00
fcl := w . firstCommentLine ( doc )
2025-04-07 20:59:55 +02:00
if fcl == "" {
doc = w . lastGenDecl . Doc
2025-07-22 10:47:37 +02:00
fcl = w . firstCommentLine ( doc )
2017-11-26 19:19:41 -08:00
}
2025-04-07 20:59:55 +02:00
w . lintTypeDoc ( v , doc , fcl )
w . checkRepetitiveNames ( v . Name , "type" )
2024-07-30 13:36:03 +05:00
2024-10-02 10:46:52 +02:00
if ! w . disabledChecks . PublicInterfaces {
2024-07-30 13:36:03 +05:00
if iface , ok := v . Type . ( * ast . InterfaceType ) ; ok {
if ast . IsExported ( v . Name . Name ) {
w . doCheckPublicInterface ( v . Name . Name , iface )
}
}
}
2017-11-26 19:19:41 -08:00
return nil
case * ast . ValueSpec :
2025-04-07 20:59:55 +02:00
w . lintValueSpecDoc ( v , w . lastGenDecl , w . genDeclMissingComments )
2017-11-26 19:19:41 -08:00
return nil
}
return w
}
2024-07-30 13:36:03 +05:00
func ( w * lintExported ) doCheckPublicInterface ( typeName string , iface * ast . InterfaceType ) {
for _ , m := range iface . Methods . List {
w . lintInterfaceMethod ( typeName , m )
}
}
func ( w * lintExported ) lintInterfaceMethod ( typeName string , m * ast . Field ) {
if len ( m . Names ) == 0 {
2024-09-29 22:50:49 +02:00
return
2024-07-30 13:36:03 +05:00
}
if ! ast . IsExported ( m . Names [ 0 ] . Name ) {
return
}
2025-07-22 10:47:37 +02:00
2024-07-30 13:36:03 +05:00
name := m . Names [ 0 ] . Name
2025-07-22 10:47:37 +02:00
status := w . checkGoDocStatus ( m . Doc , name )
switch status {
case exportedGoDocStatusOK :
return // comment is fine
case exportedGoDocStatusMissing :
w . addFailuref ( m , status . Confidence ( ) , lint . FailureCategoryComments ,
2025-04-07 20:59:55 +02:00
"public interface method %s.%s should be commented" , typeName , name ,
)
2024-07-30 13:36:03 +05:00
return
}
2025-04-07 20:59:55 +02:00
2025-07-22 10:47:37 +02:00
firstCommentLine := w . firstCommentLine ( m . Doc )
w . addFailuref ( m . Doc , status . Confidence ( ) , lint . FailureCategoryComments ,
` comment on exported interface method %s.%s should be of the form "%s ..."%s ` , typeName , name , name , status . CorrectionHint ( firstCommentLine ) ,
)
2025-04-07 20:59:55 +02:00
}
2025-05-27 08:44:24 +03:00
// mustCheckMethod returns true if the method must be checked by this rule, false otherwise.
2025-04-07 20:59:55 +02:00
func ( w * lintExported ) mustCheckMethod ( fn * ast . FuncDecl ) bool {
recv := typeparams . ReceiverType ( fn )
if ! ast . IsExported ( recv ) && w . disabledChecks . PrivateReceivers {
return false
2024-07-30 13:36:03 +05:00
}
2025-04-07 20:59:55 +02:00
name := fn . Name . Name
if commonMethods [ name ] {
return false
}
switch name {
case "Len" , "Less" , "Swap" :
sortables := w . file . Pkg . Sortable ( )
if sortables [ recv ] {
return false
}
}
return true
}
func ( w * lintExported ) addFailuref ( node ast . Node , confidence float64 , category lint . FailureCategory , message string , args ... any ) {
w . onFailure ( lint . Failure {
Node : node ,
Confidence : confidence ,
Category : category ,
Failure : fmt . Sprintf ( message , args ... ) ,
} )
2024-07-30 13:36:03 +05:00
}