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  }