diff --git a/Makefile b/Makefile index a5b8399..5f88103 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ fmt: build: go generate ./... - go build + go build ./cmd/... run: ./php-parser -d go $(PHPFILE) diff --git a/main.go b/cmd/php-parser/main.go similarity index 70% rename from main.go rename to cmd/php-parser/main.go index a1ad92c..569dd30 100644 --- a/main.go +++ b/cmd/php-parser/main.go @@ -1,9 +1,9 @@ package main import ( - "bytes" "flag" "fmt" + "github.com/z7zmey/php-parser/pkg/ast/traverser" "io/ioutil" "log" "os" @@ -13,15 +13,14 @@ import ( "github.com/pkg/profile" "github.com/yookoala/realpath" - "github.com/z7zmey/php-parser/parser" - "github.com/z7zmey/php-parser/printer" - "github.com/z7zmey/php-parser/visitor" + "github.com/z7zmey/php-parser/pkg/parser" + "github.com/z7zmey/php-parser/pkg/ast/visitor" ) var wg sync.WaitGroup var phpVersion string -var dumpType string var profiler string +var dump *bool var withFreeFloating *bool var showResolvedNs *bool var printBack *bool @@ -42,7 +41,7 @@ func main() { showResolvedNs = flag.Bool("r", false, "resolve names") printBack = flag.Bool("pb", false, "print AST back into the parsed file") printPath = flag.Bool("p", false, "print filepath") - flag.StringVar(&dumpType, "d", "", "dump format: [custom, go, json, pretty_json]") + dump = flag.Bool("d", false, "dump") flag.StringVar(&profiler, "prof", "", "start profiler: [cpu, mem, trace]") flag.StringVar(&phpVersion, "phpver", "7.4", "php version") @@ -115,7 +114,7 @@ func parserWorker(fileCh <-chan *file, r chan<- result) { } if *withFreeFloating { - parserWorker.WithFreeFloating() + parserWorker.WithTokens() } parserWorker.Parse() @@ -143,44 +142,26 @@ func printerWorker(r <-chan result) { fmt.Fprintf(os.Stdout, "==> %s\n", e) } - if *printBack { - o := bytes.NewBuffer([]byte{}) - p := printer.NewPrinter(o) - p.Print(res.parser.GetRootNode()) + //if *printBack { + // o := bytes.NewBuffer([]byte{}) + // p := printer.NewPrinter(o) + // p.Print(res.parser.GetRootNode()) + // + // err := ioutil.WriteFile(res.path, o.Bytes(), 0644) + // checkErr(err) + //} - err := ioutil.WriteFile(res.path, o.Bytes(), 0644) - checkErr(err) - } - - var nsResolver *visitor.NamespaceResolver if *showResolvedNs { - nsResolver = visitor.NewNamespaceResolver() - res.parser.GetRootNode().Walk(nsResolver) + v := visitor.NewNamespaceResolver() + t := traverser.NewDFS(v) + t.Traverse(res.parser.GetRootNode()) + fmt.Printf("%+v", v.ResolvedNames) } - switch dumpType { - case "custom": - dumper := &visitor.Dumper{ - Writer: os.Stdout, - Indent: "| ", - NsResolver: nsResolver, - } - res.parser.GetRootNode().Walk(dumper) - case "json": - dumper := &visitor.JsonDumper{ - Writer: os.Stdout, - NsResolver: nsResolver, - } - res.parser.GetRootNode().Walk(dumper) - case "pretty_json": - dumper := &visitor.PrettyJsonDumper{ - Writer: os.Stdout, - NsResolver: nsResolver, - } - res.parser.GetRootNode().Walk(dumper) - case "go": - dumper := &visitor.GoDumper{Writer: os.Stdout} - res.parser.GetRootNode().Walk(dumper) + if *dump == true { + v := visitor.NewDump(os.Stdout) + t := traverser.NewDFS(v) + t.Traverse(res.parser.GetRootNode()) } wg.Done() diff --git a/pkg/ast/traverser/dfs.go b/pkg/ast/traverser/dfs.go index f3d745e..4618bc9 100644 --- a/pkg/ast/traverser/dfs.go +++ b/pkg/ast/traverser/dfs.go @@ -13,479 +13,2641 @@ func NewDFS(visitor ast.Visitor) *DFS { } func (t *DFS) Traverse(n ast.Vertex) { - if n == nil { - return - } - - if !t.visitor.EnterNode(n) { - return - } - switch nn := n.(type) { case *ast.Root: - t.traverseArray("Stmts", nn.Stmts) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Stmts != nil { + t.visitor.Enter("Stmts", false) + for _, c := range nn.Stmts { + t.Traverse(c) + } + t.visitor.Leave("Stmts", false) + } case *ast.Nullable: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.Parameter: - t.traverseSingle("Type", nn.Type) - t.traverseSingle("Var", nn.Var) - t.traverseSingle("DefaultValue", nn.DefaultValue) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Type != nil { + t.visitor.Enter("Type", true) + t.Traverse(nn.Type) + t.visitor.Leave("Type", true) + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.DefaultValue != nil { + t.visitor.Enter("DefaultValue", true) + t.Traverse(nn.DefaultValue) + t.visitor.Leave("DefaultValue", true) + } case *ast.Identifier: + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } case *ast.ArgumentList: - t.traverseArray("Arguments", nn.Arguments) + if nn.Arguments != nil { + t.visitor.Enter("Arguments", false) + for _, c := range nn.Arguments { + t.Traverse(c) + } + t.visitor.Leave("Arguments", false) + } case *ast.Argument: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.StmtAltElse: - t.traverseSingle("Stmt", nn.Stmt) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Stmt != nil { + t.visitor.Enter("Stmt", true) + t.Traverse(nn.Stmt) + t.visitor.Leave("Stmt", true) + } case *ast.StmtAltElseIf: - t.traverseSingle("Cond", nn.Cond) - t.traverseSingle("Stmt", nn.Stmt) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Cond != nil { + t.visitor.Enter("Cond", true) + t.Traverse(nn.Cond) + t.visitor.Leave("Cond", true) + } + if nn.Stmt != nil { + t.visitor.Enter("Stmt", true) + t.Traverse(nn.Stmt) + t.visitor.Leave("Stmt", true) + } case *ast.StmtAltFor: - t.traverseArray("Init", nn.Init) - t.traverseArray("Cond", nn.Cond) - t.traverseArray("Loop", nn.Loop) - t.traverseSingle("Stmt", nn.Stmt) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Init != nil { + t.visitor.Enter("Init", false) + for _, c := range nn.Init { + t.Traverse(c) + } + t.visitor.Leave("Init", false) + } + if nn.Cond != nil { + t.visitor.Enter("Cond", false) + for _, c := range nn.Cond { + t.Traverse(c) + } + t.visitor.Leave("Cond", false) + } + if nn.Loop != nil { + t.visitor.Enter("Loop", false) + for _, c := range nn.Loop { + t.Traverse(c) + } + t.visitor.Leave("Loop", false) + } + if nn.Stmt != nil { + t.visitor.Enter("Stmt", true) + t.Traverse(nn.Stmt) + t.visitor.Leave("Stmt", true) + } case *ast.StmtAltForeach: - t.traverseSingle("Expr", nn.Expr) - t.traverseSingle("Key", nn.Key) - t.traverseSingle("Var", nn.Var) - t.traverseSingle("Stmt", nn.Stmt) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } + if nn.Key != nil { + t.visitor.Enter("Key", true) + t.Traverse(nn.Key) + t.visitor.Leave("Key", true) + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.Stmt != nil { + t.visitor.Enter("Stmt", true) + t.Traverse(nn.Stmt) + t.visitor.Leave("Stmt", true) + } case *ast.StmtAltIf: - t.traverseSingle("Cond", nn.Cond) - t.traverseSingle("Stmt", nn.Stmt) - t.traverseArray("ElseIf", nn.ElseIf) - t.traverseSingle("Else", nn.Else) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Cond != nil { + t.visitor.Enter("Cond", true) + t.Traverse(nn.Cond) + t.visitor.Leave("Cond", true) + } + if nn.Stmt != nil { + t.visitor.Enter("Stmt", true) + t.Traverse(nn.Stmt) + t.visitor.Leave("Stmt", true) + } + if nn.ElseIf != nil { + t.visitor.Enter("ElseIf", false) + for _, c := range nn.ElseIf { + t.Traverse(c) + } + t.visitor.Leave("ElseIf", false) + } + if nn.Else != nil { + t.visitor.Enter("Else", true) + t.Traverse(nn.Else) + t.visitor.Leave("Else", true) + } case *ast.StmtAltSwitch: - t.traverseSingle("Cond", nn.Cond) - t.traverseSingle("CaseList", nn.CaseList) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Cond != nil { + t.visitor.Enter("Cond", true) + t.Traverse(nn.Cond) + t.visitor.Leave("Cond", true) + } + if nn.CaseList != nil { + t.visitor.Enter("CaseList", true) + t.Traverse(nn.CaseList) + t.visitor.Leave("CaseList", true) + } case *ast.StmtAltWhile: - t.traverseSingle("Cond", nn.Cond) - t.traverseSingle("Stmt", nn.Stmt) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Cond != nil { + t.visitor.Enter("Cond", true) + t.Traverse(nn.Cond) + t.visitor.Leave("Cond", true) + } + if nn.Stmt != nil { + t.visitor.Enter("Stmt", true) + t.Traverse(nn.Stmt) + t.visitor.Leave("Stmt", true) + } case *ast.StmtBreak: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.StmtCase: - t.traverseSingle("Cond", nn.Cond) - t.traverseArray("Stmts", nn.Stmts) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Cond != nil { + t.visitor.Enter("Cond", true) + t.Traverse(nn.Cond) + t.visitor.Leave("Cond", true) + } + if nn.Stmts != nil { + t.visitor.Enter("Stmts", false) + for _, c := range nn.Stmts { + t.Traverse(c) + } + t.visitor.Leave("Stmts", false) + } case *ast.StmtCaseList: - t.traverseArray("Cases", nn.Cases) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Cases != nil { + t.visitor.Enter("Cases", false) + for _, c := range nn.Cases { + t.Traverse(c) + } + t.visitor.Leave("Cases", false) + } case *ast.StmtCatch: - t.traverseArray("Types", nn.Types) - t.traverseSingle("Var", nn.Var) - t.traverseArray("Stmts", nn.Stmts) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Types != nil { + t.visitor.Enter("Types", false) + for _, c := range nn.Types { + t.Traverse(c) + } + t.visitor.Leave("Types", false) + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.Stmts != nil { + t.visitor.Enter("Stmts", false) + for _, c := range nn.Stmts { + t.Traverse(c) + } + t.visitor.Leave("Stmts", false) + } case *ast.StmtClass: - t.traverseSingle("ClassName", nn.ClassName) - t.traverseArray("Modifiers", nn.Modifiers) - t.traverseArray("Stmts", nn.Stmts) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.ClassName != nil { + t.visitor.Enter("ClassName", true) + t.Traverse(nn.ClassName) + t.visitor.Leave("ClassName", true) + } + if nn.Modifiers != nil { + t.visitor.Enter("Modifiers", false) + for _, c := range nn.Modifiers { + t.Traverse(c) + } + t.visitor.Leave("Modifiers", false) + } + if nn.Stmts != nil { + t.visitor.Enter("Stmts", false) + for _, c := range nn.Stmts { + t.Traverse(c) + } + t.visitor.Leave("Stmts", false) + } case *ast.StmtClassConstList: - t.traverseArray("Modifiers", nn.Modifiers) - t.traverseArray("Consts", nn.Consts) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Modifiers != nil { + t.visitor.Enter("Modifiers", false) + for _, c := range nn.Modifiers { + t.Traverse(c) + } + t.visitor.Leave("Modifiers", false) + } + if nn.Consts != nil { + t.visitor.Enter("Consts", false) + for _, c := range nn.Consts { + t.Traverse(c) + } + t.visitor.Leave("Consts", false) + } case *ast.StmtClassExtends: - t.traverseSingle("ClassName", nn.ClassName) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.ClassName != nil { + t.visitor.Enter("ClassName", true) + t.Traverse(nn.ClassName) + t.visitor.Leave("ClassName", true) + } case *ast.StmtClassImplements: - t.traverseArray("InterfaceNames", nn.InterfaceNames) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.InterfaceNames != nil { + t.visitor.Enter("InterfaceNames", false) + for _, c := range nn.InterfaceNames { + t.Traverse(c) + } + t.visitor.Leave("InterfaceNames", false) + } case *ast.StmtClassMethod: - t.traverseSingle("MethodName", nn.MethodName) - t.traverseArray("Modifiers", nn.Modifiers) - t.traverseArray("Params", nn.Params) - t.traverseSingle("ReturnType", nn.ReturnType) - t.traverseSingle("Stmt", nn.Stmt) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.MethodName != nil { + t.visitor.Enter("MethodName", true) + t.Traverse(nn.MethodName) + t.visitor.Leave("MethodName", true) + } + if nn.Modifiers != nil { + t.visitor.Enter("Modifiers", false) + for _, c := range nn.Modifiers { + t.Traverse(c) + } + t.visitor.Leave("Modifiers", false) + } + if nn.Params != nil { + t.visitor.Enter("Params", false) + for _, c := range nn.Params { + t.Traverse(c) + } + t.visitor.Leave("Params", false) + } + if nn.ReturnType != nil { + t.visitor.Enter("ReturnType", true) + t.Traverse(nn.ReturnType) + t.visitor.Leave("ReturnType", true) + } + if nn.Stmt != nil { + t.visitor.Enter("Stmt", true) + t.Traverse(nn.Stmt) + t.visitor.Leave("Stmt", true) + } case *ast.StmtConstList: - t.traverseArray("Consts", nn.Consts) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Consts != nil { + t.visitor.Enter("Consts", false) + for _, c := range nn.Consts { + t.Traverse(c) + } + t.visitor.Leave("Consts", false) + } case *ast.StmtConstant: - t.traverseSingle("ConstantName", nn.ConstantName) - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.ConstantName != nil { + t.visitor.Enter("ConstantName", true) + t.Traverse(nn.ConstantName) + t.visitor.Leave("ConstantName", true) + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.StmtContinue: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.StmtDeclare: - t.traverseArray("Consts", nn.Consts) - t.traverseSingle("Stmt", nn.Stmt) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Consts != nil { + t.visitor.Enter("Consts", false) + for _, c := range nn.Consts { + t.Traverse(c) + } + t.visitor.Leave("Consts", false) + } + if nn.Stmt != nil { + t.visitor.Enter("Stmt", true) + t.Traverse(nn.Stmt) + t.visitor.Leave("Stmt", true) + } case *ast.StmtDefault: - t.traverseArray("Stmts", nn.Stmts) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Stmts != nil { + t.visitor.Enter("Stmts", false) + for _, c := range nn.Stmts { + t.Traverse(c) + } + t.visitor.Leave("Stmts", false) + } case *ast.StmtDo: - t.traverseSingle("Stmt", nn.Stmt) - t.traverseSingle("Cond", nn.Cond) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Stmt != nil { + t.visitor.Enter("Stmt", true) + t.Traverse(nn.Stmt) + t.visitor.Leave("Stmt", true) + } + if nn.Cond != nil { + t.visitor.Enter("Cond", true) + t.Traverse(nn.Cond) + t.visitor.Leave("Cond", true) + } case *ast.StmtEcho: - t.traverseArray("Exprs", nn.Exprs) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Exprs != nil { + t.visitor.Enter("Exprs", false) + for _, c := range nn.Exprs { + t.Traverse(c) + } + t.visitor.Leave("Exprs", false) + } case *ast.StmtElse: - t.traverseSingle("Stmt", nn.Stmt) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Stmt != nil { + t.visitor.Enter("Stmt", true) + t.Traverse(nn.Stmt) + t.visitor.Leave("Stmt", true) + } case *ast.StmtElseIf: - t.traverseSingle("Cond", nn.Cond) - t.traverseSingle("Stmt", nn.Stmt) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Cond != nil { + t.visitor.Enter("Cond", true) + t.Traverse(nn.Cond) + t.visitor.Leave("Cond", true) + } + if nn.Stmt != nil { + t.visitor.Enter("Stmt", true) + t.Traverse(nn.Stmt) + t.visitor.Leave("Stmt", true) + } case *ast.StmtExpression: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.StmtFinally: - t.traverseArray("Stmts", nn.Stmts) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Stmts != nil { + t.visitor.Enter("Stmts", false) + for _, c := range nn.Stmts { + t.Traverse(c) + } + t.visitor.Leave("Stmts", false) + } case *ast.StmtFor: - t.traverseArray("Init", nn.Init) - t.traverseArray("Cond", nn.Cond) - t.traverseArray("Loop", nn.Loop) - t.traverseSingle("Stmt", nn.Stmt) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Init != nil { + t.visitor.Enter("Init", false) + for _, c := range nn.Init { + t.Traverse(c) + } + t.visitor.Leave("Init", false) + } + if nn.Cond != nil { + t.visitor.Enter("Cond", false) + for _, c := range nn.Cond { + t.Traverse(c) + } + t.visitor.Leave("Cond", false) + } + if nn.Loop != nil { + t.visitor.Enter("Loop", false) + for _, c := range nn.Loop { + t.Traverse(c) + } + t.visitor.Leave("Loop", false) + } + if nn.Stmt != nil { + t.visitor.Enter("Stmt", true) + t.Traverse(nn.Stmt) + t.visitor.Leave("Stmt", true) + } case *ast.StmtForeach: - t.traverseSingle("Expr", nn.Expr) - t.traverseSingle("Key", nn.Key) - t.traverseSingle("Var", nn.Var) - t.traverseSingle("Stmt", nn.Stmt) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } + if nn.Key != nil { + t.visitor.Enter("Key", true) + t.Traverse(nn.Key) + t.visitor.Leave("Key", true) + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.Stmt != nil { + t.visitor.Enter("Stmt", true) + t.Traverse(nn.Stmt) + t.visitor.Leave("Stmt", true) + } case *ast.StmtFunction: - t.traverseSingle("FunctionName", nn.FunctionName) - t.traverseArray("Params", nn.Params) - t.traverseSingle("ReturnType", nn.ReturnType) - t.traverseArray("Stmts", nn.Stmts) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.FunctionName != nil { + t.visitor.Enter("FunctionName", true) + t.Traverse(nn.FunctionName) + t.visitor.Leave("FunctionName", true) + } + if nn.Params != nil { + t.visitor.Enter("Params", false) + for _, c := range nn.Params { + t.Traverse(c) + } + t.visitor.Leave("Params", false) + } + if nn.ReturnType != nil { + t.visitor.Enter("ReturnType", true) + t.Traverse(nn.ReturnType) + t.visitor.Leave("ReturnType", true) + } + if nn.Stmts != nil { + t.visitor.Enter("Stmts", false) + for _, c := range nn.Stmts { + t.Traverse(c) + } + t.visitor.Leave("Stmts", false) + } case *ast.StmtGlobal: - t.traverseArray("Vars", nn.Vars) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Vars != nil { + t.visitor.Enter("Vars", false) + for _, c := range nn.Vars { + t.Traverse(c) + } + t.visitor.Leave("Vars", false) + } case *ast.StmtGoto: - t.traverseSingle("Label", nn.Label) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Label != nil { + t.visitor.Enter("Label", true) + t.Traverse(nn.Label) + t.visitor.Leave("Label", true) + } case *ast.StmtGroupUse: - t.traverseSingle("UseType", nn.UseType) - t.traverseSingle("Prefix", nn.Prefix) - t.traverseArray("UseList", nn.UseList) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.UseType != nil { + t.visitor.Enter("UseType", true) + t.Traverse(nn.UseType) + t.visitor.Leave("UseType", true) + } + if nn.Prefix != nil { + t.visitor.Enter("Prefix", true) + t.Traverse(nn.Prefix) + t.visitor.Leave("Prefix", true) + } + if nn.UseList != nil { + t.visitor.Enter("UseList", false) + for _, c := range nn.UseList { + t.Traverse(c) + } + t.visitor.Leave("UseList", false) + } case *ast.StmtHaltCompiler: + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } case *ast.StmtIf: - t.traverseSingle("Cond", nn.Cond) - t.traverseSingle("Stmt", nn.Stmt) - t.traverseArray("ElseIf", nn.ElseIf) - t.traverseSingle("Else", nn.Else) + if nn.Cond != nil { + t.visitor.Enter("Cond", true) + t.Traverse(nn.Cond) + t.visitor.Leave("Cond", true) + } + if nn.Stmt != nil { + t.visitor.Enter("Stmt", true) + t.Traverse(nn.Stmt) + t.visitor.Leave("Stmt", true) + } + if nn.ElseIf != nil { + t.visitor.Enter("ElseIf", false) + for _, c := range nn.ElseIf { + t.Traverse(c) + } + t.visitor.Leave("ElseIf", false) + } + if nn.Else != nil { + t.visitor.Enter("Else", true) + t.Traverse(nn.Else) + t.visitor.Leave("Else", true) + } case *ast.StmtInlineHtml: + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } case *ast.StmtInterface: - t.traverseSingle("InterfaceName", nn.InterfaceName) - t.traverseArray("Stmts", nn.Stmts) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.InterfaceName != nil { + t.visitor.Enter("InterfaceName", true) + t.Traverse(nn.InterfaceName) + t.visitor.Leave("InterfaceName", true) + } + if nn.Stmts != nil { + t.visitor.Enter("Stmts", false) + for _, c := range nn.Stmts { + t.Traverse(c) + } + t.visitor.Leave("Stmts", false) + } case *ast.StmtInterfaceExtends: - t.traverseArray("InterfaceNames", nn.InterfaceNames) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.InterfaceNames != nil { + t.visitor.Enter("InterfaceNames", false) + for _, c := range nn.InterfaceNames { + t.Traverse(c) + } + t.visitor.Leave("InterfaceNames", false) + } case *ast.StmtLabel: - t.traverseSingle("LabelName", nn.LabelName) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.LabelName != nil { + t.visitor.Enter("LabelName", true) + t.Traverse(nn.LabelName) + t.visitor.Leave("LabelName", true) + } case *ast.StmtNamespace: - t.traverseSingle("NamespaceName", nn.NamespaceName) - t.traverseArray("Stmts", nn.Stmts) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.NamespaceName != nil { + t.visitor.Enter("NamespaceName", true) + t.Traverse(nn.NamespaceName) + t.visitor.Leave("NamespaceName", true) + } + if nn.Stmts != nil { + t.visitor.Enter("Stmts", false) + for _, c := range nn.Stmts { + t.Traverse(c) + } + t.visitor.Leave("Stmts", false) + } case *ast.StmtNop: + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } case *ast.StmtProperty: - t.traverseSingle("Var", nn.Var) - t.traverseSingle("Expr", nn.Expr) + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.StmtPropertyList: - t.traverseArray("Modifiers", nn.Modifiers) - t.traverseSingle("Type", nn.Type) - t.traverseArray("Properties", nn.Properties) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Modifiers != nil { + t.visitor.Enter("Modifiers", false) + for _, c := range nn.Modifiers { + t.Traverse(c) + } + t.visitor.Leave("Modifiers", false) + } + if nn.Type != nil { + t.visitor.Enter("Type", true) + t.Traverse(nn.Type) + t.visitor.Leave("Type", true) + } + if nn.Properties != nil { + t.visitor.Enter("Properties", false) + for _, c := range nn.Properties { + t.Traverse(c) + } + t.visitor.Leave("Properties", false) + } case *ast.StmtReturn: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.StmtStatic: - t.traverseArray("Vars", nn.Vars) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Vars != nil { + t.visitor.Enter("Vars", false) + for _, c := range nn.Vars { + t.Traverse(c) + } + t.visitor.Leave("Vars", false) + } case *ast.StmtStaticVar: - t.traverseSingle("Var", nn.Var) - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.StmtStmtList: - t.traverseArray("Stmts", nn.Stmts) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Stmts != nil { + t.visitor.Enter("Stmts", false) + for _, c := range nn.Stmts { + t.Traverse(c) + } + t.visitor.Leave("Stmts", false) + } case *ast.StmtSwitch: - t.traverseSingle("Cond", nn.Cond) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Cond != nil { + t.visitor.Enter("Cond", true) + t.Traverse(nn.Cond) + t.visitor.Leave("Cond", true) + } case *ast.StmtThrow: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.StmtTrait: - t.traverseSingle("TraitName", nn.TraitName) - t.traverseArray("Stmts", nn.Stmts) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.TraitName != nil { + t.visitor.Enter("TraitName", true) + t.Traverse(nn.TraitName) + t.visitor.Leave("TraitName", true) + } + if nn.Stmts != nil { + t.visitor.Enter("Stmts", false) + for _, c := range nn.Stmts { + t.Traverse(c) + } + t.visitor.Leave("Stmts", false) + } case *ast.StmtTraitAdaptationList: - t.traverseArray("Adaptations", nn.Adaptations) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Adaptations != nil { + t.visitor.Enter("Adaptations", false) + for _, c := range nn.Adaptations { + t.Traverse(c) + } + t.visitor.Leave("Adaptations", false) + } case *ast.StmtTraitMethodRef: - t.traverseSingle("Trait", nn.Trait) - t.traverseSingle("Method", nn.Method) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Trait != nil { + t.visitor.Enter("Trait", true) + t.Traverse(nn.Trait) + t.visitor.Leave("Trait", true) + } + if nn.Method != nil { + t.visitor.Enter("Method", true) + t.Traverse(nn.Method) + t.visitor.Leave("Method", true) + } case *ast.StmtTraitUse: - t.traverseArray("Traits", nn.Traits) - t.traverseSingle("TraitAdaptationList", nn.TraitAdaptationList) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Traits != nil { + t.visitor.Enter("Traits", false) + for _, c := range nn.Traits { + t.Traverse(c) + } + t.visitor.Leave("Traits", false) + } + if nn.TraitAdaptationList != nil { + t.visitor.Enter("TraitAdaptationList", true) + t.Traverse(nn.TraitAdaptationList) + t.visitor.Leave("TraitAdaptationList", true) + } case *ast.StmtTraitUseAlias: - t.traverseSingle("Ref", nn.Ref) - t.traverseSingle("Modifier", nn.Modifier) - t.traverseSingle("Alias", nn.Alias) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Ref != nil { + t.visitor.Enter("Ref", true) + t.Traverse(nn.Ref) + t.visitor.Leave("Ref", true) + } + if nn.Modifier != nil { + t.visitor.Enter("Modifier", true) + t.Traverse(nn.Modifier) + t.visitor.Leave("Modifier", true) + } + if nn.Alias != nil { + t.visitor.Enter("Alias", true) + t.Traverse(nn.Alias) + t.visitor.Leave("Alias", true) + } case *ast.StmtTraitUsePrecedence: - t.traverseSingle("Ref", nn.Ref) - t.traverseArray("Insteadof", nn.Insteadof) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Ref != nil { + t.visitor.Enter("Ref", true) + t.Traverse(nn.Ref) + t.visitor.Leave("Ref", true) + } + if nn.Insteadof != nil { + t.visitor.Enter("Insteadof", false) + for _, c := range nn.Insteadof { + t.Traverse(c) + } + t.visitor.Leave("Insteadof", false) + } case *ast.StmtTry: - t.traverseArray("Stmts", nn.Stmts) - t.traverseArray("Catches", nn.Catches) - t.traverseSingle("Finally", nn.Finally) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Stmts != nil { + t.visitor.Enter("Stmts", false) + for _, c := range nn.Stmts { + t.Traverse(c) + } + t.visitor.Leave("Stmts", false) + } + if nn.Catches != nil { + t.visitor.Enter("Catches", false) + for _, c := range nn.Catches { + t.Traverse(c) + } + t.visitor.Leave("Catches", false) + } + if nn.Finally != nil { + t.visitor.Enter("Finally", true) + t.Traverse(nn.Finally) + t.visitor.Leave("Finally", true) + } case *ast.StmtUnset: - t.traverseArray("Vars", nn.Vars) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Vars != nil { + t.visitor.Enter("Vars", false) + for _, c := range nn.Vars { + t.Traverse(c) + } + t.visitor.Leave("Vars", false) + } case *ast.StmtUse: - t.traverseSingle("UseType", nn.UseType) - t.traverseSingle("Use", nn.Use) - t.traverseSingle("Alias", nn.Alias) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.UseType != nil { + t.visitor.Enter("UseType", true) + t.Traverse(nn.UseType) + t.visitor.Leave("UseType", true) + } + if nn.Use != nil { + t.visitor.Enter("Use", true) + t.Traverse(nn.Use) + t.visitor.Leave("Use", true) + } + if nn.Alias != nil { + t.visitor.Enter("Alias", true) + t.Traverse(nn.Alias) + t.visitor.Leave("Alias", true) + } case *ast.StmtUseList: - t.traverseSingle("UseType", nn.UseType) - t.traverseArray("Uses", nn.Uses) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.UseType != nil { + t.visitor.Enter("UseType", true) + t.Traverse(nn.UseType) + t.visitor.Leave("UseType", true) + } + if nn.Uses != nil { + t.visitor.Enter("Uses", false) + for _, c := range nn.Uses { + t.Traverse(c) + } + t.visitor.Leave("Uses", false) + } case *ast.StmtWhile: - t.traverseSingle("Cond", nn.Cond) - t.traverseSingle("Stmt", nn.Stmt) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Cond != nil { + t.visitor.Enter("Cond", true) + t.Traverse(nn.Cond) + t.visitor.Leave("Cond", true) + } + if nn.Stmt != nil { + t.visitor.Enter("Stmt", true) + t.Traverse(nn.Stmt) + t.visitor.Leave("Stmt", true) + } case *ast.ExprArray: - t.traverseArray("Items", nn.Items) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Items != nil { + t.visitor.Enter("Items", false) + for _, c := range nn.Items { + t.Traverse(c) + } + t.visitor.Leave("Items", false) + } case *ast.ExprArrayDimFetch: - t.traverseSingle("Var", nn.Var) - t.traverseSingle("Dim", nn.Dim) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.Dim != nil { + t.visitor.Enter("Dim", true) + t.Traverse(nn.Dim) + t.visitor.Leave("Dim", true) + } case *ast.ExprArrayItem: - t.traverseSingle("Key", nn.Key) - t.traverseSingle("Val", nn.Val) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Key != nil { + t.visitor.Enter("Key", true) + t.Traverse(nn.Key) + t.visitor.Leave("Key", true) + } + if nn.Val != nil { + t.visitor.Enter("Val", true) + t.Traverse(nn.Val) + t.visitor.Leave("Val", true) + } case *ast.ExprArrowFunction: - t.traverseArray("Params", nn.Params) - t.traverseSingle("ReturnType", nn.ReturnType) - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Params != nil { + t.visitor.Enter("Params", false) + for _, c := range nn.Params { + t.Traverse(c) + } + t.visitor.Leave("Params", false) + } + if nn.ReturnType != nil { + t.visitor.Enter("ReturnType", true) + t.Traverse(nn.ReturnType) + t.visitor.Leave("ReturnType", true) + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprBitwiseNot: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprBooleanNot: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprClassConstFetch: - t.traverseSingle("Class", nn.Class) - t.traverseSingle("ConstantName", nn.ConstantName) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Class != nil { + t.visitor.Enter("Class", true) + t.Traverse(nn.Class) + t.visitor.Leave("Class", true) + } + if nn.ConstantName != nil { + t.visitor.Enter("ConstantName", true) + t.Traverse(nn.ConstantName) + t.visitor.Leave("ConstantName", true) + } case *ast.ExprClone: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprClosure: - t.traverseArray("Params", nn.Params) - t.traverseSingle("ClosureUse", nn.ClosureUse) - t.traverseSingle("ReturnType", nn.ReturnType) - t.traverseArray("Stmts", nn.Stmts) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Params != nil { + t.visitor.Enter("Params", false) + for _, c := range nn.Params { + t.Traverse(c) + } + t.visitor.Leave("Params", false) + } + if nn.ClosureUse != nil { + t.visitor.Enter("ClosureUse", true) + t.Traverse(nn.ClosureUse) + t.visitor.Leave("ClosureUse", true) + } + if nn.ReturnType != nil { + t.visitor.Enter("ReturnType", true) + t.Traverse(nn.ReturnType) + t.visitor.Leave("ReturnType", true) + } + if nn.Stmts != nil { + t.visitor.Enter("Stmts", false) + for _, c := range nn.Stmts { + t.Traverse(c) + } + t.visitor.Leave("Stmts", false) + } case *ast.ExprClosureUse: - t.traverseArray("Uses", nn.Uses) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Uses != nil { + t.visitor.Enter("Uses", false) + for _, c := range nn.Uses { + t.Traverse(c) + } + t.visitor.Leave("Uses", false) + } case *ast.ExprConstFetch: - t.traverseSingle("Const", nn.Const) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Const != nil { + t.visitor.Enter("Const", true) + t.Traverse(nn.Const) + t.visitor.Leave("Const", true) + } case *ast.ExprEmpty: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprErrorSuppress: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprEval: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprExit: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprFunctionCall: - t.traverseSingle("Function", nn.Function) - t.traverseSingle("ArgumentList", nn.ArgumentList) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Function != nil { + t.visitor.Enter("Function", true) + t.Traverse(nn.Function) + t.visitor.Leave("Function", true) + } + if nn.ArgumentList != nil { + t.visitor.Enter("ArgumentList", true) + t.Traverse(nn.ArgumentList) + t.visitor.Leave("ArgumentList", true) + } case *ast.ExprInclude: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprIncludeOnce: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprInstanceOf: - t.traverseSingle("Expr", nn.Expr) - t.traverseSingle("Class", nn.Class) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } + if nn.Class != nil { + t.visitor.Enter("Class", true) + t.Traverse(nn.Class) + t.visitor.Leave("Class", true) + } case *ast.ExprIsset: - t.traverseArray("Vars", nn.Vars) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Vars != nil { + t.visitor.Enter("Vars", false) + for _, c := range nn.Vars { + t.Traverse(c) + } + t.visitor.Leave("Vars", false) + } case *ast.ExprList: - t.traverseArray("Items", nn.Items) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Items != nil { + t.visitor.Enter("Items", false) + for _, c := range nn.Items { + t.Traverse(c) + } + t.visitor.Leave("Items", false) + } case *ast.ExprMethodCall: - t.traverseSingle("Var", nn.Var) - t.traverseSingle("Method", nn.Method) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.Method != nil { + t.visitor.Enter("Method", true) + t.Traverse(nn.Method) + t.visitor.Leave("Method", true) + } case *ast.ExprNew: - t.traverseSingle("Class", nn.Class) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Class != nil { + t.visitor.Enter("Class", true) + t.Traverse(nn.Class) + t.visitor.Leave("Class", true) + } case *ast.ExprPostDec: - t.traverseSingle("Var", nn.Var) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } case *ast.ExprPostInc: - t.traverseSingle("Var", nn.Var) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } case *ast.ExprPreDec: - t.traverseSingle("Var", nn.Var) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } case *ast.ExprPreInc: - t.traverseSingle("Var", nn.Var) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } case *ast.ExprPrint: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprPropertyFetch: - t.traverseSingle("Var", nn.Var) - t.traverseSingle("Property", nn.Property) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.Property != nil { + t.visitor.Enter("Property", true) + t.Traverse(nn.Property) + t.visitor.Leave("Property", true) + } case *ast.ExprReference: - t.traverseSingle("Var", nn.Var) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } case *ast.ExprRequire: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprRequireOnce: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprShellExec: - t.traverseArray("Parts", nn.Parts) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Parts != nil { + t.visitor.Enter("Parts", false) + for _, c := range nn.Parts { + t.Traverse(c) + } + t.visitor.Leave("Parts", false) + } case *ast.ExprShortArray: - t.traverseArray("Items", nn.Items) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Items != nil { + t.visitor.Enter("Items", false) + for _, c := range nn.Items { + t.Traverse(c) + } + t.visitor.Leave("Items", false) + } case *ast.ExprShortList: - t.traverseArray("Items", nn.Items) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Items != nil { + t.visitor.Enter("Items", false) + for _, c := range nn.Items { + t.Traverse(c) + } + t.visitor.Leave("Items", false) + } case *ast.ExprStaticCall: - t.traverseSingle("Class", nn.Class) - t.traverseSingle("Call", nn.Call) - t.traverseSingle("ArgumentList", nn.ArgumentList) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Class != nil { + t.visitor.Enter("Class", true) + t.Traverse(nn.Class) + t.visitor.Leave("Class", true) + } + if nn.Call != nil { + t.visitor.Enter("Call", true) + t.Traverse(nn.Call) + t.visitor.Leave("Call", true) + } + if nn.ArgumentList != nil { + t.visitor.Enter("ArgumentList", true) + t.Traverse(nn.ArgumentList) + t.visitor.Leave("ArgumentList", true) + } case *ast.ExprStaticPropertyFetch: - t.traverseSingle("Class", nn.Class) - t.traverseSingle("Property", nn.Property) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Class != nil { + t.visitor.Enter("Class", true) + t.Traverse(nn.Class) + t.visitor.Leave("Class", true) + } + if nn.Property != nil { + t.visitor.Enter("Property", true) + t.Traverse(nn.Property) + t.visitor.Leave("Property", true) + } case *ast.ExprTernary: - t.traverseSingle("Condition", nn.Condition) - t.traverseSingle("IfTrue", nn.IfTrue) - t.traverseSingle("IfFalse", nn.IfFalse) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Condition != nil { + t.visitor.Enter("Condition", true) + t.Traverse(nn.Condition) + t.visitor.Leave("Condition", true) + } + if nn.IfTrue != nil { + t.visitor.Enter("IfTrue", true) + t.Traverse(nn.IfTrue) + t.visitor.Leave("IfTrue", true) + } + if nn.IfFalse != nil { + t.visitor.Enter("IfFalse", true) + t.Traverse(nn.IfFalse) + t.visitor.Leave("IfFalse", true) + } case *ast.ExprUnaryMinus: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprUnaryPlus: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprVariable: - t.traverseSingle("VarName", nn.VarName) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.VarName != nil { + t.visitor.Enter("VarName", true) + t.Traverse(nn.VarName) + t.visitor.Leave("VarName", true) + } case *ast.ExprYield: - t.traverseSingle("Key", nn.Key) - t.traverseSingle("Value", nn.Value) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Key != nil { + t.visitor.Enter("Key", true) + t.Traverse(nn.Key) + t.visitor.Leave("Key", true) + } + if nn.Value != nil { + t.visitor.Enter("Value", true) + t.Traverse(nn.Value) + t.visitor.Leave("Value", true) + } case *ast.ExprYieldFrom: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprAssign: - t.traverseSingle("Var", nn.Var) - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprAssignReference: - t.traverseSingle("Var", nn.Var) - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprAssignBitwiseAnd: - t.traverseSingle("Var", nn.Var) - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprAssignBitwiseOr: - t.traverseSingle("Var", nn.Var) - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprAssignBitwiseXor: - t.traverseSingle("Var", nn.Var) - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprAssignCoalesce: - t.traverseSingle("Var", nn.Var) - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprAssignConcat: - t.traverseSingle("Var", nn.Var) - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprAssignDiv: - t.traverseSingle("Var", nn.Var) - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprAssignMinus: - t.traverseSingle("Var", nn.Var) - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprAssignMod: - t.traverseSingle("Var", nn.Var) - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprAssignMul: - t.traverseSingle("Var", nn.Var) - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprAssignPlus: - t.traverseSingle("Var", nn.Var) - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprAssignPow: - t.traverseSingle("Var", nn.Var) - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprAssignShiftLeft: - t.traverseSingle("Var", nn.Var) - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprAssignShiftRight: - t.traverseSingle("Var", nn.Var) - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Var != nil { + t.visitor.Enter("Var", true) + t.Traverse(nn.Var) + t.visitor.Leave("Var", true) + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprBinaryBitwiseAnd: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinaryBitwiseOr: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinaryBitwiseXor: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinaryBooleanAnd: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinaryBooleanOr: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinaryCoalesce: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinaryConcat: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinaryDiv: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinaryEqual: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinaryGreater: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinaryGreaterOrEqual: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinaryIdentical: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinaryLogicalAnd: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinaryLogicalOr: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinaryLogicalXor: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinaryMinus: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinaryMod: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinaryMul: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinaryNotEqual: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinaryNotIdentical: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinaryPlus: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinaryPow: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinaryShiftLeft: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinaryShiftRight: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinarySmaller: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinarySmallerOrEqual: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprBinarySpaceship: - t.traverseSingle("Left", nn.Left) - t.traverseSingle("Right", nn.Right) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Left != nil { + t.visitor.Enter("Left", true) + t.Traverse(nn.Left) + t.visitor.Leave("Left", true) + } + if nn.Right != nil { + t.visitor.Enter("Right", true) + t.Traverse(nn.Right) + t.visitor.Leave("Right", true) + } case *ast.ExprCastArray: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprCastBool: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprCastDouble: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprCastInt: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprCastObject: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprCastString: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ExprCastUnset: - t.traverseSingle("Expr", nn.Expr) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Expr != nil { + t.visitor.Enter("Expr", true) + t.Traverse(nn.Expr) + t.visitor.Leave("Expr", true) + } case *ast.ScalarDnumber: + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } case *ast.ScalarEncapsed: - t.traverseArray("Parts", nn.Parts) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Parts != nil { + t.visitor.Enter("Parts", false) + for _, c := range nn.Parts { + t.Traverse(c) + } + t.visitor.Leave("Parts", false) + } case *ast.ScalarEncapsedStringPart: + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } case *ast.ScalarHeredoc: - t.traverseArray("Parts", nn.Parts) + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + if nn.Parts != nil { + t.visitor.Enter("Parts", false) + for _, c := range nn.Parts { + t.Traverse(c) + } + t.visitor.Leave("Parts", false) + } case *ast.ScalarLnumber: + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } case *ast.ScalarMagicConstant: + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } case *ast.ScalarString: + if nn == nil { + return + } + if !t.visitor.EnterNode(nn) { + return + } + default: + panic("unexpected type of node") } t.visitor.LeaveNode(n) } - -func (t *DFS) traverseSingle(key string, n ast.Vertex) { - if n == nil { - return - } - - t.visitor.Enter(key, true) - t.Traverse(n) - t.visitor.Leave(key, true) -} - -func (t *DFS) traverseArray(key string, nn []ast.Vertex) { - if nn == nil { - return - } - - t.visitor.Enter(key, false) - for _, c := range nn { - t.Traverse(c) - } - t.visitor.Leave(key, false) -} diff --git a/pkg/ast/visitor/dump.go b/pkg/ast/visitor/dump.go index a000a59..9d4c316 100644 --- a/pkg/ast/visitor/dump.go +++ b/pkg/ast/visitor/dump.go @@ -117,7 +117,7 @@ func (v *Dump) Identifier(n *ast.Identifier) { v.printIndentIfNotSingle(v.indent - 1) v.print("&ast.Identifier{\n") - v.printIndentIfNotSingle(v.indent) + v.printIndent(v.indent) v.print(fmt.Sprintf("Value: %q,\n", n.Value)) } @@ -335,7 +335,7 @@ func (v *Dump) StmtInlineHtml(n *ast.StmtInlineHtml) { v.printIndentIfNotSingle(v.indent - 1) v.print("&ast.StmtInlineHtml{\n") - v.printIndentIfNotSingle(v.indent) + v.printIndent(v.indent) v.print(fmt.Sprintf("Value: %q,\n", n.Value)) } @@ -953,7 +953,7 @@ func (v *Dump) ScalarDnumber(n *ast.ScalarDnumber) { v.printIndentIfNotSingle(v.indent - 1) v.print("&ast.ScalarDnumber{\n") - v.printIndentIfNotSingle(v.indent) + v.printIndent(v.indent) v.print(fmt.Sprintf("Value: %q,\n", n.Value)) } @@ -966,7 +966,7 @@ func (v *Dump) ScalarEncapsedStringPart(n *ast.ScalarEncapsedStringPart) { v.printIndentIfNotSingle(v.indent - 1) v.print("&ast.ScalarEncapsedStringPart{\n") - v.printIndentIfNotSingle(v.indent) + v.printIndent(v.indent) v.print(fmt.Sprintf("Value: %q,\n", n.Value)) } @@ -974,7 +974,7 @@ func (v *Dump) ScalarHeredoc(n *ast.ScalarHeredoc) { v.printIndentIfNotSingle(v.indent - 1) v.print("&ast.ScalarHeredoc{\n") - v.printIndentIfNotSingle(v.indent) + v.printIndent(v.indent) v.print(fmt.Sprintf("Label: %q,\n", n.Label)) } @@ -982,7 +982,7 @@ func (v *Dump) ScalarLnumber(n *ast.ScalarLnumber) { v.printIndentIfNotSingle(v.indent - 1) v.print("&ast.ScalarLnumber{\n") - v.printIndentIfNotSingle(v.indent) + v.printIndent(v.indent) v.print(fmt.Sprintf("Value: %q,\n", n.Value)) } @@ -990,7 +990,7 @@ func (v *Dump) ScalarMagicConstant(n *ast.ScalarMagicConstant) { v.printIndentIfNotSingle(v.indent - 1) v.print("&ast.ScalarMagicConstant{\n") - v.printIndentIfNotSingle(v.indent) + v.printIndent(v.indent) v.print(fmt.Sprintf("Value: %q,\n", n.Value)) } @@ -998,6 +998,29 @@ func (v *Dump) ScalarString(n *ast.ScalarString) { v.printIndentIfNotSingle(v.indent - 1) v.print("&ast.ScalarString{\n") - v.printIndentIfNotSingle(v.indent) + v.printIndent(v.indent) + v.print(fmt.Sprintf("Value: %q,\n", n.Value)) +} + +func (v *Dump) NameName(_ *ast.NameName) { + v.printIndentIfNotSingle(v.indent - 1) + v.print("&ast.NameName{\n") +} + +func (v *Dump) NameFullyQualified(_ *ast.NameFullyQualified) { + v.printIndentIfNotSingle(v.indent - 1) + v.print("&ast.NameFullyQualified{\n") +} + +func (v *Dump) NameRelative(_ *ast.NameRelative) { + v.printIndentIfNotSingle(v.indent - 1) + v.print("&ast.NameRelative{\n") +} + +func (v *Dump) NameNamePart(n *ast.NameNamePart) { + v.printIndentIfNotSingle(v.indent - 1) + v.print("&ast.NameNamePart{\n") + + v.printIndent(v.indent) v.print(fmt.Sprintf("Value: %q,\n", n.Value)) } diff --git a/pkg/ast/visitor/dump_test.go b/pkg/ast/visitor/dump_test.go index b1f5153..3d68a0d 100644 --- a/pkg/ast/visitor/dump_test.go +++ b/pkg/ast/visitor/dump_test.go @@ -16,7 +16,7 @@ func ExampleDump() { Var: &ast.ExprVariable{}, }, &ast.StmtInlineHtml{ - Value: "foo", + Value: []byte("foo"), }, }, } diff --git a/pkg/ast/visitor/namespace_resolver.go b/pkg/ast/visitor/namespace_resolver.go new file mode 100644 index 0000000..f79378c --- /dev/null +++ b/pkg/ast/visitor/namespace_resolver.go @@ -0,0 +1,399 @@ +// Package visitor contains walker.visitor implementations +package visitor + +import ( + "errors" + "github.com/z7zmey/php-parser/pkg/ast" + "strings" +) + +// NamespaceResolver visitor +type NamespaceResolver struct { + Null + Namespace *Namespace + ResolvedNames map[ast.Vertex]string + + goDeep bool +} + +// NewNamespaceResolver NamespaceResolver type constructor +func NewNamespaceResolver() *NamespaceResolver { + return &NamespaceResolver{ + Namespace: NewNamespace(""), + ResolvedNames: map[ast.Vertex]string{}, + goDeep: true, + } +} + +func (nsr *NamespaceResolver) EnterNode(n ast.Vertex) bool { + n.Accept(nsr) + + if !nsr.goDeep { + nsr.goDeep = true + return false + } + + return true +} + +func (nsr *NamespaceResolver) StmtNamespace(n *ast.StmtNamespace) { + if n.NamespaceName == nil { + nsr.Namespace = NewNamespace("") + } else { + NSParts := n.NamespaceName.(*ast.NameName).Parts + nsr.Namespace = NewNamespace(concatNameParts(NSParts)) + } +} + +func (nsr *NamespaceResolver) StmtUseList(n *ast.StmtUseList) { + useType := "" + if n.UseType != nil { + useType = string(n.UseType.(*ast.Identifier).Value) + } + + for _, nn := range n.Uses { + nsr.AddAlias(useType, nn, nil) + } + + nsr.goDeep = false +} + +func (nsr *NamespaceResolver) StmtGroupUse(n *ast.StmtGroupUse) { + useType := "" + if n.UseType != nil { + useType = string(n.UseType.(*ast.Identifier).Value) + } + + for _, nn := range n.UseList { + nsr.AddAlias(useType, nn, n.Prefix.(*ast.NameName).Parts) + } + + nsr.goDeep = false +} + +func (nsr *NamespaceResolver) StmtClass(n *ast.StmtClass) { + if n.Extends != nil { + nsr.ResolveName(n.Extends.ClassName, "") + } + + if n.Implements != nil { + for _, interfaceName := range n.Implements.InterfaceNames { + nsr.ResolveName(interfaceName, "") + } + } + + if n.ClassName != nil { + nsr.AddNamespacedName(n, string(n.ClassName.(*ast.Identifier).Value)) + } +} + +func (nsr *NamespaceResolver) StmtInterface(n *ast.StmtInterface) { + if n.Extends != nil { + for _, interfaceName := range n.Extends.InterfaceNames { + nsr.ResolveName(interfaceName, "") + } + } + + nsr.AddNamespacedName(n, string(n.InterfaceName.(*ast.Identifier).Value)) +} + +func (nsr *NamespaceResolver) StmtTrait(n *ast.StmtTrait) { + nsr.AddNamespacedName(n, string(n.TraitName.(*ast.Identifier).Value)) +} + +func (nsr *NamespaceResolver) StmtFunction(n *ast.StmtFunction) { + nsr.AddNamespacedName(n, string(n.FunctionName.(*ast.Identifier).Value)) + + for _, parameter := range n.Params { + nsr.ResolveType(parameter.(*ast.Parameter).Type) + } + + if n.ReturnType != nil { + nsr.ResolveType(n.ReturnType) + } +} + +func (nsr *NamespaceResolver) StmtClassMethod(n *ast.StmtClassMethod) { + for _, parameter := range n.Params { + nsr.ResolveType(parameter.(*ast.Parameter).Type) + } + + if n.ReturnType != nil { + nsr.ResolveType(n.ReturnType) + } +} + +func (nsr *NamespaceResolver) ExprClosure(n *ast.ExprClosure) { + for _, parameter := range n.Params { + nsr.ResolveType(parameter.(*ast.Parameter).Type) + } + + if n.ReturnType != nil { + nsr.ResolveType(n.ReturnType) + } +} + +func (nsr *NamespaceResolver) StmtConstList(n *ast.StmtConstList) { + for _, constant := range n.Consts { + nsr.AddNamespacedName(constant, string(constant.(*ast.StmtConstant).ConstantName.(*ast.Identifier).Value)) + } +} + +func (nsr *NamespaceResolver) ExprStaticCall(n *ast.ExprStaticCall) { + nsr.ResolveName(n.Class, "") +} + +func (nsr *NamespaceResolver) ExprStaticPropertyFetch(n *ast.ExprStaticPropertyFetch) { + nsr.ResolveName(n.Class, "") +} + +func (nsr *NamespaceResolver) ExprClassConstFetch(n *ast.ExprClassConstFetch) { + nsr.ResolveName(n.Class, "") +} + +func (nsr *NamespaceResolver) ExprNew(n *ast.ExprNew) { + nsr.ResolveName(n.Class, "") +} + +func (nsr *NamespaceResolver) ExprInstanceOf(n *ast.ExprInstanceOf) { + nsr.ResolveName(n.Class, "") +} + +func (nsr *NamespaceResolver) StmtCatch(n *ast.StmtCatch) { + for _, t := range n.Types { + nsr.ResolveName(t, "") + } +} + +func (nsr *NamespaceResolver) ExprFunctionCall(n *ast.ExprFunctionCall) { + nsr.ResolveName(n.Function, "function") +} + +func (nsr *NamespaceResolver) ExprConstFetch(n *ast.ExprConstFetch) { + nsr.ResolveName(n.Const, "const") +} + +func (nsr *NamespaceResolver) StmtTraitUse(n *ast.StmtTraitUse) { + for _, t := range n.Traits { + nsr.ResolveName(t, "") + } + + if adaptationList, ok := n.TraitAdaptationList.(*ast.StmtTraitAdaptationList); ok { + for _, a := range adaptationList.Adaptations { + switch aa := a.(type) { + case *ast.StmtTraitUsePrecedence: + refTrait := aa.Ref.(*ast.StmtTraitMethodRef).Trait + if refTrait != nil { + nsr.ResolveName(refTrait, "") + } + for _, insteadOf := range aa.Insteadof { + nsr.ResolveName(insteadOf, "") + } + + case *ast.StmtTraitUseAlias: + refTrait := aa.Ref.(*ast.StmtTraitMethodRef).Trait + if refTrait != nil { + nsr.ResolveName(refTrait, "") + } + } + } + } +} + +// LeaveNode is invoked after node process +func (nsr *NamespaceResolver) LeaveNode(n ast.Vertex) { + switch nn := n.(type) { + case *ast.StmtNamespace: + if nn.Stmts != nil { + nsr.Namespace = NewNamespace("") + } + } +} + +// AddAlias adds a new alias +func (nsr *NamespaceResolver) AddAlias(useType string, nn ast.Vertex, prefix []ast.Vertex) { + switch use := nn.(type) { + case *ast.StmtUse: + if use.UseType != nil { + useType = string(use.UseType.(*ast.Identifier).Value) + } + + useNameParts := use.Use.(*ast.NameName).Parts + var alias string + if use.Alias == nil { + alias = string(useNameParts[len(useNameParts)-1].(*ast.NameNamePart).Value) + } else { + alias = string(use.Alias.(*ast.Identifier).Value) + } + + nsr.Namespace.AddAlias(useType, concatNameParts(prefix, useNameParts), alias) + } +} + +// AddNamespacedName adds namespaced name by node +func (nsr *NamespaceResolver) AddNamespacedName(nn ast.Vertex, nodeName string) { + if nsr.Namespace.Namespace == "" { + nsr.ResolvedNames[nn] = nodeName + } else { + nsr.ResolvedNames[nn] = nsr.Namespace.Namespace + "\\" + nodeName + } +} + +// ResolveName adds a resolved fully qualified name by node +func (nsr *NamespaceResolver) ResolveName(nameNode ast.Vertex, aliasType string) { + resolved, err := nsr.Namespace.ResolveName(nameNode, aliasType) + if err == nil { + nsr.ResolvedNames[nameNode] = resolved + } +} + +// ResolveType adds a resolved fully qualified type name +func (nsr *NamespaceResolver) ResolveType(n ast.Vertex) { + switch nn := n.(type) { + case *ast.Nullable: + nsr.ResolveType(nn.Expr) + case *ast.NameName: + nsr.ResolveName(n, "") + case *ast.NameRelative: + nsr.ResolveName(n, "") + case *ast.NameFullyQualified: + nsr.ResolveName(n, "") + } +} + +// Namespace context +type Namespace struct { + Namespace string + Aliases map[string]map[string]string +} + +// NewNamespace constructor +func NewNamespace(NSName string) *Namespace { + return &Namespace{ + Namespace: NSName, + Aliases: map[string]map[string]string{ + "": {}, + "const": {}, + "function": {}, + }, + } +} + +// AddAlias adds a new alias +func (ns *Namespace) AddAlias(aliasType string, aliasName string, alias string) { + aliasType = strings.ToLower(aliasType) + + if aliasType == "const" { + ns.Aliases[aliasType][alias] = aliasName + } else { + ns.Aliases[aliasType][strings.ToLower(alias)] = aliasName + } +} + +// ResolveName returns a resolved fully qualified name +func (ns *Namespace) ResolveName(nameNode ast.Vertex, aliasType string) (string, error) { + switch n := nameNode.(type) { + case *ast.NameFullyQualified: + // Fully qualifid name is already resolved + return concatNameParts(n.Parts), nil + + case *ast.NameRelative: + if ns.Namespace == "" { + return concatNameParts(n.Parts), nil + } + return ns.Namespace + "\\" + concatNameParts(n.Parts), nil + + case *ast.NameName: + if aliasType == "const" && len(n.Parts) == 1 { + part := strings.ToLower(string(n.Parts[0].(*ast.NameNamePart).Value)) + if part == "true" || part == "false" || part == "null" { + return part, nil + } + } + + if aliasType == "" && len(n.Parts) == 1 { + part := strings.ToLower(string(n.Parts[0].(*ast.NameNamePart).Value)) + + switch part { + case "self": + fallthrough + case "static": + fallthrough + case "parent": + fallthrough + case "int": + fallthrough + case "float": + fallthrough + case "bool": + fallthrough + case "string": + fallthrough + case "void": + fallthrough + case "iterable": + fallthrough + case "object": + return part, nil + } + } + + aliasName, err := ns.ResolveAlias(nameNode, aliasType) + if err != nil { + // resolve as relative name if alias not found + if ns.Namespace == "" { + return concatNameParts(n.Parts), nil + } + return ns.Namespace + "\\" + concatNameParts(n.Parts), nil + } + + if len(n.Parts) > 1 { + // if name qualified, replace first part by alias + return aliasName + "\\" + concatNameParts(n.Parts[1:]), nil + } + + return aliasName, nil + } + + return "", errors.New("must be instance of name.Names") +} + +// ResolveAlias returns alias or error if not found +func (ns *Namespace) ResolveAlias(nameNode ast.Vertex, aliasType string) (string, error) { + aliasType = strings.ToLower(aliasType) + nameParts := nameNode.(*ast.NameName).Parts + + firstPartStr := string(nameParts[0].(*ast.NameNamePart).Value) + + if len(nameParts) > 1 { // resolve aliases for qualified names, always against class alias type + firstPartStr = strings.ToLower(firstPartStr) + aliasType = "" + } else { + if aliasType != "const" { // constants are case-sensitive + firstPartStr = strings.ToLower(firstPartStr) + } + } + + aliasName, ok := ns.Aliases[aliasType][firstPartStr] + if !ok { + return "", errors.New("Not found") + } + + return aliasName, nil +} + +func concatNameParts(parts ...[]ast.Vertex) string { + str := "" + + for _, p := range parts { + for _, n := range p { + if str == "" { + str = string(n.(*ast.NameNamePart).Value) + } else { + str = str + "\\" + string(n.(*ast.NameNamePart).Value) + } + } + } + + return str +} diff --git a/pkg/ast/visitor/namespace_resolver_test.go b/pkg/ast/visitor/namespace_resolver_test.go new file mode 100644 index 0000000..61ed537 --- /dev/null +++ b/pkg/ast/visitor/namespace_resolver_test.go @@ -0,0 +1,986 @@ +package visitor_test + +import ( + "testing" + + "gotest.tools/assert" + + "github.com/z7zmey/php-parser/pkg/ast" + "github.com/z7zmey/php-parser/pkg/ast/traverser" + "github.com/z7zmey/php-parser/pkg/ast/visitor" +) + +func TestResolveStaticCall(t *testing.T) { + nameAB := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("A")}, &ast.NameNamePart{Value: []byte("B")}}} + nameBC := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("B")}, &ast.NameNamePart{Value: []byte("C")}}} + + stxTree := &ast.StmtStmtList{ + Stmts: []ast.Vertex{ + &ast.StmtUseList{ + Uses: []ast.Vertex{ + &ast.StmtUse{ + Use: nameAB, + }, + }, + }, + &ast.ExprStaticCall{ + Class: nameBC, + Call: &ast.Identifier{Value: []byte("foo")}, + ArgumentList: &ast.ArgumentList{}, + }, + }, + } + + expected := map[ast.Vertex]string{ + nameBC: "A\\B\\C", + } + + nsResolver := visitor.NewNamespaceResolver() + dfsTraverser := traverser.NewDFS(nsResolver) + dfsTraverser.Traverse(stxTree) + + assert.DeepEqual(t, expected, nsResolver.ResolvedNames) +} + +func TestResolveStaticPropertyFetch(t *testing.T) { + nameAB := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("A")}, &ast.NameNamePart{Value: []byte("B")}}} + nameBC := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("B")}, &ast.NameNamePart{Value: []byte("C")}}} + + stxTree := &ast.StmtStmtList{ + Stmts: []ast.Vertex{ + &ast.StmtUseList{ + Uses: []ast.Vertex{ + &ast.StmtUse{ + Use: nameAB, + }, + }, + }, + &ast.ExprStaticPropertyFetch{ + Class: nameBC, + Property: &ast.Identifier{Value: []byte("foo")}, + }, + }, + } + + expected := map[ast.Vertex]string{ + nameBC: "A\\B\\C", + } + + nsResolver := visitor.NewNamespaceResolver() + dfsTraverser := traverser.NewDFS(nsResolver) + dfsTraverser.Traverse(stxTree) + + assert.DeepEqual(t, expected, nsResolver.ResolvedNames) +} + +func TestResolveClassConstFetch(t *testing.T) { + nameAB := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("A")}, &ast.NameNamePart{Value: []byte("B")}}} + nameBC := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("B")}, &ast.NameNamePart{Value: []byte("C")}}} + + stxTree := &ast.StmtStmtList{ + Stmts: []ast.Vertex{ + &ast.StmtUseList{ + Uses: []ast.Vertex{ + &ast.StmtUse{ + Use: nameAB, + }, + }, + }, + &ast.ExprClassConstFetch{ + Class: nameBC, + ConstantName: &ast.Identifier{Value: []byte("FOO")}, + }, + }, + } + + expected := map[ast.Vertex]string{ + nameBC: "A\\B\\C", + } + + nsResolver := visitor.NewNamespaceResolver() + dfsTraverser := traverser.NewDFS(nsResolver) + dfsTraverser.Traverse(stxTree) + + assert.DeepEqual(t, expected, nsResolver.ResolvedNames) +} + +func TestResolveNew(t *testing.T) { + nameAB := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("A")}, &ast.NameNamePart{Value: []byte("B")}}} + nameBC := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("B")}, &ast.NameNamePart{Value: []byte("C")}}} + + stxTree := &ast.StmtStmtList{ + Stmts: []ast.Vertex{ + &ast.StmtUseList{ + Uses: []ast.Vertex{ + &ast.StmtUse{ + Use: nameAB, + }, + }, + }, + &ast.ExprNew{ + Class: nameBC, + ArgumentList: &ast.ArgumentList{}, + }, + }, + } + + expected := map[ast.Vertex]string{ + nameBC: "A\\B\\C", + } + + nsResolver := visitor.NewNamespaceResolver() + dfsTraverser := traverser.NewDFS(nsResolver) + dfsTraverser.Traverse(stxTree) + + assert.DeepEqual(t, expected, nsResolver.ResolvedNames) +} + +func TestResolveInstanceOf(t *testing.T) { + nameAB := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("A")}, &ast.NameNamePart{Value: []byte("B")}}} + nameBC := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("B")}, &ast.NameNamePart{Value: []byte("C")}}} + + stxTree := &ast.StmtStmtList{ + Stmts: []ast.Vertex{ + &ast.StmtUseList{ + Uses: []ast.Vertex{ + &ast.StmtUse{ + Use: nameAB, + }, + }, + }, + &ast.ExprInstanceOf{ + Expr: &ast.ExprVariable{VarName: &ast.Identifier{Value: []byte("foo")}}, + Class: nameBC, + }, + }, + } + + expected := map[ast.Vertex]string{ + nameBC: "A\\B\\C", + } + + nsResolver := visitor.NewNamespaceResolver() + dfsTraverser := traverser.NewDFS(nsResolver) + dfsTraverser.Traverse(stxTree) + + assert.DeepEqual(t, expected, nsResolver.ResolvedNames) +} + +func TestResolveInstanceCatch(t *testing.T) { + nameAB := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("A")}, &ast.NameNamePart{Value: []byte("B")}}} + nameBC := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("B")}, &ast.NameNamePart{Value: []byte("C")}}} + + nameDE := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("D")}, &ast.NameNamePart{Value: []byte("E")}}} + nameF := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("F")}}} + + stxTree := &ast.StmtStmtList{ + Stmts: []ast.Vertex{ + &ast.StmtUseList{ + Uses: []ast.Vertex{ + &ast.StmtUse{ + Use: nameAB, + }, + &ast.StmtUse{ + Use: nameDE, + Alias: &ast.Identifier{Value: []byte("F")}, + }, + }, + }, + &ast.StmtTry{ + Stmts: []ast.Vertex{}, + Catches: []ast.Vertex{ + &ast.StmtCatch{ + Types: []ast.Vertex{ + nameBC, + nameF, + }, + Var: &ast.ExprVariable{VarName: &ast.Identifier{Value: []byte("foo")}}, + Stmts: []ast.Vertex{}, + }, + }, + }, + }, + } + + expected := map[ast.Vertex]string{ + nameBC: "A\\B\\C", + nameF: "D\\E", + } + + nsResolver := visitor.NewNamespaceResolver() + dfsTraverser := traverser.NewDFS(nsResolver) + dfsTraverser.Traverse(stxTree) + + assert.DeepEqual(t, expected, nsResolver.ResolvedNames) +} + +func TestResolveFunctionCall(t *testing.T) { + nameAB := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("A")}, &ast.NameNamePart{Value: []byte("B")}}} + nameB := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("B")}}} + + stxTree := &ast.StmtStmtList{ + Stmts: []ast.Vertex{ + &ast.StmtUseList{ + UseType: &ast.Identifier{Value: []byte("function")}, + Uses: []ast.Vertex{ + &ast.StmtUse{ + Use: nameAB, + }, + }, + }, + &ast.ExprFunctionCall{ + Function: nameB, + ArgumentList: &ast.ArgumentList{}, + }, + }, + } + + expected := map[ast.Vertex]string{ + nameB: "A\\B", + } + + nsResolver := visitor.NewNamespaceResolver() + dfsTraverser := traverser.NewDFS(nsResolver) + dfsTraverser.Traverse(stxTree) + + assert.DeepEqual(t, expected, nsResolver.ResolvedNames) +} + +func TestResolveConstFetch(t *testing.T) { + nameAB := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("A")}, &ast.NameNamePart{Value: []byte("B")}}} + nameB := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("B")}}} + + stxTree := &ast.StmtStmtList{ + Stmts: []ast.Vertex{ + &ast.StmtUseList{ + UseType: &ast.Identifier{Value: []byte("const")}, + Uses: []ast.Vertex{ + &ast.StmtUse{ + Use: nameAB, + }, + }, + }, + &ast.ExprConstFetch{ + Const: nameB, + }, + }, + } + + expected := map[ast.Vertex]string{ + nameB: "A\\B", + } + + nsResolver := visitor.NewNamespaceResolver() + dfsTraverser := traverser.NewDFS(nsResolver) + dfsTraverser.Traverse(stxTree) + + assert.DeepEqual(t, expected, nsResolver.ResolvedNames) +} + +func TestResolveGroupUse(t *testing.T) { + nameAB := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("A")}, &ast.NameNamePart{Value: []byte("B")}}} + nameBD := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("B")}, &ast.NameNamePart{Value: []byte("D")}}} + nameE := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("E")}}} + nameC := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("C")}}} + nameF := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("F")}}} + + stxTree := &ast.StmtStmtList{ + Stmts: []ast.Vertex{ + &ast.StmtGroupUse{ + Prefix: nameAB, + UseList: []ast.Vertex{ + &ast.StmtUse{ + UseType: &ast.Identifier{Value: []byte("Function")}, + Use: nameF, + }, + &ast.StmtUse{ + UseType: &ast.Identifier{Value: []byte("const")}, + Use: nameC, + }, + }, + }, + &ast.StmtGroupUse{ + Prefix: nameBD, + UseType: &ast.Identifier{Value: []byte("Function")}, + UseList: []ast.Vertex{ + &ast.StmtUse{ + Use: nameE, + }, + }, + }, + &ast.ExprConstFetch{ + Const: nameC, + }, + &ast.ExprFunctionCall{ + Function: nameF, + ArgumentList: &ast.ArgumentList{}, + }, + &ast.ExprFunctionCall{ + Function: nameE, + ArgumentList: &ast.ArgumentList{}, + }, + }, + } + + expected := map[ast.Vertex]string{ + nameC: "A\\B\\C", + nameF: "A\\B\\F", + nameE: "B\\D\\E", + } + + nsResolver := visitor.NewNamespaceResolver() + dfsTraverser := traverser.NewDFS(nsResolver) + dfsTraverser.Traverse(stxTree) + + assert.DeepEqual(t, expected, nsResolver.ResolvedNames) +} + +func TestResolveTraitUse(t *testing.T) { + nameAB := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("A")}, &ast.NameNamePart{Value: []byte("B")}}} + nameB := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("B")}}} + nameD := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("D")}}} + + fullyQualifiedNameB := &ast.NameFullyQualified{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("B")}}} + fullyQualifiedNameBC := &ast.NameFullyQualified{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("B")}, &ast.NameNamePart{Value: []byte("C")}}} + relativeNameB := &ast.NameRelative{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("B")}}} + relativeNameBC := &ast.NameRelative{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("B")}, &ast.NameNamePart{Value: []byte("C")}}} + + stxTree := &ast.StmtStmtList{ + Stmts: []ast.Vertex{ + &ast.StmtUseList{ + Uses: []ast.Vertex{ + &ast.StmtUse{ + Use: nameAB, + }, + }, + }, + &ast.StmtTraitUse{ + Traits: []ast.Vertex{ + nameB, + relativeNameB, + }, + TraitAdaptationList: &ast.StmtTraitAdaptationList{ + Adaptations: []ast.Vertex{ + &ast.StmtTraitUsePrecedence{ + Ref: &ast.StmtTraitMethodRef{ + Trait: fullyQualifiedNameB, + Method: &ast.Identifier{Value: []byte("foo")}, + }, + Insteadof: []ast.Vertex{fullyQualifiedNameBC}, + }, + &ast.StmtTraitUseAlias{ + Ref: &ast.StmtTraitMethodRef{ + Trait: relativeNameBC, + Method: &ast.Identifier{Value: []byte("foo")}, + }, + Alias: &ast.Identifier{Value: []byte("bar")}, + }, + }, + }, + }, + &ast.StmtTraitUse{ + Traits: []ast.Vertex{ + nameD, + }, + }, + }, + } + + expected := map[ast.Vertex]string{ + nameB: "A\\B", + nameD: "D", + relativeNameB: "B", + fullyQualifiedNameB: "B", + fullyQualifiedNameBC: "B\\C", + relativeNameBC: "B\\C", + } + + nsResolver := visitor.NewNamespaceResolver() + dfsTraverser := traverser.NewDFS(nsResolver) + dfsTraverser.Traverse(stxTree) + + assert.DeepEqual(t, expected, nsResolver.ResolvedNames) +} + +func TestResolveClassName(t *testing.T) { + nameAB := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("A")}, &ast.NameNamePart{Value: []byte("B")}}} + nameBC := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("B")}, &ast.NameNamePart{Value: []byte("C")}}} + + class := &ast.StmtClass{ + ClassName: &ast.Identifier{Value: []byte("A")}, + Extends: &ast.StmtClassExtends{ + ClassName: nameAB, + }, + Implements: &ast.StmtClassImplements{ + InterfaceNames: []ast.Vertex{ + nameBC, + }, + }, + } + + stxTree := &ast.StmtStmtList{ + Stmts: []ast.Vertex{ + class, + }, + } + + expected := map[ast.Vertex]string{ + class: "A", + nameAB: "A\\B", + nameBC: "B\\C", + } + + nsResolver := visitor.NewNamespaceResolver() + dfsTraverser := traverser.NewDFS(nsResolver) + dfsTraverser.Traverse(stxTree) + + assert.DeepEqual(t, expected, nsResolver.ResolvedNames) +} + +func TestResolveInterfaceName(t *testing.T) { + nameAB := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("A")}, &ast.NameNamePart{Value: []byte("B")}}} + nameBC := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("B")}, &ast.NameNamePart{Value: []byte("C")}}} + + interfaceNode := &ast.StmtInterface{ + InterfaceName: &ast.Identifier{Value: []byte("A")}, + Extends: &ast.StmtInterfaceExtends{ + InterfaceNames: []ast.Vertex{ + nameAB, + nameBC, + }, + }, + } + + stxTree := &ast.StmtStmtList{ + Stmts: []ast.Vertex{ + interfaceNode, + }, + } + + expected := map[ast.Vertex]string{ + interfaceNode: "A", + nameAB: "A\\B", + nameBC: "B\\C", + } + + nsResolver := visitor.NewNamespaceResolver() + dfsTraverser := traverser.NewDFS(nsResolver) + dfsTraverser.Traverse(stxTree) + + assert.DeepEqual(t, expected, nsResolver.ResolvedNames) +} + +func TestResolveTraitName(t *testing.T) { + traitNode := &ast.StmtTrait{ + TraitName: &ast.Identifier{Value: []byte("A")}, + Stmts: []ast.Vertex{}, + } + + stxTree := &ast.StmtStmtList{ + Stmts: []ast.Vertex{ + traitNode, + }, + } + + expected := map[ast.Vertex]string{ + traitNode: "A", + } + + nsResolver := visitor.NewNamespaceResolver() + dfsTraverser := traverser.NewDFS(nsResolver) + dfsTraverser.Traverse(stxTree) + + assert.DeepEqual(t, expected, nsResolver.ResolvedNames) +} + +func TestResolveFunctionName(t *testing.T) { + nameAB := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("A")}, &ast.NameNamePart{Value: []byte("B")}}} + nameBC := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("B")}, &ast.NameNamePart{Value: []byte("C")}}} + + functionNode := &ast.StmtFunction{ + ReturnsRef: false, + FunctionName: &ast.Identifier{Value: []byte("A")}, + Params: []ast.Vertex{ + &ast.Parameter{ + ByRef: false, + Variadic: false, + Type: nameAB, + Var: &ast.ExprVariable{VarName: &ast.Identifier{Value: []byte("foo")}}, + }, + }, + ReturnType: &ast.Nullable{Expr: nameBC}, + Stmts: []ast.Vertex{}, + } + + stxTree := &ast.StmtStmtList{ + Stmts: []ast.Vertex{ + functionNode, + }, + } + + expected := map[ast.Vertex]string{ + functionNode: "A", + nameAB: "A\\B", + nameBC: "B\\C", + } + + nsResolver := visitor.NewNamespaceResolver() + dfsTraverser := traverser.NewDFS(nsResolver) + dfsTraverser.Traverse(stxTree) + + assert.DeepEqual(t, expected, nsResolver.ResolvedNames) +} + +func TestResolveMethodName(t *testing.T) { + nameAB := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("A")}, &ast.NameNamePart{Value: []byte("B")}}} + nameBC := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("B")}, &ast.NameNamePart{Value: []byte("C")}}} + + methodNode := &ast.StmtClassMethod{ + ReturnsRef: false, + MethodName: &ast.Identifier{Value: []byte("A")}, + Params: []ast.Vertex{ + &ast.Parameter{ + ByRef: false, + Variadic: false, + Type: nameAB, + Var: &ast.ExprVariable{VarName: &ast.Identifier{Value: []byte("foo")}}, + }, + }, + ReturnType: &ast.Nullable{Expr: nameBC}, + Stmt: &ast.StmtStmtList{ + Stmts: []ast.Vertex{}, + }, + } + + expected := map[ast.Vertex]string{ + nameAB: "A\\B", + nameBC: "B\\C", + } + + nsResolver := visitor.NewNamespaceResolver() + dfsTraverser := traverser.NewDFS(nsResolver) + dfsTraverser.Traverse(methodNode) + + assert.DeepEqual(t, expected, nsResolver.ResolvedNames) +} + +func TestResolveClosureName(t *testing.T) { + nameAB := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("A")}, &ast.NameNamePart{Value: []byte("B")}}} + nameBC := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("B")}, &ast.NameNamePart{Value: []byte("C")}}} + + closureNode := &ast.ExprClosure{ + ReturnsRef: false, + Static: false, + Params: []ast.Vertex{ + &ast.Parameter{ + ByRef: false, + Variadic: false, + Type: nameAB, + Var: &ast.ExprVariable{VarName: &ast.Identifier{Value: []byte("foo")}}, + }, + }, + ClosureUse: nil, + ReturnType: &ast.Nullable{Expr: nameBC}, + Stmts: []ast.Vertex{}, + } + + expected := map[ast.Vertex]string{ + nameAB: "A\\B", + nameBC: "B\\C", + } + + nsResolver := visitor.NewNamespaceResolver() + dfsTraverser := traverser.NewDFS(nsResolver) + dfsTraverser.Traverse(closureNode) + + assert.DeepEqual(t, expected, nsResolver.ResolvedNames) +} + +func TestResolveConstantsName(t *testing.T) { + nameAB := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("A")}, &ast.NameNamePart{Value: []byte("B")}}} + + constantB := &ast.StmtConstant{ + ConstantName: &ast.Identifier{Value: []byte("B")}, + Expr: &ast.ScalarLnumber{Value: []byte("1")}, + } + constantC := &ast.StmtConstant{ + ConstantName: &ast.Identifier{Value: []byte("C")}, + Expr: &ast.ScalarLnumber{Value: []byte("1")}, + } + + stxTree := &ast.StmtStmtList{ + Stmts: []ast.Vertex{ + &ast.StmtNamespace{ + NamespaceName: nameAB, + }, + &ast.StmtConstList{ + Consts: []ast.Vertex{ + constantB, + constantC, + }, + }, + }, + } + + expected := map[ast.Vertex]string{ + constantB: "A\\B\\B", + constantC: "A\\B\\C", + } + + nsResolver := visitor.NewNamespaceResolver() + dfsTraverser := traverser.NewDFS(nsResolver) + dfsTraverser.Traverse(stxTree) + + assert.DeepEqual(t, expected, nsResolver.ResolvedNames) +} + +func TestResolveNamespaces(t *testing.T) { + namespaceAB := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("A")}, &ast.NameNamePart{Value: []byte("B")}}} + namespaceCD := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("C")}, &ast.NameNamePart{Value: []byte("D")}}} + + nameAC := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("A")}, &ast.NameNamePart{Value: []byte("C")}}} + nameCF := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("C")}, &ast.NameNamePart{Value: []byte("F")}}} + nameFG := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("F")}, &ast.NameNamePart{Value: []byte("G")}}} + relativeNameCE := &ast.NameRelative{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("C")}, &ast.NameNamePart{Value: []byte("E")}}} + + constantB := &ast.StmtConstant{ + ConstantName: &ast.Identifier{Value: []byte("B")}, + Expr: &ast.ScalarLnumber{Value: []byte("1")}, + } + constantC := &ast.StmtConstant{ + ConstantName: &ast.Identifier{Value: []byte("C")}, + Expr: &ast.ScalarLnumber{Value: []byte("1")}, + } + + stxTree := &ast.StmtStmtList{ + Stmts: []ast.Vertex{ + &ast.StmtNamespace{ + NamespaceName: namespaceAB, + }, + &ast.StmtConstList{ + Consts: []ast.Vertex{ + constantB, + constantC, + }, + }, + &ast.ExprStaticCall{ + Class: nameFG, + Call: &ast.Identifier{Value: []byte("foo")}, + ArgumentList: &ast.ArgumentList{}, + }, + &ast.StmtNamespace{ + Stmts: []ast.Vertex{}, + }, + &ast.StmtNamespace{ + NamespaceName: namespaceCD, + Stmts: []ast.Vertex{ + &ast.StmtUseList{ + Uses: []ast.Vertex{ + &ast.StmtUse{ + Use: nameAC, + }, + }, + }, + &ast.ExprStaticCall{ + Class: relativeNameCE, + Call: &ast.Identifier{Value: []byte("foo")}, + ArgumentList: &ast.ArgumentList{}, + }, + &ast.ExprStaticCall{ + Class: nameCF, + Call: &ast.Identifier{Value: []byte("foo")}, + ArgumentList: &ast.ArgumentList{}, + }, + }, + }, + }, + } + + expected := map[ast.Vertex]string{ + constantB: "A\\B\\B", + constantC: "A\\B\\C", + nameFG: "A\\B\\F\\G", + relativeNameCE: "C\\D\\C\\E", + nameCF: "A\\C\\F", + } + + nsResolver := visitor.NewNamespaceResolver() + dfsTraverser := traverser.NewDFS(nsResolver) + dfsTraverser.Traverse(stxTree) + + assert.DeepEqual(t, expected, nsResolver.ResolvedNames) +} + +func TestResolveStaticCallDinamicClassName(t *testing.T) { + stxTree := &ast.StmtStmtList{ + Stmts: []ast.Vertex{ + &ast.ExprStaticCall{ + Class: &ast.ExprVariable{VarName: &ast.Identifier{Value: []byte("foo")}}, + Call: &ast.Identifier{Value: []byte("foo")}, + ArgumentList: &ast.ArgumentList{}, + }, + }, + } + + expected := map[ast.Vertex]string{} + + nsResolver := visitor.NewNamespaceResolver() + dfsTraverser := traverser.NewDFS(nsResolver) + dfsTraverser.Traverse(stxTree) + + assert.DeepEqual(t, expected, nsResolver.ResolvedNames) +} + +func TestDoNotResolveReservedConstants(t *testing.T) { + namespaceName := &ast.NameName{Parts: []ast.Vertex{&ast.NameNamePart{Value: []byte("Foo")}}} + + constantTrue := &ast.NameName{ + Parts: []ast.Vertex{ + &ast.NameNamePart{Value: []byte("True")}, + }, + } + + constantFalse := &ast.NameName{ + Parts: []ast.Vertex{ + &ast.NameNamePart{Value: []byte("False")}, + }, + } + + constantNull := &ast.NameName{ + Parts: []ast.Vertex{ + &ast.NameNamePart{Value: []byte("NULL")}, + }, + } + + stxTree := &ast.StmtStmtList{ + Stmts: []ast.Vertex{ + &ast.StmtNamespace{ + NamespaceName: namespaceName, + }, + &ast.StmtExpression{ + Expr: &ast.ExprConstFetch{ + Const: constantTrue, + }, + }, + &ast.StmtExpression{ + Expr: &ast.ExprConstFetch{ + Const: constantFalse, + }, + }, + &ast.StmtExpression{ + Expr: &ast.ExprConstFetch{ + Const: constantNull, + }, + }, + }, + } + + expected := map[ast.Vertex]string{ + constantTrue: "true", + constantFalse: "false", + constantNull: "null", + } + + nsResolver := visitor.NewNamespaceResolver() + dfsTraverser := traverser.NewDFS(nsResolver) + dfsTraverser.Traverse(stxTree) + + assert.DeepEqual(t, expected, nsResolver.ResolvedNames) +} + +func TestDoNotResolveReservedNames(t *testing.T) { + + nameInt := &ast.NameName{ + Parts: []ast.Vertex{ + &ast.NameNamePart{Value: []byte("int")}, + }, + } + + nameFloat := &ast.NameName{ + Parts: []ast.Vertex{ + &ast.NameNamePart{Value: []byte("float")}, + }, + } + + nameBool := &ast.NameName{ + Parts: []ast.Vertex{ + &ast.NameNamePart{Value: []byte("bool")}, + }, + } + + nameString := &ast.NameName{ + Parts: []ast.Vertex{ + &ast.NameNamePart{Value: []byte("string")}, + }, + } + + nameVoid := &ast.NameName{ + Parts: []ast.Vertex{ + &ast.NameNamePart{Value: []byte("void")}, + }, + } + + nameIterable := &ast.NameName{ + Parts: []ast.Vertex{ + &ast.NameNamePart{Value: []byte("iterable")}, + }, + } + + nameObject := &ast.NameName{ + Parts: []ast.Vertex{ + &ast.NameNamePart{Value: []byte("object")}, + }, + } + + function := &ast.StmtFunction{ + FunctionName: &ast.Identifier{Value: []byte("bar")}, + Params: []ast.Vertex{ + &ast.Parameter{ + Type: nameInt, + Var: &ast.ExprVariable{ + VarName: &ast.Identifier{Value: []byte("Int")}, + }, + }, + &ast.Parameter{ + Type: nameFloat, + Var: &ast.ExprVariable{ + VarName: &ast.Identifier{Value: []byte("Float")}, + }, + }, + &ast.Parameter{ + Type: nameBool, + Var: &ast.ExprVariable{ + VarName: &ast.Identifier{Value: []byte("Bool")}, + }, + }, + &ast.Parameter{ + Type: nameString, + Var: &ast.ExprVariable{ + VarName: &ast.Identifier{Value: []byte("String")}, + }, + }, + &ast.Parameter{ + Type: nameVoid, + Var: &ast.ExprVariable{ + VarName: &ast.Identifier{Value: []byte("Void")}, + }, + }, + &ast.Parameter{ + Type: nameIterable, + Var: &ast.ExprVariable{ + VarName: &ast.Identifier{Value: []byte("Iterable")}, + }, + }, + &ast.Parameter{ + Type: nameObject, + Var: &ast.ExprVariable{ + VarName: &ast.Identifier{Value: []byte("Object")}, + }, + }, + }, + } + + stxTree := &ast.StmtStmtList{ + Stmts: []ast.Vertex{ + &ast.StmtNamespace{ + NamespaceName: &ast.NameName{ + Parts: []ast.Vertex{ + &ast.NameNamePart{Value: []byte("Foo")}, + }, + }, + }, + function, + }, + } + + expected := map[ast.Vertex]string{ + function: "Foo\\bar", + nameInt: "int", + nameFloat: "float", + nameBool: "bool", + nameString: "string", + nameVoid: "void", + nameIterable: "iterable", + nameObject: "object", + } + + nsResolver := visitor.NewNamespaceResolver() + dfsTraverser := traverser.NewDFS(nsResolver) + dfsTraverser.Traverse(stxTree) + + assert.DeepEqual(t, expected, nsResolver.ResolvedNames) +} + +func TestDoNotResolveReservedSpecialNames(t *testing.T) { + + nameSelf := &ast.NameName{ + Parts: []ast.Vertex{ + &ast.NameNamePart{Value: []byte("Self")}, + }, + } + + nameStatic := &ast.NameName{ + Parts: []ast.Vertex{ + &ast.NameNamePart{Value: []byte("Static")}, + }, + } + + nameParent := &ast.NameName{ + Parts: []ast.Vertex{ + &ast.NameNamePart{Value: []byte("Parent")}, + }, + } + + cls := &ast.StmtClass{ + ClassName: &ast.Identifier{Value: []byte("Bar")}, + Stmts: []ast.Vertex{ + &ast.StmtExpression{ + Expr: &ast.ExprStaticCall{ + Class: nameSelf, + Call: &ast.Identifier{Value: []byte("func")}, + ArgumentList: &ast.ArgumentList{}, + }, + }, + &ast.StmtExpression{ + Expr: &ast.ExprStaticCall{ + Class: nameStatic, + Call: &ast.Identifier{Value: []byte("func")}, + ArgumentList: &ast.ArgumentList{}, + }, + }, + &ast.StmtExpression{ + Expr: &ast.ExprStaticCall{ + Class: nameParent, + Call: &ast.Identifier{Value: []byte("func")}, + ArgumentList: &ast.ArgumentList{}, + }, + }, + }, + } + + stxTree := &ast.StmtStmtList{ + Stmts: []ast.Vertex{ + &ast.StmtNamespace{ + NamespaceName: &ast.NameName{ + Parts: []ast.Vertex{ + &ast.NameNamePart{Value: []byte("Foo")}, + }, + }, + }, + cls, + }, + } + + expected := map[ast.Vertex]string{ + cls: "Foo\\Bar", + nameSelf: "self", + nameStatic: "static", + nameParent: "parent", + } + + nsResolver := visitor.NewNamespaceResolver() + dfsTraverser := traverser.NewDFS(nsResolver) + dfsTraverser.Traverse(stxTree) + + assert.DeepEqual(t, expected, nsResolver.ResolvedNames) +} diff --git a/pkg/ast/visitor/null.go b/pkg/ast/visitor/null.go index 41ab67b..bdcacbf 100644 --- a/pkg/ast/visitor/null.go +++ b/pkg/ast/visitor/null.go @@ -681,3 +681,19 @@ func (v *Null) ScalarMagicConstant(_ *ast.ScalarMagicConstant) { func (v *Null) ScalarString(_ *ast.ScalarString) { // do nothing } + +func (v *Null) NameName(_ *ast.NameName) { + // do nothing +} + +func (v *Null) NameFullyQualified(_ *ast.NameFullyQualified) { + // do nothing +} + +func (v *Null) NameRelative(_ *ast.NameRelative) { + // do nothing +} + +func (v *Null) NameNamePart(_ *ast.NameNamePart) { + // do nothing +}