Skip to content

Commit

Permalink
added new @link tag for dynamic rules, test
Browse files Browse the repository at this point in the history
  • Loading branch information
Hidanio committed Jul 27, 2024
1 parent fa7b184 commit 38a94b1
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 38 deletions.
70 changes: 36 additions & 34 deletions src/linter/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,40 +16,42 @@ import (
// cacheVersions is a magic number that helps to distinguish incompatible caches.
//
// Version log:
// 27 - added Static field to meta.FuncInfo
// 28 - array type parsed as mixed[]
// 29 - updated type inference for ClassConstFetch
// 30 - resolve ClassConstFetch to a wrapped type string
// 31 - fixed plus operator type inference for arrays
// 32 - replaced Static:bool with Flags:uint8 in meta.FuncInfo
// 33 - support parsing of array<k,v> and list<type>
// 34 - support parsing of ?ClassName as "ClassName|null"
// 35 - added Flags:uint8 to meta.ClassInfo
// 36 - added FuncAbstract bit to FuncFlags
// added FuncFinal bit to FuncFlags
// added ClassFinal bit to ClassFlags
// FuncInfo now stores original function name
// ClassInfo now stores original class name
// 37 - added ClassShape bit to ClassFlags
// changed meta.scopeVar bool fields representation
// 38 - replaced TypesMap.immutable:bool with flags:uint8.
// added mapPrecise flag to mark precise type maps.
// 39 - added new field Value in ConstantInfo
// 40 - changed string const value storage (no quotes)
// 41 - const-folding affected const definition values
// 42 - bool-typed consts are now stored in meta info
// 43 - define'd const values stored in cache
// 44 - rename ConstantInfo => ConstInfo
// 45 - added Mixins field to meta.ClassInfo
// 46 - changed the way of inferring the return type of functions and methods
// 47 - forced cache version invalidation due to the #921
// 48 - renamed meta.TypesMap to types.Map; this affects gob encoding
// 49 - for shape, names are now generated using the keys that make up this shape
// 50 - added Flags field for meta.PropertyInfo
// 51 - added anonymous classes
// 52 - renamed all PhpDoc and Phpdoc with PHPDoc
// 53 - added DeprecationInfo for functions and methods and support for some attributes
// 54 - forced cache version invalidation due to the #1165
//
// 27 - added Static field to meta.FuncInfo
// 28 - array type parsed as mixed[]
// 29 - updated type inference for ClassConstFetch
// 30 - resolve ClassConstFetch to a wrapped type string
// 31 - fixed plus operator type inference for arrays
// 32 - replaced Static:bool with Flags:uint8 in meta.FuncInfo
// 33 - support parsing of array<k,v> and list<type>
// 34 - support parsing of ?ClassName as "ClassName|null"
// 35 - added Flags:uint8 to meta.ClassInfo
// 36 - added FuncAbstract bit to FuncFlags
// added FuncFinal bit to FuncFlags
// added ClassFinal bit to ClassFlags
// FuncInfo now stores original function name
// ClassInfo now stores original class name
// 37 - added ClassShape bit to ClassFlags
// changed meta.scopeVar bool fields representation
// 38 - replaced TypesMap.immutable:bool with flags:uint8.
// added mapPrecise flag to mark precise type maps.
// 39 - added new field Value in ConstantInfo
// 40 - changed string const value storage (no quotes)
// 41 - const-folding affected const definition values
// 42 - bool-typed consts are now stored in meta info
// 43 - define'd const values stored in cache
// 44 - rename ConstantInfo => ConstInfo
// 45 - added Mixins field to meta.ClassInfo
// 46 - changed the way of inferring the return type of functions and methods
// 47 - forced cache version invalidation due to the #921
// 48 - renamed meta.TypesMap to types.Map; this affects gob encoding
// 49 - for shape, names are now generated using the keys that make up this shape
// 50 - added Flags field for meta.PropertyInfo
// 51 - added anonymous classes
// 52 - renamed all PhpDoc and Phpdoc with PHPDoc
// 53 - added DeprecationInfo for functions and methods and support for some attributes
// 54 - forced cache version invalidation due to the #1165
// 55 - added @link tag -> new field in Rule
const cacheVersion = 54

var (
Expand Down
6 changes: 3 additions & 3 deletions src/linter/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ main();
// 1. Check cache contents length.
//
// If cache encoding changes, there is a very high chance that
// encoded data lengh will change as well.
wantLen := 5953
// encoded data length will change as well.
wantLen := 5952
haveLen := buf.Len()
if haveLen != wantLen {
t.Errorf("cache len mismatch:\nhave: %d\nwant: %d", haveLen, wantLen)
Expand All @@ -158,7 +158,7 @@ main();
// 2. Check cache "strings" hash.
//
// It catches new fields in cached types, field renames and encoding of additional named attributes.
wantStrings := "df69cfe531807fe5e317e5f894ac1ad2a68020edf03a053a30b25c70488b741b6d992d37e7a76ac4b8a760756631ee9ae309ba57e29253ffae896c3492b90939"
wantStrings := "690e77c94ecdd7878de0bf6f6881d786cf1fafa4588f7905f54d700646c4952aad359008ae2dcddb1c7f29163ecee62355d525672090ac30257bc414f690006f"
haveStrings := collectCacheStrings(buf.String())
if haveStrings != wantStrings {
t.Errorf("cache strings mismatch:\nhave: %q\nwant: %q", haveStrings, wantStrings)
Expand Down
5 changes: 5 additions & 0 deletions src/linter/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -1780,6 +1780,11 @@ func (d *rootWalker) runRule(n ir.Node, sc *meta.Scope, rule *rules.Rule) bool {
}

message := d.renderRuleMessage(rule.Message, n, m, true)

if rule.Link != "" {
message += " | More about this rule: " + rule.Link
}

d.Report(location, rule.Level, rule.Name, "%s", message)

if d.config.ApplyQuickFixes && rule.Fix != "" {
Expand Down
16 changes: 15 additions & 1 deletion src/rules/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,16 @@ func (p *parser) parseRuleInfo(st ir.Node, labelStmt ir.Node, proto *Rule) (Rule
}
rule.Name = part.Params[0]

case "link":
if len(part.Params) != 1 {
return rule, p.errorf(st, "@link expects exactly 1 param, got %d", len(part.Params))
}
var link = part.Params[0]
if !isURL(link) {
return rule, p.errorf(st, "@link argument is not link")
}
rule.Link = link

case "location":
if len(part.Params) != 1 {
return rule, p.errorf(st, "@location expects exactly 1 params, got %d", len(part.Params))
Expand Down Expand Up @@ -318,7 +328,6 @@ func (p *parser) parseRuleInfo(st ir.Node, labelStmt ir.Node, proto *Rule) (Rule
}
filter.Regexp = regex
filterSet[name] = filter

default:
return rule, p.errorf(st, "unknown attribute @%s on line %d", part.Name(), part.Line())
}
Expand All @@ -339,6 +348,11 @@ func (p *parser) parseRuleInfo(st ir.Node, labelStmt ir.Node, proto *Rule) (Rule
return rule, nil
}

func isURL(str string) bool {
re := regexp.MustCompile(`^(https?|ftp)://[^\s/$.?#].\S*$`)
return re.MatchString(str)
}

func (p *parser) parseRules(stmts []ir.Node, proto *Rule) error {
for len(stmts) > 0 {
stmt := stmts[0]
Expand Down
3 changes: 3 additions & 0 deletions src/rules/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ type Rule struct {
// Name tells whether this rule causes critical report.
Name string

// Link to the documentation about rule
Link string

// Matcher is an object that is used to check whether a given AST node
// should trigger a warning that is associated with rule.
Matcher *phpgrep.Matcher
Expand Down
27 changes: 27 additions & 0 deletions src/tests/rules/rules_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,33 @@ function f() {
test.RunRulesTest()
}

func TestLinkTag(t *testing.T) {
rfile := `<?php
/**
* @name emptyIf
* @warning suspicious empty body of the if statement
* @scope local
* @link https://goodrule.com
*/
if ($_);
`

test := linttest.NewSuite(t)
test.RuleFile = rfile
test.AddFile(`<?php
if (123); // No warning
function f() {
if (123); // Warning
}
`)

test.Expect = []string{
` | More about this rule: https://goodrule.com`,
}
test.RunRulesTest()
}

func TestRootRules(t *testing.T) {
rfile := `<?php
/**
Expand Down

0 comments on commit 38a94b1

Please sign in to comment.