diff --git a/node/expr/n_class_const_fetch.go b/node/expr/n_class_const_fetch.go index 2dac6f8..bbf1c0b 100644 --- a/node/expr/n_class_const_fetch.go +++ b/node/expr/n_class_const_fetch.go @@ -31,15 +31,15 @@ func (n *ClassConstFetch) Walk(v walker.Visitor) { return } - if n.ConstantName != nil { - vv := v.GetChildrenVisitor("ConstantName") - n.ConstantName.Walk(vv) - } - if n.Class != nil { vv := v.GetChildrenVisitor("Class") n.Class.Walk(vv) } + if n.ConstantName != nil { + vv := v.GetChildrenVisitor("ConstantName") + n.ConstantName.Walk(vv) + } + v.LeaveNode(n) } diff --git a/node/expr/n_closure_use.go b/node/expr/n_closure_use.go index 2ba7191..af33a22 100644 --- a/node/expr/n_closure_use.go +++ b/node/expr/n_closure_use.go @@ -5,22 +5,22 @@ import ( "github.com/z7zmey/php-parser/walker" ) -// ClusureUse node -type ClusureUse struct { +// ClosureUse node +type ClosureUse struct { ByRef bool Variable node.Node } -// NewClusureUse node constuctor -func NewClusureUse(Variable node.Node, ByRef bool) *ClusureUse { - return &ClusureUse{ +// NewClosureUse node constuctor +func NewClosureUse(Variable node.Node, ByRef bool) *ClosureUse { + return &ClosureUse{ ByRef, Variable, } } // Attributes returns node attributes as map -func (n *ClusureUse) Attributes() map[string]interface{} { +func (n *ClosureUse) Attributes() map[string]interface{} { return map[string]interface{}{ "ByRef": n.ByRef, } @@ -28,7 +28,7 @@ func (n *ClusureUse) Attributes() map[string]interface{} { // Walk traverses nodes // Walk is invoked recursively until v.EnterNode returns true -func (n *ClusureUse) Walk(v walker.Visitor) { +func (n *ClosureUse) Walk(v walker.Visitor) { if v.EnterNode(n) == false { return } diff --git a/node/expr/t_visitor_test.go b/node/expr/t_visitor_test.go new file mode 100644 index 0000000..7be9a11 --- /dev/null +++ b/node/expr/t_visitor_test.go @@ -0,0 +1,406 @@ +package expr_test + +import ( + "reflect" + "testing" + + "github.com/z7zmey/php-parser/node/stmt" + + "github.com/z7zmey/php-parser/node/name" + + "github.com/z7zmey/php-parser/node/scalar" + + "github.com/kylelemons/godebug/pretty" + + "github.com/z7zmey/php-parser/node" + "github.com/z7zmey/php-parser/node/expr" + "github.com/z7zmey/php-parser/walker" +) + +var nodesToTest = []struct { + node node.Node // node + expectedVisitedKeys []string // visited keys + expectedAttributes map[string]interface{} +}{ + { + &expr.ArrayDimFetch{ + Variable: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + Dim: &scalar.Lnumber{Value: "1"}, + }, + []string{"Variable", "Dim"}, + map[string]interface{}{}, + }, + { + &expr.ArrayItem{ + ByRef: false, + Key: &scalar.String{Value: "key"}, + Val: &scalar.Lnumber{Value: "1"}, + }, + []string{"Key", "Val"}, + map[string]interface{}{"ByRef": false}, + }, + { + &expr.Array{ + Items: []node.Node{ + &expr.ArrayItem{ + ByRef: false, + Key: &scalar.String{Value: "key"}, + Val: &scalar.Lnumber{Value: "1"}, + }, + }, + }, + []string{"Items"}, + map[string]interface{}{}, + }, + { + &expr.BitwiseNot{ + Expr: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + }, + []string{"Expr"}, + map[string]interface{}{}, + }, + { + &expr.BooleanNot{ + Expr: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + }, + []string{"Expr"}, + map[string]interface{}{}, + }, + { + &expr.ClassConstFetch{ + Class: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + ConstantName: &node.Identifier{Value: "foo"}, + }, + []string{"Class", "ConstantName"}, + map[string]interface{}{}, + }, + { + &expr.Clone{ + Expr: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + }, + []string{"Expr"}, + map[string]interface{}{}, + }, + { + &expr.ClosureUse{ + ByRef: false, + Variable: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + }, + []string{"Variable"}, + map[string]interface{}{"ByRef": false}, + }, + { + &expr.Closure{ + ReturnsRef: true, + Static: false, + PhpDocComment: "", + Params: []node.Node{&node.Parameter{}}, + Uses: []node.Node{&expr.ClosureUse{}}, + ReturnType: &name.Name{}, + Stmts: []node.Node{&stmt.Nop{}}, + }, + []string{"Params", "Uses", "ReturnType", "Stmts"}, + map[string]interface{}{"ReturnsRef": true, "Static": false, "PhpDocComment": ""}, + }, + { + &expr.ConstFetch{ + Constant: &node.Identifier{Value: "foo"}, + }, + []string{"Constant"}, + map[string]interface{}{}, + }, + { + &expr.Empty{ + Expr: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + }, + []string{"Expr"}, + map[string]interface{}{}, + }, + { + &expr.ErrorSuppress{ + Expr: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + }, + []string{"Expr"}, + map[string]interface{}{}, + }, + { + &expr.Eval{ + Expr: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + }, + []string{"Expr"}, + map[string]interface{}{}, + }, + { + &expr.Exit{ + Expr: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + IsDie: false, + }, + []string{"Expr"}, + map[string]interface{}{"IsDie": false}, + }, + { + &expr.FunctionCall{ + Function: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + Arguments: []node.Node{&node.Argument{}}, + }, + []string{"Function", "Arguments"}, + map[string]interface{}{}, + }, + { + &expr.IncludeOnce{ + Expr: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + }, + []string{"Expr"}, + map[string]interface{}{}, + }, + { + &expr.Include{ + Expr: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + }, + []string{"Expr"}, + map[string]interface{}{}, + }, + { + &expr.InstanceOf{ + Expr: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + Class: &name.Name{}, + }, + []string{"Expr", "Class"}, + map[string]interface{}{}, + }, + { + &expr.Isset{ + Variables: []node.Node{ + &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + }, + }, + []string{"Variables"}, + map[string]interface{}{}, + }, + { + &expr.List{ + Items: []node.Node{ + &expr.ArrayItem{}, + }, + }, + []string{"Items"}, + map[string]interface{}{}, + }, + { + &expr.MethodCall{ + Variable: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + Method: &node.Identifier{Value: "foo"}, + Arguments: []node.Node{&node.Argument{}}, + }, + []string{"Variable", "Method", "Arguments"}, + map[string]interface{}{}, + }, + { + &expr.New{ + Class: &name.Name{}, + Arguments: []node.Node{&node.Argument{}}, + }, + []string{"Class", "Arguments"}, + map[string]interface{}{}, + }, + { + &expr.PostDec{ + Variable: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + }, + []string{"Variable"}, + map[string]interface{}{}, + }, + { + &expr.PostInc{ + Variable: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + }, + []string{"Variable"}, + map[string]interface{}{}, + }, + { + &expr.PreDec{ + Variable: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + }, + []string{"Variable"}, + map[string]interface{}{}, + }, + { + &expr.PreInc{ + Variable: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + }, + []string{"Variable"}, + map[string]interface{}{}, + }, + { + &expr.Print{ + Expr: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + }, + []string{"Expr"}, + map[string]interface{}{}, + }, + { + &expr.PropertyFetch{ + Variable: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + Property: &node.Identifier{Value: "foo"}, + }, + []string{"Variable", "Property"}, + map[string]interface{}{}, + }, + { + &expr.RequireOnce{ + Expr: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + }, + []string{"Expr"}, + map[string]interface{}{}, + }, + { + &expr.Require{ + Expr: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + }, + []string{"Expr"}, + map[string]interface{}{}, + }, + { + &expr.ShellExec{ + Parts: []node.Node{ + &scalar.EncapsedStringPart{}, + }, + }, + []string{"Parts"}, + map[string]interface{}{}, + }, + { + &expr.ShortArray{ + Items: []node.Node{ + &expr.ArrayItem{}, + }, + }, + []string{"Items"}, + map[string]interface{}{}, + }, + { + &expr.ShortList{ + Items: []node.Node{ + &expr.ArrayItem{}, + }, + }, + []string{"Items"}, + map[string]interface{}{}, + }, + { + &expr.StaticCall{ + Class: &name.Name{}, + Call: &node.Identifier{Value: "foo"}, + Arguments: []node.Node{&node.Argument{}}, + }, + []string{"Class", "Call", "Arguments"}, + map[string]interface{}{}, + }, + { + &expr.StaticPropertyFetch{ + Class: &name.Name{}, + Property: &node.Identifier{Value: "foo"}, + }, + []string{"Class", "Property"}, + map[string]interface{}{}, + }, + { + &expr.Ternary{ + Condition: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + IfTrue: &expr.Variable{VarName: &node.Identifier{Value: "$b"}}, + IfFalse: &expr.Variable{VarName: &node.Identifier{Value: "$c"}}, + }, + []string{"Condition", "IfTrue", "IfFalse"}, + map[string]interface{}{}, + }, + { + &expr.UnaryMinus{ + Expr: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + }, + []string{"Expr"}, + map[string]interface{}{}, + }, + { + &expr.UnaryPlus{ + Expr: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + }, + []string{"Expr"}, + map[string]interface{}{}, + }, + { + &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + []string{"VarName"}, + map[string]interface{}{}, + }, + { + &expr.YieldFrom{ + Expr: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + }, + []string{"Expr"}, + map[string]interface{}{}, + }, + { + &expr.Yield{ + Key: &expr.Variable{VarName: &node.Identifier{Value: "$a"}}, + Value: &expr.Variable{VarName: &node.Identifier{Value: "$b"}}, + }, + []string{"Key", "Value"}, + map[string]interface{}{}, + }, +} + +type visitorMock struct { + visitChildren bool + visitedKeys []string +} + +func (v *visitorMock) EnterNode(n walker.Walker) bool { return v.visitChildren } +func (v *visitorMock) GetChildrenVisitor(key string) walker.Visitor { + v.visitedKeys = append(v.visitedKeys, key) + return &visitorMock{v.visitChildren, nil} +} +func (v *visitorMock) LeaveNode(n walker.Walker) {} + +func TestVisitorDisableChildren(t *testing.T) { + for _, tt := range nodesToTest { + v := &visitorMock{false, nil} + tt.node.Walk(v) + + expected := []string{} + actual := v.visitedKeys + + diff := pretty.Compare(expected, actual) + if diff != "" { + t.Errorf("%s diff: (-expected +actual)\n%s", reflect.TypeOf(tt.node), diff) + } + } +} + +func TestVisitor(t *testing.T) { + for _, tt := range nodesToTest { + v := &visitorMock{true, nil} + tt.node.Walk(v) + + expected := tt.expectedVisitedKeys + actual := v.visitedKeys + + diff := pretty.Compare(expected, actual) + if diff != "" { + t.Errorf("%s diff: (-expected +actual)\n%s", reflect.TypeOf(tt.node), diff) + } + } +} + +// test Attributes() + +func TestNameAttributes(t *testing.T) { + for _, tt := range nodesToTest { + expected := tt.expectedAttributes + actual := tt.node.Attributes() + + diff := pretty.Compare(expected, actual) + if diff != "" { + t.Errorf("%s diff: (-expected +actual)\n%s", reflect.TypeOf(tt.node), diff) + } + } +} diff --git a/php5/php5.go b/php5/php5.go index 9a1478a..a6b8466 100644 --- a/php5/php5.go +++ b/php5/php5.go @@ -4935,7 +4935,7 @@ yydefault: positions.AddPosition(variable, positionBuilder.NewTokenPosition(yyDollar[3].token)) comments.AddComments(variable, yyDollar[3].token.Comments()) - use := expr.NewClusureUse(variable, false) + use := expr.NewClosureUse(variable, false) positions.AddPosition(use, positionBuilder.NewTokenPosition(yyDollar[3].token)) comments.AddComments(use, yyDollar[3].token.Comments()) @@ -4953,7 +4953,7 @@ yydefault: positions.AddPosition(variable, positionBuilder.NewTokenPosition(yyDollar[4].token)) comments.AddComments(variable, yyDollar[3].token.Comments()) - use := expr.NewClusureUse(variable, true) + use := expr.NewClosureUse(variable, true) positions.AddPosition(use, positionBuilder.NewTokensPosition(yyDollar[3].token, yyDollar[4].token)) comments.AddComments(use, yyDollar[3].token.Comments()) @@ -4971,7 +4971,7 @@ yydefault: positions.AddPosition(variable, positionBuilder.NewTokenPosition(yyDollar[1].token)) comments.AddComments(variable, yyDollar[1].token.Comments()) - use := expr.NewClusureUse(variable, false) + use := expr.NewClosureUse(variable, false) positions.AddPosition(use, positionBuilder.NewTokenPosition(yyDollar[1].token)) comments.AddComments(use, yyDollar[1].token.Comments()) @@ -4989,7 +4989,7 @@ yydefault: positions.AddPosition(variable, positionBuilder.NewTokenPosition(yyDollar[2].token)) comments.AddComments(variable, yyDollar[1].token.Comments()) - use := expr.NewClusureUse(variable, true) + use := expr.NewClosureUse(variable, true) positions.AddPosition(use, positionBuilder.NewTokensPosition(yyDollar[1].token, yyDollar[2].token)) comments.AddComments(use, yyDollar[1].token.Comments()) diff --git a/php5/php5.y b/php5/php5.y index b8623ca..4c994c3 100644 --- a/php5/php5.y +++ b/php5/php5.y @@ -2369,7 +2369,7 @@ lexical_var_list: positions.AddPosition(variable, positionBuilder.NewTokenPosition($3)) comments.AddComments(variable, $3.Comments()) - use := expr.NewClusureUse(variable, false) + use := expr.NewClosureUse(variable, false) positions.AddPosition(use, positionBuilder.NewTokenPosition($3)) comments.AddComments(use, $3.Comments()) @@ -2385,7 +2385,7 @@ lexical_var_list: positions.AddPosition(variable, positionBuilder.NewTokenPosition($4)) comments.AddComments(variable, $3.Comments()) - use := expr.NewClusureUse(variable, true) + use := expr.NewClosureUse(variable, true) positions.AddPosition(use, positionBuilder.NewTokensPosition($3, $4)) comments.AddComments(use, $3.Comments()) @@ -2401,7 +2401,7 @@ lexical_var_list: positions.AddPosition(variable, positionBuilder.NewTokenPosition($1)) comments.AddComments(variable, $1.Comments()) - use := expr.NewClusureUse(variable, false) + use := expr.NewClosureUse(variable, false) positions.AddPosition(use, positionBuilder.NewTokenPosition($1)) comments.AddComments(use, $1.Comments()) @@ -2417,7 +2417,7 @@ lexical_var_list: positions.AddPosition(variable, positionBuilder.NewTokenPosition($2)) comments.AddComments(variable, $1.Comments()) - use := expr.NewClusureUse(variable, true) + use := expr.NewClosureUse(variable, true) positions.AddPosition(use, positionBuilder.NewTokensPosition($1, $2)) comments.AddComments(use, $1.Comments()) diff --git a/php7/php7.go b/php7/php7.go index 5e0994e..d0eace0 100644 --- a/php7/php7.go +++ b/php7/php7.go @@ -4830,7 +4830,7 @@ yydefault: positions.AddPosition(identifier, positionBuilder.NewTokenPosition(yyDollar[1].token)) variable := expr.NewVariable(identifier) positions.AddPosition(variable, positionBuilder.NewTokenPosition(yyDollar[1].token)) - yyVAL.node = expr.NewClusureUse(variable, false) + yyVAL.node = expr.NewClosureUse(variable, false) positions.AddPosition(yyVAL.node, positionBuilder.NewTokenPosition(yyDollar[1].token)) comments.AddComments(identifier, yyDollar[1].token.Comments()) @@ -4845,7 +4845,7 @@ yydefault: positions.AddPosition(identifier, positionBuilder.NewTokenPosition(yyDollar[2].token)) variable := expr.NewVariable(identifier) positions.AddPosition(variable, positionBuilder.NewTokenPosition(yyDollar[2].token)) - yyVAL.node = expr.NewClusureUse(variable, true) + yyVAL.node = expr.NewClosureUse(variable, true) positions.AddPosition(yyVAL.node, positionBuilder.NewTokensPosition(yyDollar[1].token, yyDollar[2].token)) comments.AddComments(identifier, yyDollar[2].token.Comments()) diff --git a/php7/php7.y b/php7/php7.y index 813a07c..807f917 100644 --- a/php7/php7.y +++ b/php7/php7.y @@ -1933,7 +1933,7 @@ lexical_var: positions.AddPosition(identifier, positionBuilder.NewTokenPosition($1)) variable := expr.NewVariable(identifier) positions.AddPosition(variable, positionBuilder.NewTokenPosition($1)) - $$ = expr.NewClusureUse(variable, false) + $$ = expr.NewClosureUse(variable, false) positions.AddPosition($$, positionBuilder.NewTokenPosition($1)) comments.AddComments(identifier, $1.Comments()) @@ -1946,7 +1946,7 @@ lexical_var: positions.AddPosition(identifier, positionBuilder.NewTokenPosition($2)) variable := expr.NewVariable(identifier) positions.AddPosition(variable, positionBuilder.NewTokenPosition($2)) - $$ = expr.NewClusureUse(variable, true) + $$ = expr.NewClosureUse(variable, true) positions.AddPosition($$, positionBuilder.NewTokensPosition($1, $2)) comments.AddComments(identifier, $2.Comments())