Compare commits

..

22 Commits

Author SHA1 Message Date
Nikita Popov
a1e8e1a30e Release PHP-Parser 3.1.1 2017-09-02 19:10:46 +02:00
Nikita Popov
d77e6cd6e9 Allow TraitUse statements in trait builder
Fixes #413.
2017-08-29 23:18:59 +02:00
Nikita Popov
a10780ca0d Handle Nop statement after brace-style namespace
Fixes #412.
2017-08-29 23:14:27 +02:00
Nikita Popov
4d4896e553 Release PHP-Parser 3.1.0 2017-07-28 16:45:09 +02:00
Nikita Popov
6fa073879e Remove deprecation of Name::$parts
It doesn't look like this is going away for now, and we currently
don't have the APIs to cover all existing usages elegantly.
2017-07-19 17:13:10 +02:00
Nikita Popov
5a3a1ec25c Handle LC_NUMERIC with comma decimal separator
Closes #399.
2017-07-19 17:10:55 +02:00
Nikita Popov
4a7d011317 Add support for object type 2017-06-28 23:12:13 +02:00
Nikita Popov
7f862ac21c Add support for trailing comma in group use 2017-06-28 23:05:28 +02:00
Nikita Popov
0808939f81 Release PHP-Parser 3.0.6 2017-06-28 22:53:48 +02:00
Nikita Popov
7bfc320bda Switch to dist: trusty, so HHVM runs 2017-06-03 15:25:50 +02:00
Nikita Popov
bc0bff3f87 Fix method name in tests 2017-06-03 15:18:23 +02:00
Romain Neutron
c28b8556f5 Fix Lexer errorHandling when there is no tokens
I encounter an issue when no tokens exist. An error `Undefined offset -1` is triggered.
2017-06-03 15:17:45 +02:00
Nikita Popov
3da86df48f Deprecate Node::setLine() 2017-04-29 12:58:35 +02:00
Nikita Popov
901b895c02 Fix spelling of VISIBILITY_MODIFIER_MASK 2017-04-19 11:20:05 +02:00
SignpostMarv
c877c1a64f Add Builder\Param::makeVariadic() 2017-04-19 11:16:29 +02:00
Nikita Popov
c3cbf07946 Pretty printer: Preserve comments in arrays and calls
If call arguments or array contains comments, print it in multiline
form, so that comments may be preserved.
2017-04-09 19:49:47 +02:00
Nikita Popov
2b9e2f71b7 Release PHP-Parser 3.0.5 2017-03-05 19:23:57 +01:00
Nikita Popov
d5873b177b Adjust the end attributes on the stack as well 2017-02-26 23:45:14 +01:00
Nikita Popov
48ec654d0c Make Expr\Error nodes empty
Resolves issue #359.
2017-02-26 23:38:32 +01:00
Nikita Popov
c12a4c8239 Fix start attribute assignment for Error in ClassConstFetch 2017-02-26 23:00:38 +01:00
Nikita Popov
86ea6fe8c4 Remove leftover code 2017-02-26 22:53:08 +01:00
Nikita Popov
1b59e918f7 Perform NullableType resolution earlier
This makes sure function signatures are already fully resolved in
enterNode(). Resolves issue #360.
2017-02-26 22:50:31 +01:00
28 changed files with 1527 additions and 1193 deletions

View File

@@ -1,5 +1,5 @@
language: php language: php
dist: trusty
sudo: false sudo: false
cache: cache:

View File

