Skip to content

Commit 387d727

Browse files
authored
Make package comment more confident (#694)
1 parent dc30eb1 commit 387d727

File tree

3 files changed

+58
-10
lines changed

3 files changed

+58
-10
lines changed

lint/package.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ var (
3636
notSet = 3
3737
)
3838

39+
// Files return package's files.
40+
func (p *Package) Files() map[string]*File {
41+
return p.files
42+
}
43+
3944
// IsMain returns if that's the main package.
4045
func (p *Package) IsMain() bool {
4146
p.Lock()

rule/package-comments.go

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"go/ast"
66
"go/token"
77
"strings"
8+
"sync"
89

910
"github.com/mgechev/revive/lint"
1011
)
@@ -14,10 +15,12 @@ import (
1415
// This has a notable false positive in that a package comment
1516
// could rightfully appear in a different file of the same package,
1617
// but that's not easy to fix since this linter is file-oriented.
17-
type PackageCommentsRule struct{}
18+
type PackageCommentsRule struct {
19+
checkPackageCommentCache sync.Map
20+
}
1821

1922
// Apply applies the rule to given file.
20-
func (*PackageCommentsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
23+
func (r *PackageCommentsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
2124
var failures []lint.Failure
2225

2326
if file.IsTest() {
@@ -29,7 +32,7 @@ func (*PackageCommentsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Fail
2932
}
3033

3134
fileAst := file.AST
32-
w := &lintPackageComments{fileAst, file, onFailure}
35+
w := &lintPackageComments{fileAst, file, onFailure, r}
3336
ast.Walk(w, fileAst)
3437
return failures
3538
}
@@ -43,6 +46,49 @@ type lintPackageComments struct {
4346
fileAst *ast.File
4447
file *lint.File
4548
onFailure func(lint.Failure)
49+
rule *PackageCommentsRule
50+
}
51+
52+
func (l *lintPackageComments) checkPackageComment() []lint.Failure {
53+
// deduplicate warnings in package
54+
if _, exists := l.rule.checkPackageCommentCache.LoadOrStore(l.file.Pkg, struct{}{}); exists {
55+
return nil
56+
}
57+
var docFile *ast.File // which name is doc.go
58+
var packageFile *ast.File // which name is $package.go
59+
var firstFile *ast.File
60+
var firstFileName string
61+
for name, file := range l.file.Pkg.Files() {
62+
if file.AST.Doc != nil {
63+
return nil
64+
}
65+
if name == "doc.go" {
66+
docFile = file.AST
67+
}
68+
if name == file.AST.Name.String()+".go" {
69+
packageFile = file.AST
70+
}
71+
if firstFileName == "" || firstFileName > name {
72+
firstFile = file.AST
73+
firstFileName = name
74+
}
75+
}
76+
// prefer warning on doc.go, $package.go over first file
77+
if docFile == nil {
78+
docFile = packageFile
79+
}
80+
if docFile == nil {
81+
docFile = firstFile
82+
}
83+
if docFile != nil {
84+
return []lint.Failure{{
85+
Category: "comments",
86+
Node: docFile,
87+
Confidence: 1,
88+
Failure: "should have a package comment",
89+
}}
90+
}
91+
return nil
4692
}
4793

4894
func (l *lintPackageComments) Visit(_ ast.Node) ast.Visitor {
@@ -89,12 +135,9 @@ func (l *lintPackageComments) Visit(_ ast.Node) ast.Visitor {
89135
}
90136

91137
if l.fileAst.Doc == nil {
92-
l.onFailure(lint.Failure{
93-
Category: "comments",
94-
Node: l.fileAst,
95-
Confidence: 0.2,
96-
Failure: "should have a package comment, unless it's in another file for this package",
97-
})
138+
for _, failure := range l.checkPackageComment() {
139+
l.onFailure(failure)
140+
}
98141
return nil
99142
}
100143
s := l.fileAst.Doc.Text()

testdata/golint/package-doc1.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
// Test of missing package comment.
22

3-
package foo // MATCH /should have a package comment, unless it's in another file for this package/
3+
package foo // MATCH /should have a package comment/

0 commit comments

Comments
 (0)