diff --git a/analyzer/testdata/src/extra/rules.go b/analyzer/testdata/src/extra/rules.go index 7c0c27c..0bf8d47 100644 --- a/analyzer/testdata/src/extra/rules.go +++ b/analyzer/testdata/src/extra/rules.go @@ -169,7 +169,7 @@ func testRules(m dsl.Matcher) { Where(m["$$"].Node.Parent().Is(`ExprStmt`)). Report(`context.WithValue result should not be ignored`) - m.Match(`var $v = $_`). + m.Match(`var $v = $_`, `var ($*_; $v = $_; $*_)`). Where(m["$$"].Node.Parent().Is(`File`) && m["v"].Type.Implements(`error`) && !m["v"].Text.Matches(`^Err`)). diff --git a/internal/gogrep/compile.go b/internal/gogrep/compile.go index 8fd6b78..c1d30d6 100644 --- a/internal/gogrep/compile.go +++ b/internal/gogrep/compile.go @@ -71,6 +71,13 @@ func (c *compiler) toUint8(n ast.Node, v int) uint8 { return uint8(v) } +func (c *compiler) toBool(v bool) uint8 { + if v { + return 1 + } + return 0 +} + func (c *compiler) internVar(n ast.Node, s string) uint8 { c.info.Vars[s] = struct{}{} index := c.internString(n, s) @@ -293,13 +300,19 @@ func (c *compiler) compileGenDecl(n *ast.GenDecl) { switch n.Tok { case token.CONST, token.VAR: - c.emitInstOp(pickOp(n.Tok == token.CONST, opConstDecl, opVarDecl)) + c.emitInst(instruction{ + op: pickOp(n.Tok == token.CONST, opConstDecl, opVarDecl), + value: c.toBool(n.Lparen.IsValid()), + }) for _, spec := range n.Specs { c.compileValueSpec(spec.(*ast.ValueSpec)) } c.emitInstOp(opEnd) case token.TYPE: - c.emitInstOp(opTypeDecl) + c.emitInst(instruction{ + op: opTypeDecl, + value: c.toBool(n.Lparen.IsValid()), + }) for _, spec := range n.Specs { c.compileTypeSpec(spec.(*ast.TypeSpec)) } diff --git a/internal/gogrep/compile_test.go b/internal/gogrep/compile_test.go index c9d9759..16b73c3 100644 --- a/internal/gogrep/compile_test.go +++ b/internal/gogrep/compile_test.go @@ -175,7 +175,7 @@ func TestCompileWildcard(t *testing.T) { }, `const $x = $y`: { - `ConstDecl`, + `ConstDecl false`, ` • ValueInitSpec`, ` • • NamedNode x`, ` • • End`, @@ -185,7 +185,7 @@ func TestCompileWildcard(t *testing.T) { }, `const ($_ $_ = iota; $_; $*_)`: { - `ConstDecl`, + `ConstDecl true`, ` • TypedValueInitSpec`, ` • • Node`, ` • • End`, @@ -306,7 +306,7 @@ func TestCompileWildcard(t *testing.T) { `var x int; if true { f() }`: { `MultiStmt`, ` • DeclStmt`, - ` • • VarDecl`, + ` • • VarDecl false`, ` • • • TypedValueSpec`, ` • • • • Ident x`, ` • • • • End`, @@ -358,7 +358,7 @@ func TestCompileWildcard(t *testing.T) { }, `const $_ $*_ = iota`: { - `ConstDecl`, + `ConstDecl false`, ` • TypedValueInitSpec`, ` • • Node`, ` • • End`, @@ -401,18 +401,18 @@ func TestCompile(t *testing.T) { }, `var ()`: { - `VarDecl`, + `VarDecl true`, ` • End`, }, `type foo = int`: { - `TypeDecl`, + `TypeDecl false`, ` • TypeAliasSpec`, ` • • Ident foo`, ` • • Ident int`, ` • End`, }, `type (a int64; b string)`: { - `TypeDecl`, + `TypeDecl true`, ` • TypeSpec`, ` • • Ident a`, ` • • Ident int64`, @@ -981,7 +981,7 @@ func TestCompile(t *testing.T) { }, `const (x = 1; y = 2)`: { - `ConstDecl`, + `ConstDecl true`, ` • ValueInitSpec`, ` • • Ident x`, ` • • End`, @@ -996,7 +996,7 @@ func TestCompile(t *testing.T) { }, `const (x = iota; y)`: { - `ConstDecl`, + `ConstDecl true`, ` • ValueInitSpec`, ` • • Ident x`, ` • • End`, diff --git a/internal/gogrep/gen_operations.go b/internal/gogrep/gen_operations.go index d01d55b..322aa69 100644 --- a/internal/gogrep/gen_operations.go +++ b/internal/gogrep/gen_operations.go @@ -170,9 +170,9 @@ var opPrototypes = []operationProto{ {name: "MethodProtoDecl", tag: "FuncDecl", args: "recv name type"}, {name: "DeclStmt", tag: "DeclStmt", args: "decl"}, - {name: "ConstDecl", tag: "GenDecl", args: "valuespecs..."}, - {name: "VarDecl", tag: "GenDecl", args: "valuespecs..."}, - {name: "TypeDecl", tag: "GenDecl", args: "typespecs..."}, + {name: "ConstDecl", tag: "GenDecl", args: "valuespecs...", value: "bool | whether it has parens or not"}, + {name: "VarDecl", tag: "GenDecl", args: "valuespecs...", value: "bool | whether it has parens or not"}, + {name: "TypeDecl", tag: "GenDecl", args: "typespecs...", value: "bool | whether it has parens or not"}, {name: "EmptyPackage", tag: "File", args: "name"}, } @@ -305,6 +305,8 @@ func main() { valueKindName = "chandirValue" case "int": valueKindName = "intValue" + case "bool": + valueKindName = "boolValue" default: panic(fmt.Sprintf("%s: unexpected %s type", proto.name, typ)) } diff --git a/internal/gogrep/instructions.go b/internal/gogrep/instructions.go index 9f4f72d..ab00c7b 100644 --- a/internal/gogrep/instructions.go +++ b/internal/gogrep/instructions.go @@ -22,6 +22,7 @@ const ( tokenValue // token.Token chandirValue // ast.CharDir intValue // int + boolValue // bool ) type program struct { @@ -97,6 +98,8 @@ func formatInstruction(p *program, inst instruction) string { parts = append(parts, token.Token(inst.value).String()) case intValue: parts = append(parts, fmt.Sprint(inst.value)) + case boolValue: + parts = append(parts, fmt.Sprint(inst.BoolValue())) } switch info.ExtraValueKind { @@ -114,3 +117,5 @@ type instruction struct { value uint8 valueIndex uint8 } + +func (inst instruction) BoolValue() bool { return inst.value != 0 } diff --git a/internal/gogrep/match.go b/internal/gogrep/match.go index 0e658ff..ec3e1fc 100644 --- a/internal/gogrep/match.go +++ b/internal/gogrep/match.go @@ -545,13 +545,16 @@ func (m *matcher) matchNodeWithInst(state *MatcherState, inst instruction, n ast case opConstDecl: n, ok := n.(*ast.GenDecl) - return ok && n.Tok == token.CONST && m.matchSpecSlice(state, n.Specs) + return ok && n.Tok == token.CONST && n.Lparen.IsValid() == inst.BoolValue() && + m.matchSpecSlice(state, n.Specs) case opVarDecl: n, ok := n.(*ast.GenDecl) - return ok && n.Tok == token.VAR && m.matchSpecSlice(state, n.Specs) + return ok && n.Tok == token.VAR && n.Lparen.IsValid() == inst.BoolValue() && + m.matchSpecSlice(state, n.Specs) case opTypeDecl: n, ok := n.(*ast.GenDecl) - return ok && n.Tok == token.TYPE && m.matchSpecSlice(state, n.Specs) + return ok && n.Tok == token.TYPE && n.Lparen.IsValid() == inst.BoolValue() && + m.matchSpecSlice(state, n.Specs) case opEmptyPackage: n, ok := n.(*ast.File) diff --git a/internal/gogrep/match_test.go b/internal/gogrep/match_test.go index a884f28..b969c8a 100644 --- a/internal/gogrep/match_test.go +++ b/internal/gogrep/match_test.go @@ -805,14 +805,16 @@ func TestMatch(t *testing.T) { // Gen decl. {`const $_ $*_ = iota`, 1, `const foo = iota`}, {`const $_ $*_ = iota`, 1, `const foo int = iota`}, - {`const $_ $*_ = iota`, 1, `const (foo = iota)`}, - {`const $_ $*_ = iota`, 1, `const (foo int = iota)`}, + {`const $_ $*_ = iota`, 0, `const (foo = iota)`}, + {`const $_ $*_ = iota`, 0, `const (foo int = iota)`}, {`const $_ $*_ = iota`, 0, `const foo int = 0`}, {`const $_ $*_ = iota`, 0, `var foo = iota`}, {`const $_ $_ = iota`, 1, `const foo int = iota`}, {`const $_ $_ = iota`, 0, `const foo = iota`}, + {`const ($_ = 1)`, 1, `const (x = 1)`}, + {`const ($_ = 1)`, 0, `const x = 1`}, {`const $x = $y`, 1, `const a = b`}, - {`const $x = $y`, 1, `const (a = b)`}, + {`const $x = $y`, 0, `const (a = b)`}, {`const $x = $y`, 0, "const (a = b\nc = d)"}, {`const (x = 1; y = 2)`, 1, `const (x = 1; y = 2)`}, {`const (x = 1; y = 2)`, 0, `const (x = 1; y = 1)`}, @@ -827,10 +829,13 @@ func TestMatch(t *testing.T) { {`var x $_`, 1, `var x int`}, {`var x $_`, 0, `var y int`}, {`var $x int`, 1, `var a int`}, + {`var $x int`, 0, `var (a int)`}, {`var $x int`, 0, `var a int = 3`}, {`var ()`, 1, `var()`}, {`var ()`, 0, `var(x int)`}, + {`var ()`, 0, `var x int`}, {`type x int`, 1, `type x int`}, + {`type x int`, 0, `type (x int)`}, {`type x int`, 0, `type _ int`}, {`type x int`, 0, `type x _`}, {`type x int`, 0, `type x = int`}, @@ -840,6 +845,8 @@ func TestMatch(t *testing.T) { {`type x = int`, 0, `type x int`}, {`type $x int`, 1, `type foo int`}, {`type x $t`, 1, `type x string`}, + {`type (x int)`, 1, `type (x int)`}, + {`type (x int)`, 0, `type x int`}, {`type ()`, 1, `type ()`}, {`type ()`, 0, `type (x int)`}, {`type ()`, 0, `type x int`}, diff --git a/internal/gogrep/operations.gen.go b/internal/gogrep/operations.gen.go index 7f6471b..c03d576 100644 --- a/internal/gogrep/operations.gen.go +++ b/internal/gogrep/operations.gen.go @@ -527,14 +527,17 @@ const ( // Tag: GenDecl // Args: valuespecs... + // Value: bool | whether it has parens or not opConstDecl operation = 114 // Tag: GenDecl // Args: valuespecs... + // Value: bool | whether it has parens or not opVarDecl operation = 115 // Tag: GenDecl // Args: typespecs... + // Value: bool | whether it has parens or not opTypeDecl operation = 116 // Tag: File @@ -1461,7 +1464,7 @@ var operationInfoTable = [256]operationInfo{ opConstDecl: { Tag: nodetag.GenDecl, NumArgs: 1, - ValueKind: emptyValue, + ValueKind: boolValue, ExtraValueKind: emptyValue, VariadicMap: 1, // 1 SliceIndex: -1, @@ -1469,7 +1472,7 @@ var operationInfoTable = [256]operationInfo{ opVarDecl: { Tag: nodetag.GenDecl, NumArgs: 1, - ValueKind: emptyValue, + ValueKind: boolValue, ExtraValueKind: emptyValue, VariadicMap: 1, // 1 SliceIndex: -1, @@ -1477,7 +1480,7 @@ var operationInfoTable = [256]operationInfo{ opTypeDecl: { Tag: nodetag.GenDecl, NumArgs: 1, - ValueKind: emptyValue, + ValueKind: boolValue, ExtraValueKind: emptyValue, VariadicMap: 1, // 1 SliceIndex: -1,