From b1fe2efb8ddac359011c9740763d8c245b816697 Mon Sep 17 00:00:00 2001 From: Richardas Kuchinskas Date: Tue, 22 Oct 2024 20:01:29 +0300 Subject: [PATCH] base solution --- src/linter/conf.go | 84 ++++++++++++++++++++++++++++++++++++++++++++++ src/linter/root.go | 19 +++++++++++ 2 files changed, 103 insertions(+) diff --git a/src/linter/conf.go b/src/linter/conf.go index 9055f2ea..9adb2830 100644 --- a/src/linter/conf.go +++ b/src/linter/conf.go @@ -1,8 +1,10 @@ package linter import ( + "path/filepath" "regexp" "runtime" + "strings" "time" "github.com/VKCOM/php-parser/pkg/version" @@ -38,8 +40,13 @@ type Config struct { // Rules is a set of dynamically loaded linter diagnostics. Rules *rules.Set + // PathRules is a set of specific rules for paths. + PathRules *RuleNode + // settings + ProjectPath string + StubsDir string Debug bool @@ -68,6 +75,82 @@ type Config struct { StrictMixed bool } +type RuleNode struct { + Children map[string]*RuleNode // Child nodes (subdirectories and files) + Enable map[string]bool // Rules enabled at this level + Disable map[string]bool // Disabled rules at this level +} + +func NewRuleNode() *RuleNode { + return &RuleNode{ + Children: make(map[string]*RuleNode), + Enable: make(map[string]bool), + Disable: make(map[string]bool), + } +} + +type PathRuleSet struct { + Enable map[string]bool // Rules that are enabled for this path + Disable map[string]bool // Rules that are disabled for this path +} + +func BuildRuleTree(pathRules map[string]*PathRuleSet) *RuleNode { + root := NewRuleNode() + + for path, ruleSet := range pathRules { + normalizedPath := filepath.ToSlash(filepath.Clean(path)) + parts := strings.Split(normalizedPath, "/") + currentNode := root + + for _, part := range parts { + if part == "" { + continue + } + if _, exists := currentNode.Children[part]; !exists { + currentNode.Children[part] = NewRuleNode() + } + currentNode = currentNode.Children[part] + } + + for rule := range ruleSet.Enable { + currentNode.Enable[rule] = true + } + for rule := range ruleSet.Disable { + currentNode.Disable[rule] = true + } + } + + return root +} + +func IsRuleEnabled(root *RuleNode, filePath string, checkRule string) bool { + normalizedPath := filepath.ToSlash(filepath.Clean(filePath)) + parts := strings.Split(normalizedPath, "/") + currentNode := root + + // Starting with global state. We have guarantee while parsing config that rule is `on` and exist + ruleState := true + + for _, part := range parts { + if part == "" { + continue + } + if node, exists := currentNode.Children[part]; exists { + if node.Disable[checkRule] { + ruleState = false // Disable on this path + } + if node.Enable[checkRule] { + ruleState = true // Enable on this path + } + currentNode = node + } else { + break + } + } + + return ruleState +} + func NewConfig(ver string) *Config { reg := &CheckersRegistry{ info: map[string]CheckerInfo{}, @@ -79,6 +162,7 @@ func NewConfig(ver string) *Config { return &Config{ SrcInput: inputs.NewDefaultSourceInput(), Rules: rules.NewSet(), + PathRules: NewRuleNode(), MaxConcurrency: runtime.NumCPU(), IsDiscardVar: isUnderscore, Checkers: reg, diff --git a/src/linter/root.go b/src/linter/root.go index 3c23699a..8d276c2f 100644 --- a/src/linter/root.go +++ b/src/linter/root.go @@ -1556,6 +1556,25 @@ func (d *rootWalker) Report(n ir.Node, level int, checkName, msg string, args .. } } + var rootNode = d.config.PathRules + + filePath := d.file.Name() + + if d.config.ProjectPath != "" { + // Convert absolute path to relative to the project root + + relativePath, err := filepath.Rel(d.config.ProjectPath, filePath) + if err != nil { + d.ReportLocation(loc, level, checkName, msg, args...) + return + } + filePath = relativePath + } + + if !IsRuleEnabled(rootNode, filePath, checkName) { + return + } + d.ReportLocation(loc, level, checkName, msg, args...) }