Skip to content

Commit c82050a

Browse files
committed
implement ImplicitUsage for method inside "AsEventListener" attribute
1 parent feb3c03 commit c82050a

File tree

2 files changed

+61
-13
lines changed

2 files changed

+61
-13
lines changed

src/main/java/fr/adrienbrault/idea/symfony2plugin/codeInsight/SymfonyImplicitUsageProvider.java

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,21 @@
55
import com.intellij.psi.util.PsiTreeUtil;
66
import com.jetbrains.php.lang.parser.PhpElementTypes;
77
import com.jetbrains.php.lang.psi.elements.*;
8+
import com.jetbrains.php.lang.psi.stubs.indexes.expectedArguments.PhpExpectedFunctionScalarArgument;
89
import de.espend.idea.php.annotation.dict.PhpDocCommentAnnotation;
910
import de.espend.idea.php.annotation.util.AnnotationUtil;
1011
import fr.adrienbrault.idea.symfony2plugin.doctrine.metadata.util.DoctrineMetadataUtil;
1112
import fr.adrienbrault.idea.symfony2plugin.routing.RouteHelper;
1213
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
14+
import fr.adrienbrault.idea.symfony2plugin.util.PsiElementUtils;
1315
import fr.adrienbrault.idea.symfony2plugin.util.dict.ServiceUtil;
1416
import org.apache.commons.lang.StringUtils;
1517
import org.jetbrains.annotations.NotNull;
1618

17-
import java.util.*;
19+
import java.util.Arrays;
20+
import java.util.Collection;
21+
import java.util.HashSet;
22+
import java.util.Set;
1823
import java.util.stream.Collectors;
1924

2025
/**
@@ -28,18 +33,19 @@ public class SymfonyImplicitUsageProvider implements ImplicitUsageProvider {
2833

2934
@Override
3035
public boolean isImplicitUsage(@NotNull PsiElement element) {
31-
if (element instanceof Method && ((Method) element).getAccess() == PhpModifier.Access.PUBLIC) {
32-
return isMethodARoute((Method) element)
33-
|| isSubscribedEvent((Method) element);
34-
} else if (element instanceof PhpClass) {
35-
return isRouteClass((PhpClass) element)
36-
|| isCommandAndService((PhpClass) element)
37-
|| isSubscribedEvent((PhpClass) element)
38-
|| isVoter((PhpClass) element)
39-
|| isTwigExtension((PhpClass) element)
40-
|| isEntityRepository((PhpClass) element)
41-
|| isConstraint((PhpClass) element)
42-
|| isKernelEventListener((PhpClass) element);
36+
if (element instanceof Method method && method.getAccess() == PhpModifier.Access.PUBLIC) {
37+
return isMethodARoute(method)
38+
|| isSubscribedEvent(method)
39+
|| isAsEventListenerMethodPhpAttribute(method);
40+
} else if (element instanceof PhpClass phpClass) {
41+
return isRouteClass(phpClass)
42+
|| isCommandAndService(phpClass)
43+
|| isSubscribedEvent(phpClass)
44+
|| isVoter(phpClass)
45+
|| isTwigExtension(phpClass)
46+
|| isEntityRepository(phpClass)
47+
|| isConstraint(phpClass)
48+
|| isKernelEventListener(phpClass);
4349
}
4450

4551
return false;
@@ -176,4 +182,25 @@ private boolean isSubscribedEvent(@NotNull Method method) {
176182

177183
return false;
178184
}
185+
186+
private boolean isAsEventListenerMethodPhpAttribute(@NotNull Method method) {
187+
PhpClass containingClass = method.getContainingClass();
188+
if (containingClass == null || !PhpElementsUtil.isInstanceOf(containingClass, "\\Symfony\\Component\\EventDispatcher\\EventSubscriberInterface")) {
189+
return false;
190+
}
191+
192+
for (PhpAttribute attribute : containingClass.getAttributes("\\Symfony\\Component\\EventDispatcher\\Attribute\\AsEventListener")) {
193+
for (PhpAttribute.PhpAttributeArgument argument : attribute.getArguments()) {
194+
if ("method".equals(argument.getName()) && argument.getArgument() instanceof PhpExpectedFunctionScalarArgument scalarArgument) {
195+
String value = PsiElementUtils.trimQuote(scalarArgument.getNormalizedValue());
196+
197+
if (method.getName().equals(value)) {
198+
return true;
199+
}
200+
}
201+
}
202+
}
203+
204+
return false;
205+
}
179206
}

src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/codeInsight/SymfonyImplicitUsageProviderTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,27 @@ public void testEventSubscriberGetSubscribedEventsArray() {
221221
assertTrue(new SymfonyImplicitUsageProvider().isImplicitUsage(phpClass));
222222
}
223223

224+
225+
public void testEventSubscriberGetAsEventListener() {
226+
PsiFile psiFile = myFixture.configureByText(PhpFileType.INSTANCE, "<?php\n" +
227+
"namespace App\\EventListener;\n" +
228+
"\n" +
229+
"use Symfony\\Component\\EventDispatcher\\Attribute\\AsEventListener;\n" +
230+
"\n" +
231+
"#[AsEventListener(event: 'bar', method: 'onFoo')]\n" +
232+
"final class MyMultiListener implements \\Symfony\\Component\\EventDispatcher\\EventSubscriberInterface\n" +
233+
"{\n" +
234+
" public function onFoo(): void\n" +
235+
" {\n" +
236+
" }\n" +
237+
"\n" +
238+
"}"
239+
);
240+
241+
PhpClass phpClass = PhpElementsUtil.getFirstClassFromFile((PhpFile) psiFile.getContainingFile());
242+
243+
assertTrue(new SymfonyImplicitUsageProvider().isImplicitUsage(phpClass.findOwnMethodByName("onFoo")));
244+
}
224245
public void testTwigExtensionRegisteredAsServiceWithFunctionMethodImplementedIsMarkedUsed() {
225246
PsiFile psiFile = myFixture.configureByText(PhpFileType.INSTANCE, "<?php\n" +
226247
"\n" +

0 commit comments

Comments
 (0)