@@ -1,8 +1,64 @@
Version 3.0.5-dev Version 3.1.2-dev
----------------- -----------------
Nothing yet. Nothing yet.
Version 3.1.1 (2017-09-02)
--------------------------
### Fixed
* Fixed syntax error on comment after brace-style namespace declaration. (#412)
* Added support for TraitUse statements in trait builder. (#413)
Version 3.1.0 (2017-07-28)
--------------------------
### Added
* [PHP 7.2] Added support for trailing comma in group use statements.
* [PHP 7.2] Added support for `object` type. This means `object` types will now be represented as a
builtin type (a simple `"object"` string), rather than a class `Name`.
### Fixed
* Floating-point numbers are now printed correctly if the LC_NUMERIC locale uses a comma as decimal
separator.
### Changed
* `Name::$parts` is no longer deprecated.
Version 3.0.6 (2017-06-28)
--------------------------
### Fixed
* Fixed the spelling of `Class_::VISIBILITY_MODIFIER_MASK`. The previous spelling of
`Class_::VISIBILITY_MODIFER_MASK` is preserved for backwards compatibility.
* The pretty printing will now preserve comments inside array literals and function calls by
printing the array items / function arguments on separate lines. Array literals and functions that
do not contain comments are not affected.
### Added
* Added `Builder\Param::makeVariadic()`.
### Deprecated
* The `Node::setLine()` method has been deprecated.
Version 3.0.5 (2017-03-05)
--------------------------
### Fixed
* Name resolution of `NullableType`s is now performed earlier, so that a fully resolved signature is
available when a function is entered. (#360)
* `Error` nodes are now considered empty, while previously they extended until the token where the
error occurred. This made some nodes larger than expected. (#359)
* Fixed notices being thrown during error recovery in some situations. (#362)
Version 3.0.4 (2017-02-10) Version 3.0.4 (2017-02-10)
-------------------------- --------------------------

View File

@@ -59,6 +59,10 @@ no_comma:
| ',' { $this->emitError(new Error('A trailing comma is not allowed here', attributes())); } | ',' { $this->emitError(new Error('A trailing comma is not allowed here', attributes())); }
; ;
optional_comma:
/* empty */
| ','
top_statement: top_statement:
statement { $$ = $1; } statement { $$ = $1; }
| function_declaration_statement { $$ = $1; } | function_declaration_statement { $$ = $1; }
@@ -95,7 +99,7 @@ group_use_declaration:
; ;
unprefixed_use_declarations: unprefixed_use_declarations:
non_empty_unprefixed_use_declarations no_comma { $$ = $1; } non_empty_unprefixed_use_declarations optional_comma { $$ = $1; }
; ;
non_empty_unprefixed_use_declarations: non_empty_unprefixed_use_declarations:
@@ -114,7 +118,7 @@ non_empty_use_declarations:
; ;
inline_use_declarations: inline_use_declarations:
non_empty_inline_use_declarations no_comma { $$ = $1; } non_empty_inline_use_declarations optional_comma { $$ = $1; }
; ;
non_empty_inline_use_declarations: non_empty_inline_use_declarations:
@@ -763,7 +767,7 @@ constant:
/* We interpret and isolated FOO:: as an unfinished class constant fetch. It could also be /* We interpret and isolated FOO:: as an unfinished class constant fetch. It could also be
an unfinished static property fetch or unfinished scoped call. */ an unfinished static property fetch or unfinished scoped call. */
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM error | class_name_or_var T_PAAMAYIM_NEKUDOTAYIM error
{ $$ = Expr\ClassConstFetch[$1, Expr\Error[]]; $this->errorState = 2; } { $$ = Expr\ClassConstFetch[$1, new Expr\Error(stackAttributes(#3))]; $this->errorState = 2; }
; ;
array_short_syntax: array_short_syntax:

View File

@@ -16,6 +16,8 @@ class Param extends PhpParser\BuilderAbstract
protected $byRef = false; protected $byRef = false;
protected $variadic = false;
/** /**
* Creates a parameter builder. * Creates a parameter builder.
* *
@@ -65,6 +67,17 @@ class Param extends PhpParser\BuilderAbstract
return $this; return $this;
} }
/**
* Make the parameter variadic
*
* @return $this The builder instance (for fluid interface)
*/
public function makeVariadic() {
$this->variadic = true;
return $this;
}
/** /**
* Returns the built parameter node. * Returns the built parameter node.
* *
@@ -72,7 +85,7 @@ class Param extends PhpParser\BuilderAbstract
*/ */
public function getNode() { public function getNode() {
return new Node\Param( return new Node\Param(
$this->name, $this->default, $this->type, $this->byRef $this->name, $this->default, $this->type, $this->byRef, $this->variadic
); );
} }
} }

View File

@@ -8,6 +8,7 @@ use PhpParser\Node\Stmt;
class Trait_ extends Declaration class Trait_ extends Declaration
{ {
protected $name; protected $name;
protected $uses = array();
protected $properties = array(); protected $properties = array();
protected $methods = array(); protected $methods = array();
@@ -34,6 +35,8 @@ class Trait_ extends Declaration
$this->properties[] = $stmt; $this->properties[] = $stmt;
} else if ($stmt instanceof Stmt\ClassMethod) { } else if ($stmt instanceof Stmt\ClassMethod) {
$this->methods[] = $stmt; $this->methods[] = $stmt;
} else if ($stmt instanceof Stmt\TraitUse) {
$this->uses[] = $stmt;
} else { } else {
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType())); throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
} }
@@ -49,7 +52,7 @@ class Trait_ extends Declaration
public function getNode() { public function getNode() {
return new Stmt\Trait_( return new Stmt\Trait_(
$this->name, array( $this->name, array(
'stmts' => array_merge($this->properties, $this->methods) 'stmts' => array_merge($this->uses, $this->properties, $this->methods)
), $this->attributes ), $this->attributes
); );
} }

View File

@@ -80,7 +80,7 @@ abstract class BuilderAbstract implements Builder {
} }
$builtinTypes = array( $builtinTypes = array(
'array', 'callable', 'string', 'int', 'float', 'bool', 'iterable', 'void' 'array', 'callable', 'string', 'int', 'float', 'bool', 'iterable', 'void', 'object'
); );
$lowerType = strtolower($type); $lowerType = strtolower($type);

View File

@@ -185,15 +185,17 @@ class Lexer
return; return;
} }
// Check for unterminated comment if (count($this->tokens) > 0) {
$lastToken = $this->tokens[count($this->tokens) - 1]; // Check for unterminated comment
if ($this->isUnterminatedComment($lastToken)) { $lastToken = $this->tokens[count($this->tokens) - 1];
$errorHandler->handleError(new Error('Unterminated comment', [ if ($this->isUnterminatedComment($lastToken)) {
'startLine' => $line - substr_count($lastToken[1], "\n"), $errorHandler->handleError(new Error('Unterminated comment', [
'endLine' => $line, 'startLine' => $line - substr_count($lastToken[1], "\n"),
'startFilePos' => $filePos - \strlen($lastToken[1]), 'endLine' => $line,
'endFilePos' => $filePos, 'startFilePos' => $filePos - \strlen($lastToken[1]),
])); 'endFilePos' => $filePos,
]));
}
} }
} }

