cache diamond inference results

This commit is contained in:
Anna Kozlova
2015-09-30 10:59:13 +02:00
parent 17f7affff2
commit c111105d30
4 changed files with 109 additions and 69 deletions

View File

@@ -33,8 +33,7 @@ import com.intellij.psi.scope.PsiConflictResolver;
import com.intellij.psi.scope.conflictResolvers.JavaMethodsConflictResolver;
import com.intellij.psi.scope.processor.MethodCandidatesProcessor;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.*;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.VisibilityUtil;
@@ -152,27 +151,31 @@ public class PsiDiamondTypeImpl extends PsiDiamondType {
if (psiClass == null) return DiamondInferenceResult.NULL_RESULT;
final PsiExpressionList argumentList = newExpression.getArgumentList();
if (argumentList == null) return DiamondInferenceResult.NULL_RESULT;
final Ref<PsiMethod> staticFactoryRef = new Ref<PsiMethod>();
final Ref<MethodCandidateInfo> staticFactoryRef = new Ref<MethodCandidateInfo>();
final PsiSubstitutor inferredSubstitutor = ourDiamondGuard.doPreventingRecursion(context, false, new Computable<PsiSubstitutor>() {
@Override
public PsiSubstitutor compute() {
final PsiMethod staticFactory = findConstructorStaticFactory(psiClass, newExpression);
if (staticFactory == null) {
return null;
}
staticFactoryRef.set(staticFactory);
return inferTypeParametersForStaticFactory(staticFactory, newExpression, context, false);
final MethodCandidateInfo staticFactoryCandidateInfo = CachedValuesManager.getCachedValue(newExpression,
new CachedValueProvider<MethodCandidateInfo>() {
@Nullable
@Override
public Result<MethodCandidateInfo> compute() {
return new Result<MethodCandidateInfo>(getStaticFactoryCandidateInfo(psiClass, newExpression, context, argumentList), PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
}
});
staticFactoryRef.set(staticFactoryCandidateInfo);
return staticFactoryCandidateInfo != null ? staticFactoryCandidateInfo.getSubstitutor() : null;
}
});
if (inferredSubstitutor == null) {
return DiamondInferenceResult.NULL_RESULT;
}
final PsiMethod staticFactory = staticFactoryRef.get();
if (staticFactory == null) {
final MethodCandidateInfo staticFactoryInfo = staticFactoryRef.get();
if (staticFactoryInfo == null) {
LOG.error(inferredSubstitutor);
return DiamondInferenceResult.NULL_RESULT;
}
final PsiMethod staticFactory = staticFactoryInfo.getElement();
final PsiTypeParameter[] parameters = staticFactory.getTypeParameters();
final PsiTypeParameter[] classParameters = psiClass.getTypeParameters();
final PsiJavaCodeReferenceElement classOrAnonymousClassReference = newExpression.getClassOrAnonymousClassReference();
@@ -200,6 +203,30 @@ public class PsiDiamondTypeImpl extends PsiDiamondType {
return result;
}
private static MethodCandidateInfo getStaticFactoryCandidateInfo(PsiClass psiClass,
PsiNewExpression newExpression,
PsiElement context,
PsiExpressionList argumentList) {
final PsiMethod staticFactory = findConstructorStaticFactory(psiClass, newExpression);
if (staticFactory == null) {
return null;
}
final MethodCandidateInfo staticFactoryCandidateInfo = createMethodCandidate(staticFactory, context, false, argumentList);
if (staticFactory.isVarArgs()) {
final Computable<Integer> computable = new Computable<Integer>() {
@Override
public Integer compute() {
return staticFactoryCandidateInfo.getPertinentApplicabilityLevel();
}
};
final Integer applicability = MethodCandidateInfo.ourOverloadGuard.doPreventingRecursion(newExpression, true, computable);
if ((applicability != null ? applicability : staticFactoryCandidateInfo.getApplicabilityLevel()) < MethodCandidateInfo.ApplicabilityLevel.FIXED_ARITY) {
return createMethodCandidate(staticFactory, context, true, argumentList);
}
}
return staticFactoryCandidateInfo;
}
@Nullable
private static PsiMethod findConstructorStaticFactory(final PsiClass containingClass, PsiNewExpression newExpression) {
@@ -370,60 +397,40 @@ public class PsiDiamondTypeImpl extends PsiDiamondType {
}
private static PsiSubstitutor inferTypeParametersForStaticFactory(@NotNull PsiMethod staticFactoryMethod,
PsiNewExpression expression,
final PsiElement parent,
final boolean varargs) {
final PsiExpressionList argumentList = expression.getArgumentList();
if (argumentList != null) {
final MethodCandidateInfo staticFactoryCandidateInfo =
new MethodCandidateInfo(staticFactoryMethod, PsiSubstitutor.EMPTY, false, false, argumentList, parent, null, null) {
private PsiType[] myExpressionTypes;
private static MethodCandidateInfo createMethodCandidate(@NotNull final PsiMethod staticFactoryMethod,
final PsiElement parent,
final boolean varargs,
final PsiExpressionList argumentList) {
return new MethodCandidateInfo(staticFactoryMethod, PsiSubstitutor.EMPTY, false, false, argumentList, parent, null, null) {
private PsiType[] myExpressionTypes;
@Override
public boolean isVarargs() {
return varargs;
}
@Override
protected PsiElement getParent() {
return parent;
}
@Override
public PsiType[] getArgumentTypes() {
if (myExpressionTypes == null) {
final PsiType[] expressionTypes = argumentList.getExpressionTypes();
if (MethodCandidateInfo.isOverloadCheck()) {
return expressionTypes;
}
myExpressionTypes = expressionTypes;
}
return myExpressionTypes;
}
@Override
protected PsiElement getMarkerList() {
return parent instanceof PsiNewExpression ? ((PsiNewExpression)parent).getArgumentList() : super.getMarkerList();
}
};
if (!varargs && staticFactoryMethod.isVarArgs()) {
final Computable<Integer> computable = new Computable<Integer>() {
@Override
public Integer compute() {
return staticFactoryCandidateInfo.getPertinentApplicabilityLevel();
}
};
final Integer applicability = MethodCandidateInfo.ourOverloadGuard.doPreventingRecursion(expression, true, computable);
if ((applicability != null ? applicability : staticFactoryCandidateInfo.getApplicabilityLevel()) < MethodCandidateInfo.ApplicabilityLevel.FIXED_ARITY) {
return inferTypeParametersForStaticFactory(staticFactoryMethod, expression, parent, true);
}
@Override
public boolean isVarargs() {
return varargs;
}
return staticFactoryCandidateInfo.getSubstitutor();
}
else {
return PsiSubstitutor.EMPTY;
}
@Override
protected PsiElement getParent() {
return parent;
}
@Override
public PsiType[] getArgumentTypes() {
if (myExpressionTypes == null) {
final PsiType[] expressionTypes = argumentList.getExpressionTypes();
if (MethodCandidateInfo.isOverloadCheck()) {
return expressionTypes;
}
myExpressionTypes = expressionTypes;
}
return myExpressionTypes;
}
@Override
protected PsiElement getMarkerList() {
return parent instanceof PsiNewExpression ? ((PsiNewExpression)parent).getArgumentList() : super.getMarkerList();
}
};
}
public static boolean hasDefaultConstructor(@NotNull final PsiClass psiClass) {

View File

@@ -442,16 +442,22 @@ public class InferenceSession {
: PsiResolveHelper.ourGraphGuard.doPreventingRecursion(expression, false, computableResolve);
}
public static JavaResolveResult getResolveResult(PsiCall callExpression, PsiExpressionList argumentList) {
public static JavaResolveResult getResolveResult(final PsiCall callExpression, final PsiExpressionList argumentList) {
if (callExpression instanceof PsiNewExpression) {
final PsiJavaCodeReferenceElement classReference = ((PsiNewExpression)callExpression).getClassOrAnonymousClassReference();
final JavaResolveResult resolveResult = classReference != null ? classReference.advancedResolve(false) : null;
final PsiElement psiClass = resolveResult != null ? resolveResult.getElement() : null;
if (psiClass instanceof PsiClass) {
final JavaPsiFacade facade = JavaPsiFacade.getInstance(callExpression.getProject());
final JavaResolveResult constructor = facade.getResolveHelper()
.resolveConstructor(facade.getElementFactory().createType((PsiClass)psiClass).rawType(), argumentList, callExpression);
return constructor.getElement() == null ? resolveResult : constructor;
return CachedValuesManager.getCachedValue(classReference, new CachedValueProvider<JavaResolveResult>() {
@Nullable
@Override
public Result<JavaResolveResult> compute() {
final JavaPsiFacade facade = JavaPsiFacade.getInstance(callExpression.getProject());
final JavaResolveResult constructor = facade.getResolveHelper()
.resolveConstructor(facade.getElementFactory().createType((PsiClass)psiClass).rawType(), argumentList, callExpression);
return new Result<JavaResolveResult>(constructor.getElement() == null ? resolveResult : constructor, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
}
});
}
else {
return JavaResolveResult.EMPTY;

View File

@@ -0,0 +1,18 @@
class Test {
static class C<U> {
U u;
C(C<U> other) {
u = other.u;
}
C(U u) {
this.u = u;
}
}
static <U> C<U> foo(C<U> c) { return new C<U>(c); }
{
C<String> c = foo(new C<>(foo(new C<>(foo(new C<>(foo(new C<>(foo(null)))))))));
}
}

View File

@@ -102,6 +102,15 @@ public class OverloadResolutionTest extends LightDaemonAnalyzerTestCase {
}).assertTiming();
}
public void testConstructorOverloadsWithDiamonds() throws Exception {
PlatformTestUtil.startPerformanceTest("Overload resolution with chain constructor calls with diamonds", 10000, new ThrowableRunnable() {
@Override
public void run() throws Throwable {
doTest(false);
}
}).assertTiming();
}
public void testMultipleOverloadsWithNestedGeneric() throws Exception {
doTest(false);
}