Treat special names correctly in getShortName()

Also change the API to accept a string rather than a FullyQualified
name instance, as this is not appropriate for symbols like "self".
This commit is contained in:
Nikita Popov 2017-06-13 19:51:22 +02:00
parent 5b43809b48
commit 19dde1363e
2 changed files with 39 additions and 18 deletions

View File

@ -148,21 +148,27 @@ class NameContext {
}
/**
* Get possible ways of writing a fully qualified name (e.g., by making use of aliases)
* Get possible ways of writing a fully qualified name (e.g., by making use of aliases).
*
* @param FullyQualified $name Fully-qualified name
* @param string $name Fully-qualified name (without leading namespace separator)
* @param int $type One of Stmt\Use_::TYPE_*
*
* @return Name[] Possible representations of the name
*/
public function getPossibleNames(FullyQualified $name, int $type) : array {
$nameStr = (string) $name;
public function getPossibleNames(string $name, int $type) : array {
$lcName = strtolower($name);
// Collect possible ways to write this name, starting with the fully-qualified name
$possibleNames = [$name];
if ($type === Stmt\Use_::TYPE_NORMAL) {
// self, parent and static must always be unqualified
if ($lcName === "self" || $lcName === "parent" || $lcName === "static") {
return [new Name($name)];
}
}
if (null !== $nsRelativeName = $this->getNamespaceRelativeName($name, $nameStr, $lcName)) {
// Collect possible ways to write this name, starting with the fully-qualified name
$possibleNames = [new FullyQualified($name)];
if (null !== $nsRelativeName = $this->getNamespaceRelativeName($name, $lcName, $type)) {
// Make sure there is no alias that makes the normally namespace-relative name
// into something else
if (null === $this->resolveAlias($nsRelativeName, $type)) {
@ -182,7 +188,7 @@ class NameContext {
foreach ($this->origAliases[$type] as $alias => $orig) {
if ($type === Stmt\Use_::TYPE_CONSTANT) {
// Constants are are complicated-sensitive
if ($this->normalizeConstName($orig) === $this->normalizeConstName($nameStr)) {
if ($this->normalizeConstName($orig) === $this->normalizeConstName($name)) {
$possibleNames[] = new Name($alias);
}
} else {
@ -199,12 +205,12 @@ class NameContext {
/**
* Get shortest representation of this fully-qualified name.
*
* @param FullyQualified $name Fully-qualified name to shorten
* @param string $name Fully-qualified name (without leading namespace separator)
* @param int $type One of Stmt\Use_::TYPE_*
*
* @return Name Shortest representation
*/
public function getShortName(Name\FullyQualified $name, int $type) : Name {
public function getShortName(string $name, int $type) : Name {
$possibleNames = $this->getPossibleNames($name, $type);
// Find shortest name
@ -244,20 +250,28 @@ class NameContext {
return null;
}
private function getNamespaceRelativeName(Name\FullyQualified $name, $nameStr, $lcName) {
private function getNamespaceRelativeName(string $name, string $lcName, int $type) {
if (null === $this->namespace) {
return new Name($name);
}
if ($type === Stmt\Use_::TYPE_CONSTANT) {
// The constants true/false/null always resolve to the global symbols, even inside a
// namespace, so they may be used without qualification
if ($lcName === "true" || $lcName === "false" || $lcName === "null") {
return new Name($name);
}
}
$namespacePrefix = strtolower($this->namespace . '\\');
if (0 === strpos($lcName, $namespacePrefix)) {
return new Name(substr($nameStr, strlen($namespacePrefix)));
return new Name(substr($name, strlen($namespacePrefix)));
}
return null;
}
private function normalizeConstName($name) {
private function normalizeConstName(string $name) {
$nsSep = strrpos($name, '\\');
if (false === $nsSep) {
return $name;

View File

@ -18,8 +18,7 @@ class NameContextTest extends TestCase {
$nameContext->addAlias(new Name('Foo\fn'), 'fn', Use_::TYPE_FUNCTION);
$nameContext->addAlias(new Name('Foo\CN'), 'CN', Use_::TYPE_CONSTANT);
$fqName = new Name\FullyQualified($name);
$possibleNames = $nameContext->getPossibleNames($fqName, $type);
$possibleNames = $nameContext->getPossibleNames($name, $type);
$possibleNames = array_map(function (Name $name) {
return $name->toCodeString();
}, $possibleNames);
@ -30,7 +29,7 @@ class NameContextTest extends TestCase {
$expectedShortName = $expectedPossibleNames[count($expectedPossibleNames) - 1];
$this->assertSame(
$expectedShortName,
$nameContext->getShortName($fqName, $type)->toCodeString()
$nameContext->getShortName($name, $type)->toCodeString()
);
}
@ -53,6 +52,14 @@ class NameContextTest extends TestCase {
[Use_::TYPE_CONSTANT, 'Foo\CN', ['\Foo\CN', 'Foo\CN', 'CN']],
[Use_::TYPE_CONSTANT, 'foo\CN', ['\foo\CN', 'Foo\CN', 'CN']],
[Use_::TYPE_CONSTANT, 'foo\cn', ['\foo\cn', 'Foo\cn']],
// self/parent/static must not be fully qualified
[Use_::TYPE_NORMAL, 'self', ['self']],
[Use_::TYPE_NORMAL, 'parent', ['parent']],
[Use_::TYPE_NORMAL, 'static', ['static']],
// true/false/null do not need to be fully qualified, even in namespaces
[Use_::TYPE_CONSTANT, 'true', ['\true', 'true']],
[Use_::TYPE_CONSTANT, 'false', ['\false', 'false']],
[Use_::TYPE_CONSTANT, 'null', ['\null', 'null']],
];
}
}