View File

@@ -29,6 +29,8 @@ interface Node
* Sets line the node started in. * Sets line the node started in.
* *
* @param int $line Line * @param int $line Line
*
* @deprecated
*/ */
public function setLine($line); public function setLine($line);

View File

@@ -8,7 +8,6 @@ class Name extends NodeAbstract
{ {
/** /**
* @var string[] Parts of the name * @var string[] Parts of the name
* @deprecated Avoid directly accessing $parts, use methods instead.
*/ */
public $parts; public $parts;

View File

@@ -30,7 +30,7 @@ class ClassConst extends Node\Stmt
public function isPublic() { public function isPublic() {
return ($this->flags & Class_::MODIFIER_PUBLIC) !== 0 return ($this->flags & Class_::MODIFIER_PUBLIC) !== 0
|| ($this->flags & Class_::VISIBILITY_MODIFER_MASK) === 0; || ($this->flags & Class_::VISIBILITY_MODIFIER_MASK) === 0;
} }
public function isProtected() { public function isProtected() {

View File

@@ -69,7 +69,7 @@ class ClassMethod extends Node\Stmt implements FunctionLike
public function isPublic() { public function isPublic() {
return ($this->flags & Class_::MODIFIER_PUBLIC) !== 0 return ($this->flags & Class_::MODIFIER_PUBLIC) !== 0
|| ($this->flags & Class_::VISIBILITY_MODIFER_MASK) === 0; || ($this->flags & Class_::VISIBILITY_MODIFIER_MASK) === 0;
} }
public function isProtected() { public function isProtected() {

View File

@@ -14,7 +14,9 @@ class Class_ extends ClassLike
const MODIFIER_ABSTRACT = 16; const MODIFIER_ABSTRACT = 16;
const MODIFIER_FINAL = 32; const MODIFIER_FINAL = 32;
const VISIBILITY_MODIFER_MASK = 7; // 1 | 2 | 4 const VISIBILITY_MODIFIER_MASK = 7; // 1 | 2 | 4
/** @deprecated */
const VISIBILITY_MODIFER_MASK = self::VISIBILITY_MODIFIER_MASK;
/** @var int Type */ /** @var int Type */
public $flags; public $flags;
@@ -74,7 +76,7 @@ class Class_ extends ClassLike
* @internal * @internal
*/ */
public static function verifyModifier($a, $b) { public static function verifyModifier($a, $b) {
if ($a & self::VISIBILITY_MODIFER_MASK && $b & self::VISIBILITY_MODIFER_MASK) { if ($a & self::VISIBILITY_MODIFIER_MASK && $b & self::VISIBILITY_MODIFIER_MASK) {
throw new Error('Multiple access type modifiers are not allowed'); throw new Error('Multiple access type modifiers are not allowed');
} }

View File

@@ -34,7 +34,7 @@ class Property extends Node\Stmt
public function isPublic() { public function isPublic() {
return ($this->flags & Class_::MODIFIER_PUBLIC) !== 0 return ($this->flags & Class_::MODIFIER_PUBLIC) !== 0
|| ($this->flags & Class_::VISIBILITY_MODIFER_MASK) === 0; || ($this->flags & Class_::VISIBILITY_MODIFIER_MASK) === 0;
} }
public function isProtected() { public function isProtected() {

View File

@@ -37,6 +37,8 @@ abstract class NodeAbstract implements Node, \JsonSerializable
* Sets line the node started in. * Sets line the node started in.
* *
* @param int $line Line * @param int $line Line
*
* @deprecated
*/ */
public function setLine($line) { public function setLine($line) {
$this->setAttribute('startLine', (int) $line); $this->setAttribute('startLine', (int) $line);

View File

@@ -120,10 +120,6 @@ class NameResolver extends NodeVisitorAbstract
} }
} }
} }
} elseif ($node instanceof Node\NullableType) {
if ($node->type instanceof Name) {
$node->type = $this->resolveClassName($node->type);
}
} }
} }
@@ -172,13 +168,20 @@ class NameResolver extends NodeVisitorAbstract
/** @param Stmt\Function_|Stmt\ClassMethod|Expr\Closure $node */ /** @param Stmt\Function_|Stmt\ClassMethod|Expr\Closure $node */
private function resolveSignature($node) { private function resolveSignature($node) {
foreach ($node->params as $param) { foreach ($node->params as $param) {
if ($param->type instanceof Name) { $param->type = $this->resolveType($param->type);
$param->type = $this->resolveClassName($param->type);
}
} }
if ($node->returnType instanceof Name) { $node->returnType = $this->resolveType($node->returnType);
$node->returnType = $this->resolveClassName($node->returnType); }
private function resolveType($node) {
if ($node instanceof Node\NullableType) {
$node->type = $this->resolveType($node->type);
return $node;
} }
if ($node instanceof Name) {
return $this->resolveClassName($node);
}
return $node;
} }
protected function resolveClassName(Name $name) { protected function resolveClassName(Name $name) {

File diff suppressed because it is too large Load Diff

View File

@@ -315,7 +315,11 @@ abstract class ParserAbstract implements Parser
//$this->traceShift($this->errorSymbol); //$this->traceShift($this->errorSymbol);
++$this->stackPos; ++$this->stackPos;
$stateStack[$this->stackPos] = $state = $action; $stateStack[$this->stackPos] = $state = $action;
$this->endAttributes = $this->endAttributeStack[$this->stackPos];
// We treat the error symbol as being empty, so we reset the end attributes
// to the end attributes of the last non-error symbol
$this->endAttributeStack[$this->stackPos] = $this->endAttributeStack[$this->stackPos - 1];
$this->endAttributes = $this->endAttributeStack[$this->stackPos - 1];
break; break;
case 3: case 3:
@@ -441,6 +445,7 @@ abstract class ParserAbstract implements Parser
if ($stmt instanceof Node\Stmt\Namespace_) { if ($stmt instanceof Node\Stmt\Namespace_) {
$afterFirstNamespace = true; $afterFirstNamespace = true;
} elseif (!$stmt instanceof Node\Stmt\HaltCompiler } elseif (!$stmt instanceof Node\Stmt\HaltCompiler
&& !$stmt instanceof Node\Stmt\Nop
&& $afterFirstNamespace && !$hasErrored) { && $afterFirstNamespace && !$hasErrored) {
$this->emitError(new Error( $this->emitError(new Error(
'No code may exist outside of namespace {}', $stmt->getAttributes())); 'No code may exist outside of namespace {}', $stmt->getAttributes()));
@@ -525,6 +530,7 @@ abstract class ParserAbstract implements Parser
'string' => true, 'string' => true,
'iterable' => true, 'iterable' => true,
'void' => true, 'void' => true,
'object' => true,
]; ];
if (!$name->isUnqualified()) { if (!$name->isUnqualified()) {

View File

@@ -182,6 +182,11 @@ class Standard extends PrettyPrinterAbstract
$stringValue = sprintf('%.17G', $node->value); $stringValue = sprintf('%.17G', $node->value);
} }
// %G is locale dependent and there exists no locale-independent alternative. We don't want
// mess with switching locales here, so let's assume that a comma is the only non-standard
// decimal separator we may encounter...
$stringValue = str_replace(',', '.', $stringValue);
// ensure that number is really printed as float // ensure that number is really printed as float
return preg_match('/^-?[0-9]+$/', $stringValue) ? $stringValue . '.0' : $stringValue; return preg_match('/^-?[0-9]+$/', $stringValue) ? $stringValue . '.0' : $stringValue;
} }
@@ -438,12 +443,12 @@ class Standard extends PrettyPrinterAbstract
protected function pExpr_FuncCall(Expr\FuncCall $node) { protected function pExpr_FuncCall(Expr\FuncCall $node) {
return $this->pCallLhs($node->name) return $this->pCallLhs($node->name)
. '(' . $this->pCommaSeparated($node->args) . ')'; . '(' . $this->pMaybeMultiline($node->args) . ')';
} }
protected function pExpr_MethodCall(Expr\MethodCall $node) { protected function pExpr_MethodCall(Expr\MethodCall $node) {
return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name) return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name)
. '(' . $this->pCommaSeparated($node->args) . ')'; . '(' . $this->pMaybeMultiline($node->args) . ')';
} }
protected function pExpr_StaticCall(Expr\StaticCall $node) { protected function pExpr_StaticCall(Expr\StaticCall $node) {
@@ -453,7 +458,7 @@ class Standard extends PrettyPrinterAbstract
? $this->p($node->name) ? $this->p($node->name)
: '{' . $this->p($node->name) . '}') : '{' . $this->p($node->name) . '}')
: $node->name) : $node->name)
. '(' . $this->pCommaSeparated($node->args) . ')'; . '(' . $this->pMaybeMultiline($node->args) . ')';
} }
protected function pExpr_Empty(Expr\Empty_ $node) { protected function pExpr_Empty(Expr\Empty_ $node) {
@@ -501,9 +506,9 @@ class Standard extends PrettyPrinterAbstract
$syntax = $node->getAttribute('kind', $syntax = $node->getAttribute('kind',
$this->options['shortArraySyntax'] ? Expr\Array_::KIND_SHORT : Expr\Array_::KIND_LONG); $this->options['shortArraySyntax'] ? Expr\Array_::KIND_SHORT : Expr\Array_::KIND_LONG);
if ($syntax === Expr\Array_::KIND_SHORT) { if ($syntax === Expr\Array_::KIND_SHORT) {
return '[' . $this->pCommaSeparated($node->items) . ']'; return '[' . $this->pMaybeMultiline($node->items, true) . ']';
} else { } else {
return 'array(' . $this->pCommaSeparated($node->items) . ')'; return 'array(' . $this->pMaybeMultiline($node->items, true) . ')';
} }
} }
@@ -553,10 +558,10 @@ class Standard extends PrettyPrinterAbstract
protected function pExpr_New(Expr\New_ $node) { protected function pExpr_New(Expr\New_ $node) {
if ($node->class instanceof Stmt\Class_) { if ($node->class instanceof Stmt\Class_) {
$args = $node->args ? '(' . $this->pCommaSeparated($node->args) . ')' : ''; $args = $node->args ? '(' . $this->pMaybeMultiline($node->args) . ')' : '';
return 'new ' . $this->pClassCommon($node->class, $args); return 'new ' . $this->pClassCommon($node->class, $args);
} }
return 'new ' . $this->p($node->class) . '(' . $this->pCommaSeparated($node->args) . ')'; return 'new ' . $this->p($node->class) . '(' . $this->pMaybeMultiline($node->args) . ')';
} }
protected function pExpr_Clone(Expr\Clone_ $node) { protected function pExpr_Clone(Expr\Clone_ $node) {
@@ -944,4 +949,21 @@ class Standard extends PrettyPrinterAbstract
return '(' . $this->p($node) . ')'; return '(' . $this->p($node) . ')';
} }
} }
private function hasNodeWithComments(array $nodes) {
foreach ($nodes as $node) {
if ($node && $node->getAttribute('comments')) {
return true;
}
}
return false;
}
private function pMaybeMultiline(array $nodes, $trailingComma = false) {
if (!$this->hasNodeWithComments($nodes)) {
return $this->pCommaSeparated($nodes);
} else {
return $this->pCommaSeparatedMultiline($nodes, $trailingComma) . "\n";
}
}
} }

