method rename using custom reference provider - problem with isReferenceTo() (throws PsiInvalidElementAccessException..)
hello,
I'm trying to make use of IDEA's built-in java rename refactoring mechanism using custom reference provider (ElLiteralCustomReferenceProvider).
The provider handles ELStringLiterals pointing at some Java methods, for example :
<p:commandButton value="Zakres danych" action="#{v.executeAction('adminReports')}" />
here 'adminReports' ELStringLiteral has an injected reference pointing at 'adminReports' java method.
'Find Usage' feature works as expected - when I place a caret at 'adminReports' method's name and press Alt + F7, the result list contains the code snippet above.
The same goes for 'Go To Declaration' (ctrl + click on 'adminReports' EL string literal) - it jumps to the method definition.
But when I try to rename 'adminReports' Java method, the following exception occurs :
[ 96295] ERROR - oring.BaseRefactoringProcessor - Element: class com.intellij.psi.impl.source.PsiMethodImpl because: parent is null com.intellij.psi.PsiInvalidElementAccessException: Element: class com.intellij.psi.impl.source.PsiMethodImpl because: parent is null at com.intellij.psi.impl.PsiElementBase.getContainingFile(PsiElementBase.java:197) at com.intellij.extapi.psi.StubBasedPsiElementBase.getContainingFile(StubBasedPsiElementBase.java:186) at com.intellij.extapi.psi.StubBasedPsiElementBase.getManager(StubBasedPsiElementBase.java:217) at com.intellij.extapi.psi.StubBasedPsiElementBase.getManager(StubBasedPsiElementBase.java:53) at com.intellij.psi.impl.PsiClassImplUtil.isMethodEquivalentTo(PsiClassImplUtil.java:1063) at com.intellij.psi.impl.source.PsiMethodImpl.isEquivalentTo(PsiMethodImpl.java:368) at com.intellij.psi.impl.PsiManagerImpl.areElementsEquivalent(PsiManagerImpl.java:177) at com.intellij.psi.PsiPolyVariantReferenceBase.isReferenceTo(PsiPolyVariantReferenceBase.java:53) at com.intellij.psi.impl.search.MethodTextOccurrenceProcessor.a(MethodTextOccurrenceProcessor.java:64) at com.intellij.psi.impl.search.MethodTextOccurrenceProcessor.processTextOccurrence(MethodTextOccurrenceProcessor.java:47) at com.intellij.psi.impl.search.PsiSearchHelperImpl$20.execute(PsiSearchHelperImpl.java:681) at com.intellij.psi.impl.search.LowLevelSearchUtil.a(LowLevelSearchUtil.java:123) at com.intellij.psi.impl.search.LowLevelSearchUtil.processElementsContainingWordInElement(LowLevelSearchUtil.java:171) at com.intellij.psi.impl.search.PsiSearchHelperImpl$18.process(PsiSearchHelperImpl.java:636) at com.intellij.psi.impl.search.PsiSearchHelperImpl$18.process(PsiSearchHelperImpl.java:629) at com.intellij.psi.impl.search.PsiSearchHelperImpl$7$2.run(PsiSearchHelperImpl.java:251) at com.intellij.openapi.application.impl.ApplicationImpl.runReadAction(ApplicationImpl.java:929) at com.intellij.psi.impl.search.PsiSearchHelperImpl$7.process(PsiSearchHelperImpl.java:238) at com.intellij.psi.impl.search.PsiSearchHelperImpl$7.process(PsiSearchHelperImpl.java:227) at com.intellij.concurrency.JobLauncherImpl$2$1.run(JobLauncherImpl.java:115) at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:226) at com.intellij.concurrency.JobLauncherImpl$2.process(JobLauncherImpl.java:113) at com.intellij.concurrency.JobLauncherImpl$1.run(JobLauncherImpl.java:54) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334) at java.util.concurrent.FutureTask.run(FutureTask.java:166) at com.intellij.concurrency.PrioritizedFutureTask.access$101(PrioritizedFutureTask.java:31) at com.intellij.concurrency.PrioritizedFutureTask$1.run(PrioritizedFutureTask.java:70) at com.intellij.concurrency.PrioritizedFutureTask.run(PrioritizedFutureTask.java:105) at com.intellij.concurrency.JobImpl.scheduleAndWaitForResults(JobImpl.java:136) at com.intellij.concurrency.JobLauncherImpl.a(JobLauncherImpl.java:68) at com.intellij.concurrency.JobLauncherImpl.invokeConcurrentlyUnderProgress(JobLauncherImpl.java:110) at com.intellij.concurrency.JobLauncherImpl.invokeConcurrentlyUnderProgressAsync(JobLauncherImpl.java:131) at com.intellij.concurrency.JobLauncherImpl.invokeConcurrentlyUnderProgressAsync(JobLauncherImpl.java:37) at com.intellij.psi.impl.search.PsiSearchHelperImpl.a(PsiSearchHelperImpl.java:227) at com.intellij.psi.impl.search.PsiSearchHelperImpl.a(PsiSearchHelperImpl.java:628) at com.intellij.psi.impl.search.PsiSearchHelperImpl.access$400(PsiSearchHelperImpl.java:55) at com.intellij.psi.impl.search.PsiSearchHelperImpl$17.body(PsiSearchHelperImpl.java:530) at com.intellij.concurrency.DoWhile.getResult(DoWhile.java:32) at com.intellij.psi.impl.search.PsiSearchHelperImpl.processRequestsAsync(PsiSearchHelperImpl.java:574) at com.intellij.psi.impl.search.PsiSearchHelperImpl.processRequests(PsiSearchHelperImpl.java:510) at com.intellij.psi.search.SearchRequestQuery.processResults(SearchRequestQuery.java:29) at com.intellij.util.AbstractQuery.forEach(AbstractQuery.java:77) at com.intellij.util.MergeQuery.processSubQuery(MergeQuery.java:83) at com.intellij.util.MergeQuery.forEach(MergeQuery.java:56) at com.intellij.util.UniqueResultsQuery.process(UniqueResultsQuery.java:66) at com.intellij.util.UniqueResultsQuery.forEach(UniqueResultsQuery.java:56) at com.intellij.util.UniqueResultsQuery.findAll(UniqueResultsQuery.java:85) at com.intellij.refactoring.rename.RenameJavaMethodProcessor.findReferences(RenameJavaMethodProcessor.java:166) at com.intellij.refactoring.rename.RenamePsiElementProcessor.findReferences(RenamePsiElementProcessor.java:64) at com.intellij.refactoring.rename.RenameUtil.findUsages(RenameUtil.java:71) at com.intellij.refactoring.rename.RenameProcessor.findUsages(RenameProcessor.java:271) at com.intellij.refactoring.BaseRefactoringProcessor$1$1.compute(BaseRefactoringProcessor.java:160) at com.intellij.refactoring.BaseRefactoringProcessor$1$1.compute(BaseRefactoringProcessor.java:157) at com.intellij.openapi.application.impl.ApplicationImpl.runReadAction(ApplicationImpl.java:950) at com.intellij.refactoring.BaseRefactoringProcessor$1.run(BaseRefactoringProcessor.java:157) at com.intellij.openapi.progress.impl.ProgressManagerImpl$5.run(ProgressManagerImpl.java:291) at com.intellij.openapi.progress.impl.ProgressManagerImpl$TaskRunnable.run(ProgressManagerImpl.java:495) at com.intellij.openapi.progress.impl.ProgressManagerImpl$6.run(ProgressManagerImpl.java:304) at com.intellij.openapi.progress.impl.ProgressManagerImpl$2.run(ProgressManagerImpl.java:185) at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:226) at com.intellij.openapi.progress.impl.ProgressManagerImpl.runProcess(ProgressManagerImpl.java:175) at com.intellij.openapi.application.impl.ApplicationImpl$10$1.run(ApplicationImpl.java:681) at com.intellij.openapi.application.impl.ApplicationImpl$8.run(ApplicationImpl.java:454) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334) at java.util.concurrent.FutureTask.run(FutureTask.java:166) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603) at java.lang.Thread.run(Thread.java:722) at com.intellij.openapi.application.impl.ApplicationImpl$1$1.run(ApplicationImpl.java:152)
The problem seems to be caused by an invalid reference target - the reference returned by my provider is pointing at an already altered (and hence - invalid) PsiMethod. And therefore element comparison in isReferenceTo() method fails.
Here's the code of a poly-variant reference, returned by my custom ref provider :
new PsiPolyVariantReferenceBase(elLiteralExpression) { @NotNull @Override public ResolveResult[] multiResolve(boolean incompleteCode) { List resolveResults = new LinkedList<>(); for (Action targetAction : targetActions) { // add reference to action's implementation method within parent state class) resolveResults.add(new PsiElementResolveResult(targetAction.getSourcePointer(), targetAction.getSourcePointer().isValid())); } return resolveResults.toArray(new ResolveResult[resolveResults.size()]); } @Override public TextRange getRangeInElement() { return new TextRange(1, getElement().getTextLength()-1); } @NotNull @Override public String getCanonicalText() { return StringUtil.stripQuotesAroundValue(getElement().getText()); } @NotNull @Override public Object[] getVariants() { return LookupElement.EMPTY_ARRAY; } }
As you can see - nothing fancy; jump to definition (ctrl + click) and find usages both work ok; the problem is with rename refactoring - the isReferenceTo(PsiElement element) superclass method call fails with an exception.
Shouldn't it be invoked from some 'beforePsiTreeChange' method to ensure that the referenced psi elements have not been invalidated yet ?
What can I do to fix that ?
Any help will be much appreciated.
regards,
Simon
Please sign in to leave a comment.
UPDATE : I provided my own implementation of 'isReferenceTo' based on method name and enclosing class FQN comparison - the referencing element gets renamed successfully but the 'link' between reference source and target is not updated.
After 'rename' completes, 'createReferences' method of my reference provider gets triggered but since my plugin's internal data structures are 'tainted' (the edited file has not been saved and I refresh these structures on a file change event..) - I cannot provide a reference to an updated target and return an empty PsiReference array. So both 'ends' of the reference in question contain the same text string but neither 'find usages' nor 'go to declaration' work.
I tried forcing 'save document' in 'handleElementRename' to trigger a contentsChanged event and refresh my internal structures but it turned out to be a dead end.
What can I do to update the reference after a successfull rename ?
Thanks in advance.
friendly,
Simon
Anyone ?