This repository has been archived by the owner on Nov 3, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 633
Implementation of LombokReferenceSearcher and LombokMethodReferenceSearcher (fix for #685) #722
Open
npiguet
wants to merge
10
commits into
mplushnikov:master
Choose a base branch
from
npiguet:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
b82ccbd
References to fields inside generated methods/constructors are now vi…
npiguet 7bd6a27
LombokMethodReferenceSearcher is now able to find references to the l…
npiguet 01d9035
Tried to implement resolution of Class constructor usage inside the B…
npiguet 0270001
1. Don't set the navigation element on builder fields. They are an in…
npiguet ed019ae
References can now be followed all the way through Builder classes. "…
npiguet d48fd92
Discovered a valid way to make sure that getContainingFile() on all g…
npiguet eaf0794
Revert commit d48fd927: After further reflection, the output of the f…
c5f6fa6
Returning a physical file for a PsiLightField confuses the IDE and pr…
cb18093
Make LombokMethodReferenceSearcher smarter. It can now also find cons…
70525f9
re-enable LombokFieldFindUsagesHandlerFactory
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
141 changes: 141 additions & 0 deletions
141
src/main/java/de/plushnikov/intellij/plugin/extension/LombokMethodReferenceSearcher.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
package de.plushnikov.intellij.plugin.extension; | ||
|
||
import com.intellij.openapi.application.QueryExecutorBase; | ||
import com.intellij.openapi.util.TextRange; | ||
import com.intellij.psi.PsiClass; | ||
import com.intellij.psi.PsiElement; | ||
import com.intellij.psi.PsiMethod; | ||
import com.intellij.psi.PsiNewExpression; | ||
import com.intellij.psi.PsiReference; | ||
import com.intellij.psi.PsiReferenceBase; | ||
import com.intellij.psi.search.searches.MethodReferencesSearch; | ||
import com.intellij.util.Processor; | ||
import de.plushnikov.intellij.plugin.psi.LombokLightMethodBuilder; | ||
import lombok.Builder; | ||
import lombok.With; | ||
import lombok.experimental.Wither; | ||
import org.jetbrains.annotations.NotNull; | ||
import org.jetbrains.annotations.Nullable; | ||
|
||
public class LombokMethodReferenceSearcher extends QueryExecutorBase<PsiReference, MethodReferencesSearch.SearchParameters> { | ||
|
||
public LombokMethodReferenceSearcher(boolean requireReadAction) { | ||
super(requireReadAction); | ||
System.out.println("LombokMethodReferenceSearcher(boolean)"); | ||
} | ||
|
||
public LombokMethodReferenceSearcher() { | ||
System.out.println("LombokMethodReferenceSearcher()"); | ||
} | ||
|
||
@Override | ||
public void processQuery(@NotNull MethodReferencesSearch.SearchParameters queryParameters, @NotNull Processor<? super PsiReference> consumer) { | ||
PsiMethod queryMethod = queryParameters.getMethod(); | ||
LombokLightMethodBuilder lombokMethod = LombokLightMethodBuilder.getLightMethodBuilder(queryMethod); | ||
PsiMethod actualMethod = lombokMethod == null ? queryMethod : lombokMethod; | ||
|
||
// Each Lombok-generated PsiMethod exists twice. | ||
// - Once as an instance of LombokLightMethodBuilder which is properly wired by to its containing class. | ||
// - Once as a degenerated instance of PsiMethodImpl which is built by LombokLightMethodBuilder and is not properly wired to its containing class | ||
|
||
if (lombokMethod != null && queryMethod != lombokMethod) { | ||
// We replace the search for the degenerated PsiMethodImpl by a search for the rich LombokLightMethodBuilder | ||
// (but only the query is not already the LombokLightMethodBuilder, otherwise we'll cause an infinite loop) | ||
MethodReferencesSearch.SearchParameters newParameters = new MethodReferencesSearch.SearchParameters( | ||
lombokMethod, | ||
queryParameters.getScopeDeterminedByUser(), | ||
queryParameters.isStrictSignatureSearch(), | ||
queryParameters.getOptimizer()); | ||
MethodReferencesSearch.search(newParameters).forEach(consumer); | ||
} | ||
|
||
|
||
if (shouldLookForConstructorReferences(actualMethod)) { | ||
findConstructorReferencesInClassMethods(actualMethod.getContainingClass(), actualMethod, consumer); | ||
findConstructorReferencesInInnerClasses(actualMethod.getContainingClass(), actualMethod, consumer); | ||
} | ||
} | ||
|
||
/** | ||
* If the method is a constructor, we must also look into class methods (it might be used in @With methods), | ||
* and inner classes (it might be used in @Builder methods). Note that @With and @Builder will use physical | ||
* constructors if available, so we have to look for constructor references even if the constructor is not | ||
* generated by lombok. | ||
* <p> | ||
* However, in order to avoid searching deep into method implementations for *each* Constructor reference search and | ||
* save a bit of CPU, we will only search for constructor references if the containing class is annotated with | ||
* | ||
* @Builder, @Wither or @With | ||
*/ | ||
private boolean shouldLookForConstructorReferences(PsiMethod constructor) { | ||
if (!constructor.isConstructor()) { | ||
return false; | ||
} | ||
|
||
PsiClass containingClass = constructor.getContainingClass(); | ||
if (containingClass == null) { | ||
return false; | ||
} | ||
|
||
return containingClass.getAnnotation(Builder.class.getName()) != null | ||
|| containingClass.getAnnotation(With.class.getName()) != null | ||
|| containingClass.getAnnotation(Wither.class.getName()) != null; | ||
} | ||
|
||
private void findConstructorReferencesInInnerClasses(PsiClass containingClass, PsiMethod constructor, Processor<? super PsiReference> consumer) { | ||
for (PsiClass clazz : containingClass.getInnerClasses()) { | ||
findConstructorReferencesInClassMethods(clazz, constructor, consumer); | ||
} | ||
} | ||
|
||
private boolean findConstructorReferencesInClassMethods(PsiClass clazz, PsiMethod constructor, Processor<? super PsiReference> consumer) { | ||
for (PsiMethod method : clazz.getMethods()) { | ||
if (method instanceof LombokLightMethodBuilder) { | ||
// only look in the methods we have generated ourselves. Other methods are already indexed and the references they contain can | ||
// already be found by the native ReferenceSearch | ||
if (!reportConstructorReferencesInElement(method, constructor, consumer)) { | ||
return false; | ||
} | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
private boolean reportConstructorReferencesInElement(PsiElement haystack, PsiMethod needle, Processor<? super PsiReference> consumer) { | ||
if (haystack instanceof PsiNewExpression) { | ||
PsiNewExpression newExpression = (PsiNewExpression) haystack; | ||
PsiMethod resolvedConstructor = newExpression.resolveConstructor(); | ||
if (resolvedConstructor == needle) { | ||
consumer.process(new LombokConstructorReference<>( | ||
newExpression, | ||
new TextRange(3, 4), | ||
resolvedConstructor | ||
)); | ||
} | ||
} | ||
|
||
PsiElement[] children = haystack.getChildren(); | ||
for (PsiElement child : children) { | ||
if (!reportConstructorReferencesInElement(child, needle, consumer)) { | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
private static class LombokConstructorReference<T extends PsiElement> extends PsiReferenceBase<T> { | ||
private final PsiMethod resolved; | ||
|
||
public LombokConstructorReference(@NotNull T element, TextRange rangeInElement, PsiMethod resolved) { | ||
super(element, rangeInElement); | ||
this.resolved = resolved; | ||
} | ||
|
||
@Nullable | ||
@Override | ||
public PsiElement resolve() { | ||
return resolved; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Care to remove System.out statements?