From d63447412315bf9645447a979f35d3da1dbce503 Mon Sep 17 00:00:00 2001 From: Daniel Espendiller Date: Sat, 25 Mar 2023 10:34:46 +0100 Subject: [PATCH] fully replace recursive template method scanning --- .../PhpTwigTemplateUsageStubIndex.java | 19 ++-- .../util/PhpMethodVariableResolveUtil.java | 96 ++++++------------- 2 files changed, 40 insertions(+), 75 deletions(-) diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/stubs/indexes/PhpTwigTemplateUsageStubIndex.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/stubs/indexes/PhpTwigTemplateUsageStubIndex.java index 20cfbd25e..760f6d042 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/stubs/indexes/PhpTwigTemplateUsageStubIndex.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/stubs/indexes/PhpTwigTemplateUsageStubIndex.java @@ -1,5 +1,6 @@ package fr.adrienbrault.idea.symfony2plugin.stubs.indexes; +import com.intellij.openapi.util.NotNullLazyValue; import com.intellij.psi.PsiFile; import com.intellij.util.indexing.*; import com.intellij.util.io.DataExternalizer; @@ -16,6 +17,7 @@ import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent; import fr.adrienbrault.idea.symfony2plugin.stubs.dict.TemplateUsage; import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.externalizer.ObjectStreamDataExternalizer; +import fr.adrienbrault.idea.symfony2plugin.templating.util.PhpMethodVariableResolveUtil; import kotlin.Triple; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; @@ -23,8 +25,6 @@ import java.util.*; import java.util.function.Consumer; -import static fr.adrienbrault.idea.symfony2plugin.templating.util.PhpMethodVariableResolveUtil.TemplateRenderPsiRecursiveElementWalkingVisitor.*; - /** * @author Daniel Espendiller */ @@ -32,8 +32,8 @@ public class PhpTwigTemplateUsageStubIndex extends FileBasedIndexExtension KEY = ID.create("fr.adrienbrault.idea.symfony2plugin.twig_php_usage"); private final KeyDescriptor myKeyDescriptor = new EnumeratorStringDescriptor(); - private static int MAX_FILE_BYTE_SIZE = 2097152; - private static ObjectStreamDataExternalizer EXTERNALIZER = new ObjectStreamDataExternalizer<>(); + private static final int MAX_FILE_BYTE_SIZE = 2097152; + private static final ObjectStreamDataExternalizer EXTERNALIZER = new ObjectStreamDataExternalizer<>(); @NotNull @Override @@ -68,15 +68,16 @@ public DataIndexer getIndexer() { items.get(templateName).add(StringUtils.stripStart(triple.getSecond().getFQN(), "\\")); }; - Set methods = collectMethods(psiFile.getProject()); + + @NotNull NotNullLazyValue> methods = PhpMethodVariableResolveUtil.TemplateRenderVisitor.createLazyMethodNamesCollector(psiFile.getProject()); for (PhpNamedElement topLevelElement : ((PhpFile) psiFile).getTopLevelDefs().values()) { if (topLevelElement instanceof PhpClass clazz) { for (Method method : clazz.getOwnMethods()) { - processMethodAttributes(method, consumer); + PhpMethodVariableResolveUtil.TemplateRenderVisitor.processMethodAttributes(method, consumer); PhpDocComment docComment = method.getDocComment(); if (docComment != null) { PhpDocUtil.processTagElementsByName(docComment, null, docTag -> { - processDocTag(docTag, consumer); + PhpMethodVariableResolveUtil.TemplateRenderVisitor.processDocTag(docTag, consumer); return true; }); } @@ -98,12 +99,12 @@ public DataIndexer getIndexer() { }; } - private static void processMethodReferences(Consumer> consumer, Set methods, Function function) { + private static void processMethodReferences(@NotNull Consumer> consumer, @NotNull NotNullLazyValue> methods, @NotNull Function function) { PhpControlFlowUtil.processFlow(function.getControlFlow(), new PhpInstructionProcessor() { @Override public boolean processPhpCallInstruction(PhpCallInstruction instruction) { if (instruction.getFunctionReference() instanceof MethodReference methodReference) { - processMethodReference(methodReference, methods, consumer); + PhpMethodVariableResolveUtil.TemplateRenderVisitor.processMethodReference(methodReference, methods, consumer); } return super.processPhpCallInstruction(instruction); } diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/util/PhpMethodVariableResolveUtil.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/util/PhpMethodVariableResolveUtil.java index 4c4e29929..5241b286d 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/util/PhpMethodVariableResolveUtil.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/util/PhpMethodVariableResolveUtil.java @@ -1,10 +1,13 @@ package fr.adrienbrault.idea.symfony2plugin.templating.util; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.NotNullLazyValue; import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiRecursiveElementWalkingVisitor; import com.intellij.psi.util.PsiTreeUtil; +import com.jetbrains.php.codeInsight.controlFlow.PhpControlFlowUtil; +import com.jetbrains.php.codeInsight.controlFlow.PhpInstructionProcessor; +import com.jetbrains.php.codeInsight.controlFlow.instructions.PhpCallInstruction; import com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment; import com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag; import com.jetbrains.php.lang.parser.PhpElementTypes; @@ -238,57 +241,32 @@ public static Map getTypesOnArrayHash(@NotNull ArrayCreatio * * As annotations are not in scope of the method itself */ - public static void visitRenderTemplateFunctions(@NotNull Method method, @NotNull Consumer> consumer) { - TemplateRenderPsiRecursiveElementWalkingVisitor psiElementVisitor = new TemplateRenderPsiRecursiveElementWalkingVisitor(method, consumer); - - PhpDocComment docComment = method.getDocComment(); + public static void visitRenderTemplateFunctions(@NotNull Function function, @NotNull Consumer> consumer) { + PhpDocComment docComment = function.getDocComment(); for (PhpDocTag phpDocTag : PsiTreeUtil.getChildrenOfTypeAsList(docComment, PhpDocTag.class)) { - psiElementVisitor.visitPhpDocTag(phpDocTag); - } - - for (PhpAttributesList phpAttributesList : PsiTreeUtil.getChildrenOfTypeAsList(method, PhpAttributesList.class)) { - psiElementVisitor.visitPhpAttribute(phpAttributesList); + TemplateRenderVisitor.processDocTag(phpDocTag, consumer); } - method.accept(psiElementVisitor); - } - - /** - * Visit all possible elements for render clements, scope shop be the class or a file itself - */ - public static void visitRenderTemplateFunctions(@NotNull PsiElement context, @NotNull Consumer> consumer) { - context.accept(new TemplateRenderPsiRecursiveElementWalkingVisitor(context, consumer)); - } - - public static class TemplateRenderPsiRecursiveElementWalkingVisitor extends PsiRecursiveElementWalkingVisitor { - private final PsiElement context; - private final Consumer> consumer; - private Set methods; - - TemplateRenderPsiRecursiveElementWalkingVisitor(PsiElement context, Consumer> consumer) { - this.context = context; - this.consumer = consumer; - methods = null; - } - - @Override - public void visitElement(@NotNull PsiElement element) { - if(element instanceof MethodReference) { - visitMethodReference((MethodReference) element); - } else if(element instanceof PhpDocTag) { - visitPhpDocTag((PhpDocTag) element); - } else if(element instanceof PhpAttributesList) { - visitPhpAttribute((PhpAttributesList) element); + for (PhpAttributesList phpAttributesList : PsiTreeUtil.getChildrenOfTypeAsList(function, PhpAttributesList.class)) { + if (phpAttributesList.getParent() instanceof Method phpAttributeMethod) { + TemplateRenderVisitor.processMethodAttributes(phpAttributeMethod, consumer); } - super.visitElement(element); } - private void visitPhpAttribute(@NotNull PhpAttributesList phpAttributesList) { - if (phpAttributesList.getParent() instanceof Method method) { - processMethodAttributes(method, consumer); + NotNullLazyValue> lazyMethodNamesCollector = TemplateRenderVisitor.createLazyMethodNamesCollector(function.getProject()); + + PhpControlFlowUtil.processFlow(function.getControlFlow(), new PhpInstructionProcessor() { + @Override + public boolean processPhpCallInstruction(PhpCallInstruction instruction) { + if (instruction.getFunctionReference() instanceof MethodReference methodReference) { + TemplateRenderVisitor.processMethodReference(methodReference, lazyMethodNamesCollector, consumer); + } + return super.processPhpCallInstruction(instruction); } - } + }); + } + public static class TemplateRenderVisitor { public static void processMethodAttributes(@NotNull Method method, Consumer> consumer) { Collection<@NotNull PhpAttribute> attributes = method.getAttributes(TwigUtil.TEMPLATE_ANNOTATION_CLASS); for (PhpAttribute attribute : attributes) { @@ -306,22 +284,8 @@ public static void processMethodAttributes(@NotNull Method method, Consumer collectMethods(Project project) { + private static Set collectMethodInner(@NotNull Project project) { Set methods = new HashSet<>(); PluginConfigurationExtension[] extensions = Symfony2ProjectComponent.PLUGIN_CONFIGURATION_EXTENSION.getExtensions(); @@ -336,13 +300,17 @@ public static Set collectMethods(Project project) { return methods; } - public static void processMethodReference(@NotNull MethodReference methodReference, Set methods, Consumer> consumer) { + public static @NotNull NotNullLazyValue> createLazyMethodNamesCollector(@NotNull Project project) { + return NotNullLazyValue.lazy(() -> collectMethodInner(project)); + } + + public static void processMethodReference(@NotNull MethodReference methodReference, NotNullLazyValue> methods, Consumer> consumer) { String methodName = methodReference.getName(); if (methodName == null) { return; } - if(!methods.contains(methodName) && !methodName.toLowerCase().contains("render")) { + if(!methods.get().contains(methodName) && !methodName.toLowerCase().contains("render")) { return; } @@ -432,12 +400,8 @@ private static void addStringLiteralScope(@NotNull MethodReference methodReferen * "@Template("foobar.html.twig")" * "@Template(template="foobar.html.twig")" */ - private void visitPhpDocTag(@NotNull PhpDocTag phpDocTag) { - processDocTag(phpDocTag, consumer); - } - public static void processDocTag(@NotNull PhpDocTag phpDocTag, Consumer> consumer) { - // "@var" and user non related tags dont need an action + // "@var" and user non-related tags don't need an action if(AnnotationBackportUtil.NON_ANNOTATION_TAGS.contains(phpDocTag.getName())) { return; }