Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rule @path-exclude rule #1153

Merged
merged 2 commits into from
Oct 11, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ phplinter.exe
phplinter-output-*.json
err.log
src/cmd/stubs/phpstorm-stubs/
y.output
y.output
.idea
vendor
21 changes: 21 additions & 0 deletions docs/dynamic_rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,27 @@ function ternarySimplify() {

This rule will now only apply to files with the `common/` folder in the path.

##### `@path-exclude`

The `@path-exclude` restriction allows you to restrict the rule by file path.

Thus, the rule will be applied only if there is a substring from `@path-exclude` in the file path.

For example:

```php
function ternarySimplify() {
/**
* @maybe Could rewrite as '$x ?: $y'
* @pure $x
* @path-exclude common/
*/
$x ? $x : $y;
}
```

This rule will now don't apply to files with the `common/` folder in the path.

#### Underline location (`@location`)

For every warning that NoVerify finds, it underlines the location. However, for dynamic rules, the right place is not always underlined.
Expand Down
16 changes: 15 additions & 1 deletion src/linter/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,20 @@ func classHasProp(st *meta.ClassParseState, className, propName string) bool {
return ok
}

func isFilePathExcluded(filename string, rule rules.Rule) bool {
if len(rule.PathExcludes) == 0 {
return false
}

for exclude := range rule.PathExcludes {
if strings.Contains(filename, exclude) {
return true
}
}

return false
}

func cloneRulesForFile(filename string, ruleSet *rules.ScopedSet) *rules.ScopedSet {
if ruleSet.CountRules == 0 {
return nil
Expand All @@ -480,7 +494,7 @@ func cloneRulesForFile(filename string, ruleSet *rules.ScopedSet) *rules.ScopedS
for kind, ruleByKind := range &ruleSet.RulesByKind {
res := make([]rules.Rule, 0, len(ruleByKind))
for _, rule := range ruleByKind {
if !strings.Contains(filename, rule.Path) {
if !strings.Contains(filename, rule.Path) || isFilePathExcluded(filename, rule) {
continue
}
res = append(res, rule)
Expand Down
8 changes: 8 additions & 0 deletions src/rules/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,14 @@ func (p *parser) parseRuleInfo(st ir.Node, labelStmt ir.Node, proto *Rule) (Rule
return rule, p.errorf(st, "duplicate @path constraint")
}
rule.Path = part.Params[0]
case "path-exclude":
if len(part.Params) != 1 {
return rule, p.errorf(st, "@exclude expects exactly 1 param, got %d", len(part.Params))
}
if rule.PathExcludes == nil {
rule.PathExcludes = make(map[string]bool, 1)
}
rule.PathExcludes[part.Params[0]] = true
case "type":
if len(part.Params) != 2 {
return rule, p.errorf(st, "@type expects exactly 2 params, got %d", len(part.Params))
Expand Down
6 changes: 3 additions & 3 deletions src/rules/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import (
"github.com/google/go-cmp/cmp"
)

func TestParser(t *testing.T) {
func TestParser_Parse(t *testing.T) {
p := NewParser()
rset, err := p.Parse("rules.php", strings.NewReader(`<?php
/**
* @comment This is an example of fully-docummented rule.
* @comment This is an example of fully-documented rule.
* @before array(1, 2)
* @after [1, 2]
*/
Expand All @@ -38,7 +38,7 @@ function arraySyntax() {
{
name: "arraySyntax",
doc: RuleDoc{
Comment: "This is an example of fully-docummented rule.",
Comment: "This is an example of fully-documented rule.",
Before: "array(1, 2)",
After: "[1, 2]",
},
Expand Down
4 changes: 4 additions & 0 deletions src/rules/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ type Rule struct {
// A rule is only applied to a file that contains a Path as a substring in its name.
Path string

// PathExcludes is a filter-like rule switcher.
// A rule is not applied to a file that contains a PathExcludes as a substring in its name.
PathExcludes map[string]bool

// Filters is a list of OR-connected filter sets.
// Every filter set is a mapping of phpgrep variable to a filter.
Filters []map[string]Filter
Expand Down
6 changes: 6 additions & 0 deletions src/rules/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ func formatRule(r *Rule) string {
buf.WriteString(" * @path " + r.Path + "\n")
}

if r.PathExcludes != nil {
for pathExclude := range r.PathExcludes {
buf.WriteString(" * @path-exclude " + pathExclude + "\n")
}
}

if r.Location != "" {
buf.WriteString(" * @location $" + r.Location + "\n")
}
Expand Down