Skip to content

Commit 6894d85

Browse files
phil-nelsonfelixfbecker
authored andcommitted
fix(DefinitionResolver): resolve self correctly for docblock @return self (#576)
1 parent c48ee55 commit 6894d85

File tree

3 files changed

+53
-5
lines changed

3 files changed

+53
-5
lines changed

src/DefinitionResolver.php

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,20 @@ private static function getContainingClassFqn(Node $node)
526526
return (string)$classNode->getNamespacedName();
527527
}
528528

529+
/**
530+
* Returns the type of the class a node is contained in
531+
* Returns null if the class is anonymous or the node is not contained in a class
532+
*
533+
* @param Node $node The node used to find the containing class
534+
*
535+
* @return Types\Object_|null
536+
*/
537+
private function getContainingClassType(Node $node)
538+
{
539+
$classFqn = $this->getContainingClassFqn($node);
540+
return $classFqn ? new Types\Object_(new Fqsen('\\' . $classFqn)) : null;
541+
}
542+
529543
/**
530544
* Returns the assignment or parameter node where a variable was defined
531545
*
@@ -1110,18 +1124,24 @@ public function getTypeFromNode($node)
11101124
&& $returnTags[0]->getType() !== null
11111125
) {
11121126
// Use @return tag
1113-
return $returnTags[0]->getType();
1127+
$returnType = $returnTags[0]->getType();
1128+
if ($returnType instanceof Types\Self_) {
1129+
$selfType = $this->getContainingClassType($node);
1130+
if ($selfType) {
1131+
return $selfType;
1132+
}
1133+
}
1134+
return $returnType;
11141135
}
11151136
if ($node->returnType !== null && !($node->returnType instanceof PhpParser\MissingToken)) {
11161137
// Use PHP7 return type hint
11171138
if ($node->returnType instanceof PhpParser\Token) {
11181139
// Resolve a string like "bool" to a type object
11191140
return $this->typeResolver->resolve($node->returnType->getText($node->getFileContents()));
11201141
} elseif ($node->returnType->getResolvedName() === 'self') {
1121-
$classNode = $node->getFirstAncestor(Node\Statement\ClassDeclaration::class);
1122-
if ($classNode) {
1123-
$classFqn = (string)$classNode->getNamespacedName();
1124-
return new Types\Object_(new Fqsen('\\' . $classFqn));
1142+
$selfType = $this->getContainingClassType($node);
1143+
if ($selfType !== null) {
1144+
return $selfType;
11251145
}
11261146
}
11271147
return new Types\Object_(new Fqsen('\\' . (string)$node->returnType->getResolvedName()));

tests/Validation/cases/methodReturnType.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,7 @@ class FooClass {
44
public function foo(): FooClass {
55
return $this;
66
}
7+
8+
/** @return self */
9+
public function bar() { }
710
}

tests/Validation/cases/methodReturnType.php.expected.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,31 @@
4949
"documentation": null,
5050
"parameters": []
5151
}
52+
},
53+
"FooClass->bar()": {
54+
"fqn": "FooClass->bar()",
55+
"extends": [],
56+
"isMember": true,
57+
"roamed": false,
58+
"isStatic": false,
59+
"canBeInstantiated": false,
60+
"symbolInformation": {
61+
"name": "bar",
62+
"kind": 6,
63+
"location": {
64+
"uri": "./methodReturnType.php"
65+
},
66+
"containerName": "FooClass"
67+
},
68+
"type__tostring": "\\FooClass",
69+
"type": {},
70+
"declarationLine": "public function bar() { }",
71+
"documentation": "",
72+
"signatureInformation": {
73+
"label": "()",
74+
"documentation": "",
75+
"parameters": []
76+
}
5277
}
5378
}
5479
}

0 commit comments

Comments
 (0)