@@ -395,6 +395,79 @@ public void testTwigExtensionRegisteredAsServiceWithFunctionMethodImplementedIsM
395395 assertFalse (new SymfonyImplicitUsageProvider ().isImplicitUsage (firstClassFromFile2 ));
396396 }
397397
398+ public void testTwigExtensionMethodsWithAttributesAreMarkedAsUsed () {
399+ PsiFile psiFile = myFixture .configureByText (PhpFileType .INSTANCE , "<?php\n " +
400+ "\n " +
401+ "namespace App\\ Twig;\n " +
402+ "\n " +
403+ "use Twig\\ Attribute\\ AsTwigFunction;\n " +
404+ "use Twig\\ Attribute\\ AsTwigFilter;\n " +
405+ "use Twig\\ Attribute\\ AsTwigTest;\n " +
406+ "\n " +
407+ "class MyTwigExtension\n " +
408+ "{\n " +
409+ " #[AsTwigFunction('my_function')]\n " +
410+ " public function myFunction(): string\n " +
411+ " {\n " +
412+ " return 'Hello';\n " +
413+ " }\n " +
414+ "\n " +
415+ " #[AsTwigFilter('my_filter')]\n " +
416+ " public function myFilter(): string\n " +
417+ " {\n " +
418+ " return 'Filtered';\n " +
419+ " }\n " +
420+ "\n " +
421+ " #[AsTwigTest('my_test')]\n " +
422+ " public function myTest(): bool\n " +
423+ " {\n " +
424+ " return true;\n " +
425+ " }\n " +
426+ "\n " +
427+ " public function unusedMethod(): string\n " +
428+ " {\n " +
429+ " return 'Not used';\n " +
430+ " }\n " +
431+ "}"
432+ );
433+
434+ PhpClass phpClass = PhpElementsUtil .getFirstClassFromFile ((PhpFile ) psiFile .getContainingFile ());
435+
436+ // Methods with Twig attributes should be marked as used
437+ assertTrue (new SymfonyImplicitUsageProvider ().isImplicitUsage (phpClass .findOwnMethodByName ("myFunction" )));
438+ assertTrue (new SymfonyImplicitUsageProvider ().isImplicitUsage (phpClass .findOwnMethodByName ("myFilter" )));
439+ assertTrue (new SymfonyImplicitUsageProvider ().isImplicitUsage (phpClass .findOwnMethodByName ("myTest" )));
440+
441+ // Method without attributes should not be marked as used
442+ assertFalse (new SymfonyImplicitUsageProvider ().isImplicitUsage (phpClass .findOwnMethodByName ("unusedMethod" )));
443+ }
444+
445+
446+ public void testTwigExtensionMethodWithMultipleAttributes () {
447+ PsiFile psiFile = myFixture .configureByText (PhpFileType .INSTANCE , "<?php\n " +
448+ "\n " +
449+ "namespace App\\ Twig;\n " +
450+ "\n " +
451+ "use Twig\\ Attribute\\ AsTwigFunction;\n " +
452+ "use Twig\\ Attribute\\ AsTwigFilter;\n " +
453+ "\n " +
454+ "class MyTwigExtension\n " +
455+ "{\n " +
456+ " #[AsTwigFunction('callable_method')]\n " +
457+ " #[AsTwigFilter('callable_filter')]\n " +
458+ " public function callableMethod(): string\n " +
459+ " {\n " +
460+ " return 'Callable';\n " +
461+ " }\n " +
462+ "}"
463+ );
464+
465+ PhpClass phpClass = PhpElementsUtil .getFirstClassFromFile ((PhpFile ) psiFile .getContainingFile ());
466+
467+ // Method with multiple attributes should be marked as used
468+ assertTrue (new SymfonyImplicitUsageProvider ().isImplicitUsage (phpClass .findOwnMethodByName ("callableMethod" )));
469+ }
470+
398471 private PhpClass createPhpControllerClassWithRouteContent (@ NotNull String content ) {
399472 return createPhpControllerClassWithRouteContent ("\\ App\\ Controller\\ FooController" , content );
400473 }
0 commit comments