1
0
Fork 0

Update + clarify type resolution logic

pull/357/head
Sara Itani 2017-04-20 00:20:47 -07:00
parent bfbad095ee
commit 347a5a12ae
3 changed files with 170 additions and 93 deletions

View File

@ -402,7 +402,18 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
} }
private function resolveScopedPropertyAccessExpressionNodeToFqn(Tolerant\Node\Expression\ScopedPropertyAccessExpression $scoped) { private function resolveScopedPropertyAccessExpressionNodeToFqn(Tolerant\Node\Expression\ScopedPropertyAccessExpression $scoped) {
$className = $scoped->scopeResolutionQualifier->getText(); if ($scoped->scopeResolutionQualifier instanceof Tolerant\Node\Expression\Variable) {
$varType = $this->getTypeFromNode($scoped->scopeResolutionQualifier);
if ($varType === null) {
return null;
}
$className = substr((string)$varType->getFqsen(), 1);
} elseif ($scoped->scopeResolutionQualifier instanceof Tolerant\Node\QualifiedName) {
$className = (string)$scoped->scopeResolutionQualifier->getResolvedName();
} else {
return null;
}
if ($className === 'self' || $className === 'static' || $className === 'parent') { if ($className === 'self' || $className === 'static' || $className === 'parent') {
// self and static are resolved to the containing class // self and static are resolved to the containing class
$classNode = $scoped->getFirstAncestor(Tolerant\Node\Statement\ClassDeclaration::class); $classNode = $scoped->getFirstAncestor(Tolerant\Node\Statement\ClassDeclaration::class);
@ -422,7 +433,11 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
$className = $scoped->scopeResolutionQualifier->getResolvedName(); $className = $scoped->scopeResolutionQualifier->getResolvedName();
} }
if ($scoped->memberName instanceof Tolerant\Node\Expression\Variable) { if ($scoped->memberName instanceof Tolerant\Node\Expression\Variable) {
$name = (string)$className . '::$' . $scoped->memberName->getName(); $memberName = $scoped->memberName->getName();
if (empty($memberName)) {
return null;
}
$name = (string)$className . '::$' . $memberName;
} else { } else {
$name = (string)$className . '::' . $scoped->memberName->getText($scoped->getFileContents()); $name = (string)$className . '::' . $scoped->memberName->getText($scoped->getFileContents());
} }
@ -524,10 +539,15 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
return new Types\Mixed; return new Types\Mixed;
} }
// PARENTHESIZED EXPRESSION
// Retrieve inner expression from parenthesized expression
while ($expr instanceof Tolerant\Node\Expression\ParenthesizedExpression) { while ($expr instanceof Tolerant\Node\Expression\ParenthesizedExpression) {
$expr = $expr->expression; $expr = $expr->expression;
} }
// VARIABLE
// $this -> Type\this
// $myVariable -> type of corresponding assignment expression
if ($expr instanceof Tolerant\Node\Expression\Variable || $expr instanceof Tolerant\Node\UseVariableName) { if ($expr instanceof Tolerant\Node\Expression\Variable || $expr instanceof Tolerant\Node\UseVariableName) {
if ($expr->getName() === 'this') { if ($expr->getName() === 'this') {
return new Types\This; return new Types\This;
@ -542,11 +562,13 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
} }
} }
// FUNCTION CALL
// Function calls are resolved to type corresponding to their FQN
if ($expr instanceof Tolerant\Node\Expression\CallExpression && if ($expr instanceof Tolerant\Node\Expression\CallExpression &&
!( !(
$expr->callableExpression instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression || $expr->callableExpression instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression ||
$expr->callableExpression instanceof Tolerant\Node\Expression\MemberAccessExpression) $expr->callableExpression instanceof Tolerant\Node\Expression\MemberAccessExpression)
) { ) {
// Find the function definition // Find the function definition
if ($expr->callableExpression instanceof Tolerant\Node\Expression) { if ($expr->callableExpression instanceof Tolerant\Node\Expression) {
@ -564,13 +586,22 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
} }
} }
$lowerText = strtolower($expr->getText()); // TRUE / FALSE / NULL
if ($lowerText === 'true' || $lowerText === 'false') { // Resolve true and false reserved words to Types\Boolean
return new Types\Boolean; if ($expr instanceof Tolerant\Node\ReservedWord) {
$token = $expr->children->kind;
if ($token === Tolerant\TokenKind::TrueReservedWord || $token === Tolerant\TokenKind::FalseReservedWord) {
return new Types\Boolean;
}
if ($token === Tolerant\TokenKind::NullReservedWord) {
return new Types\Null_;
}
} }
// CONSTANT FETCH
// Resolve constants by retrieving corresponding definition type from FQN
if (TolerantParserHelpers::isConstantFetch($expr)) { if (TolerantParserHelpers::isConstantFetch($expr)) {
// Resolve constant
$fqn = (string)$expr->getNamespacedName(); $fqn = (string)$expr->getNamespacedName();
$def = $this->index->getDefinition($fqn, true); $def = $this->index->getDefinition($fqn, true);
if ($def !== null) { if ($def !== null) {
@ -578,11 +609,12 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
} }
} }
if (($access = $expr) instanceof Tolerant\Node\Expression\MemberAccessExpression) { // MEMBER ACCESS EXPRESSION
if ($access->memberName instanceof Tolerant\Node\Expression) { if ($expr instanceof Tolerant\Node\Expression\MemberAccessExpression) {
if ($expr->memberName instanceof Tolerant\Node\Expression) {
return new Types\Mixed; return new Types\Mixed;
} }
$var = $access->dereferencableExpression; $var = $expr->dereferencableExpression;
// Resolve object // Resolve object
$objType = $this->resolveExpressionNodeToType($var); $objType = $this->resolveExpressionNodeToType($var);
@ -600,7 +632,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
} else { } else {
$classFqn = substr((string)$t->getFqsen(), 1); $classFqn = substr((string)$t->getFqsen(), 1);
} }
$fqn = $classFqn . '->' . $access->memberName->getText($expr->getFileContents()); $fqn = $classFqn . '->' . $expr->memberName->getText($expr->getFileContents());
if ($expr->parent instanceof Tolerant\Node\Expression\CallExpression) { if ($expr->parent instanceof Tolerant\Node\Expression\CallExpression) {
$fqn .= '()'; $fqn .= '()';
} }
@ -609,18 +641,18 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
return $def->type; return $def->type;
} }
} }
} }
if ( // SCOPED PROPERTY ACCESS EXPRESSION
($scopedAccess = $expr) instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression if ($expr instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression) {
) { $classType = $this->resolveClassNameToType($expr->scopeResolutionQualifier);
$classType = $this->resolveClassNameToType($scopedAccess->scopeResolutionQualifier); if (!($classType instanceof Types\Object_) || $classType->getFqsen() === null) {
if (!($classType instanceof Types\Object_) || $classType->getFqsen() === null /*|| $expr->name instanceof Tolerant\Node\Expression*/) {
return new Types\Mixed; return new Types\Mixed;
} }
$fqn = substr((string)$classType->getFqsen(), 1) . '::'; $fqn = substr((string)$classType->getFqsen(), 1) . '::';
$fqn .= $scopedAccess->memberName->getText() ?? $scopedAccess->memberName->getText($expr->getFileContents()); // TODO is there a cleaner way to do this? // TODO is there a cleaner way to do this?
$fqn .= $expr->memberName->getText() ?? $expr->memberName->getText($expr->getFileContents());
if ($expr->parent instanceof Tolerant\Node\Expression\CallExpression) { if ($expr->parent instanceof Tolerant\Node\Expression\CallExpression) {
$fqn .= '()'; $fqn .= '()';
} }
@ -632,23 +664,33 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
return $def->type; return $def->type;
} }
// OBJECT CREATION EXPRESSION
// new A() => resolves to the type of the class type designator (A)
// TODO: new $this->a => resolves to the string represented by "a"
if ($expr instanceof Tolerant\Node\Expression\ObjectCreationExpression) { if ($expr instanceof Tolerant\Node\Expression\ObjectCreationExpression) {
return $this->resolveClassNameToType($expr->classTypeDesignator); return $this->resolveClassNameToType($expr->classTypeDesignator);
} }
// CLONE EXPRESSION
// clone($a) => resolves to the type of $a
if ($expr instanceof Tolerant\Node\Expression\CloneExpression) { if ($expr instanceof Tolerant\Node\Expression\CloneExpression) {
return $this->resolveExpressionNodeToType($expr->expression); return $this->resolveExpressionNodeToType($expr->expression);
} }
// ASSIGNMENT EXPRESSION
// $a = $myExpression => resolves to the type of the right-hand operand
if ($expr instanceof Tolerant\Node\Expression\AssignmentExpression) { if ($expr instanceof Tolerant\Node\Expression\AssignmentExpression) {
return $this->resolveExpressionNodeToType($expr->rightOperand); return $this->resolveExpressionNodeToType($expr->rightOperand);
} }
// TERNARY EXPRESSION
// $condition ? $ifExpression : $elseExpression => reslves to type of $ifCondition or $elseExpression
// $condition ?: $elseExpression => resolves to type of $condition or $elseExpression
if ($expr instanceof Tolerant\Node\Expression\TernaryExpression) { if ($expr instanceof Tolerant\Node\Expression\TernaryExpression) {
// ?: // ?:
if ($expr->ifExpression === null) { if ($expr->ifExpression === null) {
return new Types\Compound([ return new Types\Compound([
$this->resolveExpressionNodeToType($expr->condition), // why? $this->resolveExpressionNodeToType($expr->condition), // TODO: why?
$this->resolveExpressionNodeToType($expr->elseExpression) $this->resolveExpressionNodeToType($expr->elseExpression)
]); ]);
} }
@ -659,6 +701,8 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
]); ]);
} }
// NULL COALLESCE
// $rightOperand ?? $leftOperand => resolves to type of $rightOperand or $leftOperand
if ($expr instanceof Tolerant\Node\Expression\BinaryExpression && $expr->operator->kind === Tolerant\TokenKind::QuestionQuestionToken) { if ($expr instanceof Tolerant\Node\Expression\BinaryExpression && $expr->operator->kind === Tolerant\TokenKind::QuestionQuestionToken) {
// ?? operator // ?? operator
return new Types\Compound([ return new Types\Compound([
@ -667,6 +711,12 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
]); ]);
} }
// BOOLEAN EXPRESSIONS: resolve to Types\Boolean
// (bool) $expression
// !$expression
// empty($var)
// isset($var)
// >, >=, <, <=, &&, ||, AND, OR, XOR, ==, ===, !=, !==
if ( if (
TolerantParserHelpers::isBooleanExpression($expr) TolerantParserHelpers::isBooleanExpression($expr)
@ -678,27 +728,27 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
return new Types\Boolean; return new Types\Boolean;
} }
// STRING EXPRESSIONS: resolve to Types\String
// [concatenation] .=, .
// [literals] "hello", \b"hello", \B"hello", 'hello', \b'hello', HEREDOC, NOWDOC
// [cast] (string) "hello"
//
// TODO: Magic constants (__CLASS__, __DIR__, __FUNCTION__, __METHOD__, __NAMESPACE__, __TRAIT__, __FILE__)
if ( if (
($expr instanceof Tolerant\Node\Expression\BinaryExpression && ($expr instanceof Tolerant\Node\Expression\BinaryExpression &&
($expr->operator->kind === Tolerant\TokenKind::DotToken || $expr->operator->kind === Tolerant\TokenKind::DotEqualsToken)) || ($expr->operator->kind === Tolerant\TokenKind::DotToken || $expr->operator->kind === Tolerant\TokenKind::DotEqualsToken)) ||
$expr instanceof Tolerant\Node\StringLiteral || $expr instanceof Tolerant\Node\StringLiteral ||
($expr instanceof Tolerant\Node\Expression\CastExpression && $expr->castType->kind === Tolerant\TokenKind::StringCastToken) ($expr instanceof Tolerant\Node\Expression\CastExpression && $expr->castType->kind === Tolerant\TokenKind::StringCastToken)
// TODO
// || $expr instanceof Node\Expr\Scalar\String_
// || $expr instanceof Node\Expr\Scalar\Encapsed
// || $expr instanceof Node\Expr\Scalar\EncapsedStringPart
// || $expr instanceof Node\Expr\Scalar\MagicConst\Class_
// || $expr instanceof Node\Expr\Scalar\MagicConst\Dir
// || $expr instanceof Node\Expr\Scalar\MagicConst\Function_
// || $expr instanceof Node\Expr\Scalar\MagicConst\Method
// || $expr instanceof Node\Expr\Scalar\MagicConst\Namespace_
// || $expr instanceof Node\Expr\Scalar\MagicConst\Trait_
) { ) {
var_dump("string literal");
return new Types\String_; return new Types\String_;
} }
// BINARY EXPRESSIONS:
// Resolve to Types\Integer if both left and right operands are integer types, otherwise Types\Float
// [operator] +, -, *, **
// [assignment] *=, **=, -=, +=
// Resolve to Types\Float
// [assignment] /=
if ( if (
$expr instanceof Tolerant\Node\Expression\BinaryExpression && $expr instanceof Tolerant\Node\Expression\BinaryExpression &&
($operator = $expr->operator->kind) ($operator = $expr->operator->kind)
@ -706,23 +756,34 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
$operator === Tolerant\TokenKind::AsteriskAsteriskToken || $operator === Tolerant\TokenKind::AsteriskAsteriskToken ||
$operator === Tolerant\TokenKind::AsteriskToken || $operator === Tolerant\TokenKind::AsteriskToken ||
$operator === Tolerant\TokenKind::MinusToken || $operator === Tolerant\TokenKind::MinusToken ||
// Assignment expressions (TODO: consider making this a type of AssignmentExpression rather than kind of BinaryExpression)
$operator === Tolerant\TokenKind::AsteriskEqualsToken|| $operator === Tolerant\TokenKind::AsteriskEqualsToken||
$operator === Tolerant\TokenKind::AsteriskAsteriskEqualsToken || $operator === Tolerant\TokenKind::AsteriskAsteriskEqualsToken ||
$operator === Tolerant\TokenKind::MinusEqualsToken || $operator === Tolerant\TokenKind::MinusEqualsToken ||
$operator === Tolerant\TokenKind::PlusEqualsToken // TODO - this should be a type of assigment expression $operator === Tolerant\TokenKind::PlusEqualsToken
) )
) { ) {
if ( if (
$this->resolveExpressionNodeToType($expr->leftOperand) instanceof Types\Integer_ $this->resolveExpressionNodeToType($expr->leftOperand) instanceof Types\Integer
&& $this->resolveExpressionNodeToType($expr->rightOperand) instanceof Types\Integer_ && $this->resolveExpressionNodeToType($expr->rightOperand) instanceof Types\Integer
) { ) {
return new Types\Integer; return new Types\Integer;
} }
return new Types\Float_; return new Types\Float_;
} else if (
$expr instanceof Tolerant\Node\Expression\BinaryExpression &&
$expr->operator->kind === Tolerant\TokenKind::SlashEqualsToken
) {
return new Types\Float_;
} }
// INTEGER EXPRESSIONS: resolve to Types\Integer
// [literal] 1
// [operator] <=>, &, ^, |
// TODO: Magic constants (__LINE__)
if ( if (
// TODO better naming // TODO: consider different Node types of float/int, also better property name (not "children")
($expr instanceof Tolerant\Node\NumericLiteral && $expr->children->kind === Tolerant\TokenKind::IntegerLiteralToken) || ($expr instanceof Tolerant\Node\NumericLiteral && $expr->children->kind === Tolerant\TokenKind::IntegerLiteralToken) ||
$expr instanceof Tolerant\Node\Expression\BinaryExpression && ( $expr instanceof Tolerant\Node\Expression\BinaryExpression && (
($operator = $expr->operator->kind) ($operator = $expr->operator->kind)
@ -735,14 +796,22 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
return new Types\Integer; return new Types\Integer;
} }
// FLOAT EXPRESSIONS: resolve to Types\Float
// [literal] 1.5
// [operator] /
// [cast] (double)
if ( if (
$expr instanceof Tolerant\Node\NumericLiteral && $expr->children->kind === Tolerant\TokenKind::FloatingLiteralToken $expr instanceof Tolerant\Node\NumericLiteral && $expr->children->kind === Tolerant\TokenKind::FloatingLiteralToken ||
|| ($expr instanceof Tolerant\Node\Expression\CastExpression && $expr->castType->kind === Tolerant\TokenKind::DoubleCastToken) ||
($expr instanceof Tolerant\Node\Expression\CastExpression && $expr->castType->kind === Tolerant\TokenKind::DoubleCastToken) ($expr instanceof Tolerant\Node\Expression\BinaryExpression && $expr->operator->kind === Tolerant\TokenKind::SlashToken)
) { ) {
return new Types\Float_; return new Types\Float_;
} }
// ARRAY CREATION EXPRESSION:
// Resolve to Types\Array (Types\Compound of value and key types)
// [a, b, c]
// [1=>"hello", "hi"=>1, 4=>[]]s
if ($expr instanceof Tolerant\Node\Expression\ArrayCreationExpression) { if ($expr instanceof Tolerant\Node\Expression\ArrayCreationExpression) {
$valueTypes = []; $valueTypes = [];
$keyTypes = []; $keyTypes = [];
@ -771,6 +840,9 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
return new Types\Array_($valueType, $keyType); return new Types\Array_($valueType, $keyType);
} }
// SUBSCRIPT EXPRESSION
// $myArray[3]
// $myArray{"hello"}
if ($expr instanceof Tolerant\Node\Expression\SubscriptExpression) { if ($expr instanceof Tolerant\Node\Expression\SubscriptExpression) {
$varType = $this->resolveExpressionNodeToType($expr->postfixExpression); $varType = $this->resolveExpressionNodeToType($expr->postfixExpression);
if (!($varType instanceof Types\Array_)) { if (!($varType instanceof Types\Array_)) {
@ -779,6 +851,8 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
return $varType->getValueType(); return $varType->getValueType();
} }
// SCRIPT INCLUSION EXPRESSION
// include, require, include_once, require_once
if ($expr instanceof Tolerant\Node\Expression\ScriptInclusionExpression) { if ($expr instanceof Tolerant\Node\Expression\ScriptInclusionExpression) {
// TODO: resolve path to PhpDocument and find return statement // TODO: resolve path to PhpDocument and find return statement
return new Types\Mixed; return new Types\Mixed;
@ -805,14 +879,13 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
return new Types\Object_; return new Types\Object_;
} }
$className = (string)$class->getResolvedName(); $className = (string)$class->getResolvedName();
$lowerClassName = strtolower($className);
if ($lowerClassName === 'static') { if ($className === 'static') {
return new Types\Static_; return new Types\Static_;
} }
if ($lowerClassName === 'self' || $lowerClassName === 'parent') { if ($className === 'self' || $className === 'parent') {
$classNode = $class->getFirstAncestor(Tolerant\Node\Statement\ClassDeclaration::class); $classNode = $class->getFirstAncestor(Tolerant\Node\Statement\ClassDeclaration::class);
if ($lowerClassName === 'parent') { if ($className === 'parent') {
if ($classNode === null || $classNode->classBaseClause === null) { if ($classNode === null || $classNode->classBaseClause === null) {
return new Types\Object_; return new Types\Object_;
} }
@ -846,7 +919,10 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
*/ */
public function getTypeFromNode($node) public function getTypeFromNode($node)
{ {
// For parameters, get the type of the parameter [first from doc block, then from combo of param type and default // PARAMETERS
// Get the type of the parameter:
// 1. Doc block
// 2. Parameter type and default
if ($node instanceof Tolerant\Node\Parameter) { if ($node instanceof Tolerant\Node\Parameter) {
// Parameters // Parameters
// Get the doc block for the the function call // Get the doc block for the the function call
@ -885,7 +961,12 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
} }
return $type ?? new Types\Mixed; return $type ?? new Types\Mixed;
} }
// for functions and methods, get the return type [first from doc block, then from return type]
// FUNCTIONS AND METHODS
// Get the return type
// 1. doc block
// 2. return type hint
// 3. TODO: infer from return statements
if (TolerantParserHelpers::isFunctionLike($node)) { if (TolerantParserHelpers::isFunctionLike($node)) {
// Functions/methods // Functions/methods
$docBlock = $this->getDocBlock($node); $docBlock = $this->getDocBlock($node);
@ -909,7 +990,8 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
return new Types\Mixed; return new Types\Mixed;
} }
// for variables / assignments, get the documented type the assignment resolves to. // PROPERTIES, CONSTS, CLASS CONSTS, ASSIGNMENT EXPRESSIONS
// Get the documented type the assignment resolves to.
if ( if (
($declarationNode = ($declarationNode =
TolerantParserHelpers::tryGetPropertyDeclaration($node) ?? TolerantParserHelpers::tryGetPropertyDeclaration($node) ??

View File

@ -91,58 +91,56 @@ class TolerantTreeAnalyzer implements TreeAnalyzerInterface {
if ($fqn !== null) { if ($fqn !== null) {
$this->definitionNodes[$fqn] = $node; $this->definitionNodes[$fqn] = $node;
$this->definitions[$fqn] = $this->definitionResolver->createDefinitionFromNode($node, $fqn); $this->definitions[$fqn] = $this->definitionResolver->createDefinitionFromNode($node, $fqn);
} } else {
$parent = $node->parent;
if (!(
(
// $node->parent instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression ||
($node instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression ||
$node instanceof Tolerant\Node\Expression\MemberAccessExpression)
&& !(
$node->parent instanceof Tolerant\Node\Expression\CallExpression ||
$node->memberName instanceof Tolerant\Token
))
|| ($parent instanceof Tolerant\Node\Statement\NamespaceDefinition && $parent->name !== null && $parent->name->getStart() === $node->getStart()))
) {
$parent = $node->parent; $fqn = $this->definitionResolver->resolveReferenceNodeToFqn($node);
if (!( if ($fqn !== null) {
( $this->addReference($fqn, $node);
// $node->parent instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression ||
($node instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression ||
$node instanceof Tolerant\Node\Expression\MemberAccessExpression)
&& !(
$node->parent instanceof Tolerant\Node\Expression\CallExpression ||
$node->memberName instanceof Tolerant\Token
))
|| ($parent instanceof Tolerant\Node\Statement\NamespaceDefinition && $parent->name !== null && $parent->name->getStart() === $node->getStart()))
) {
$fqn = $this->definitionResolver->resolveReferenceNodeToFqn($node); if (
if ($fqn !== null) { $node instanceof Tolerant\Node\QualifiedName
$this->addReference($fqn, $node); && ($node->isQualifiedName() || $node->parent instanceof Tolerant\Node\NamespaceUseClause)
&& !($parent instanceof Tolerant\Node\Statement\NamespaceDefinition && $parent->name->getStart() === $node->getStart()
if ( )
$node instanceof Tolerant\Node\QualifiedName ) {
&& ($node->isQualifiedName() || $node->parent instanceof Tolerant\Node\NamespaceUseClause) // Add references for each referenced namespace
&& !($parent instanceof Tolerant\Node\Statement\NamespaceDefinition && $parent->name->getStart() === $node->getStart() $ns = $fqn;
) while (($pos = strrpos($ns, '\\')) !== false) {
) { $ns = substr($ns, 0, $pos);
// Add references for each referenced namespace $this->addReference($ns, $node);
$ns = $fqn; }
while (($pos = strrpos($ns, '\\')) !== false) {
$ns = substr($ns, 0, $pos);
$this->addReference($ns, $node);
} }
}
// Namespaced constant access and function calls also need to register a reference // Namespaced constant access and function calls also need to register a reference
// to the global version because PHP falls back to global at runtime // to the global version because PHP falls back to global at runtime
// http://php.net/manual/en/language.namespaces.fallback.php // http://php.net/manual/en/language.namespaces.fallback.php
if (TolerantParserHelpers::isConstantFetch($node) || if (TolerantParserHelpers::isConstantFetch($node) ||
($parent instanceof Tolerant\Node\Expression\CallExpression ($parent instanceof Tolerant\Node\Expression\CallExpression
&& !( && !(
$parent->callableExpression instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression || $node instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression ||
$node instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression || $node instanceof Tolerant\Node\Expression\MemberAccessExpression
$node instanceof Tolerant\Node\Expression\MemberAccessExpression ))) {
))) { $parts = explode('\\', $fqn);
$parts = explode('\\', $fqn); if (count($parts) > 1) {
if (count($parts) > 1) { $globalFqn = end($parts);
$globalFqn = end($parts); $this->addReference($globalFqn, $node);
$this->addReference($globalFqn, $node); }
} }
} }
} }
} }
$this->collectDefinitionsAndReferences($node); $this->collectDefinitionsAndReferences($node);
} }

View File

@ -35,7 +35,7 @@ class ValidationTest extends TestCase
foreach (new RecursiveIteratorIterator($iterator) as $file) { foreach (new RecursiveIteratorIterator($iterator) as $file) {
if (strpos(\strrev((string)$file), \strrev(".php")) === 0 if (strpos(\strrev((string)$file), \strrev(".php")) === 0
// && strpos((string)$file, "memberAccess3.php")!== false // && strpos((string)$file, "ContainerFactory.php")!== false
) { ) {
if ($file->getSize() < 100000) { if ($file->getSize() < 100000) {
$testProviderArray[$frameworkName . "::" . $file->getBasename()] = [$file->getPathname(), $frameworkName]; $testProviderArray[$frameworkName . "::" . $file->getBasename()] = [$file->getPathname(), $frameworkName];
@ -152,9 +152,6 @@ class ValidationTest extends TestCase
foreach ($document->getDefinitions() as $defn) { foreach ($document->getDefinitions() as $defn) {
$fqns[] = $defn->fqn; $fqns[] = $defn->fqn;
if ($defn->type instanceof \phpDocumentor\Reflection\Types\Null_) {
$defn->type = new \phpDocumentor\Reflection\Types\Mixed;
}
$currentTypes[$defn->fqn] = $defn->type; $currentTypes[$defn->fqn] = $defn->type;
$canBeInstantiated[$defn->fqn] = $defn->canBeInstantiated; $canBeInstantiated[$defn->fqn] = $defn->canBeInstantiated;