2018-11-07 09:11:08 +03:00

140 lines
3.8 KiB
Go

package checkers
import (
"go/ast"
"regexp"
"strings"
"github.com/go-lintpack/lintpack"
"github.com/go-lintpack/lintpack/astwalk"
)
func init() {
var info lintpack.CheckerInfo
info.Name = "deprecatedComment"
info.Tags = []string{"diagnostic", "experimental"}
info.Summary = "Detects malformed 'deprecated' doc-comments"
info.Before = `
// deprecated, use FuncNew instead
func FuncOld() int`
info.After = `
// Deprecated: use FuncNew instead
func FuncOld() int`
lintpack.AddChecker(&info, func(ctx *lintpack.CheckerContext) lintpack.FileWalker {
c := &deprecatedCommentChecker{ctx: ctx}
c.commonPatterns = []*regexp.Regexp{
regexp.MustCompile(`(?i)this (?:function|type) is deprecated`),
regexp.MustCompile(`(?i)deprecated[.!]? use \S* instead`),
// TODO(quasilyte): more of these?
}
// TODO(quasilyte): may want to generate this list programmatically.
//
// TODO(quasilyte): currently it only handles a single missing letter.
// Might want to handle other kinds of common misspell/typo kinds.
c.commonTypos = []string{
"Dprecated: ",
"Derecated: ",
"Depecated: ",
"Deprcated: ",
"Depreated: ",
"Deprected: ",
"Deprecaed: ",
"Deprecatd: ",
"Deprecate: ",
"Derpecate: ",
"Derpecated: ",
}
for i := range c.commonTypos {
c.commonTypos[i] = strings.ToUpper(c.commonTypos[i])
}
return astwalk.WalkerForDocComment(c)
})
}
type deprecatedCommentChecker struct {
astwalk.WalkHandler
ctx *lintpack.CheckerContext
commonPatterns []*regexp.Regexp
commonTypos []string
}
func (c *deprecatedCommentChecker) VisitDocComment(doc *ast.CommentGroup) {
// There are 3 accepted forms of deprecation comments:
//
// 1. inline, that can't be handled with a DocCommentVisitor.
// Note that "Deprecated: " may not even be the comment prefix there.
// Example: "The line number in the input. Deprecated: Kept for compatibility."
// TODO(quasilyte): fix it.
//
// 2. Longer form-1. It's a doc-comment that only contains "deprecation" notice.
//
// 3. Like form-2, but may also include doc-comment text.
// Distinguished by an empty line.
//
// See https://github.com/golang/go/issues/10909#issuecomment-136492606.
//
// It's desirable to see how people make mistakes with the format,
// this is why there is currently no special treatment for these cases.
// TODO(quasilyte): do more audits and grow the negative tests suite.
//
// TODO(quasilyte): there are also multi-line deprecation comments.
for _, l := range strings.Split(doc.Text(), "\n") {
if len(l) < len("Deprecated: ") {
continue
}
// Check whether someone messed up with a prefix casing.
upcase := strings.ToUpper(l)
if strings.HasPrefix(upcase, "DEPRECATED: ") && !strings.HasPrefix(l, "Deprecated: ") {
c.warnCasing(doc, l)
return
}
// Check is someone used comma instead of a colon.
if strings.HasPrefix(l, "Deprecated, ") {
c.warnComma(doc)
return
}
// Check for other commonly used patterns.
for _, pat := range c.commonPatterns {
if pat.MatchString(l) {
c.warnPattern(doc)
return
}
}
// Detect some simple typos.
for _, prefixWithTypo := range c.commonTypos {
if strings.HasPrefix(upcase, prefixWithTypo) {
c.warnTypo(doc, l)
return
}
}
}
}
func (c *deprecatedCommentChecker) warnCasing(cause ast.Node, line string) {
prefix := line[:len("DEPRECATED: ")]
c.ctx.Warn(cause, "use `Deprecated: ` (note the casing) instead of `%s`", prefix)
}
func (c *deprecatedCommentChecker) warnPattern(cause ast.Node) {
c.ctx.Warn(cause, "the proper format is `Deprecated: <text>`")
}
func (c *deprecatedCommentChecker) warnComma(cause ast.Node) {
c.ctx.Warn(cause, "use `:` instead of `,` in `Deprecated, `")
}
func (c *deprecatedCommentChecker) warnTypo(cause ast.Node, line string) {
word := strings.Split(line, ":")[0]
c.ctx.Warn(cause, "typo in `%s`; should be `Deprecated`", word)
}