1
0
mirror of https://github.com/mgechev/revive.git synced 2025-05-31 22:49:41 +02:00

Refactor/exported (#1299)

* simplify comment analysis by just checking the first comment line with documentation information

* nit refactorings: common func for adding failures, reduce the use of "stutter", and other nitties

* removes use of "stutter" in test code

* move string interpolation into addFailuref
This commit is contained in:
chavacava 2025-04-07 20:59:55 +02:00 committed by GitHub
parent 5f32e8bffa
commit 343da9fbf2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 183 additions and 179 deletions

View File

@ -19,7 +19,7 @@ type disabledChecks struct {
Method bool Method bool
PrivateReceivers bool PrivateReceivers bool
PublicInterfaces bool PublicInterfaces bool
Stuttering bool RepetitiveNames bool
Type bool Type bool
Var bool Var bool
} }
@ -46,7 +46,7 @@ func (dc *disabledChecks) isDisabled(checkName string) bool {
case checkNamePublicInterfaces: case checkNamePublicInterfaces:
return dc.PublicInterfaces return dc.PublicInterfaces
case checkNameStuttering: case checkNameStuttering:
return dc.Stuttering return dc.RepetitiveNames
case "type": case "type":
return dc.Type return dc.Type
default: default:
@ -65,16 +65,16 @@ var commonMethods = map[string]bool{
// ExportedRule lints naming and commenting conventions on exported symbols. // ExportedRule lints naming and commenting conventions on exported symbols.
type ExportedRule struct { type ExportedRule struct {
stuttersMsg string isRepetitiveMsg string
disabledChecks disabledChecks disabledChecks disabledChecks
} }
// Configure validates the rule configuration, and configures the rule accordingly. // Configure validates the rule configuration, and configures the rule accordingly.
// //
// Configuration implements the [lint.ConfigurableRule] interface. // Configure makes the rule implement the [lint.ConfigurableRule] interface.
func (r *ExportedRule) Configure(arguments lint.Arguments) error { func (r *ExportedRule) Configure(arguments lint.Arguments) error {
r.disabledChecks = disabledChecks{PrivateReceivers: true, PublicInterfaces: true} r.disabledChecks = disabledChecks{PrivateReceivers: true, PublicInterfaces: true}
r.stuttersMsg = "stutters" r.isRepetitiveMsg = "stutters"
for _, flag := range arguments { for _, flag := range arguments {
switch flag := flag.(type) { switch flag := flag.(type) {
case string: case string:
@ -82,9 +82,9 @@ func (r *ExportedRule) Configure(arguments lint.Arguments) error {
case isRuleOption(flag, "checkPrivateReceivers"): case isRuleOption(flag, "checkPrivateReceivers"):
r.disabledChecks.PrivateReceivers = false r.disabledChecks.PrivateReceivers = false
case isRuleOption(flag, "disableStutteringCheck"): case isRuleOption(flag, "disableStutteringCheck"):
r.disabledChecks.Stuttering = true r.disabledChecks.RepetitiveNames = true
case isRuleOption(flag, "sayRepetitiveInsteadOfStutters"): case isRuleOption(flag, "sayRepetitiveInsteadOfStutters"):
r.stuttersMsg = "is repetitive" r.isRepetitiveMsg = "is repetitive"
case isRuleOption(flag, "checkPublicInterface"): case isRuleOption(flag, "checkPublicInterface"):
r.disabledChecks.PublicInterfaces = false r.disabledChecks.PublicInterfaces = false
case isRuleOption(flag, "disableChecksOnConstants"): case isRuleOption(flag, "disableChecksOnConstants"):
@ -115,20 +115,17 @@ func (r *ExportedRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
return failures return failures
} }
fileAst := file.AST
walker := lintExported{ walker := lintExported{
file: file, file: file,
fileAst: fileAst,
onFailure: func(failure lint.Failure) { onFailure: func(failure lint.Failure) {
failures = append(failures, failure) failures = append(failures, failure)
}, },
genDeclMissingComments: map[*ast.GenDecl]bool{}, genDeclMissingComments: map[*ast.GenDecl]bool{},
stuttersMsg: r.stuttersMsg, isRepetitiveMsg: r.isRepetitiveMsg,
disabledChecks: r.disabledChecks, disabledChecks: r.disabledChecks,
} }
ast.Walk(&walker, fileAst) ast.Walk(&walker, file.AST)
return failures return failures
} }
@ -140,11 +137,10 @@ func (*ExportedRule) Name() string {
type lintExported struct { type lintExported struct {
file *lint.File file *lint.File
fileAst *ast.File lastGenDecl *ast.GenDecl // the last visited general declaration in the AST
lastGen *ast.GenDecl
genDeclMissingComments map[*ast.GenDecl]bool genDeclMissingComments map[*ast.GenDecl]bool
onFailure func(lint.Failure) onFailure func(lint.Failure)
stuttersMsg string isRepetitiveMsg string
disabledChecks disabledChecks disabledChecks disabledChecks
} }
@ -155,26 +151,13 @@ func (w *lintExported) lintFuncDoc(fn *ast.FuncDecl) {
kind := "function" kind := "function"
name := fn.Name.Name name := fn.Name.Name
isMethod := fn.Recv != nil && len(fn.Recv.List) > 0 if isMethod := fn.Recv != nil && len(fn.Recv.List) > 0; isMethod {
if isMethod { if !w.mustCheckMethod(fn) {
return
}
kind = "method" kind = "method"
recv := typeparams.ReceiverType(fn) recv := typeparams.ReceiverType(fn)
if !ast.IsExported(recv) && w.disabledChecks.PrivateReceivers {
return
}
if commonMethods[name] {
return
}
switch name {
case "Len", "Less", "Swap":
sortables := w.file.Pkg.Sortable()
if sortables[recv] {
return
}
}
name = recv + "." + name name = recv + "." + name
} }
@ -182,42 +165,37 @@ func (w *lintExported) lintFuncDoc(fn *ast.FuncDecl) {
return return
} }
if !hasDocComment(fn.Doc) { firstCommentLine := firstCommentLine(fn.Doc)
w.onFailure(lint.Failure{
Node: fn, if firstCommentLine == "" {
Confidence: 1, w.addFailuref(fn, 1, lint.FailureCategoryComments,
Category: lint.FailureCategoryComments, "exported %s %s should have comment or be unexported", kind, name,
Failure: fmt.Sprintf("exported %s %s should have comment or be unexported", kind, name), )
})
return return
} }
s := normalizeText(fn.Doc.Text())
prefix := fn.Name.Name + " " prefix := fn.Name.Name + " "
if !strings.HasPrefix(s, prefix) { if !strings.HasPrefix(firstCommentLine, prefix) {
w.onFailure(lint.Failure{ w.addFailuref(fn.Doc, 0.8, lint.FailureCategoryComments,
Node: fn.Doc, `comment on exported %s %s should be of the form "%s..."`, kind, name, prefix,
Confidence: 0.8, )
Category: lint.FailureCategoryComments,
Failure: fmt.Sprintf(`comment on exported %s %s should be of the form "%s..."`, kind, name, prefix),
})
} }
} }
func (w *lintExported) checkStutter(id *ast.Ident, thing string) { func (w *lintExported) checkRepetitiveNames(id *ast.Ident, thing string) {
if w.disabledChecks.Stuttering { if w.disabledChecks.RepetitiveNames {
return return
} }
pkg, name := w.fileAst.Name.Name, id.Name pkg, name := w.file.AST.Name.Name, id.Name
if !ast.IsExported(name) { if !ast.IsExported(name) {
// unexported name // unexported name
return return
} }
// A name stutters if the package name is a strict prefix // A name is repetitive if the package name is a strict prefix
// and the next character of the name starts a new word. // and the next character of the name starts a new word.
if len(name) <= len(pkg) { if len(name) <= len(pkg) {
// name is too short to stutter. // name is too short to be a repetition.
// This permits the name to be the same as the package name. // This permits the name to be the same as the package name.
return return
} }
@ -226,63 +204,74 @@ func (w *lintExported) checkStutter(id *ast.Ident, thing string) {
} }
// We can assume the name is well-formed UTF-8. // We can assume the name is well-formed UTF-8.
// If the next rune after the package name is uppercase or an underscore // If the next rune after the package name is uppercase or an underscore
// the it's starting a new word and thus this name stutters. // the it's starting a new word and thus this name is repetitive.
rem := name[len(pkg):] rem := name[len(pkg):]
if next, _ := utf8.DecodeRuneInString(rem); next == '_' || unicode.IsUpper(next) { if next, _ := utf8.DecodeRuneInString(rem); next == '_' || unicode.IsUpper(next) {
w.onFailure(lint.Failure{ w.addFailuref(id, 0.8, lint.FailureCategoryNaming,
Node: id, "%s name will be used as %s.%s by other packages, and that %s; consider calling this %s", thing, pkg, name, w.isRepetitiveMsg, rem,
Confidence: 0.8, )
Category: lint.FailureCategoryNaming,
Failure: fmt.Sprintf("%s name will be used as %s.%s by other packages, and that %s; consider calling this %s", thing, pkg, name, w.stuttersMsg, rem),
})
} }
} }
func (w *lintExported) lintTypeDoc(t *ast.TypeSpec, doc *ast.CommentGroup) { var articles = [...]string{"A", "An", "The", "This"}
func (w *lintExported) lintTypeDoc(t *ast.TypeSpec, doc *ast.CommentGroup, firstCommentLine string) {
if w.disabledChecks.isDisabled("type") { if w.disabledChecks.isDisabled("type") {
return return
} }
if !ast.IsExported(t.Name.Name) { typeName := t.Name.Name
if !ast.IsExported(typeName) {
return return
} }
if !hasDocComment(doc) { if firstCommentLine == "" {
w.onFailure(lint.Failure{ w.addFailuref(t, 1, lint.FailureCategoryComments,
Node: t, "exported type %v should have comment or be unexported", t.Name,
Confidence: 1, )
Category: lint.FailureCategoryComments,
Failure: fmt.Sprintf("exported type %v should have comment or be unexported", t.Name),
})
return return
} }
s := normalizeText(doc.Text())
articles := [...]string{"A", "An", "The", "This"}
for _, a := range articles { for _, a := range articles {
if t.Name.Name == a { if typeName == a {
continue continue
} }
var found bool var found bool
if s, found = strings.CutPrefix(s, a+" "); found { if firstCommentLine, found = strings.CutPrefix(firstCommentLine, a+" "); found {
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 := typeName + " "
if strings.HasPrefix(s, expectedPrefix) { if strings.HasPrefix(firstCommentLine, expectedPrefix) {
return return
} }
w.onFailure(lint.Failure{ w.addFailuref(doc, 1, lint.FailureCategoryComments,
Node: doc, `comment on exported type %v should be of the form "%s..." (with optional leading article)`, t.Name, expectedPrefix,
Confidence: 1, )
Category: lint.FailureCategoryComments,
Failure: fmt.Sprintf(`comment on exported type %v should be of the form "%s..." (with optional leading article)`, t.Name, expectedPrefix),
})
} }
// checkValueNames returns true if names check, false otherwise
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
}
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) {
kind := "var" kind := "var"
if gd.Tok == token.CONST { if gd.Tok == token.CONST {
@ -293,19 +282,8 @@ func (w *lintExported) lintValueSpecDoc(vs *ast.ValueSpec, gd *ast.GenDecl, genD
return return
} }
if len(vs.Names) > 1 { if !w.checkValueNames(vs.Names, vs, kind) {
// Check that none are exported except for the first. return
for _, n := range vs.Names[1:] {
if ast.IsExported(n.Name) {
w.onFailure(lint.Failure{
Category: lint.FailureCategoryComments,
Confidence: 1,
Failure: fmt.Sprintf("exported %s %s should have its own declaration", kind, n.Name),
Node: vs,
})
return
}
}
} }
// Only one name. // Only one name.
@ -314,7 +292,9 @@ func (w *lintExported) lintValueSpecDoc(vs *ast.ValueSpec, gd *ast.GenDecl, genD
return return
} }
if !hasDocComment(vs.Doc) && !hasDocComment(gd.Doc) { vsFirstCommentLine := firstCommentLine(vs.Doc)
gdFirstCommentLine := firstCommentLine(gd.Doc)
if vsFirstCommentLine == "" && gdFirstCommentLine == "" {
if genDeclMissingComments[gd] { if genDeclMissingComments[gd] {
return return
} }
@ -322,99 +302,95 @@ func (w *lintExported) lintValueSpecDoc(vs *ast.ValueSpec, gd *ast.GenDecl, genD
if kind == "const" && gd.Lparen.IsValid() { if kind == "const" && gd.Lparen.IsValid() {
block = " (or a comment on this block)" block = " (or a comment on this block)"
} }
w.onFailure(lint.Failure{ w.addFailuref(vs, 1, lint.FailureCategoryComments,
Confidence: 1, "exported %s %s should have comment%s or be unexported", kind, name, block,
Node: vs, )
Category: lint.FailureCategoryComments,
Failure: fmt.Sprintf("exported %s %s should have comment%s or be unexported", kind, name, block),
})
genDeclMissingComments[gd] = true genDeclMissingComments[gd] = true
return return
} }
// If this GenDecl has parens and a comment, we don't check its comment form. // If this GenDecl has parens and a comment, we don't check its comment form.
if hasDocComment(gd.Doc) && gd.Lparen.IsValid() { if gdFirstCommentLine != "" && gd.Lparen.IsValid() {
return return
} }
// The relevant text to check will be on either vs.Doc or gd.Doc. // The relevant text to check will be on either vs.Doc or gd.Doc.
// Use vs.Doc preferentially. // Use vs.Doc preferentially.
var doc *ast.CommentGroup var doc *ast.CommentGroup
switch { switch {
case hasDocComment(vs.Doc): case vsFirstCommentLine != "":
doc = vs.Doc doc = vs.Doc
case hasDocComment(vs.Comment) && !hasDocComment(gd.Doc): case vsFirstCommentLine != "" && gdFirstCommentLine == "":
doc = vs.Comment doc = vs.Comment
default: default:
doc = gd.Doc doc = gd.Doc
} }
prefix := name + " " prefix := name + " "
s := normalizeText(doc.Text()) if !strings.HasPrefix(firstCommentLine(doc), prefix) {
if !strings.HasPrefix(s, prefix) { w.addFailuref(doc, 1, lint.FailureCategoryComments,
w.onFailure(lint.Failure{ `comment on exported %s %s should be of the form "%s..."`, kind, name, prefix,
Confidence: 1, )
Node: doc,
Category: lint.FailureCategoryComments,
Failure: fmt.Sprintf(`comment on exported %s %s should be of the form "%s..."`, kind, name, prefix),
})
} }
} }
// hasDocComment reports whether the comment group contains a documentation comment, // firstCommentLine yields the first line of interest in comment group or "" if there is nothing of interest.
// excluding directive comments and those consisting solely of a deprecation notice. // An "interesting line" is a comment line that is neither a directive (e.g. //go:...) or a deprecation comment
// e.g. //go:embed foo.txt a directive comment, not a text comment // (lines from the first line with a prefix // Deprecated: to the end of the comment group)
// e.g. //nolint:whatever is a directive comment, not a text comment // Empty or spaces-only lines are discarded.
// e.g. // Deprecated: this is a deprecation comment func firstCommentLine(comment *ast.CommentGroup) (result string) {
func hasDocComment(comment *ast.CommentGroup) bool {
if comment == nil { if comment == nil {
return false return ""
} }
// a comment could be directive and not a text comment commentWithoutDirectives := comment.Text() // removes directives from the comment block
text := comment.Text() // removes directives from the comment block lines := strings.Split(commentWithoutDirectives, "\n")
return text != "" && !isOnlyDeprecationComment(text) 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
}
// isOnlyDeprecationComment returns true if the comment starts with a standard deprecation notice. result = line
// It considers all paragraphs following the deprecation notice as part of the deprecation comment. break // first non-directive/non-empty/non-deprecation comment line found
// Assumes the comment is following the general ordering convention: (doc comment + deprecation) }
func isOnlyDeprecationComment(comment string) bool {
return strings.HasPrefix(comment, "Deprecated: ")
}
// normalizeText is a helper function that normalizes comment strings by: return result
// * removing one leading space
//
// This function is needed because ast.CommentGroup.Text() does not handle //-style and /*-style comments uniformly
func normalizeText(t string) string {
return strings.TrimSpace(t)
} }
func (w *lintExported) Visit(n ast.Node) ast.Visitor { func (w *lintExported) Visit(n ast.Node) ast.Visitor {
switch v := n.(type) { switch v := n.(type) {
case *ast.GenDecl: case *ast.GenDecl:
if v.Tok == token.IMPORT { switch v.Tok {
case token.IMPORT:
return nil return nil
case token.CONST, token.TYPE, token.VAR:
w.lastGenDecl = v
} }
// token.CONST, token.TYPE or token.VAR
w.lastGen = v
return w return w
case *ast.FuncDecl: case *ast.FuncDecl:
w.lintFuncDoc(v) w.lintFuncDoc(v)
if v.Recv == nil { if v.Recv == nil {
// Only check for stutter on functions, not methods. // Only check for repetitive names on functions, not methods.
// Method names are not used package-qualified. // Method names are not used package-qualified.
w.checkStutter(v.Name, "func") w.checkRepetitiveNames(v.Name, "func")
} }
// Don't proceed inside funcs. // Don't proceed inside funcs.
return nil return nil
case *ast.TypeSpec: case *ast.TypeSpec:
// inside a GenDecl, which usually has the doc // inside a GenDecl, which usually has the doc
doc := v.Doc doc := v.Doc
if !hasDocComment(doc) {
doc = w.lastGen.Doc fcl := firstCommentLine(doc)
if fcl == "" {
doc = w.lastGenDecl.Doc
fcl = firstCommentLine(doc)
} }
w.lintTypeDoc(v, doc) w.lintTypeDoc(v, doc, fcl)
w.checkStutter(v.Name, "type") w.checkRepetitiveNames(v.Name, "type")
if !w.disabledChecks.PublicInterfaces { if !w.disabledChecks.PublicInterfaces {
if iface, ok := v.Type.(*ast.InterfaceType); ok { if iface, ok := v.Type.(*ast.InterfaceType); ok {
@ -426,7 +402,7 @@ func (w *lintExported) Visit(n ast.Node) ast.Visitor {
return nil return nil
case *ast.ValueSpec: case *ast.ValueSpec:
w.lintValueSpecDoc(v, w.lastGen, w.genDeclMissingComments) w.lintValueSpecDoc(v, w.lastGenDecl, w.genDeclMissingComments)
return nil return nil
} }
return w return w
@ -446,23 +422,51 @@ func (w *lintExported) lintInterfaceMethod(typeName string, m *ast.Field) {
return return
} }
name := m.Names[0].Name name := m.Names[0].Name
if !hasDocComment(m.Doc) { firstCommentLine := firstCommentLine(m.Doc)
w.onFailure(lint.Failure{ if firstCommentLine == "" {
Node: m, w.addFailuref(m, 1, lint.FailureCategoryComments,
Confidence: 1, "public interface method %s.%s should be commented", typeName, name,
Category: lint.FailureCategoryComments, )
Failure: fmt.Sprintf("public interface method %s.%s should be commented", typeName, name),
})
return return
} }
s := normalizeText(m.Doc.Text())
expectedPrefix := m.Names[0].Name + " " expectedPrefix := m.Names[0].Name + " "
if !strings.HasPrefix(s, expectedPrefix) { if !strings.HasPrefix(firstCommentLine, expectedPrefix) {
w.onFailure(lint.Failure{ w.addFailuref(m.Doc, 0.8, lint.FailureCategoryComments,
Node: m.Doc, `comment on exported interface method %s.%s should be of the form "%s..."`, typeName, name, expectedPrefix,
Confidence: 0.8, )
Category: lint.FailureCategoryComments,
Failure: fmt.Sprintf(`comment on exported interface method %s.%s should be of the form "%s..."`, typeName, name, expectedPrefix),
})
} }
} }
// mustCheckMethod returns true if the method must be checked by this rule, false otherwise
func (w *lintExported) mustCheckMethod(fn *ast.FuncDecl) bool {
recv := typeparams.ReceiverType(fn)
if !ast.IsExported(recv) && w.disabledChecks.PrivateReceivers {
return false
}
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...),
})
}

View File

@ -9,11 +9,11 @@ import (
func TestExportedRule_Configure(t *testing.T) { func TestExportedRule_Configure(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
arguments lint.Arguments arguments lint.Arguments
wantErr error wantErr error
wantDisabledChecks disabledChecks wantDisabledChecks disabledChecks
wantStuttersMsg string wantIsRepetitiveMsg string
}{ }{
{ {
name: "default configuration", name: "default configuration",
@ -23,7 +23,7 @@ func TestExportedRule_Configure(t *testing.T) {
PrivateReceivers: true, PrivateReceivers: true,
PublicInterfaces: true, PublicInterfaces: true,
}, },
wantStuttersMsg: "stutters", wantIsRepetitiveMsg: "stutters",
}, },
{ {
name: "valid arguments", name: "valid arguments",
@ -44,11 +44,11 @@ func TestExportedRule_Configure(t *testing.T) {
Const: true, Const: true,
Function: true, Function: true,
Method: true, Method: true,
Stuttering: true, RepetitiveNames: true,
Type: true, Type: true,
Var: true, Var: true,
}, },
wantStuttersMsg: "stutters", wantIsRepetitiveMsg: "stutters",
}, },
{ {
name: "valid lowercased arguments", name: "valid lowercased arguments",
@ -69,11 +69,11 @@ func TestExportedRule_Configure(t *testing.T) {
Const: true, Const: true,
Function: true, Function: true,
Method: true, Method: true,
Stuttering: true, RepetitiveNames: true,
Type: true, Type: true,
Var: true, Var: true,
}, },
wantStuttersMsg: "stutters", wantIsRepetitiveMsg: "stutters",
}, },
{ {
name: "valid kebab-cased arguments", name: "valid kebab-cased arguments",
@ -94,11 +94,11 @@ func TestExportedRule_Configure(t *testing.T) {
Const: true, Const: true,
Function: true, Function: true,
Method: true, Method: true,
Stuttering: true, RepetitiveNames: true,
Type: true, Type: true,
Var: true, Var: true,
}, },
wantStuttersMsg: "stutters", wantIsRepetitiveMsg: "stutters",
}, },
{ {
name: "valid sayRepetitiveInsteadOfStutters", name: "valid sayRepetitiveInsteadOfStutters",
@ -110,7 +110,7 @@ func TestExportedRule_Configure(t *testing.T) {
PrivateReceivers: true, PrivateReceivers: true,
PublicInterfaces: true, PublicInterfaces: true,
}, },
wantStuttersMsg: "is repetitive", wantIsRepetitiveMsg: "is repetitive",
}, },
{ {
name: "valid lowercased sayRepetitiveInsteadOfStutters", name: "valid lowercased sayRepetitiveInsteadOfStutters",
@ -122,7 +122,7 @@ func TestExportedRule_Configure(t *testing.T) {
PrivateReceivers: true, PrivateReceivers: true,
PublicInterfaces: true, PublicInterfaces: true,
}, },
wantStuttersMsg: "is repetitive", wantIsRepetitiveMsg: "is repetitive",
}, },
{ {
name: "valid kebab-cased sayRepetitiveInsteadOfStutters", name: "valid kebab-cased sayRepetitiveInsteadOfStutters",
@ -134,7 +134,7 @@ func TestExportedRule_Configure(t *testing.T) {
PrivateReceivers: true, PrivateReceivers: true,
PublicInterfaces: true, PublicInterfaces: true,
}, },
wantStuttersMsg: "is repetitive", wantIsRepetitiveMsg: "is repetitive",
}, },
{ {
name: "unknown configuration flag", name: "unknown configuration flag",
@ -170,8 +170,8 @@ func TestExportedRule_Configure(t *testing.T) {
if rule.disabledChecks != tt.wantDisabledChecks { if rule.disabledChecks != tt.wantDisabledChecks {
t.Errorf("unexpected disabledChecks: got = %+v, want %+v", rule.disabledChecks, tt.wantDisabledChecks) t.Errorf("unexpected disabledChecks: got = %+v, want %+v", rule.disabledChecks, tt.wantDisabledChecks)
} }
if rule.stuttersMsg != tt.wantStuttersMsg { if rule.isRepetitiveMsg != tt.wantIsRepetitiveMsg {
t.Errorf("unexpected stuttersMsg: got = %v, want %v", rule.stuttersMsg, tt.wantStuttersMsg) t.Errorf("unexpected stuttersMsg: got = %v, want %v", rule.isRepetitiveMsg, tt.wantIsRepetitiveMsg)
} }
}) })
} }