github.com/yoheimuta/protolint@v0.49.8-0.20240515023657-4ecaebb7575d/internal/addon/rules/proto3FieldsAvoidRequiredRule.go (about)

     1  package rules
     2  
     3  import (
     4  	"github.com/yoheimuta/go-protoparser/v4/lexer"
     5  	"github.com/yoheimuta/go-protoparser/v4/parser"
     6  	"github.com/yoheimuta/protolint/linter/fixer"
     7  	"github.com/yoheimuta/protolint/linter/report"
     8  	"github.com/yoheimuta/protolint/linter/rule"
     9  	"github.com/yoheimuta/protolint/linter/visitor"
    10  )
    11  
    12  // Proto3FieldsAvoidRequiredRule verifies that all fields should avoid required for proto3.
    13  // See https://developers.google.com/protocol-buffers/docs/style#things-to-avoid
    14  type Proto3FieldsAvoidRequiredRule struct {
    15  	RuleWithSeverity
    16  	fixMode bool
    17  }
    18  
    19  // NewProto3FieldsAvoidRequiredRule creates a new Proto3FieldsAvoidRequiredRule.
    20  func NewProto3FieldsAvoidRequiredRule(
    21  	severity rule.Severity,
    22  	fixMode bool,
    23  ) Proto3FieldsAvoidRequiredRule {
    24  	return Proto3FieldsAvoidRequiredRule{
    25  		RuleWithSeverity: RuleWithSeverity{severity: severity},
    26  		fixMode:          fixMode,
    27  	}
    28  }
    29  
    30  // ID returns the ID of this rule.
    31  func (r Proto3FieldsAvoidRequiredRule) ID() string {
    32  	return "PROTO3_FIELDS_AVOID_REQUIRED"
    33  }
    34  
    35  // Purpose returns the purpose of this rule.
    36  func (r Proto3FieldsAvoidRequiredRule) Purpose() string {
    37  	return "Verifies that all fields should avoid required for proto3."
    38  }
    39  
    40  // IsOfficial decides whether or not this rule belongs to the official guide.
    41  func (r Proto3FieldsAvoidRequiredRule) IsOfficial() bool {
    42  	return true
    43  }
    44  
    45  // Apply applies the rule to the proto.
    46  func (r Proto3FieldsAvoidRequiredRule) Apply(proto *parser.Proto) ([]report.Failure, error) {
    47  	base, err := visitor.NewBaseFixableVisitor(r.ID(), r.fixMode, proto, string(r.Severity()))
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	v := &proto3FieldsAvoidRequiredVisitor{
    53  		BaseFixableVisitor: base,
    54  	}
    55  	return visitor.RunVisitor(v, proto, r.ID())
    56  }
    57  
    58  type proto3FieldsAvoidRequiredVisitor struct {
    59  	*visitor.BaseFixableVisitor
    60  	isProto3 bool
    61  }
    62  
    63  // VisitSyntax checks the syntax.
    64  func (v *proto3FieldsAvoidRequiredVisitor) VisitSyntax(s *parser.Syntax) bool {
    65  	v.isProto3 = s.ProtobufVersion == "proto3"
    66  	return false
    67  }
    68  
    69  // VisitField checks the field.
    70  func (v *proto3FieldsAvoidRequiredVisitor) VisitField(field *parser.Field) bool {
    71  	if v.isProto3 && field.IsRequired {
    72  		v.AddFailuref(field.Meta.Pos, `Field %q should avoid required for proto3`, field.FieldName)
    73  
    74  		err := v.Fixer.SearchAndReplace(field.Meta.Pos, func(lex *lexer.Lexer) fixer.TextEdit {
    75  			lex.NextKeyword()
    76  			return fixer.TextEdit{
    77  				Pos:     lex.Pos.Offset,
    78  				End:     lex.Pos.Offset + len(lex.Text),
    79  				NewText: []byte(""),
    80  			}
    81  		})
    82  		if err != nil {
    83  			panic(err)
    84  		}
    85  	}
    86  	return false
    87  }