blob: b4d384f7829fb1a7031cf73a3601b6cac51a16e8 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package core
import (
"fmt"
)
// allRules is evil global mutable state, but we're being super careful about
// how it is used. It can be mutated only through registration, all other uses
// are read only. We do not rely on the registry in tests.
var allRules = make(map[string]func(rootReporter *RootReporter) (LintRuleOverTokens, LintRuleOverPatterns))
// HasRule tests whether a given rule is registered.
func HasRule(name string) bool {
_, ok := allRules[name]
return ok
}
// AllRules returns the names of all the registered rules.
func AllRules() []string {
var names []string
for name := range allRules {
names = append(names, name)
}
return names
}
// CombineRules combines rules over tokens and rules over patterns into a single
// rule over tokens.
//
// To accomplish this combination, dedicated one-to-many rules bridge between
// the returned rule and the provided rules. Additionally, the token stream is
// recognized and turned into a pattern stream so as to drive all rules over
// patterns.
func CombineRules(rulesOverTokens []LintRuleOverTokens, rulesOverPatterns []LintRuleOverPatterns) LintRuleOverTokens {
// combining all rules into a single rule over tokens
//
// - oneToManyOverTokens rule over
// - all rules over tokens
// - a recognizer bridging to a
// - oneToManyOverPatterns rule over
// - all rules over patterns
return oneToManyOverTokens(append(
rulesOverTokens,
&recognizer{
rule: oneToManyOverPatterns(
rulesOverPatterns),
}))
}
// InstantiateRules instantiates all `enabledRules`.
func InstantiateRules(rootReporter *RootReporter, enabledRules []string) LintRuleOverTokens {
var (
rulesOverTokens []LintRuleOverTokens
rulesOverPatterns []LintRuleOverPatterns
)
if len(enabledRules) == 1 && enabledRules[0] == AllRulesName {
enabledRules = nil
for name := range allRules {
enabledRules = append(enabledRules, name)
}
}
for _, name := range enabledRules {
instantiator, ok := allRules[name]
if !ok {
panic(fmt.Sprintf("unknown rule '%s', should not happen", name))
}
overTokens, overPatterns := instantiator(rootReporter)
if overTokens != nil {
rulesOverTokens = append(rulesOverTokens, overTokens)
}
if overPatterns != nil {
rulesOverPatterns = append(rulesOverPatterns, overPatterns)
}
}
return CombineRules(rulesOverTokens, rulesOverPatterns)
}
// RegisterLintRuleOverTokens registers a lint rule over tokens. This is meant
// to be called from an `init` block.
func RegisterLintRuleOverTokens(name string, instantiator func(Reporter) LintRuleOverTokens) {
checkRegisteredName(name)
allRules[name] = func(rootReporter *RootReporter) (LintRuleOverTokens, LintRuleOverPatterns) {
return instantiator(rootReporter.ForRule(name)), nil
}
}
// RegisterLintRuleOverPatterns registers a lint rule over patterns. This is
// meant to be called from an `init` block.
func RegisterLintRuleOverPatterns(name string, instantiator func(Reporter) LintRuleOverPatterns) {
checkRegisteredName(name)
allRules[name] = func(rootReporter *RootReporter) (LintRuleOverTokens, LintRuleOverPatterns) {
return nil, instantiator(rootReporter.ForRule(name))
}
}
// generalName is the category name reserved for warnings not issued by specific
// rules, but rather 'general' warnings.
const generalName = "general"
// AllRulesName is a reserved rule name used to indicate "all" rules.
const AllRulesName = "all"
func checkRegisteredName(name string) {
if name == generalName || name == AllRulesName {
panic(fmt.Sprintf("rule name '%s' is reserved", generalName))
}
if _, ok := allRules[name]; ok {
panic(fmt.Sprintf("rule name '%s' is being registered more than once", name))
}
}