Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
121 lines
3.2 KiB
121 lines
3.2 KiB
package rule |
|
|
|
import ( |
|
"fmt" |
|
"go/ast" |
|
"go/token" |
|
"strings" |
|
|
|
"github.com/mgechev/revive/lint" |
|
) |
|
|
|
// PackageCommentsRule lints the package comments. It complains if |
|
// there is no package comment, or if it is not of the right form. |
|
// This has a notable false positive in that a package comment |
|
// could rightfully appear in a different file of the same package, |
|
// but that's not easy to fix since this linter is file-oriented. |
|
type PackageCommentsRule struct{} |
|
|
|
// Apply applies the rule to given file. |
|
func (r *PackageCommentsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { |
|
var failures []lint.Failure |
|
|
|
if isTest(file) { |
|
return failures |
|
} |
|
|
|
onFailure := func(failure lint.Failure) { |
|
failures = append(failures, failure) |
|
} |
|
|
|
fileAst := file.AST |
|
w := &lintPackageComments{fileAst, file, onFailure} |
|
ast.Walk(w, fileAst) |
|
return failures |
|
} |
|
|
|
// Name returns the rule name. |
|
func (r *PackageCommentsRule) Name() string { |
|
return "package-comments" |
|
} |
|
|
|
type lintPackageComments struct { |
|
fileAst *ast.File |
|
file *lint.File |
|
onFailure func(lint.Failure) |
|
} |
|
|
|
func (l *lintPackageComments) Visit(_ ast.Node) ast.Visitor { |
|
if l.file.IsTest() { |
|
return nil |
|
} |
|
|
|
const ref = styleGuideBase + "#package-comments" |
|
prefix := "Package " + l.fileAst.Name.Name + " " |
|
|
|
// Look for a detached package comment. |
|
// First, scan for the last comment that occurs before the "package" keyword. |
|
var lastCG *ast.CommentGroup |
|
for _, cg := range l.fileAst.Comments { |
|
if cg.Pos() > l.fileAst.Package { |
|
// Gone past "package" keyword. |
|
break |
|
} |
|
lastCG = cg |
|
} |
|
if lastCG != nil && strings.HasPrefix(lastCG.Text(), prefix) { |
|
endPos := l.file.ToPosition(lastCG.End()) |
|
pkgPos := l.file.ToPosition(l.fileAst.Package) |
|
if endPos.Line+1 < pkgPos.Line { |
|
// There isn't a great place to anchor this error; |
|
// the start of the blank lines between the doc and the package statement |
|
// is at least pointing at the location of the problem. |
|
pos := token.Position{ |
|
Filename: endPos.Filename, |
|
// Offset not set; it is non-trivial, and doesn't appear to be needed. |
|
Line: endPos.Line + 1, |
|
Column: 1, |
|
} |
|
l.onFailure(lint.Failure{ |
|
Category: "comments", |
|
Position: lint.FailurePosition{ |
|
Start: pos, |
|
End: pos, |
|
}, |
|
Confidence: 0.9, |
|
Failure: "package comment is detached; there should be no blank lines between it and the package statement", |
|
}) |
|
return nil |
|
} |
|
} |
|
|
|
if l.fileAst.Doc == nil { |
|
l.onFailure(lint.Failure{ |
|
Category: "comments", |
|
Node: l.fileAst, |
|
Confidence: 0.2, |
|
Failure: "should have a package comment, unless it's in another file for this package", |
|
}) |
|
return nil |
|
} |
|
s := l.fileAst.Doc.Text() |
|
if ts := strings.TrimLeft(s, " \t"); ts != s { |
|
l.onFailure(lint.Failure{ |
|
Category: "comments", |
|
Node: l.fileAst.Doc, |
|
Confidence: 1, |
|
Failure: "package comment should not have leading space", |
|
}) |
|
s = ts |
|
} |
|
// Only non-main packages need to keep to this form. |
|
if !l.file.Pkg.IsMain() && !strings.HasPrefix(s, prefix) { |
|
l.onFailure(lint.Failure{ |
|
Category: "comments", |
|
Node: l.fileAst.Doc, |
|
Confidence: 1, |
|
Failure: fmt.Sprintf(`package comment should be of the form "%s..."`, prefix), |
|
}) |
|
} |
|
return nil |
|
}
|
|
|