github.com/yoheimuta/protolint@v0.49.8-0.20240515023657-4ecaebb7575d/internal/addon/rules/messagesHaveCommentRule.go (about) 1 package rules 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/yoheimuta/go-protoparser/v4/parser" 8 9 "github.com/yoheimuta/protolint/linter/report" 10 "github.com/yoheimuta/protolint/linter/rule" 11 "github.com/yoheimuta/protolint/linter/visitor" 12 ) 13 14 // MessagesHaveCommentRule verifies that all messages have a comment. 15 type MessagesHaveCommentRule struct { 16 RuleWithSeverity 17 // Golang style comments should begin with the name of the thing being described. 18 // See https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences 19 shouldFollowGolangStyle bool 20 } 21 22 // NewMessagesHaveCommentRule creates a new MessagesHaveCommentRule. 23 func NewMessagesHaveCommentRule( 24 severity rule.Severity, 25 shouldFollowGolangStyle bool, 26 ) MessagesHaveCommentRule { 27 return MessagesHaveCommentRule{ 28 RuleWithSeverity: RuleWithSeverity{severity: severity}, 29 shouldFollowGolangStyle: shouldFollowGolangStyle, 30 } 31 } 32 33 // ID returns the ID of this rule. 34 func (r MessagesHaveCommentRule) ID() string { 35 return "MESSAGES_HAVE_COMMENT" 36 } 37 38 // Purpose returns the purpose of this rule. 39 func (r MessagesHaveCommentRule) Purpose() string { 40 return "Verifies that all messages have a comment." 41 } 42 43 // IsOfficial decides whether or not this rule belongs to the official guide. 44 func (r MessagesHaveCommentRule) IsOfficial() bool { 45 return false 46 } 47 48 // Apply applies the rule to the proto. 49 func (r MessagesHaveCommentRule) Apply(proto *parser.Proto) ([]report.Failure, error) { 50 v := &messagesHaveCommentVisitor{ 51 BaseAddVisitor: visitor.NewBaseAddVisitor(r.ID(), string(r.Severity())), 52 shouldFollowGolangStyle: r.shouldFollowGolangStyle, 53 } 54 return visitor.RunVisitor(v, proto, r.ID()) 55 } 56 57 type messagesHaveCommentVisitor struct { 58 *visitor.BaseAddVisitor 59 shouldFollowGolangStyle bool 60 } 61 62 // VisitMessage checks the message. 63 func (v *messagesHaveCommentVisitor) VisitMessage(message *parser.Message) bool { 64 n := message.MessageName 65 if v.shouldFollowGolangStyle && !hasGolangStyleComment(message.Comments, n) { 66 v.AddFailuref(message.Meta.Pos, `Message %q should have a comment of the form "// %s ..."`, n, n) 67 } else if !hasComments(message.Comments, message.InlineComment, message.InlineCommentBehindLeftCurly) { 68 v.AddFailuref(message.Meta.Pos, `Message %q should have a comment`, n) 69 } 70 return true 71 } 72 73 func hasGolangStyleComment( 74 comments []*parser.Comment, 75 describedName string, 76 ) bool { 77 return hasComment(comments) && 78 strings.HasPrefix(comments[0].Lines()[0], fmt.Sprintf(" %s", describedName)) 79 } 80 81 func hasComment(comments []*parser.Comment) bool { 82 return 0 < len(comments) 83 } 84 85 func hasComments(comments []*parser.Comment, inlines ...*parser.Comment) bool { 86 if 0 < len(comments) { 87 return true 88 } 89 for _, inline := range inlines { 90 if inline != nil { 91 return true 92 } 93 } 94 return false 95 }