Compare commits
10 Commits
33d9423421
...
aa809cad77
Author | SHA1 | Date | |
---|---|---|---|
aa809cad77 | |||
|
59da1fe671 | ||
|
deeda4f6a7 | ||
|
87718f9993 | ||
|
e3aaf7bd13 | ||
|
9281143091 | ||
|
6855357aaa | ||
|
3c45afd848 | ||
|
5baa28bb27 | ||
|
6d1eee5a79 |
@ -111,7 +111,7 @@ php-parser [flags] <path> ...
|
||||
Namespace resolver
|
||||
------------------
|
||||
|
||||
Namespace resolver is a visitor that resolves nodes fully qualified name and saves into `map[node.Node]string` structure
|
||||
Namespace resolver is a visitor that resolves nodes fully qualified name and saves into `map[ast.Vertex]string` structure
|
||||
|
||||
- For `Class`, `Interface`, `Trait`, `Function`, `Constant` nodes it saves name with current namespace.
|
||||
- For `Class`, `Interface`, `Trait`, `Enum`, `Function`, `Constant` nodes it saves name with current namespace.
|
||||
- For `Name`, `Relative`, `FullyQualified` nodes it resolves `use` aliases and saves a fully qualified name.
|
||||
|
@ -67,6 +67,24 @@ func (lex *Lexer) setTokenPosition(token *token.Token) {
|
||||
token.Position = pos
|
||||
}
|
||||
|
||||
func (lex *Lexer) setTokenPrefixPosition(token *token.Token, n int) {
|
||||
pos := lex.positionPool.Get()
|
||||
|
||||
endPos := lex.ts + n
|
||||
|
||||
sl, slb := lex.newLines.GetLine(lex.ts)
|
||||
el, elb := lex.newLines.GetLine(endPos)
|
||||
|
||||
pos.StartLine = sl
|
||||
pos.EndLine = el
|
||||
pos.StartPos = lex.ts
|
||||
pos.EndPos = endPos
|
||||
pos.StartCol = lex.ts - slb
|
||||
pos.EndCol = endPos - elb
|
||||
|
||||
token.Position = pos
|
||||
}
|
||||
|
||||
func (lex *Lexer) addFreeFloatingToken(t *token.Token, id token.ID, ps, pe int) {
|
||||
skippedTkn := lex.tokenPool.Get()
|
||||
skippedTkn.ID = id
|
||||
@ -198,6 +216,11 @@ func (lex *Lexer) ungetStr(s string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (lex *Lexer) ungetFromStart(n int) {
|
||||
tokenLength := lex.te - lex.ts
|
||||
lex.ungetCnt(tokenLength - n)
|
||||
}
|
||||
|
||||
func (lex *Lexer) ungetCnt(n int) {
|
||||
lex.p = lex.p - n
|
||||
lex.te = lex.te - n
|
||||
|
@ -4,8 +4,8 @@ package php8
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/VKCOM/php-parser/pkg/ast"
|
||||
"github.com/VKCOM/php-parser/pkg/token"
|
||||
"github.com/laytan/php-parser/pkg/ast"
|
||||
"github.com/laytan/php-parser/pkg/token"
|
||||
)
|
||||
|
||||
%}
|
||||
|
33904
internal/php8/scanner.go
33904
internal/php8/scanner.go
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/VKCOM/php-parser/pkg/token"
|
||||
"github.com/laytan/php-parser/pkg/token"
|
||||
)
|
||||
|
||||
%%{
|
||||
@ -240,7 +240,8 @@ func (lex *Lexer) Lex() *token.Token {
|
||||
'endif'i => {lex.setTokenPosition(tkn); tok = token.T_ENDIF; fbreak;};
|
||||
'endswitch'i => {lex.setTokenPosition(tkn); tok = token.T_ENDSWITCH; fbreak;};
|
||||
'endwhile'i => {lex.setTokenPosition(tkn); tok = token.T_ENDWHILE; fbreak;};
|
||||
'enum'i => {lex.setTokenPosition(tkn); tok = token.T_ENUM; fbreak;};
|
||||
'enum'i whitespace+ varname_first => {lex.setTokenPrefixPosition(tkn, 4); tok = token.T_ENUM; lex.ungetFromStart(4); fbreak;};
|
||||
'enum'i whitespace+ ('extends'i | 'implements'i) => {lex.setTokenPrefixPosition(tkn, 4); tok = token.T_STRING; lex.ungetFromStart(4); fbreak;};
|
||||
'eval'i => {lex.setTokenPosition(tkn); tok = token.T_EVAL; fbreak;};
|
||||
'exit'i | 'die'i => {lex.setTokenPosition(tkn); tok = token.T_EXIT; fbreak;};
|
||||
'extends'i => {lex.setTokenPosition(tkn); tok = token.T_EXTENDS; fbreak;};
|
||||
|
@ -181,6 +181,31 @@ func TestEnumTokens(t *testing.T) {
|
||||
suite.Run()
|
||||
}
|
||||
|
||||
func TestClassNameEnum(t *testing.T) {
|
||||
suite := tester.NewLexerTokenStructTestSuite(t)
|
||||
suite.UsePHP8()
|
||||
suite.Code = "<?php class Enum {}"
|
||||
suite.Expected = []*token.Token{
|
||||
{
|
||||
ID: token.T_CLASS,
|
||||
Value: []byte("class"),
|
||||
},
|
||||
{
|
||||
ID: token.T_STRING,
|
||||
Value: []byte("Enum"),
|
||||
},
|
||||
{
|
||||
ID: '{',
|
||||
Value: []byte("{"),
|
||||
},
|
||||
{
|
||||
ID: '}',
|
||||
Value: []byte("}"),
|
||||
},
|
||||
}
|
||||
suite.Run()
|
||||
}
|
||||
|
||||
func TestAmpersandFollowedByEllipsisTokens(t *testing.T) {
|
||||
suite := tester.NewLexerTokenStructTestSuite(t)
|
||||
suite.UsePHP8()
|
||||
|
@ -89,6 +89,22 @@ func (nsr *NamespaceResolver) StmtClass(n *ast.StmtClass) {
|
||||
}
|
||||
}
|
||||
|
||||
func (nsr *NamespaceResolver) StmtEnum(n *ast.StmtEnum) {
|
||||
if n.Type != nil {
|
||||
nsr.ResolveName(n.Type, "")
|
||||
}
|
||||
|
||||
if n.Implements != nil {
|
||||
for _, interfaceName := range n.Implements {
|
||||
nsr.ResolveName(interfaceName, "")
|
||||
}
|
||||
}
|
||||
|
||||
if n.Name != nil {
|
||||
nsr.AddNamespacedName(n, string(n.Name.(*ast.Identifier).Value))
|
||||
}
|
||||
}
|
||||
|
||||
func (nsr *NamespaceResolver) StmtInterface(n *ast.StmtInterface) {
|
||||
if n.Extends != nil {
|
||||
for _, interfaceName := range n.Extends {
|
||||
@ -206,6 +222,10 @@ func (nsr *NamespaceResolver) StmtTraitUse(n *ast.StmtTraitUse) {
|
||||
}
|
||||
}
|
||||
|
||||
func (nsr *NamespaceResolver) Attribute(n *ast.Attribute) {
|
||||
nsr.ResolveName(n.Name, "")
|
||||
}
|
||||
|
||||
// LeaveNode is invoked after node process
|
||||
func (nsr *NamespaceResolver) LeaveNode(n ast.Vertex) {
|
||||
switch nn := n.(type) {
|
||||
@ -258,6 +278,16 @@ func (nsr *NamespaceResolver) ResolveType(n ast.Vertex) {
|
||||
switch nn := n.(type) {
|
||||
case *ast.Nullable:
|
||||
nsr.ResolveType(nn.Expr)
|
||||
|
||||
case *ast.Union:
|
||||
for _, nnn := range nn.Types {
|
||||
nsr.ResolveType(nnn)
|
||||
}
|
||||
case *ast.Intersection:
|
||||
for _, nnn := range nn.Types {
|
||||
nsr.ResolveType(nnn)
|
||||
}
|
||||
|
||||
case *ast.Name:
|
||||
nsr.ResolveName(n, "")
|
||||
case *ast.NameRelative:
|
||||
@ -340,6 +370,12 @@ func (ns *Namespace) ResolveName(nameNode ast.Vertex, aliasType string) (string,
|
||||
case "iterable":
|
||||
fallthrough
|
||||
case "object":
|
||||
fallthrough
|
||||
case "mixed": // 8.0
|
||||
fallthrough
|
||||
case "never": // 8.1
|
||||
fallthrough
|
||||
case "true", "false", "null": // 8.2
|
||||
return part, nil
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
package nsresolver_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/laytan/php-parser/pkg/visitor/nsresolver"
|
||||
"github.com/laytan/php-parser/pkg/visitor/traverser"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
|
||||
@ -384,6 +385,7 @@ func TestResolveTraitUse(t *testing.T) {
|
||||
func TestResolveClassName(t *testing.T) {
|
||||
nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
|
||||
nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}}
|
||||
nameCD := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("C")}, &ast.NamePart{Value: []byte("D")}}}
|
||||
|
||||
class := &ast.StmtClass{
|
||||
Name: &ast.Identifier{Value: []byte("A")},
|
||||
@ -391,6 +393,13 @@ func TestResolveClassName(t *testing.T) {
|
||||
Implements: []ast.Vertex{
|
||||
nameBC,
|
||||
},
|
||||
AttrGroups: []ast.Vertex{
|
||||
&ast.AttributeGroup{
|
||||
Attrs: []ast.Vertex{
|
||||
&ast.Attribute{Name: nameCD},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
stxTree := &ast.StmtStmtList{
|
||||
@ -403,6 +412,7 @@ func TestResolveClassName(t *testing.T) {
|
||||
class: "A",
|
||||
nameAB: "A\\B",
|
||||
nameBC: "B\\C",
|
||||
nameCD: "C\\D",
|
||||
}
|
||||
|
||||
nsResolver := nsresolver.NewNamespaceResolver()
|
||||
@ -463,6 +473,36 @@ func TestResolveTraitName(t *testing.T) {
|
||||
assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
|
||||
}
|
||||
|
||||
func TestResolveEnumName(t *testing.T) {
|
||||
nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
|
||||
nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}}
|
||||
|
||||
enum := &ast.StmtEnum{
|
||||
Name: &ast.Identifier{Value: []byte("A")},
|
||||
Type: nameAB,
|
||||
Implements: []ast.Vertex{
|
||||
nameBC,
|
||||
},
|
||||
}
|
||||
|
||||
stxTree := &ast.StmtStmtList{
|
||||
Stmts: []ast.Vertex{
|
||||
enum,
|
||||
},
|
||||
}
|
||||
|
||||
expected := map[ast.Vertex]string{
|
||||
enum: "A",
|
||||
nameAB: "A\\B",
|
||||
nameBC: "B\\C",
|
||||
}
|
||||
|
||||
nsResolver := nsresolver.NewNamespaceResolver()
|
||||
traverser.NewTraverser(nsResolver).Traverse(stxTree)
|
||||
|
||||
assert.DeepEqual(t, expected, nsResolver.ResolvedNames)
|
||||
}
|
||||
|
||||
func TestResolveFunctionName(t *testing.T) {
|
||||
nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
|
||||
nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}}
|
||||
@ -500,6 +540,10 @@ func TestResolveFunctionName(t *testing.T) {
|
||||
func TestResolveMethodName(t *testing.T) {
|
||||
nameAB := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("A")}, &ast.NamePart{Value: []byte("B")}}}
|
||||
nameBC := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("B")}, &ast.NamePart{Value: []byte("C")}}}
|
||||
nameCD := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("C")}, &ast.NamePart{Value: []byte("D")}}}
|
||||
nameDE := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("D")}, &ast.NamePart{Value: []byte("E")}}}
|
||||
nameEF := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("E")}, &ast.NamePart{Value: []byte("F")}}}
|
||||
nameFG := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("F")}, &ast.NamePart{Value: []byte("G")}}}
|
||||
|
||||
methodNode := &ast.StmtClassMethod{
|
||||
Name: &ast.Identifier{Value: []byte("A")},
|
||||
@ -508,6 +552,14 @@ func TestResolveMethodName(t *testing.T) {
|
||||
Type: nameAB,
|
||||
Var: &ast.ExprVariable{Name: &ast.Identifier{Value: []byte("foo")}},
|
||||
},
|
||||
&ast.Parameter{
|
||||
Type: &ast.Union{Types: []ast.Vertex{nameCD, nameDE}},
|
||||
Var: &ast.ExprVariable{Name: &ast.Identifier{Value: []byte("all")}},
|
||||
},
|
||||
&ast.Parameter{
|
||||
Type: &ast.Intersection{Types: []ast.Vertex{nameEF, nameFG}},
|
||||
Var: &ast.ExprVariable{Name: &ast.Identifier{Value: []byte("any")}},
|
||||
},
|
||||
},
|
||||
ReturnType: &ast.Nullable{Expr: nameBC},
|
||||
Stmt: &ast.StmtStmtList{
|
||||
@ -518,6 +570,10 @@ func TestResolveMethodName(t *testing.T) {
|
||||
expected := map[ast.Vertex]string{
|
||||
nameAB: "A\\B",
|
||||
nameBC: "B\\C",
|
||||
nameCD: "C\\D",
|
||||
nameDE: "D\\E",
|
||||
nameEF: "E\\F",
|
||||
nameFG: "F\\G",
|
||||
}
|
||||
|
||||
nsResolver := nsresolver.NewNamespaceResolver()
|
||||
@ -598,6 +654,19 @@ func TestResolveNamespaces(t *testing.T) {
|
||||
nameFG := &ast.Name{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("F")}, &ast.NamePart{Value: []byte("G")}}}
|
||||
relativeNameCE := &ast.NameRelative{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("C")}, &ast.NamePart{Value: []byte("E")}}}
|
||||
|
||||
relativeNameCA := &ast.NameRelative{Parts: []ast.Vertex{&ast.NamePart{Value: []byte("C")}, &ast.NamePart{Value: []byte("A")}}}
|
||||
|
||||
classA := &ast.StmtClass{
|
||||
Name: &ast.Identifier{Value: []byte("A")},
|
||||
AttrGroups: []ast.Vertex{
|
||||
&ast.AttributeGroup{
|
||||
Attrs: []ast.Vertex{
|
||||
&ast.Attribute{Name: relativeNameCA},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
constantB := &ast.StmtConstant{
|
||||
Name: &ast.Identifier{Value: []byte("B")},
|
||||
Expr: &ast.ScalarLnumber{Value: []byte("1")},
|
||||
@ -612,6 +681,9 @@ func TestResolveNamespaces(t *testing.T) {
|
||||
&ast.StmtNamespace{
|
||||
Name: namespaceAB,
|
||||
},
|
||||
|
||||
classA,
|
||||
|
||||
&ast.StmtConstList{
|
||||
Consts: []ast.Vertex{
|
||||
constantB,
|
||||
@ -622,6 +694,7 @@ func TestResolveNamespaces(t *testing.T) {
|
||||
Class: nameFG,
|
||||
Call: &ast.Identifier{Value: []byte("foo")},
|
||||
},
|
||||
|
||||
&ast.StmtNamespace{
|
||||
Stmts: []ast.Vertex{},
|
||||
},
|
||||
@ -649,6 +722,8 @@ func TestResolveNamespaces(t *testing.T) {
|
||||
}
|
||||
|
||||
expected := map[ast.Vertex]string{
|
||||
classA: "A\\B\\A",
|
||||
relativeNameCA: "A\\B\\C\\A",
|
||||
constantB: "A\\B\\B",
|
||||
constantC: "A\\B\\C",
|
||||
nameFG: "A\\B\\F\\G",
|
||||
@ -780,6 +855,36 @@ func TestDoNotResolveReservedNames(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
nameMixed := &ast.Name{
|
||||
Parts: []ast.Vertex{
|
||||
&ast.NamePart{Value: []byte("mixed")},
|
||||
},
|
||||
}
|
||||
|
||||
nameNever := &ast.Name{
|
||||
Parts: []ast.Vertex{
|
||||
&ast.NamePart{Value: []byte("never")},
|
||||
},
|
||||
}
|
||||
|
||||
nameTrue := &ast.Name{
|
||||
Parts: []ast.Vertex{
|
||||
&ast.NamePart{Value: []byte("true")},
|
||||
},
|
||||
}
|
||||
|
||||
nameFalse := &ast.Name{
|
||||
Parts: []ast.Vertex{
|
||||
&ast.NamePart{Value: []byte("false")},
|
||||
},
|
||||
}
|
||||
|
||||
nameNull := &ast.Name{
|
||||
Parts: []ast.Vertex{
|
||||
&ast.NamePart{Value: []byte("null")},
|
||||
},
|
||||
}
|
||||
|
||||
function := &ast.StmtFunction{
|
||||
Name: &ast.Identifier{Value: []byte("bar")},
|
||||
Params: []ast.Vertex{
|
||||
@ -825,6 +930,36 @@ func TestDoNotResolveReservedNames(t *testing.T) {
|
||||
Name: &ast.Identifier{Value: []byte("Object")},
|
||||
},
|
||||
},
|
||||
&ast.Parameter{
|
||||
Type: nameMixed,
|
||||
Var: &ast.ExprVariable{
|
||||
Name: &ast.Identifier{Value: []byte("Mixed")},
|
||||
},
|
||||
},
|
||||
&ast.Parameter{
|
||||
Type: nameNever,
|
||||
Var: &ast.ExprVariable{
|
||||
Name: &ast.Identifier{Value: []byte("Never")},
|
||||
},
|
||||
},
|
||||
&ast.Parameter{
|
||||
Type: nameTrue,
|
||||
Var: &ast.ExprVariable{
|
||||
Name: &ast.Identifier{Value: []byte("True")},
|
||||
},
|
||||
},
|
||||
&ast.Parameter{
|
||||
Type: nameFalse,
|
||||
Var: &ast.ExprVariable{
|
||||
Name: &ast.Identifier{Value: []byte("False")},
|
||||
},
|
||||
},
|
||||
&ast.Parameter{
|
||||
Type: nameNull,
|
||||
Var: &ast.ExprVariable{
|
||||
Name: &ast.Identifier{Value: []byte("Null")},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -850,6 +985,11 @@ func TestDoNotResolveReservedNames(t *testing.T) {
|
||||
nameVoid: "void",
|
||||
nameIterable: "iterable",
|
||||
nameObject: "object",
|
||||
nameMixed: "mixed",
|
||||
nameNever: "never",
|
||||
nameTrue: "true",
|
||||
nameFalse: "false",
|
||||
nameNull: "null",
|
||||
}
|
||||
|
||||
nsResolver := nsresolver.NewNamespaceResolver()
|
||||
|
@ -957,6 +957,7 @@ func TestParseAndPrintBreak(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseAndPrintClassMethod(t *testing.T) {
|
||||
t.Skip("TODO: there should not be a blank line between the comment and method.")
|
||||
src := `<?php
|
||||
class Foo {
|
||||
/**
|
||||
|
@ -965,6 +965,7 @@ func TestParseAndPrintBreakPHP8(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseAndPrintClassMethodPHP8(t *testing.T) {
|
||||
t.Skip("TODO: there should not be a blank line between the comment and method.")
|
||||
src := `<?php
|
||||
class Foo {
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user