View File

@@ -286,6 +286,38 @@ abstract class PrettyPrinterAbstract
return $this->pImplode($nodes, ', '); return $this->pImplode($nodes, ', ');
} }
/**
* Pretty prints a comma-separated list of nodes in multiline style, including comments.
*
* The result includes a leading newline and one level of indentation (same as pStmts).
*
* @param Node[] $nodes Array of Nodes to be printed
* @param bool $trailingComma Whether to use a trailing comma
*
* @return string Comma separated pretty printed nodes in multiline style
*/
protected function pCommaSeparatedMultiline(array $nodes, $trailingComma) {
$result = '';
$lastIdx = count($nodes) - 1;
foreach ($nodes as $idx => $node) {
if ($node !== null) {
$comments = $node->getAttribute('comments', array());
if ($comments) {
$result .= "\n" . $this->pComments($comments);
}
$result .= "\n" . $this->p($node);
} else {
$result .= "\n";
}
if ($trailingComma || $idx !== $lastIdx) {
$result .= ',';
}
}
return preg_replace('~\n(?!$|' . $this->noIndentToken . ')~', "\n ", $result);
}
/** /**
* Signals the pretty printer that a string shall not be indented. * Signals the pretty printer that a string shall not be indented.
* *

View File

@@ -112,6 +112,7 @@ class ParamTest extends \PHPUnit_Framework_TestCase
array('float', 'float'), array('float', 'float'),
array('string', 'string'), array('string', 'string'),
array('iterable', 'iterable'), array('iterable', 'iterable'),
array('object', 'object'),
array('Array', 'array'), array('Array', 'array'),
array('CALLABLE', 'callable'), array('CALLABLE', 'callable'),
array('Some\Class', new Node\Name('Some\Class')), array('Some\Class', new Node\Name('Some\Class')),
@@ -155,4 +156,16 @@ class ParamTest extends \PHPUnit_Framework_TestCase
$node $node
); );
} }
public function testVariadic() {
$node = $this->createParamBuilder('test')
->makeVariadic()
->getNode()
;
$this->assertEquals(
new Node\Param('test', null, null, false, true),
$node
);
}
} }

View File

@@ -3,6 +3,7 @@
namespace PhpParser\Builder; namespace PhpParser\Builder;
use PhpParser\Comment; use PhpParser\Comment;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt; use PhpParser\Node\Stmt;
class TraitTest extends \PHPUnit_Framework_TestCase class TraitTest extends \PHPUnit_Framework_TestCase
@@ -18,19 +19,21 @@ class TraitTest extends \PHPUnit_Framework_TestCase
$prop = new Stmt\Property(Stmt\Class_::MODIFIER_PUBLIC, array( $prop = new Stmt\Property(Stmt\Class_::MODIFIER_PUBLIC, array(
new Stmt\PropertyProperty('test') new Stmt\PropertyProperty('test')
)); ));
$use = new Stmt\TraitUse([new Name('OtherTrait')]);
$trait = $this->createTraitBuilder('TestTrait') $trait = $this->createTraitBuilder('TestTrait')
->setDocComment('/** Nice trait */') ->setDocComment('/** Nice trait */')
->addStmt($method1) ->addStmt($method1)
->addStmts(array($method2, $method3)) ->addStmts([$method2, $method3])
->addStmt($prop) ->addStmt($prop)
->addStmt($use)
->getNode(); ->getNode();
$this->assertEquals(new Stmt\Trait_('TestTrait', array( $this->assertEquals(new Stmt\Trait_('TestTrait', [
'stmts' => array($prop, $method1, $method2, $method3) 'stmts' => [$use, $prop, $method1, $method2, $method3]
), array( ], [
'comments' => array( 'comments' => [
new Comment\Doc('/** Nice trait */') new Comment\Doc('/** Nice trait */')
) ]
)), $trait); ]), $trait);
} }
/** /**

View File

@@ -201,7 +201,13 @@ class LexerTest extends \PHPUnit_Framework_TestCase
array(), array() array(), array()
) )
) )
) ),
// tests no tokens
array(
'',
array(),
array()
),
); );
} }

View File

@@ -178,7 +178,7 @@ class NodeTraverserTest extends \PHPUnit_Framework_TestCase
$visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock(); $visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$visitor->expects($this->at(1))->method('enterNode')->with($mulNode) $visitor->expects($this->at(1))->method('enterNode')->with($mulNode)
->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL)); ->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL));
$visitor->expects($this->at(2))->method('afterTraversal'); $visitor->expects($this->at(2))->method('afterTraverse');
$traverser = new NodeTraverser; $traverser = new NodeTraverser;
$traverser->addVisitor($visitor); $traverser->addVisitor($visitor);
$this->assertEquals($stmts, $traverser->traverse($stmts)); $this->assertEquals($stmts, $traverser->traverse($stmts));
@@ -187,7 +187,7 @@ class NodeTraverserTest extends \PHPUnit_Framework_TestCase
$visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock(); $visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$visitor->expects($this->at(2))->method('enterNode')->with($varNode1) $visitor->expects($this->at(2))->method('enterNode')->with($varNode1)
->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL)); ->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL));
$visitor->expects($this->at(3))->method('afterTraversal'); $visitor->expects($this->at(3))->method('afterTraverse');
$traverser = new NodeTraverser; $traverser = new NodeTraverser;
$traverser->addVisitor($visitor); $traverser->addVisitor($visitor);
$this->assertEquals($stmts, $traverser->traverse($stmts)); $this->assertEquals($stmts, $traverser->traverse($stmts));
@@ -196,7 +196,7 @@ class NodeTraverserTest extends \PHPUnit_Framework_TestCase
$visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock(); $visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$visitor->expects($this->at(3))->method('leaveNode')->with($varNode1) $visitor->expects($this->at(3))->method('leaveNode')->with($varNode1)
->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL)); ->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL));
$visitor->expects($this->at(4))->method('afterTraversal'); $visitor->expects($this->at(4))->method('afterTraverse');
$traverser = new NodeTraverser; $traverser = new NodeTraverser;
$traverser->addVisitor($visitor); $traverser->addVisitor($visitor);
$this->assertEquals($stmts, $traverser->traverse($stmts)); $this->assertEquals($stmts, $traverser->traverse($stmts));
@@ -205,7 +205,7 @@ class NodeTraverserTest extends \PHPUnit_Framework_TestCase
$visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock(); $visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$visitor->expects($this->at(6))->method('leaveNode')->with($mulNode) $visitor->expects($this->at(6))->method('leaveNode')->with($mulNode)
->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL)); ->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL));
$visitor->expects($this->at(7))->method('afterTraversal'); $visitor->expects($this->at(7))->method('afterTraverse');
$traverser = new NodeTraverser; $traverser = new NodeTraverser;
$traverser->addVisitor($visitor); $traverser->addVisitor($visitor);
$this->assertEquals($stmts, $traverser->traverse($stmts)); $this->assertEquals($stmts, $traverser->traverse($stmts));
@@ -216,7 +216,7 @@ class NodeTraverserTest extends \PHPUnit_Framework_TestCase
->will($this->returnValue(NodeTraverser::REMOVE_NODE)); ->will($this->returnValue(NodeTraverser::REMOVE_NODE));
$visitor->expects($this->at(7))->method('enterNode')->with($printNode) $visitor->expects($this->at(7))->method('enterNode')->with($printNode)
->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL)); ->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL));
$visitor->expects($this->at(8))->method('afterTraversal'); $visitor->expects($this->at(8))->method('afterTraverse');
$traverser = new NodeTraverser; $traverser = new NodeTraverser;
$traverser->addVisitor($visitor); $traverser->addVisitor($visitor);
$this->assertEquals([$printNode], $traverser->traverse($stmts)); $this->assertEquals([$printNode], $traverser->traverse($stmts));

View File

@@ -248,11 +248,11 @@ $foo->
!!positions !!positions
Syntax error, unexpected ';', expecting T_STRING or T_VARIABLE or '{' or '$' from 3:1 to 3:1 Syntax error, unexpected ';', expecting T_STRING or T_VARIABLE or '{' or '$' from 3:1 to 3:1
array( array(
0: Expr_PropertyFetch[2:1 - 3:1]( 0: Expr_PropertyFetch[2:1 - 2:6](
var: Expr_Variable[2:1 - 2:4]( var: Expr_Variable[2:1 - 2:4](
name: foo name: foo
) )
name: Expr_Error[3:1 - 3:1]( name: Expr_Error[3:1 - 2:6](
) )
) )
) )
@@ -272,11 +272,11 @@ array(
) )
returnType: null returnType: null
stmts: array( stmts: array(
0: Expr_PropertyFetch[3:5 - 4:1]( 0: Expr_PropertyFetch[3:5 - 3:10](
var: Expr_Variable[3:5 - 3:8]( var: Expr_Variable[3:5 - 3:8](
name: bar name: bar
) )
name: Expr_Error[4:1 - 4:1]( name: Expr_Error[4:1 - 3:10](
) )
) )
) )
@@ -305,8 +305,8 @@ new
!!php7,positions !!php7,positions
Syntax error, unexpected EOF from 2:4 to 2:4 Syntax error, unexpected EOF from 2:4 to 2:4
array( array(
0: Expr_New[2:1 - 2:4]( 0: Expr_New[2:1 - 2:3](
class: Expr_Error[2:4 - 2:4]( class: Expr_Error[2:4 - 2:3](
) )
args: array( args: array(
) )
@@ -544,8 +544,6 @@ for ($a, ; $b, ; $c, );
function ($a, ) use ($b, ) {}; function ($a, ) use ($b, ) {};
----- -----
!!php7 !!php7
A trailing comma is not allowed here from 3:9 to 3:9
A trailing comma is not allowed here from 4:18 to 4:18
A trailing comma is not allowed here from 5:6 to 5:6 A trailing comma is not allowed here from 5:6 to 5:6
A trailing comma is not allowed here from 6:13 to 6:13 A trailing comma is not allowed here from 6:13 to 6:13
A trailing comma is not allowed here from 8:21 to 8:21 A trailing comma is not allowed here from 8:21 to 8:21
@@ -834,4 +832,35 @@ array(
stmts: array( stmts: array(
) )
) )
)
-----
<?php
foo(Bar::);
-----
!!php7,positions
Syntax error, unexpected ')' from 3:10 to 3:10
array(
0: Expr_FuncCall[3:1 - 3:10](
name: Name[3:1 - 3:3](
parts: array(
0: foo
)
)
args: array(
0: Arg[3:5 - 3:9](
value: Expr_ClassConstFetch[3:5 - 3:9](
class: Name[3:5 - 3:7](
parts: array(
0: Bar
)
)
name: Expr_Error[3:10 - 3:9](
)
)
byRef: false
unpack: false
)
)
)
) )

View File

@@ -1,7 +1,7 @@
Scalar type declarations Scalar type declarations
----- -----
<?php <?php
function test(bool $a, Int $b, FLOAT $c, StRiNg $d, iterable $e) : void {} function test(bool $a, Int $b, FLOAT $c, StRiNg $d, iterable $e, object $f) : void {}
----- -----
!!php7 !!php7
array( array(
@@ -44,9 +44,16 @@ array(
name: e name: e
default: null default: null
) )
5: Param(
type: object
byRef: false
variadic: false
name: f
default: null
)
) )
returnType: void returnType: void
stmts: array( stmts: array(
) )
) )
) )

View File

@@ -0,0 +1,22 @@
Trailing comment after braced namespace declaration
-----
<?php
namespace Foo {}
// Comment
-----
array(
0: Stmt_Namespace(
name: Name(
parts: array(
0: Foo
)
)
stmts: array(
)
)
1: Stmt_Nop(
comments: array(
0: // Comment
)
)
)

View File

@@ -0,0 +1,47 @@
Group use can have trailing comma
-----
<?php
use A\{B,};
use function A\{b,};
-----
!!php7
array(
0: Stmt_GroupUse(
type: TYPE_UNKNOWN (0)
prefix: Name(
parts: array(
0: A
)
)
uses: array(
0: Stmt_UseUse(
type: TYPE_NORMAL (1)
name: Name(
parts: array(
0: B
)
)
alias: B
)
)
)
1: Stmt_GroupUse(
type: TYPE_FUNCTION (2)
prefix: Name(
parts: array(
0: A
)
)
uses: array(
0: Stmt_UseUse(
type: TYPE_UNKNOWN (0)
name: Name(
parts: array(
0: b
)
)
alias: b
)
)
)
)

View File

@@ -0,0 +1,53 @@
Comments in arrays and function calls
-----
<?php
$arr = [
// Foo
$foo,
// Bar
$bar,
// Discarded
];
[
// Foo
$foo,
,
// Bar
$bar,
] = $arr;
foo(
// Foo
$foo,
// Bar
$bar
);
new Foo(
// Foo
$foo
);
-----
!!php7
$arr = [
// Foo
$foo,
// Bar
$bar,
];
[
// Foo
$foo,
,
// Bar
$bar,
] = $arr;
foo(
// Foo
$foo,
// Bar
$bar
);
new Foo(
// Foo
$foo
);