Skip to content

Commit

Permalink
Stubs - no need to repeat inherited methods
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Jan 3, 2020
1 parent 65e578c commit dc9221b
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 84 deletions.
2 changes: 1 addition & 1 deletion src/PhpDoc/StubPhpDocProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ public function findFunctionPhpDoc(string $functionName): ?ResolvedPhpDocBlock
return null;
}

private function isKnownClass(string $className): bool
public function isKnownClass(string $className): bool
{
$this->initializeKnownElements();

Expand Down
2 changes: 1 addition & 1 deletion src/Reflection/ClassReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -817,7 +817,7 @@ private function getTemplateTags(): array
/**
* @return array<string,ClassReflection>
*/
private function getAncestors(): array
public function getAncestors(): array
{
$ancestors = $this->ancestors;

Expand Down
37 changes: 35 additions & 2 deletions src/Reflection/Php/PhpClassReflectionExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ private function createMethod(
$stubPhpDocParameterTypes = [];
$stubPhpDocParameterVariadicity = [];
if (count($variantNames) === 1) {
$stubPhpDoc = $this->stubPhpDocProvider->findMethodPhpDoc($declaringClassName, $methodReflection->getName());
$stubPhpDoc = $this->findMethodPhpDocIncludingAncestors($declaringClassName, $methodReflection->getName());
if ($stubPhpDoc !== null) {
$stubPhpDocString = $stubPhpDoc->getPhpDocString();
$templateTypeMap = $declaringClass->getActiveTemplateTypeMap();
Expand Down Expand Up @@ -486,7 +486,7 @@ private function createMethod(
}

$declaringTraitName = $this->findMethodTrait($methodReflection);
$resolvedPhpDoc = $this->stubPhpDocProvider->findMethodPhpDoc($declaringClassName, $methodReflection->getName());
$resolvedPhpDoc = $this->findMethodPhpDocIncludingAncestors($declaringClassName, $methodReflection->getName());
$stubPhpDocString = null;
if ($resolvedPhpDoc === null) {
if ($declaringClass->getFileName() !== false) {
Expand Down Expand Up @@ -866,4 +866,37 @@ private function getPhpDocReturnType(ClassReflection $phpDocBlockClassReflection
return null;
}

private function findMethodPhpDocIncludingAncestors(string $declaringClassName, string $methodName): ?ResolvedPhpDocBlock
{
$resolved = $this->stubPhpDocProvider->findMethodPhpDoc($declaringClassName, $methodName);
if ($resolved !== null) {
return $resolved;
}
if (!$this->stubPhpDocProvider->isKnownClass($declaringClassName)) {
return null;
}
if (!$this->reflectionProvider->hasClass($declaringClassName)) {
return null;
}

$ancestors = $this->reflectionProvider->getClass($declaringClassName)->getAncestors();
foreach ($ancestors as $ancestor) {
if ($ancestor->getName() === $declaringClassName) {
continue;
}
if (!$ancestor->hasNativeMethod($methodName)) {
continue;
}

$resolved = $this->stubPhpDocProvider->findMethodPhpDoc($ancestor->getName(), $methodName);
if ($resolved === null) {
continue;
}

return $resolved;
}

return null;
}

}
25 changes: 0 additions & 25 deletions stubs/ArrayObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,31 +50,6 @@ class ArrayObject implements IteratorAggregate, ArrayAccess
*/
public function __construct($input = null, $flags = 0, $iterator_class = "ArrayIterator") { }

/**
* @param TKey $index
* @return bool
*/
public function offsetExists($index) { }

/**
* @param TKey $index
* @return TValue
*/
public function offsetGet($index) { }

/**
* @param TKey $index
* @param TValue $newval
* @return void
*/
public function offsetSet($index, $newval) { }

/**
* @param TKey $index
* @return void
*/
public function offsetUnset($index) { }

/**
* @param TValue $value
* @return void
Expand Down
55 changes: 0 additions & 55 deletions stubs/iterable.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,6 @@ public function key();
class Generator implements Iterator
{

/**
* @return TValue
*/
public function current() {}

/**
* @return TKey
*/
public function key() {}

/**
* @return TReturn
*/
Expand Down Expand Up @@ -95,16 +85,6 @@ class SimpleXMLElement implements Traversable
interface SeekableIterator extends Iterator
{

/**
* @return TValue
*/
public function current();

/**
* @return TKey
*/
public function key();

}

/**
Expand All @@ -122,31 +102,6 @@ class ArrayIterator implements SeekableIterator, ArrayAccess, Countable, Seriali
*/
public function __construct($array = array(), $flags = 0) { }

/**
* @param TKey $index
* @return bool
*/
public function offsetExists($index) { }

/**
* @param TKey $index
* @return TValue
*/
public function offsetGet($index) { }

/**
* @param TKey $index
* @param TValue $newval
* @return void
*/
public function offsetSet($index, $newval) { }

/**
* @param TKey $index
* @return void
*/
public function offsetUnset($index) { }

/**
* @param TValue $value
* @return void
Expand All @@ -170,16 +125,6 @@ public function uasort($cmp_function) { }
*/
public function uksort($cmp_function) { }

/**
* @return TValue
*/
public function current();

/**
* @return TKey
*/
public function key();

}

class DOMDocument
Expand Down
22 changes: 22 additions & 0 deletions tests/PHPStan/Levels/data/stubs-methods-3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[
{
"message": "Return type (string) of method StubsIntegrationTest\\AnotherInterfaceExtendingInterfaceWithStubPhpDoc::doFoo() should be compatible with return type (int) of method StubsIntegrationTest\\InterfaceWithStubPhpDoc::doFoo()",
"line": 68,
"ignorable": true
},
{
"message": "Anonymous function should return int but returns string.",
"line": 74,
"ignorable": true
},
{
"message": "Return type (string) of method StubsIntegrationTest\\YetAnotherClassExtendingInterfaceWithStubPhpDoc::doFoo() should be compatible with return type (int) of method StubsIntegrationTest\\InterfaceWithStubPhpDoc::doFoo()",
"line": 119,
"ignorable": true
},
{
"message": "Anonymous function should return int but returns string.",
"line": 128,
"ignorable": true
}
]
22 changes: 22 additions & 0 deletions tests/PHPStan/Levels/data/stubs-methods-4.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[
{
"message": "Strict comparison using === between int and array() will always evaluate to false.",
"line": 47,
"ignorable": true
},
{
"message": "Strict comparison using === between int and array() will always evaluate to false.",
"line": 58,
"ignorable": true
},
{
"message": "Strict comparison using === between int and array() will always evaluate to false.",
"line": 89,
"ignorable": true
},
{
"message": "Strict comparison using === between int and array() will always evaluate to false.",
"line": 108,
"ignorable": true
}
]
96 changes: 96 additions & 0 deletions tests/PHPStan/Levels/data/stubs-methods.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,99 @@ function (FooChild $fooChild) {
$string = $fooChild->doFoo('test');
$fooChild->doFoo($string);
};

interface InterfaceWithStubPhpDoc
{

/**
* @return string
*/
public function doFoo();

}

function (InterfaceWithStubPhpDoc $stub): int
{
$stub->doFoo() === [];
return $stub->doFoo(); // stub wins
};

interface InterfaceExtendingInterfaceWithStubPhpDoc extends InterfaceWithStubPhpDoc
{

}

function (InterfaceExtendingInterfaceWithStubPhpDoc $stub): int
{
$stub->doFoo() === [];
return $stub->doFoo(); // stub wins
};

interface AnotherInterfaceExtendingInterfaceWithStubPhpDoc extends InterfaceWithStubPhpDoc
{

/**
* @return string
*/
public function doFoo();

}

function (AnotherInterfaceExtendingInterfaceWithStubPhpDoc $stub): int
{
return $stub->doFoo(); // implementation wins - string -> int mismatch reported
};

class ClassExtendingInterfaceWithStubPhpDoc implements InterfaceWithStubPhpDoc
{

public function doFoo()
{
throw new \Exception();
}

}

function (ClassExtendingInterfaceWithStubPhpDoc $stub): int
{
$stub->doFoo() === [];
return $stub->doFoo(); // stub wins
};

class AnotherClassExtendingInterfaceWithStubPhpDoc implements InterfaceWithStubPhpDoc
{

/**
* @return string
*/
public function doFoo()
{
throw new \Exception();
}

}

function (AnotherClassExtendingInterfaceWithStubPhpDoc $stub): int
{
$stub->doFoo() === [];
return $stub->doFoo(); // stub wins
};

/** This one is missing in the stubs */
class YetAnotherClassExtendingInterfaceWithStubPhpDoc implements InterfaceWithStubPhpDoc
{

/**
* @return string
*/
public function doFoo()
{
throw new \Exception();
}

}

function (YetAnotherClassExtendingInterfaceWithStubPhpDoc $stub): int
{
return $stub->doFoo(); // implementation wins - string -> int mismatch reported
};
20 changes: 20 additions & 0 deletions tests/notAutoloaded/stubs-methods.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,23 @@ public function doFoo(Collection $collection): void
}

}

interface InterfaceWithStubPhpDoc
{

/**
* @return int
*/
public function doFoo();

}

class ClassExtendingInterfaceWithStubPhpDoc implements InterfaceWithStubPhpDoc
{

}

class AnotherClassExtendingInterfaceWithStubPhpDoc implements InterfaceWithStubPhpDoc
{

}

0 comments on commit dc9221b

Please sign in to comment.