github.com/googleapis/api-linter@v1.65.2/lint/rule_enabled.go (about)

     1  // Copyright 2019 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  // 		https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package lint
    16  
    17  import (
    18  	"strings"
    19  
    20  	"github.com/jhump/protoreflect/desc"
    21  	dpb "google.golang.org/protobuf/types/descriptorpb"
    22  )
    23  
    24  // defaultDisabledRules is the list of rules or groups that are by default
    25  // disabled, because they are scoped to a very specific set of AIPs.
    26  var defaultDisabledRules = []string{"cloud"}
    27  
    28  // Disable all rules for deprecated descriptors.
    29  func disableDeprecated(d desc.Descriptor) bool {
    30  	switch v := d.(type) {
    31  	case *desc.EnumDescriptor:
    32  		return v.GetEnumOptions().GetDeprecated()
    33  	case *desc.EnumValueDescriptor:
    34  		return v.GetEnumValueOptions().GetDeprecated()
    35  	case *desc.FieldDescriptor:
    36  		return v.GetFieldOptions().GetDeprecated()
    37  	case *desc.FileDescriptor:
    38  		return v.GetFileOptions().GetDeprecated()
    39  	case *desc.MessageDescriptor:
    40  		return v.GetMessageOptions().GetDeprecated()
    41  	case *desc.MethodDescriptor:
    42  		return v.GetMethodOptions().GetDeprecated()
    43  	case *desc.ServiceDescriptor:
    44  		return v.GetServiceOptions().GetDeprecated()
    45  	}
    46  	return false
    47  }
    48  
    49  // Provide methods that are able to disable rules.
    50  //
    51  // This pattern is a hook for internal extension, by creating an additional
    52  // file in this package that can add an additional check:
    53  //
    54  //	func disableForInternalReason(d desc.Descriptor) bool { ... }
    55  //
    56  //	func init() {
    57  //	  descriptorDisableChecks = append(descriptorDisableChecks, disableForInternalReason)
    58  //	}
    59  var descriptorDisableChecks = []func(d desc.Descriptor) bool{
    60  	disableDeprecated,
    61  }
    62  
    63  // ruleIsEnabled returns true if the rule is enabled (not disabled by the comments
    64  // for the given descriptor or its file), false otherwise.
    65  //
    66  // Note, if the given source code location is not nil, it will be used to
    67  // augment the set of commentLines.
    68  func ruleIsEnabled(rule ProtoRule, d desc.Descriptor, l *dpb.SourceCodeInfo_Location,
    69  	aliasMap map[string]string, ignoreCommentDisables bool) bool {
    70  	// If the rule is disabled because of something on the descriptor itself
    71  	// (e.g. a deprecated annotation), address that.
    72  	for _, mustDisable := range descriptorDisableChecks {
    73  		// The only thing the disable functions can do is force a rule to
    74  		// be disabled. (They can not force a rule to be enabled.)
    75  		if mustDisable(d) {
    76  			return false
    77  		}
    78  	}
    79  
    80  	if !ignoreCommentDisables {
    81  		if ruleIsDisabledByComments(rule, d, l, aliasMap) {
    82  			return false
    83  		}
    84  	}
    85  
    86  	// The rule may have been disabled on a parent. (For example, a field rule
    87  	// may be disabled at the message level to cover all fields in the message).
    88  	//
    89  	// Do not pass the source code location here, the source location in relation
    90  	// to the parent is not helpful.
    91  	if parent := d.GetParent(); parent != nil {
    92  		return ruleIsEnabled(rule, parent, nil, aliasMap, ignoreCommentDisables)
    93  	}
    94  
    95  	return true
    96  }
    97  
    98  // ruleIsDisabledByComments returns true if the rule has been disabled
    99  // by comments in the file or leading the element.
   100  func ruleIsDisabledByComments(rule ProtoRule, d desc.Descriptor, l *dpb.SourceCodeInfo_Location, aliasMap map[string]string) bool {
   101  	// Some rules have a legacy name. We add it to the check list.
   102  	ruleName := string(rule.GetName())
   103  	names := []string{ruleName, aliasMap[ruleName]}
   104  
   105  	commentLines := []string{}
   106  	if l != nil {
   107  		commentLines = append(commentLines, strings.Split(l.GetLeadingComments(), "\n")...)
   108  	}
   109  	if f, ok := d.(*desc.FileDescriptor); ok {
   110  		commentLines = append(commentLines, strings.Split(fileHeader(f), "\n")...)
   111  	} else {
   112  		commentLines = append(commentLines, strings.Split(getLeadingComments(d), "\n")...)
   113  	}
   114  	disabledRules := []string{}
   115  	for _, commentLine := range commentLines {
   116  		r := extractDisabledRuleName(commentLine)
   117  		if r != "" {
   118  			disabledRules = append(disabledRules, r)
   119  		}
   120  	}
   121  
   122  	for _, name := range names {
   123  		if matchRule(name, disabledRules...) {
   124  			return true
   125  		}
   126  	}
   127  
   128  	return false
   129  }