IDEA-179038 When navigating to method definition specific type information known from DFA could be used to find actual implementation

This commit is contained in:
Tagir Valeev
2017-09-15 14:29:43 +07:00
parent 4c1d7a71d6
commit d837111973
2 changed files with 56 additions and 3 deletions
@@ -15,6 +15,9 @@
*/
package com.intellij.codeInsight;
import com.intellij.codeInspection.dataFlow.CommonDataflow;
import com.intellij.codeInspection.dataFlow.DfaFactType;
import com.intellij.codeInspection.dataFlow.TypeConstraint;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.Computable;
@@ -38,18 +41,19 @@ public class JavaTargetElementEvaluator extends TargetElementEvaluatorEx2 implem
public static final int NEW_AS_CONSTRUCTOR = 0x04;
public static final int THIS_ACCEPTED = 0x10;
public static final int SUPER_ACCEPTED = 0x20;
public static final int USE_DFA = 0x40;
@Override
public int getAllAdditionalFlags() {
return NEW_AS_CONSTRUCTOR | THIS_ACCEPTED | SUPER_ACCEPTED;
return NEW_AS_CONSTRUCTOR | THIS_ACCEPTED | SUPER_ACCEPTED | USE_DFA;
}
/**
* Accepts THIS or SUPER but not NEW_AS_CONSTRUCTOR.
* Accepts THIS or SUPER or USE_DFA but not NEW_AS_CONSTRUCTOR.
*/
@Override
public int getAdditionalDefinitionSearchFlags() {
return THIS_ACCEPTED | SUPER_ACCEPTED;
return THIS_ACCEPTED | SUPER_ACCEPTED | USE_DFA;
}
/**
@@ -78,6 +82,32 @@ public class JavaTargetElementEvaluator extends TargetElementEvaluatorEx2 implem
return ((PsiClassType)type).resolve();
}
}
if (targetElement instanceof PsiMethod && BitUtil.isSet(flags, USE_DFA)) {
PsiMethod method = (PsiMethod)targetElement;
PsiClass qualifierClass = method.getContainingClass();
if (!method.hasModifierProperty(PsiModifier.PRIVATE) &&
!method.hasModifierProperty(PsiModifier.FINAL) &&
!method.hasModifierProperty(PsiModifier.STATIC) &&
qualifierClass != null) {
PsiReference reference = TargetElementUtil.findReference(editor, offset);
if (reference instanceof PsiReferenceExpression && reference.isReferenceTo(targetElement)) {
PsiExpression qualifier = ((PsiReferenceExpression)reference).getQualifierExpression();
if (qualifier != null) {
TypeConstraint constraint = CommonDataflow.getExpressionFact(qualifier, DfaFactType.TYPE_CONSTRAINT);
if (constraint != null) {
PsiClass specificQualifierClass = PsiUtil.resolveClassInClassTypeOnly(constraint.getPsiType());
if (specificQualifierClass != null && !specificQualifierClass.equals(qualifierClass) &&
InheritanceUtil.isInheritorOrSelf(specificQualifierClass, qualifierClass, true)) {
PsiMethod realMethod = specificQualifierClass.findMethodBySignature(method, true);
if (realMethod != null) {
return realMethod;
}
}
}
}
}
}
}
return super.adjustTargetElement(editor, offset, flags, targetElement);
}
@@ -55,6 +55,29 @@ public class GotoImplementationHandlerTest extends JavaCodeInsightFixtureTestCas
assertEquals(2, impls.length);
}
public void testUnderInstanceOf() {
PsiFile file = myFixture.addFileToProject("Foo.java", "abstract class Hello {\n" +
" abstract void foo();\n" +
"\n" +
" void test(Hello h) {\n" +
" if(h instanceof Hello1) h.fo<caret>o();\n" +
" }\n" +
" \n" +
" class Hello1 extends Hello {\n" +
" void foo() {}\n" +
" }\n" +
" class Hello2 extends Hello {\n" +
" void foo() {}\n" +
" }\n" +
"}");
myFixture.configureFromExistingVirtualFile(file.getVirtualFile());
final PsiElement[] impls = getTargets(file);
assertEquals(1, impls.length);
assertTrue(impls[0] instanceof PsiMethod);
assertEquals("Hello.Hello1", ((PsiMethod)impls[0]).getContainingClass().getQualifiedName());
}
public void testFromIncompleteCode() {
PsiFile file = myFixture.addFileToProject("Foo.java", "public abstract class Hello {\n" +
" abstract void foo();\n" +