method refs: patch getClass return value in the same way it is done in PsiMethodCallExpression

This commit is contained in:
anna
2013-02-26 19:51:01 +01:00
parent a68a88d014
commit 23056b8d20
6 changed files with 91 additions and 47 deletions
@@ -18,10 +18,7 @@ package com.intellij.psi;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.psi.util.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -79,12 +76,7 @@ public class PsiMethodReferenceUtil {
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
final PsiExpression expression = methodReferenceExpression.getQualifierExpression();
if (expression != null) {
final PsiType expressionType = expression.getType();
if (expressionType instanceof PsiArrayType) {
containingClass = JavaPsiFacade.getInstance(methodReferenceExpression.getProject())
.findClass(CommonClassNames.JAVA_LANG_OBJECT, methodReferenceExpression.getResolveScope());
return new QualifierResolveResult(containingClass, substitutor, false);
}
final PsiType expressionType = getExpandedType(expression.getType(), expression);
PsiClassType.ClassResolveResult result = PsiUtil.resolveGenericsClassInType(expressionType);
containingClass = result.getElement();
if (containingClass != null) {
@@ -152,7 +144,14 @@ public class PsiMethodReferenceUtil {
final MethodSignature signature2 = ((PsiMethod)resolve).getSignature(subst);
final PsiType interfaceReturnType = LambdaUtil.getFunctionalInterfaceReturnType(left);
PsiType methodReturnType = subst.substitute(((PsiMethod)resolve).getReturnType());
PsiType returnType = PsiTypesUtil.patchMethodGetClassReturnType(methodReferenceExpression, methodReferenceExpression,
(PsiMethod)resolve, null,
PsiUtil.getLanguageLevel(methodReferenceExpression));
if (returnType == null) {
returnType = ((PsiMethod)resolve).getReturnType();
}
PsiType methodReturnType = subst.substitute(returnType);
if (interfaceReturnType != null && interfaceReturnType != PsiType.VOID) {
if (methodReturnType == null) {
methodReturnType = JavaPsiFacade.getElementFactory(methodReferenceExpression.getProject()).createType(((PsiMethod)resolve).getContainingClass(), subst);
@@ -15,7 +15,13 @@
*/
package com.intellij.psi.util;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.containers.HashMap;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -47,6 +53,8 @@ public class PsiTypesUtil {
ourBoxedTypes.put("char", CommonClassNames.JAVA_LANG_CHARACTER);
}
@NonNls private static final String GET_CLASS_METHOD = "getClass";
private PsiTypesUtil() { }
public static String getDefaultValueOfType(PsiType type) {
@@ -135,4 +143,45 @@ public class PsiTypesUtil {
}
return null;
}
public static PsiType patchMethodGetClassReturnType(@NotNull PsiExpression call,
@NotNull PsiReferenceExpression methodExpression,
@NotNull PsiMethod method,
@Nullable Condition<IElementType> condition,
@NotNull LanguageLevel languageLevel) {
//JLS3 15.8.2
if (languageLevel.isAtLeast(LanguageLevel.JDK_1_5) &&
GET_CLASS_METHOD.equals(method.getName()) &&
CommonClassNames.JAVA_LANG_OBJECT.equals(method.getContainingClass().getQualifiedName())) {
PsiExpression qualifier = methodExpression.getQualifierExpression();
PsiType qualifierType = null;
final Project project = call.getProject();
if (qualifier != null) {
qualifierType = TypeConversionUtil.erasure(qualifier.getType());
}
else if (condition != null) {
ASTNode parent = call.getNode().getTreeParent();
while (parent != null && condition.value(parent.getElementType())) {
parent = parent.getTreeParent();
}
if (parent != null) {
qualifierType = JavaPsiFacade.getInstance(project).getElementFactory().createType((PsiClass)parent.getPsi());
}
}
if (qualifierType != null) {
PsiClass javaLangClass = JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_LANG_CLASS, call.getResolveScope());
if (javaLangClass != null && javaLangClass.getTypeParameters().length == 1) {
Map<PsiTypeParameter, PsiType> map = new HashMap<PsiTypeParameter, PsiType>();
map.put(javaLangClass.getTypeParameters()[0], PsiWildcardType.createExtends(call.getManager(), qualifierType));
PsiSubstitutor substitutor = JavaPsiFacade.getInstance(project).getElementFactory().createSubstitutor(map);
final PsiClassType classType = JavaPsiFacade.getInstance(project).getElementFactory()
.createType(javaLangClass, substitutor, languageLevel);
final PsiElement parent = call.getParent();
return parent instanceof PsiReferenceExpression && parent.getParent() instanceof PsiMethodCallExpression
? PsiUtil.captureToplevelWildcards(classType, methodExpression) : classType;
}
}
}
return null;
}
}
@@ -17,6 +17,7 @@ package com.intellij.psi.impl.source.tree.java;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Condition;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.impl.DebugUtil;
@@ -27,19 +28,15 @@ import com.intellij.psi.impl.source.tree.ElementType;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.tree.ChildRoleBase;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
public class PsiMethodCallExpressionImpl extends ExpressionPsiElement implements PsiMethodCallExpression {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.java.PsiMethodCallExpressionImpl");
@NonNls private static final String GET_CLASS_METHOD = "getClass";
public PsiMethodCallExpressionImpl() {
super(JavaElementType.METHOD_CALL_EXPRESSION);
@@ -174,39 +171,19 @@ public class PsiMethodCallExpressionImpl extends ExpressionPsiElement implements
private static PsiType getResultType(PsiExpression call, PsiReferenceExpression methodExpression, JavaResolveResult result) {
final PsiMethod method = (PsiMethod)result.getElement();
if (method == null) return null;
PsiManager manager = call.getManager();
final LanguageLevel languageLevel = PsiUtil.getLanguageLevel(call);
boolean is15OrHigher = languageLevel.compareTo(LanguageLevel.JDK_1_5) >= 0;
//JLS3 15.8.2
if (is15OrHigher &&
GET_CLASS_METHOD.equals(method.getName()) &&
CommonClassNames.JAVA_LANG_OBJECT.equals(method.getContainingClass().getQualifiedName())) {
PsiExpression qualifier = methodExpression.getQualifierExpression();
PsiType qualifierType = null;
if (qualifier != null) {
qualifierType = TypeConversionUtil.erasure(qualifier.getType());
}
else {
ASTNode parent = call.getNode().getTreeParent();
while (parent != null && parent.getElementType() != JavaElementType.CLASS) parent = parent.getTreeParent();
if (parent != null) {
qualifierType = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createType((PsiClass)parent.getPsi());
}
}
if (qualifierType != null) {
PsiClass javaLangClass = JavaPsiFacade.getInstance(manager.getProject()).findClass("java.lang.Class", call.getResolveScope());
if (javaLangClass != null && javaLangClass.getTypeParameters().length == 1) {
Map<PsiTypeParameter, PsiType> map = new HashMap<PsiTypeParameter, PsiType>();
map.put(javaLangClass.getTypeParameters()[0], PsiWildcardType.createExtends(manager, qualifierType));
PsiSubstitutor substitutor = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createSubstitutor(map);
final PsiClassType classType = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory()
.createType(javaLangClass, substitutor, languageLevel);
final PsiElement parent = call.getParent();
return parent instanceof PsiReferenceExpression && parent.getParent() instanceof PsiMethodCallExpression
? PsiUtil.captureToplevelWildcards(classType, methodExpression) : classType;
}
}
final PsiType getClassReturnType = PsiTypesUtil.patchMethodGetClassReturnType(call, methodExpression, method,
new Condition<IElementType>() {
@Override
public boolean value(IElementType type) {
return type != JavaElementType.CLASS;
}
}, languageLevel);
if (getClassReturnType != null) {
return getClassReturnType;
}
PsiType ret = method.getReturnType();
@@ -69,7 +69,7 @@ class MyTest1 {
<error descr="Incompatible types. Found: '<method reference>', required: 'MyTest1.I3'">I3 c3 = arr::clone;</error>
<error descr="Incompatible types. Found: '<method reference>', required: 'MyTest1.I4'">I4 c5 = arr::clone;</error>
<error descr="Incompatible types. Found: '<method reference>', required: 'MyTest1.I5'">I5 c4 = this::getClass;</error>
I5 c4 = this::getClass;
<error descr="Incompatible types. Found: '<method reference>', required: 'MyTest1.I6'">I6 c6 = this::getClass;</error>
}
}
@@ -0,0 +1,15 @@
import java.util.List;
class GetClassTest {
interface GetCl {
Class<? extends List> _();
}
interface GetClReturnTypeProblems {
Class<List<String>> _();
}
void test(int[] iarr, List<String> ls) {
GetCl c4 = ls::getClass;
<error descr="Incompatible types. Found: '<method reference>', required: 'GetClassTest.GetClReturnTypeProblems'">GetClReturnTypeProblems c5 = ls::getClass;</error>
}
}
@@ -196,6 +196,10 @@ public class MethodRefHighlightingTest extends LightDaemonAnalyzerTestCase {
doTest();
}
public void testGetClassSpecifics() throws Exception {
doTest();
}
private void doTest() throws Exception {
doTest(false);
}