Skip to content

Commit

Permalink
linter: mask filtering rules path (#1244)
Browse files Browse the repository at this point in the history
  • Loading branch information
Hidanio authored Feb 2, 2025
1 parent 984a729 commit 9ca19fc
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 8 deletions.
74 changes: 66 additions & 8 deletions src/linter/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -1526,21 +1526,79 @@ func IsRuleEnabledForPath(root *RuleNode, filePath string, checkRule string) boo
// Starting with global state. We have guarantee while parsing config that rule is `on` and exist
ruleState := true

for _, part := range parts {
i := 0
for i < len(parts) {
part := parts[i]
if part == "" {
i++
continue
}
if node, exists := currentNode.Children[part]; exists {
if node.Disabled[checkRule] {
ruleState = false // Disable on this path

// 1) Try to find precision node (part)
nextNode, ok := currentNode.Children[part]
if ok {
// If found precision node: apply Enabled/Disabled
if nextNode.Disabled[checkRule] {
ruleState = false
}
if node.Enabled[checkRule] {
ruleState = true // Enable on this path
if nextNode.Enabled[checkRule] {
ruleState = true
}
currentNode = node
} else {
currentNode = nextNode
i++
continue
}

// 2) If there is no exact match, lets find node "*" (wildcard)
starNode, ok := currentNode.Children["*"]
if !ok {
// not precision matching & not "*"
break
}

// Apply Enabled/Disabled for "*"
if starNode.Disabled[checkRule] {
ruleState = false
}
if starNode.Enabled[checkRule] {
ruleState = true
}

// move to node "*"
currentNode = starNode

// Now the logic is to "swallow" several directories:
//
// Until we meet an exact match in `Children` in the next step
// (except "*"), we can continue to "eat" directories while remaining in `starNode`.
//
// Or if we have reached the end of the path, we exit the loop.

for {
i++
if i >= len(parts) {
// the path ended - all remaining directories were swallowed
return ruleState
}
AfterStarPart := parts[i]

// if starNode have Children[AfterStarPart] (except "*"),
// so we found the next "literal" node, we exit the inner loop,
// to go through the usual logic at the top level.
if AfterStarPart == "" {
continue
}

_, hasLiteral := currentNode.Children[AfterStarPart]
if hasLiteral {
// Let's exit - let the outer loop handle it
break
}

// There are directories left - we eat them, continuing the while loop
}
// Exit to outer loop: i points to potential literal part
// (or "*"), but the outer iteration will re-check it
}

return ruleState
Expand Down
58 changes: 58 additions & 0 deletions src/tests/infra/pathRulesSet_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ func pathRulesSetInit(t *testing.T) *linttest.Suite {
"undefinedFunction": true,
},
},
"star/*/tests": {
Enabled: map[string]bool{
"emptyStmt": true,
},
Disabled: map[string]bool{},
},
"mixed/*/tests": {
Enabled: map[string]bool{
"emptyStmt": true,
},
Disabled: map[string]bool{
"undefinedFunction": true,
},
},
})

var suite = linttest.NewSuite(t)
Expand Down Expand Up @@ -63,6 +77,50 @@ require_once 'foo.php';;
test.RunAndMatch()
}

func TestStarPath(t *testing.T) {
test := pathRulesSetInit(t)
code := `<?php
require_once 'foo.php';;
`
test.AddNamedFile("star/something/another/tests/foo.php", code)

test.Expect = []string{
`Semicolon (;) is not needed here, it can be safely removed at`,
}
test.RunAndMatch()
}

func TestMixedStarPath(t *testing.T) {
test := pathRulesSetInit(t)
code := `<?php
require_once 'foo.php';;
`

test.AddNamedFile("mixed/something/another/tests/foo.php", code)

code = `<?php
function function_exists($name) { return 1 == 2; }
function f($cond) {
if (!function_exists('\foo')) {
\foo();
}
if ($cond && !function_exists('bar')) {
bar("a", "b");
}
if ($cond || !function_exists('a\b\baz')) {
a\b\baz(1);
}
}
`
test.AddNamedFile("mixed/something/another/tests/foo2.php", code)

test.Expect = []string{
`Semicolon (;) is not needed here, it can be safely removed at`,
}
test.RunAndMatch()
}

func TestMixedRulesPath(t *testing.T) {
test := pathRulesSetInit(t)
mergedCode := `<?php
Expand Down

0 comments on commit 9ca19fc

Please sign in to comment.