Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improving support for anonymous classes #2613

Open
wants to merge 37 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
c597a53
First try at implementing it
mamazu Mar 22, 2024
5cc2e4d
Fixups
mamazu Mar 22, 2024
71bf377
Skip real classes in `ObjectCreationExpression`s
mamazu Mar 22, 2024
f76d62f
Improvements
mamazu Mar 23, 2024
d98a9f8
Removing unused properties
mamazu Mar 23, 2024
5e5389a
Making assertions assert the correct type
mamazu Mar 23, 2024
4889c1c
Merge branch 'master' into anonymous_classes
mamazu Mar 23, 2024
836d962
Using the reflected class resolver
mamazu Mar 23, 2024
8e24b48
Using a softer exception
mamazu Mar 24, 2024
f32ad1b
More fixup
mamazu Mar 24, 2024
7a53a6b
Adding support for extending anonymous classes
mamazu Mar 25, 2024
701944a
Improving the typing
mamazu Mar 27, 2024
8b69ecb
Adding an `isAnonymous` method to the class
mamazu Mar 30, 2024
168f3e2
Improving it some more
mamazu Mar 30, 2024
cbeb726
More sensible default
mamazu Mar 31, 2024
07dd87c
Adding tests back
mamazu Mar 31, 2024
c79260d
First try at implementing it
mamazu Mar 22, 2024
f91f1b4
Fixups
mamazu Mar 22, 2024
ae72ace
Skip real classes in `ObjectCreationExpression`s
mamazu Mar 22, 2024
046ff0c
Improvements
mamazu Mar 23, 2024
f8044f4
Removing unused properties
mamazu Mar 23, 2024
10d4180
Making assertions assert the correct type
mamazu Mar 23, 2024
202087e
Using the reflected class resolver
mamazu Mar 23, 2024
175266d
Using a softer exception
mamazu Mar 24, 2024
bc3b1b4
More fixup
mamazu Mar 24, 2024
0ea9d3e
Adding support for extending anonymous classes
mamazu Mar 25, 2024
14802b4
Improving the typing
mamazu Mar 27, 2024
0ea17a9
Adding an `isAnonymous` method to the class
mamazu Mar 30, 2024
f197e6f
Improving it some more
mamazu Mar 30, 2024
e5ff010
More sensible default
mamazu Mar 31, 2024
c30b479
Adding tests back
mamazu Mar 31, 2024
4f76488
Merge remote-tracking branch 'origin/anonymous_classes' into anonymou…
mamazu Mar 31, 2024
9249c6a
Fixing type errors
mamazu Mar 31, 2024
6640629
Reverting changes
mamazu Mar 31, 2024
caa112a
Fix
mamazu Mar 31, 2024
da577f9
Moving the ReflectionClassLikeCollection creation to the reflector
mamazu Apr 9, 2024
011b095
Merge branch 'master' into anonymous_classes
mamazu Apr 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions doc/reference/configuration.rst
Expand Up @@ -894,7 +894,7 @@ FilePathResolverExtension
"""""""""""""""""""""""""""""""""""


**Default**: ``"\/home\/mamazu\/packages\/phpactor\/phpactor"``
**Default**: ``"\/home\/daniel\/www\/phpactor\/phpactor"``


.. _param_file_path_resolver.app_name:
Expand Down Expand Up @@ -1348,7 +1348,7 @@ Amount of time (in milliseconds) to wait before responding to a shutdown notific
Internal use only - name path to Phpactor binary


**Default**: ``"\/home\/mamazu\/packages\/phpactor\/phpactor\/lib\/Extension\/LanguageServer\/..\/..\/..\/bin\/phpactor"``
**Default**: ``"\/home\/daniel\/www\/phpactor\/phpactor\/lib\/Extension\/LanguageServer\/..\/..\/..\/bin\/phpactor"``


.. _param_language_server.self_destruct_timeout:
Expand Down
10 changes: 10 additions & 0 deletions lib/CodeBuilder/Adapter/TolerantParser/TolerantUpdater.php
@@ -1,6 +1,7 @@
<?php
namespace Phpactor\CodeBuilder\Adapter\TolerantParser;

use Microsoft\PhpParser\Node\Expression\ObjectCreationExpression;
use Microsoft\PhpParser\Node\SourceFileNode;
use Microsoft\PhpParser\Node\Statement\ClassDeclaration;
use Microsoft\PhpParser\Node\Statement\TraitDeclaration;
Expand All @@ -22,6 +23,8 @@
use Phpactor\CodeBuilder\Adapter\TolerantParser\Updater\InterfaceUpdater;
use Phpactor\CodeBuilder\Adapter\TolerantParser\Updater\TraitUpdater;
use Phpactor\TextDocument\TextEdits;
use Phpactor\WorseReflection\Core\Util\NodeUtil;
use Microsoft\PhpParser\Token;

class TolerantUpdater implements Updater
{
Expand Down Expand Up @@ -122,6 +125,13 @@ private function updateClasses(Edits $edits, SourceCode $prototype, SourceFileNo
}
}

foreach($node->getDescendantNodes() as $classNode) {
if ($classNode instanceof ObjectCreationExpression && $classNode->classTypeDesignator instanceof Token) {
$name = NodeUtil::nameFromTokenOrNode($classNode, $classNode);
$classNodes[$name] = $classNode;
}
}

foreach ($prototype->classes()->in(array_keys($classNodes)) as $classPrototype) {
$this->classUpdater->updateClass($edits, $classPrototype, $classNodes[$classPrototype->name()]);
}
Expand Down
Expand Up @@ -6,6 +6,7 @@
use Microsoft\PhpParser\Node;
use Microsoft\PhpParser\Node\DelimitedList\ParameterDeclarationList;
use Microsoft\PhpParser\Node\EnumCaseDeclaration;
use Microsoft\PhpParser\Node\Expression\ObjectCreationExpression;
use Microsoft\PhpParser\Node\MethodDeclaration;
use Microsoft\PhpParser\Node\Parameter;
use Microsoft\PhpParser\Node\PropertyDeclaration;
Expand All @@ -30,8 +31,11 @@ public function __construct(private Renderer $renderer)
{
}

public function updateMethods(Edits $edits, ClassLikePrototype $classPrototype, ClassLike $classNode): void
{
public function updateMethods(
Edits $edits,
ClassLikePrototype $classPrototype,
ClassLike|ObjectCreationExpression $classNode,
): void {
if (count($classPrototype->methods()) === 0) {
return;
}
Expand Down Expand Up @@ -137,10 +141,10 @@ public function updateMethods(Edits $edits, ClassLikePrototype $classPrototype,
/**
* @return array<Node>
*/
abstract protected function memberDeclarations(ClassLike $classNode): array;
abstract protected function memberDeclarations(ClassLike|ObjectCreationExpression $classNode): array;

/** @return TMembersNodeType */
abstract protected function memberDeclarationsNode(ClassLike $classNode);
abstract protected function memberDeclarationsNode(ClassLike|ObjectCreationExpression $classNode);

abstract protected function renderMethod(Renderer $renderer, Method $method): string;

Expand Down
Expand Up @@ -5,6 +5,7 @@
use Microsoft\PhpParser\Node;
use Microsoft\PhpParser\Node\ClassConstDeclaration;
use Microsoft\PhpParser\Node\Expression\AssignmentExpression;
use Microsoft\PhpParser\Node\Expression\ObjectCreationExpression;
use Microsoft\PhpParser\Node\Expression\Variable;
use Microsoft\PhpParser\Node\MethodDeclaration;
use Microsoft\PhpParser\Node\PropertyDeclaration;
Expand Down Expand Up @@ -109,11 +110,11 @@ protected function getInsertPlace(Node $classNode, array $memberDeclarations): T
return $insert;
}

/**
* @param ClassDeclaration|TraitDeclaration|EnumDeclaration|InterfaceDeclaration $classLikeDeclaration
*/
protected function updateDocblock(Edits $edits, ClassLikePrototype $classPrototype, $classLikeDeclaration): void
{
protected function updateDocblock(
Edits $edits,
ClassLikePrototype $classPrototype,
ClassDeclaration|TraitDeclaration|EnumDeclaration|InterfaceDeclaration|ObjectCreationExpression $classLikeDeclaration,
): void {
if (!$classPrototype->docblock()->notNone()) {
return;
}
Expand Down
Expand Up @@ -5,6 +5,8 @@
use Microsoft\PhpParser\Node\ClassMembersNode;
use Microsoft\PhpParser\ClassLike;
use Microsoft\PhpParser\Node\EnumMembers;
use Microsoft\PhpParser\Node\Expression\ObjectCreationExpression;
use Microsoft\PhpParser\Node\InterfaceMembers;
use Microsoft\PhpParser\Node\Statement\ClassDeclaration;
use Microsoft\PhpParser\Node\Statement\EnumDeclaration;
use Microsoft\PhpParser\Node\Statement\TraitDeclaration;
Expand All @@ -13,19 +15,20 @@
use Phpactor\CodeBuilder\Domain\Prototype\Method;
use RuntimeException;
use Microsoft\PhpParser\Node;
use Webmozart\Assert\Assert;

/**
* @extends AbstractMethodUpdater<ClassMembersNode|TraitMembers>
*/
class ClassMethodUpdater extends AbstractMethodUpdater
{
/**
* @return ClassMembersNode|TraitMembers|EnumMembers
*/
public function memberDeclarationsNode(ClassLike $classNode)
public function memberDeclarationsNode(ClassLike|ObjectCreationExpression $classNode): ClassMembersNode|TraitMembers|EnumMembers|InterfaceMembers
{
if ($classNode instanceof ClassDeclaration) {
return $classNode->classMembers;
if ($classNode instanceof ClassDeclaration || $classNode instanceof ObjectCreationExpression) {
$classNode = $classNode->classMembers;
Assert::isInstanceOf($classNode, ClassMembersNode::class);

return $classNode;
}
if ($classNode instanceof TraitDeclaration) {
return $classNode->traitMembers;
Expand All @@ -48,10 +51,10 @@ public function renderMethod(Renderer $renderer, Method $method): string
}

/** @return array<Node> */
protected function memberDeclarations(ClassLike $classNode): array
protected function memberDeclarations(ClassLike|ObjectCreationExpression $classNode): array
{
if ($classNode instanceof ClassDeclaration) {
return $classNode->classMembers->classMemberDeclarations;
if ($classNode instanceof ClassDeclaration || $classNode instanceof ObjectCreationExpression) {
return $classNode->classMembers->classMemberDeclarations ?? [];
}
if ($classNode instanceof TraitDeclaration) {
return $classNode->traitMembers->traitMemberDeclarations;
Expand Down
53 changes: 43 additions & 10 deletions lib/CodeBuilder/Adapter/TolerantParser/Updater/ClassUpdater.php
Expand Up @@ -4,6 +4,8 @@

use Microsoft\PhpParser\Node;
use Microsoft\PhpParser\Node\ClassConstDeclaration;
use Microsoft\PhpParser\Node\ClassMembersNode;
use Microsoft\PhpParser\Node\Expression\ObjectCreationExpression;
use Microsoft\PhpParser\Node\MethodDeclaration;
use Microsoft\PhpParser\Node\PropertyDeclaration;
use Microsoft\PhpParser\Node\Statement\ClassDeclaration;
Expand All @@ -12,20 +14,26 @@
use Phpactor\CodeBuilder\Domain\Prototype\Constant;
use Phpactor\CodeBuilder\Domain\Prototype\ExtendsClass;
use Phpactor\CodeBuilder\Domain\Prototype\ImplementsInterfaces;
use Microsoft\PhpParser\Token;

class ClassUpdater extends ClassLikeUpdater
{
public function updateClass(Edits $edits, ClassPrototype $classPrototype, ClassDeclaration $classNode): void
{
public function updateClass(
Edits $edits,
ClassPrototype $classPrototype,
ClassDeclaration|ObjectCreationExpression $classNode,
): void {
if (false === $classPrototype->applyUpdate()) {
return;
}

$this->updateDocblock($edits, $classPrototype, $classNode);
$this->updateExtends($edits, $classPrototype, $classNode);
$this->updateImplements($edits, $classPrototype, $classNode);
$this->updateConstants($edits, $classPrototype, $classNode->classMembers);
$this->updateProperties($edits, $classPrototype, $classNode->classMembers);
if ($classNode->classMembers instanceof ClassMembersNode) {
$this->updateConstants($edits, $classPrototype, $classNode->classMembers);
$this->updateProperties($edits, $classPrototype, $classNode->classMembers);
}

$this->methodUpdater->updateMethods($edits, $classPrototype, $classNode);
}
Expand Down Expand Up @@ -79,29 +87,41 @@ protected function memberDeclarations(Node $node): array
return $node->classMemberDeclarations;
}

private function updateExtends(Edits $edits, ClassPrototype $classPrototype, ClassDeclaration $classNode): void
{
private function updateExtends(
Edits $edits,
ClassPrototype $classPrototype,
ClassDeclaration|ObjectCreationExpression $classNode,
): void {
if (ExtendsClass::none() == $classPrototype->extendsClass()) {
return;
}

if (null === $classNode->classBaseClause) {
$edits->after($classNode->name, ' extends ' . (string) $classPrototype->extendsClass());
$edits->after(
$this->getTokenBeforeImplementsOrExtends($classNode),
' extends ' . (string) $classPrototype->extendsClass(),
);
return;
}


$edits->replace($classNode->classBaseClause, ' extends ' . (string) $classPrototype->extendsClass());
}

private function updateImplements(Edits $edits, ClassPrototype $classPrototype, ClassDeclaration $classNode): void
{
private function updateImplements(
Edits $edits,
ClassPrototype $classPrototype,
ClassDeclaration|ObjectCreationExpression $classNode,
): void {
if (ImplementsInterfaces::empty() == $classPrototype->implementsInterfaces()) {
return;
}

if (null === $classNode->classInterfaceClause) {
$edits->after($classNode->name, ' implements ' . (string) $classPrototype->implementsInterfaces()->__toString());
$edits->after(
$this->getTokenBeforeImplementsOrExtends($classNode),
' implements ' . (string) $classPrototype->implementsInterfaces(),
);
return;
}

Expand All @@ -121,4 +141,17 @@ private function updateImplements(Edits $edits, ClassPrototype $classPrototype,

$edits->replace($classNode->classInterfaceClause, ' implements ' . $names);
}

private function getTokenBeforeImplementsOrExtends(ObjectCreationExpression|ClassDeclaration $class): Token
{
/** @var Token $token */
$token = match (true) {
// class Test extends SomeClass
$class instanceof ClassDeclaration => $class->name,
// $a = new class () extends SomeClass
$class instanceof ObjectCreationExpression => $class->closeParen,
};

return $token;
}
}
Expand Up @@ -3,7 +3,9 @@
namespace Phpactor\CodeBuilder\Adapter\TolerantParser\Updater;

use Microsoft\PhpParser\ClassLike;
use Microsoft\PhpParser\Node\Expression\ObjectCreationExpression;
use Microsoft\PhpParser\Node\InterfaceMembers;
use Microsoft\PhpParser\Node\Statement\InterfaceDeclaration;
use Phpactor\CodeBuilder\Domain\Renderer;
use Phpactor\CodeBuilder\Domain\Prototype\Method;
use Microsoft\PhpParser\Node;
Expand All @@ -13,12 +15,12 @@
*/
class InterfaceMethodUpdater extends AbstractMethodUpdater
{
/**
* @return InterfaceMembers
*/
public function memberDeclarationsNode(ClassLike $classNode)
public function memberDeclarationsNode(ClassLike|ObjectCreationExpression $classNode): InterfaceMembers
{
return $classNode->interfaceMembers;
if ($classNode instanceof InterfaceDeclaration) {
return $classNode->interfaceMembers;
}
return new InterfaceMembers();
}

public function renderMethod(Renderer $renderer, Method $method): string
Expand All @@ -27,8 +29,8 @@ public function renderMethod(Renderer $renderer, Method $method): string
}

/** @return array<Node> */
protected function memberDeclarations(ClassLike $classNode): array
protected function memberDeclarations(ClassLike|ObjectCreationExpression $classNode): array
{
return $classNode->interfaceMembers->interfaceMemberDeclarations;
return $classNode->interfaceMembers?->interfaceMemberDeclarations ?? [];
}
}
Expand Up @@ -475,6 +475,35 @@ public function jump(): ?Foo
}
EOT
];

yield 'Implementing in anonymous classes' => [
<<<'EOT'
<?php

interface Animal
{
public function jump(): iterable;
}

$bird = new class() implements Animal {
}
EOT
,
<<<'EOT'
<?php

interface Animal
{
public function jump(): iterable;
}

$bird = new class() implements Animal {
public function jump(): iterable
{
}
}
EOT
];
yield 'It uses "iterable"' => [
<<<'EOT'
<?php
Expand Down