php-parser/visitor/namespace_resolver.go

398 lines
9.2 KiB
Go
Raw Normal View History

2018-02-23 18:26:13 +00:00
// Package visitor contains walker.visitor implementations
package visitor
import (
2018-02-26 18:21:11 +00:00
"errors"
2018-02-23 21:59:26 +00:00
"strings"
2018-02-26 21:29:36 +00:00
"github.com/z7zmey/php-parser/node/expr"
2018-02-23 18:26:13 +00:00
"github.com/z7zmey/php-parser/node"
"github.com/z7zmey/php-parser/node/name"
"github.com/z7zmey/php-parser/node/stmt"
"github.com/z7zmey/php-parser/walker"
)
2018-02-27 13:45:58 +00:00
// NamespaceResolver visitor
type NamespaceResolver struct {
2018-02-23 21:59:26 +00:00
Namespace *Namespace
2018-02-23 18:26:13 +00:00
ResolvedNames map[node.Node]string
}
2018-02-27 13:45:58 +00:00
// NewNamespaceResolver NamespaceResolver type constructor
func NewNamespaceResolver() *NamespaceResolver {
return &NamespaceResolver{
2018-02-23 21:59:26 +00:00
Namespace: NewNamespace(""),
2018-02-23 18:26:13 +00:00
ResolvedNames: map[node.Node]string{},
}
}
// EnterNode is invoked at every node in heirerchy
2018-02-27 13:45:58 +00:00
func (nsr *NamespaceResolver) EnterNode(w walker.Walkable) bool {
2018-02-23 18:26:13 +00:00
switch n := w.(type) {
case *stmt.Namespace:
2018-02-23 21:59:26 +00:00
if n.NamespaceName == nil {
nsr.Namespace = NewNamespace("")
} else {
NSParts := n.NamespaceName.(*name.Name).Parts
nsr.Namespace = NewNamespace(concatNameParts(NSParts))
}
2018-02-23 22:17:51 +00:00
2018-02-23 21:59:26 +00:00
case *stmt.UseList:
useType := ""
if n.UseType != nil {
useType = n.UseType.(*node.Identifier).Value
}
2018-02-23 22:17:51 +00:00
2018-02-23 21:59:26 +00:00
for _, nn := range n.Uses {
2018-02-23 22:11:53 +00:00
nsr.AddAlias(useType, nn, nil)
2018-02-23 21:59:26 +00:00
}
2018-02-23 22:17:51 +00:00
// no reason to iterate into depth
return false
2018-02-23 21:59:26 +00:00
case *stmt.GroupUse:
useType := ""
if n.UseType != nil {
useType = n.UseType.(*node.Identifier).Value
}
2018-02-23 22:17:51 +00:00
2018-02-23 21:59:26 +00:00
for _, nn := range n.UseList {
2018-02-23 22:11:53 +00:00
nsr.AddAlias(useType, nn, n.Prefix.(*name.Name).Parts)
2018-02-23 21:59:26 +00:00
}
2018-02-23 22:17:51 +00:00
// no reason to iterate into depth
return false
2018-02-26 18:21:11 +00:00
case *stmt.Class:
if n.Extends != nil {
nsr.ResolveName(n.Extends.ClassName, "")
2018-02-26 18:21:11 +00:00
}
if n.Implements != nil {
for _, interfaceName := range n.Implements.InterfaceNames {
nsr.ResolveName(interfaceName, "")
}
2018-02-26 18:21:11 +00:00
}
if n.ClassName != nil {
nsr.AddNamespacedName(n, n.ClassName.(*node.Identifier).Value)
}
2018-02-26 21:29:36 +00:00
case *stmt.Interface:
if n.Extends != nil {
for _, interfaceName := range n.Extends.InterfaceNames {
nsr.ResolveName(interfaceName, "")
}
2018-02-26 21:29:36 +00:00
}
2018-02-27 13:09:40 +00:00
nsr.AddNamespacedName(n, n.InterfaceName.(*node.Identifier).Value)
2018-02-26 21:29:36 +00:00
case *stmt.Trait:
2018-02-27 13:09:40 +00:00
nsr.AddNamespacedName(n, n.TraitName.(*node.Identifier).Value)
2018-02-26 21:29:36 +00:00
case *stmt.Function:
2018-02-27 13:09:40 +00:00
nsr.AddNamespacedName(n, n.FunctionName.(*node.Identifier).Value)
2018-02-26 21:29:36 +00:00
for _, parameter := range n.Params {
2018-02-27 13:09:40 +00:00
nsr.ResolveType(parameter.(*node.Parameter).VariableType)
2018-02-26 21:29:36 +00:00
}
if n.ReturnType != nil {
2018-02-27 13:09:40 +00:00
nsr.ResolveType(n.ReturnType)
2018-02-26 21:29:36 +00:00
}
case *stmt.ClassMethod:
for _, parameter := range n.Params {
2018-02-27 13:09:40 +00:00
nsr.ResolveType(parameter.(*node.Parameter).VariableType)
2018-02-26 21:29:36 +00:00
}
if n.ReturnType != nil {
2018-02-27 13:09:40 +00:00
nsr.ResolveType(n.ReturnType)
2018-02-26 21:29:36 +00:00
}
case *stmt.PropertyList:
if n.Type != nil {
nsr.ResolveType(n.Type)
}
2018-02-26 21:29:36 +00:00
case *expr.Closure:
for _, parameter := range n.Params {
2018-02-27 13:09:40 +00:00
nsr.ResolveType(parameter.(*node.Parameter).VariableType)
2018-02-26 21:29:36 +00:00
}
if n.ReturnType != nil {
2018-02-27 13:09:40 +00:00
nsr.ResolveType(n.ReturnType)
2018-02-26 21:29:36 +00:00
}
case *stmt.ConstList:
for _, constant := range n.Consts {
2018-02-27 13:09:40 +00:00
nsr.AddNamespacedName(constant, constant.(*stmt.Constant).ConstantName.(*node.Identifier).Value)
2018-02-26 21:29:36 +00:00
}
case *expr.StaticCall:
2018-02-27 21:38:05 +00:00
nsr.ResolveName(n.Class, "")
2018-02-26 21:29:36 +00:00
case *expr.StaticPropertyFetch:
2018-02-27 21:38:05 +00:00
nsr.ResolveName(n.Class, "")
2018-02-26 21:29:36 +00:00
case *expr.ClassConstFetch:
2018-02-27 21:38:05 +00:00
nsr.ResolveName(n.Class, "")
2018-02-26 21:29:36 +00:00
case *expr.New:
2018-02-27 21:38:05 +00:00
nsr.ResolveName(n.Class, "")
2018-02-26 21:29:36 +00:00
case *expr.InstanceOf:
2018-02-27 21:38:05 +00:00
nsr.ResolveName(n.Class, "")
2018-02-26 21:29:36 +00:00
case *stmt.Catch:
for _, t := range n.Types {
2018-02-27 13:09:40 +00:00
nsr.ResolveName(t, "")
2018-02-26 21:29:36 +00:00
}
case *expr.FunctionCall:
2018-02-27 21:38:05 +00:00
nsr.ResolveName(n.Function, "function")
2018-02-26 21:29:36 +00:00
case *expr.ConstFetch:
2018-02-27 13:09:40 +00:00
nsr.ResolveName(n.Constant, "const")
2018-02-26 21:29:36 +00:00
case *stmt.TraitUse:
for _, t := range n.Traits {
2018-02-27 13:09:40 +00:00
nsr.ResolveName(t, "")
2018-02-26 21:29:36 +00:00
}
if adaptationList, ok := n.TraitAdaptationList.(*stmt.TraitAdaptationList); ok {
for _, a := range adaptationList.Adaptations {
switch aa := a.(type) {
case *stmt.TraitUsePrecedence:
refTrait := aa.Ref.(*stmt.TraitMethodRef).Trait
if refTrait != nil {
nsr.ResolveName(refTrait, "")
}
for _, insteadOf := range aa.Insteadof {
nsr.ResolveName(insteadOf, "")
}
case *stmt.TraitUseAlias:
refTrait := aa.Ref.(*stmt.TraitMethodRef).Trait
if refTrait != nil {
nsr.ResolveName(refTrait, "")
}
2018-02-26 21:29:36 +00:00
}
}
}
2018-02-23 18:26:13 +00:00
}
return true
}
2018-02-26 18:21:11 +00:00
// LeaveNode is invoked after node process
2018-02-27 13:45:58 +00:00
func (nsr *NamespaceResolver) LeaveNode(w walker.Walkable) {
2018-02-26 18:21:11 +00:00
switch n := w.(type) {
case *stmt.Namespace:
if n.Stmts != nil {
nsr.Namespace = NewNamespace("")
}
}
}
2018-06-18 20:29:52 +00:00
func (nsr *NamespaceResolver) EnterChildNode(key string, w walker.Walkable) {
// do nothing
}
func (nsr *NamespaceResolver) LeaveChildNode(key string, w walker.Walkable) {
// do nothing
}
func (nsr *NamespaceResolver) EnterChildList(key string, w walker.Walkable) {
// do nothing
}
func (nsr *NamespaceResolver) LeaveChildList(key string, w walker.Walkable) {
// do nothing
}
2018-02-27 13:09:40 +00:00
// AddAlias adds a new alias
2018-02-27 13:45:58 +00:00
func (nsr *NamespaceResolver) AddAlias(useType string, nn node.Node, prefix []node.Node) {
2018-02-23 22:11:53 +00:00
switch use := nn.(type) {
case *stmt.Use:
if use.UseType != nil {
useType = use.UseType.(*node.Identifier).Value
}
useNameParts := use.Use.(*name.Name).Parts
var alias string
if use.Alias == nil {
alias = useNameParts[len(useNameParts)-1].(*name.NamePart).Value
} else {
alias = use.Alias.(*node.Identifier).Value
}
nsr.Namespace.AddAlias(useType, concatNameParts(prefix, useNameParts), alias)
}
}
2018-02-27 13:09:40 +00:00
// AddNamespacedName adds namespaced name by node
2018-02-27 13:45:58 +00:00
func (nsr *NamespaceResolver) AddNamespacedName(nn node.Node, nodeName string) {
2018-02-26 18:21:11 +00:00
if nsr.Namespace.Namespace == "" {
nsr.ResolvedNames[nn] = nodeName
} else {
nsr.ResolvedNames[nn] = nsr.Namespace.Namespace + "\\" + nodeName
2018-02-23 18:26:13 +00:00
}
}
2018-02-27 13:09:40 +00:00
// ResolveName adds a resolved fully qualified name by node
2018-02-27 13:45:58 +00:00
func (nsr *NamespaceResolver) ResolveName(nameNode node.Node, aliasType string) {
2018-02-27 21:38:05 +00:00
resolved, err := nsr.Namespace.ResolveName(nameNode, aliasType)
if err == nil {
nsr.ResolvedNames[nameNode] = resolved
}
2018-02-23 18:26:13 +00:00
}
2018-02-26 21:29:36 +00:00
2018-02-27 13:09:40 +00:00
// ResolveType adds a resolved fully qualified type name
2018-02-27 13:45:58 +00:00
func (nsr *NamespaceResolver) ResolveType(n node.Node) {
2018-02-26 21:29:36 +00:00
switch nn := n.(type) {
case *node.Nullable:
2018-02-27 13:09:40 +00:00
nsr.ResolveType(nn.Expr)
case name.Names:
nsr.ResolveName(n, "")
2018-02-26 21:29:36 +00:00
}
}
2018-02-27 13:38:58 +00:00
// 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
2018-02-27 21:38:05 +00:00
func (ns *Namespace) ResolveName(nameNode node.Node, aliasType string) (string, error) {
2018-02-27 13:38:58 +00:00
switch n := nameNode.(type) {
case *name.FullyQualified:
// Fully qualifid name is already resolved
2018-02-27 21:38:05 +00:00
return concatNameParts(n.Parts), nil
2018-02-27 13:38:58 +00:00
case *name.Relative:
2018-02-27 21:38:05 +00:00
if ns.Namespace == "" {
return concatNameParts(n.Parts), nil
}
return ns.Namespace + "\\" + concatNameParts(n.Parts), nil
2018-02-27 13:38:58 +00:00
case *name.Name:
if aliasType == "const" && len(n.Parts) == 1 {
part := strings.ToLower(n.Parts[0].(*name.NamePart).Value)
if part == "true" || part == "false" || part == "null" {
return part, nil
}
}
if aliasType == "" && len(n.Parts) == 1 {
part := strings.ToLower(n.Parts[0].(*name.NamePart).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
}
}
2018-02-27 13:38:58 +00:00
aliasName, err := ns.ResolveAlias(nameNode, aliasType)
if err != nil {
// resolve as relative name if alias not found
2018-02-27 21:38:05 +00:00
if ns.Namespace == "" {
return concatNameParts(n.Parts), nil
}
return ns.Namespace + "\\" + concatNameParts(n.Parts), nil
2018-02-27 13:38:58 +00:00
}
if len(n.Parts) > 1 {
// if name qualified, replace first part by alias
2018-02-27 21:38:05 +00:00
return aliasName + "\\" + concatNameParts(n.Parts[1:]), nil
2018-02-27 13:38:58 +00:00
}
2018-02-27 21:38:05 +00:00
return aliasName, nil
2018-02-27 13:38:58 +00:00
}
2018-02-27 21:38:05 +00:00
return "", errors.New("must be instance of name.Names")
2018-02-27 13:38:58 +00:00
}
// ResolveAlias returns alias or error if not found
func (ns *Namespace) ResolveAlias(nameNode node.Node, aliasType string) (string, error) {
aliasType = strings.ToLower(aliasType)
nameParts := nameNode.(*name.Name).Parts
firstPartStr := nameParts[0].(*name.NamePart).Value
2018-02-28 10:09:50 +00:00
if len(nameParts) > 1 { // resolve aliases for qualified names, always against class alias type
2018-02-27 13:38:58 +00:00
firstPartStr = strings.ToLower(firstPartStr)
aliasType = ""
} else {
2018-02-28 10:09:50 +00:00
if aliasType != "const" { // constants are case-sensitive
2018-02-27 13:38:58 +00:00
firstPartStr = strings.ToLower(firstPartStr)
}
}
aliasName, ok := ns.Aliases[aliasType][firstPartStr]
if !ok {
return "", errors.New("Not found")
}
return aliasName, nil
}
func concatNameParts(parts ...[]node.Node) string {
str := ""
for _, p := range parts {
for _, n := range p {
if str == "" {
str = n.(*name.NamePart).Value
} else {
str = str + "\\" + n.(*name.NamePart).Value
}
}
}
return str
}