Skip to content

Commit f227a8e

Browse files
committed
#1366 mark TwigExtension classes as used code, if any implementation exists
1 parent 51dab69 commit f227a8e

File tree

4 files changed

+72
-3
lines changed

4 files changed

+72
-3
lines changed

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

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,17 @@
1313
import org.apache.commons.lang.StringUtils;
1414
import org.jetbrains.annotations.NotNull;
1515

16+
import java.util.Arrays;
1617
import java.util.Collection;
18+
import java.util.HashSet;
19+
import java.util.Set;
20+
import java.util.stream.Collectors;
1721

1822
/**
1923
* @author Daniel Espendiller <daniel@espendiller.net>
2024
*/
2125
public class SymfonyImplicitUsageProvider implements ImplicitUsageProvider {
22-
private static final String[] ROUTE_ANNOTATIONS = new String[]{
26+
private static final String[] ROUTE_ANNOTATIONS = new String[] {
2327
"\\Symfony\\Component\\Routing\\Annotation\\Route",
2428
"\\Sensio\\Bundle\\FrameworkExtraBundle\\Configuration\\Route"
2529
};
@@ -33,7 +37,32 @@ public boolean isImplicitUsage(@NotNull PsiElement element) {
3337
return isRouteClass((PhpClass) element)
3438
|| isCommandAndService((PhpClass) element)
3539
|| isSubscribedEvent((PhpClass) element)
36-
|| isVoter((PhpClass) element);
40+
|| isVoter((PhpClass) element)
41+
|| isTwigExtension((PhpClass) element);
42+
}
43+
44+
return false;
45+
}
46+
47+
private boolean isTwigExtension(PhpClass phpClass) {
48+
if ((PhpElementsUtil.isInstanceOf(phpClass, "\\Twig\\Extension\\ExtensionInterface") || PhpElementsUtil.isInstanceOf(phpClass,"\\Twig_ExtensionInterface")) && ServiceUtil.isPhpClassAService(phpClass)) {
49+
Set<String> methods = new HashSet<>();
50+
51+
Collection<PhpClass> phpClasses = new HashSet<>() {{
52+
addAll(PhpElementsUtil.getClassesInterface(phpClass.getProject(), "\\Twig\\Extension\\ExtensionInterface"));
53+
addAll(PhpElementsUtil.getClassesInterface(phpClass.getProject(), "\\Twig_ExtensionInterface"));
54+
}};
55+
56+
for (PhpClass aClass : phpClasses) {
57+
methods.addAll(aClass.getMethods()
58+
.stream()
59+
.filter(method -> !method.isStatic() && method.getAccess() == PhpModifier.Access.PUBLIC).map(PhpNamedElement::getName)
60+
.collect(Collectors.toSet())
61+
);
62+
}
63+
64+
return Arrays.stream(phpClass.getOwnMethods())
65+
.anyMatch(ownMethod -> ownMethod.getAccess() == PhpModifier.Access.PUBLIC && methods.contains(ownMethod.getName()));
3766
}
3867

3968
return false;

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,34 @@ public void testEventSubscriberGetSubscribedEventsArray() {
191191
assertTrue(new SymfonyImplicitUsageProvider().isImplicitUsage(phpClass));
192192
}
193193

194+
public void testTwigExtensionRegisteredAsServiceWithFunctionMethodImplementedIsMarkedUsed() {
195+
PsiFile psiFile = myFixture.configureByText(PhpFileType.INSTANCE, "<?php\n" +
196+
"\n" +
197+
"namespace App\\TwigExtension;\n" +
198+
"\n" +
199+
"class MyTwigExtension implements \\Twig\\Extension\\ExtensionInterface\n" +
200+
"{\n" +
201+
" public function getFilters() {}\n" +
202+
"}"
203+
);
204+
205+
PhpClass firstClassFromFile = PhpElementsUtil.getFirstClassFromFile((PhpFile) psiFile.getContainingFile());
206+
assertTrue(new SymfonyImplicitUsageProvider().isImplicitUsage(firstClassFromFile));
207+
208+
PsiFile psiFile2 = myFixture.configureByText(PhpFileType.INSTANCE, "<?php\n" +
209+
"\n" +
210+
"namespace App\\TwigExtension;\n" +
211+
"\n" +
212+
"class MyTwigExtension implements \\Twig\\Extension\\ExtensionInterface\n" +
213+
"{\n" +
214+
" public function getFoobar() {}\n" +
215+
"}"
216+
);
217+
218+
PhpClass firstClassFromFile2 = PhpElementsUtil.getFirstClassFromFile((PhpFile) psiFile2.getContainingFile());
219+
assertFalse(new SymfonyImplicitUsageProvider().isImplicitUsage(firstClassFromFile2));
220+
}
221+
194222
private PhpClass createPhpControllerClassWithRouteContent(@NotNull String content) {
195223
return createPhpControllerClassWithRouteContent("\\App\\Controller\\FooController", content);
196224
}

src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/codeInsight/fixtures/classes.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,13 @@ interface VoterInterface
2929
{
3030
public function vote(TokenInterface $token, $subject, array $attributes);
3131
}
32+
}
33+
34+
namespace Twig\Extension
35+
{
36+
interface ExtensionInterface
37+
{
38+
public function getFilters();
39+
public function getFunctions();
40+
}
3241
}

src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/codeInsight/fixtures/services.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,7 @@ services:
99
class: App\Command\FooCommand
1010

1111
foo_command.my_foobar_voter:
12-
class: App\Voter\MyFoobarVoter
12+
class: App\Voter\MyFoobarVoter
13+
14+
foo_command.twig_extension:
15+
class: App\TwigExtension\MyTwigExtension

0 commit comments

Comments
 (0)