Skip to content

Commit dc9221b

Browse files
committed
Stubs - no need to repeat inherited methods
1 parent 65e578c commit dc9221b

File tree

9 files changed

+197
-84
lines changed

9 files changed

+197
-84
lines changed

src/PhpDoc/StubPhpDocProvider.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ public function findFunctionPhpDoc(string $functionName): ?ResolvedPhpDocBlock
171171
return null;
172172
}
173173

174-
private function isKnownClass(string $className): bool
174+
public function isKnownClass(string $className): bool
175175
{
176176
$this->initializeKnownElements();
177177

src/Reflection/ClassReflection.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -817,7 +817,7 @@ private function getTemplateTags(): array
817817
/**
818818
* @return array<string,ClassReflection>
819819
*/
820-
private function getAncestors(): array
820+
public function getAncestors(): array
821821
{
822822
$ancestors = $this->ancestors;
823823

src/Reflection/Php/PhpClassReflectionExtension.php

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,7 @@ private function createMethod(
429429
$stubPhpDocParameterTypes = [];
430430
$stubPhpDocParameterVariadicity = [];
431431
if (count($variantNames) === 1) {
432-
$stubPhpDoc = $this->stubPhpDocProvider->findMethodPhpDoc($declaringClassName, $methodReflection->getName());
432+
$stubPhpDoc = $this->findMethodPhpDocIncludingAncestors($declaringClassName, $methodReflection->getName());
433433
if ($stubPhpDoc !== null) {
434434
$stubPhpDocString = $stubPhpDoc->getPhpDocString();
435435
$templateTypeMap = $declaringClass->getActiveTemplateTypeMap();
@@ -486,7 +486,7 @@ private function createMethod(
486486
}
487487

488488
$declaringTraitName = $this->findMethodTrait($methodReflection);
489-
$resolvedPhpDoc = $this->stubPhpDocProvider->findMethodPhpDoc($declaringClassName, $methodReflection->getName());
489+
$resolvedPhpDoc = $this->findMethodPhpDocIncludingAncestors($declaringClassName, $methodReflection->getName());
490490
$stubPhpDocString = null;
491491
if ($resolvedPhpDoc === null) {
492492
if ($declaringClass->getFileName() !== false) {
@@ -866,4 +866,37 @@ private function getPhpDocReturnType(ClassReflection $phpDocBlockClassReflection
866866
return null;
867867
}
868868

869+
private function findMethodPhpDocIncludingAncestors(string $declaringClassName, string $methodName): ?ResolvedPhpDocBlock
870+
{
871+
$resolved = $this->stubPhpDocProvider->findMethodPhpDoc($declaringClassName, $methodName);
872+
if ($resolved !== null) {
873+
return $resolved;
874+
}
875+
if (!$this->stubPhpDocProvider->isKnownClass($declaringClassName)) {
876+
return null;
877+
}
878+
if (!$this->reflectionProvider->hasClass($declaringClassName)) {
879+
return null;
880+
}
881+
882+
$ancestors = $this->reflectionProvider->getClass($declaringClassName)->getAncestors();
883+
foreach ($ancestors as $ancestor) {
884+
if ($ancestor->getName() === $declaringClassName) {
885+
continue;
886+
}
887+
if (!$ancestor->hasNativeMethod($methodName)) {
888+
continue;
889+
}
890+
891+
$resolved = $this->stubPhpDocProvider->findMethodPhpDoc($ancestor->getName(), $methodName);
892+
if ($resolved === null) {
893+
continue;
894+
}
895+
896+
return $resolved;
897+
}
898+
899+
return null;
900+
}
901+
869902
}

stubs/ArrayObject.php

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -50,31 +50,6 @@ class ArrayObject implements IteratorAggregate, ArrayAccess
5050
*/
5151
public function __construct($input = null, $flags = 0, $iterator_class = "ArrayIterator") { }
5252

53-
/**
54-
* @param TKey $index
55-
* @return bool
56-
*/
57-
public function offsetExists($index) { }
58-
59-
/**
60-
* @param TKey $index
61-
* @return TValue
62-
*/
63-
public function offsetGet($index) { }
64-
65-
/**
66-
* @param TKey $index
67-
* @param TValue $newval
68-
* @return void
69-
*/
70-
public function offsetSet($index, $newval) { }
71-
72-
/**
73-
* @param TKey $index
74-
* @return void
75-
*/
76-
public function offsetUnset($index) { }
77-
7853
/**
7954
* @param TValue $value
8055
* @return void

stubs/iterable.php

Lines changed: 0 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,6 @@ public function key();
5656
class Generator implements Iterator
5757
{
5858

59-
/**
60-
* @return TValue
61-
*/
62-
public function current() {}
63-
64-
/**
65-
* @return TKey
66-
*/
67-
public function key() {}
68-
6959
/**
7060
* @return TReturn
7161
*/
@@ -95,16 +85,6 @@ class SimpleXMLElement implements Traversable
9585
interface SeekableIterator extends Iterator
9686
{
9787

98-
/**
99-
* @return TValue
100-
*/
101-
public function current();
102-
103-
/**
104-
* @return TKey
105-
*/
106-
public function key();
107-
10888
}
10989

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

125-
/**
126-
* @param TKey $index
127-
* @return bool
128-
*/
129-
public function offsetExists($index) { }
130-
131-
/**
132-
* @param TKey $index
133-
* @return TValue
134-
*/
135-
public function offsetGet($index) { }
136-
137-
/**
138-
* @param TKey $index
139-
* @param TValue $newval
140-
* @return void
141-
*/
142-
public function offsetSet($index, $newval) { }
143-
144-
/**
145-
* @param TKey $index
146-
* @return void
147-
*/
148-
public function offsetUnset($index) { }
149-
150105
/**
151106
* @param TValue $value
152107
* @return void
@@ -170,16 +125,6 @@ public function uasort($cmp_function) { }
170125
*/
171126
public function uksort($cmp_function) { }
172127

173-
/**
174-
* @return TValue
175-
*/
176-
public function current();
177-
178-
/**
179-
* @return TKey
180-
*/
181-
public function key();
182-
183128
}
184129

185130
class DOMDocument
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[
2+
{
3+
"message": "Return type (string) of method StubsIntegrationTest\\AnotherInterfaceExtendingInterfaceWithStubPhpDoc::doFoo() should be compatible with return type (int) of method StubsIntegrationTest\\InterfaceWithStubPhpDoc::doFoo()",
4+
"line": 68,
5+
"ignorable": true
6+
},
7+
{
8+
"message": "Anonymous function should return int but returns string.",
9+
"line": 74,
10+
"ignorable": true
11+
},
12+
{
13+
"message": "Return type (string) of method StubsIntegrationTest\\YetAnotherClassExtendingInterfaceWithStubPhpDoc::doFoo() should be compatible with return type (int) of method StubsIntegrationTest\\InterfaceWithStubPhpDoc::doFoo()",
14+
"line": 119,
15+
"ignorable": true
16+
},
17+
{
18+
"message": "Anonymous function should return int but returns string.",
19+
"line": 128,
20+
"ignorable": true
21+
}
22+
]
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[
2+
{
3+
"message": "Strict comparison using === between int and array() will always evaluate to false.",
4+
"line": 47,
5+
"ignorable": true
6+
},
7+
{
8+
"message": "Strict comparison using === between int and array() will always evaluate to false.",
9+
"line": 58,
10+
"ignorable": true
11+
},
12+
{
13+
"message": "Strict comparison using === between int and array() will always evaluate to false.",
14+
"line": 89,
15+
"ignorable": true
16+
},
17+
{
18+
"message": "Strict comparison using === between int and array() will always evaluate to false.",
19+
"line": 108,
20+
"ignorable": true
21+
}
22+
]

tests/PHPStan/Levels/data/stubs-methods.php

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,99 @@ function (FooChild $fooChild) {
3131
$string = $fooChild->doFoo('test');
3232
$fooChild->doFoo($string);
3333
};
34+
35+
interface InterfaceWithStubPhpDoc
36+
{
37+
38+
/**
39+
* @return string
40+
*/
41+
public function doFoo();
42+
43+
}
44+
45+
function (InterfaceWithStubPhpDoc $stub): int
46+
{
47+
$stub->doFoo() === [];
48+
return $stub->doFoo(); // stub wins
49+
};
50+
51+
interface InterfaceExtendingInterfaceWithStubPhpDoc extends InterfaceWithStubPhpDoc
52+
{
53+
54+
}
55+
56+
function (InterfaceExtendingInterfaceWithStubPhpDoc $stub): int
57+
{
58+
$stub->doFoo() === [];
59+
return $stub->doFoo(); // stub wins
60+
};
61+
62+
interface AnotherInterfaceExtendingInterfaceWithStubPhpDoc extends InterfaceWithStubPhpDoc
63+
{
64+
65+
/**
66+
* @return string
67+
*/
68+
public function doFoo();
69+
70+
}
71+
72+
function (AnotherInterfaceExtendingInterfaceWithStubPhpDoc $stub): int
73+
{
74+
return $stub->doFoo(); // implementation wins - string -> int mismatch reported
75+
};
76+
77+
class ClassExtendingInterfaceWithStubPhpDoc implements InterfaceWithStubPhpDoc
78+
{
79+
80+
public function doFoo()
81+
{
82+
throw new \Exception();
83+
}
84+
85+
}
86+
87+
function (ClassExtendingInterfaceWithStubPhpDoc $stub): int
88+
{
89+
$stub->doFoo() === [];
90+
return $stub->doFoo(); // stub wins
91+
};
92+
93+
class AnotherClassExtendingInterfaceWithStubPhpDoc implements InterfaceWithStubPhpDoc
94+
{
95+
96+
/**
97+
* @return string
98+
*/
99+
public function doFoo()
100+
{
101+
throw new \Exception();
102+
}
103+
104+
}
105+
106+
function (AnotherClassExtendingInterfaceWithStubPhpDoc $stub): int
107+
{
108+
$stub->doFoo() === [];
109+
return $stub->doFoo(); // stub wins
110+
};
111+
112+
/** This one is missing in the stubs */
113+
class YetAnotherClassExtendingInterfaceWithStubPhpDoc implements InterfaceWithStubPhpDoc
114+
{
115+
116+
/**
117+
* @return string
118+
*/
119+
public function doFoo()
120+
{
121+
throw new \Exception();
122+
}
123+
124+
}
125+
126+
function (YetAnotherClassExtendingInterfaceWithStubPhpDoc $stub): int
127+
{
128+
return $stub->doFoo(); // implementation wins - string -> int mismatch reported
129+
};

tests/notAutoloaded/stubs-methods.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,23 @@ public function doFoo(Collection $collection): void
3232
}
3333

3434
}
35+
36+
interface InterfaceWithStubPhpDoc
37+
{
38+
39+
/**
40+
* @return int
41+
*/
42+
public function doFoo();
43+
44+
}
45+
46+
class ClassExtendingInterfaceWithStubPhpDoc implements InterfaceWithStubPhpDoc
47+
{
48+
49+
}
50+
51+
class AnotherClassExtendingInterfaceWithStubPhpDoc implements InterfaceWithStubPhpDoc
52+
{
53+
54+
}

0 commit comments

Comments
 (0)