Poly Expressions

This commit is contained in:
Anna Kozlova
2013-09-10 21:14:02 +04:00
parent 9f5e9d5cfe
commit c848db6d47
2 changed files with 228 additions and 0 deletions
@@ -0,0 +1,146 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.impl.source.resolve.graphInference;
import com.intellij.psi.*;
import com.intellij.psi.util.TypeConversionUtil;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* User: anna
*/
public class PsiPolyExpressionUtil {
public static boolean hasStandaloneForm(PsiExpression expression) {
if (expression instanceof PsiLambdaExpression ||
expression instanceof PsiMethodReferenceExpression ||
expression instanceof PsiParenthesizedExpression ||
expression instanceof PsiConditionalExpression ||
expression instanceof PsiCallExpression) {
return false;
}
return true;
}
public static boolean isPolyExpression(PsiExpression expression) {
if (expression instanceof PsiLambdaExpression || expression instanceof PsiMethodReferenceExpression) {
return true;
}
else if (expression instanceof PsiParenthesizedExpression) {
return isPolyExpression(((PsiParenthesizedExpression)expression).getExpression());
}
else if (expression instanceof PsiNewExpression) {
final PsiJavaCodeReferenceElement classReference = ((PsiNewExpression)expression).getClassReference();
if (classReference != null) {
final PsiReferenceParameterList parameterList = classReference.getParameterList();
if (parameterList != null) {
final PsiTypeElement[] typeElements = parameterList.getTypeParameterElements();
if (typeElements.length == 1 && typeElements[0].getType() instanceof PsiDiamondType) {
return isAssignmentOrInvocationContext(expression.getParent());
}
}
}
} else if (expression instanceof PsiMethodCallExpression) {
if (isAssignmentOrInvocationContext(expression.getParent()) && ((PsiMethodCallExpression)expression).getTypeArguments().length == 0) {
final PsiMethod method = ((PsiMethodCallExpression)expression).resolveMethod();
if (method != null) {
final Set<PsiTypeParameter> typeParameters = new HashSet<PsiTypeParameter>(Arrays.asList(method.getTypeParameters()));
if (typeParameters.size() > 0) {
final PsiType returnType = method.getReturnType();
if (returnType != null) {
return returnTypeMentionsTypeParameters(typeParameters, returnType);
}
}
}
}
}
else if (expression instanceof PsiConditionalExpression) {
final ConditionalKind conditionalKind = isBooleanOrNumeric(expression);
if (conditionalKind == null) {
return isAssignmentOrInvocationContext(expression.getParent());
}
}
return false;
}
private static Boolean returnTypeMentionsTypeParameters(final Set<PsiTypeParameter> typeParameters, PsiType returnType) {
return returnType.accept(new PsiTypeVisitor<Boolean>() {
@Nullable
@Override
public Boolean visitType(PsiType type) {
return false;
}
@Nullable
@Override
public Boolean visitClassType(PsiClassType classType) {
for (PsiType type : classType.getParameters()) {
if (type.accept(this)) return true;
}
final PsiClass psiClass = classType.resolve();
return psiClass instanceof PsiTypeParameter && typeParameters.contains(psiClass);
}
@Nullable
@Override
public Boolean visitArrayType(PsiArrayType arrayType) {
return arrayType.getComponentType().accept(this);
}
});
}
private static boolean isAssignmentOrInvocationContext(PsiElement context) {
return context instanceof PsiExpressionList || isAssignmentContext(context);
}
private static boolean isAssignmentContext(PsiElement context) {
return context instanceof PsiReturnStatement ||
context instanceof PsiAssignmentExpression ||
context instanceof PsiVariable;
}
private enum ConditionalKind {
BOOLEAN, NUMERIC
}
private static ConditionalKind isBooleanOrNumeric(PsiExpression expr) {
if (expr instanceof PsiParenthesizedExpression) {
return isBooleanOrNumeric(((PsiParenthesizedExpression)expr).getExpression());
}
PsiType type = null;
if (expr instanceof PsiNewExpression || hasStandaloneForm(expr)) {
type = expr.getType();
} else if (expr instanceof PsiMethodCallExpression) {
final PsiMethod method = ((PsiMethodCallExpression)expr).resolveMethod();
if (method != null) {
type = method.getReturnType();
}
}
if (TypeConversionUtil.isNumericType(type)) return ConditionalKind.NUMERIC;
if (TypeConversionUtil.isBooleanType(type)) return ConditionalKind.BOOLEAN;
if (expr instanceof PsiConditionalExpression) {
final PsiExpression thenExpression = ((PsiConditionalExpression)expr).getThenExpression();
final PsiExpression elseExpression = ((PsiConditionalExpression)expr).getElseExpression();
final ConditionalKind thenKind = isBooleanOrNumeric(thenExpression);
final ConditionalKind elseKind = isBooleanOrNumeric(elseExpression);
if (thenKind == elseKind) return thenKind;
}
return null;
}
}
@@ -0,0 +1,82 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.daemon.lambda;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
/**
* User: anna
*/
public class PsiPolyExpressionUtilTest extends LightCodeInsightFixtureTestCase {
public void testPrefixExpression() throws Exception {
final PsiExpression psiExpression = findExpression(" int j = i<caret>++;");
assertInstanceOf(psiExpression, PsiPostfixExpression.class);
assertTrue(PsiPolyExpressionUtil.hasStandaloneForm(psiExpression));
assertFalse(PsiPolyExpressionUtil.isPolyExpression(psiExpression));
}
public void testNumericConditionExpression() throws Exception {
final PsiExpression psiExpression = findExpression(" int j = i == 0 <caret>? i + 1 : i - 1;");
assertInstanceOf(psiExpression, PsiConditionalExpression.class);
assertFalse(PsiPolyExpressionUtil.hasStandaloneForm(psiExpression));
assertFalse(PsiPolyExpressionUtil.isPolyExpression(psiExpression));
}
public void testPolyConditionExpression() throws Exception {
myFixture.configureByText("Foo.java", "import java.util.*;" +
"class Foo {" +
" String foo(int i) {" +
" return i == 0 <caret>? bar() : bar();" +
" }" +
" String bar() {return null;}" +
"}");
final PsiElement elementAtCaret = myFixture.getFile().findElementAt(myFixture.getCaretOffset());
assertNotNull(elementAtCaret);
final PsiExpression psiExpression = PsiTreeUtil.getParentOfType(elementAtCaret, PsiExpression.class);
assertInstanceOf(psiExpression, PsiConditionalExpression.class);
assertFalse(PsiPolyExpressionUtil.hasStandaloneForm(psiExpression));
assertTrue(PsiPolyExpressionUtil.isPolyExpression(psiExpression));
}
public void testNewExpressionDiamond() throws Exception {
final PsiExpression psiExpression = findExpression(" List<String> l = new Arr<caret>ayList<>();");
assertInstanceOf(psiExpression, PsiNewExpression.class);
assertFalse(PsiPolyExpressionUtil.hasStandaloneForm(psiExpression));
assertTrue(PsiPolyExpressionUtil.isPolyExpression(psiExpression));
}
public void testNewExpression() throws Exception {
final PsiExpression psiExpression = findExpression(" List<String> l = new Arr<caret>ayList<String>();");
assertInstanceOf(psiExpression, PsiNewExpression.class);
assertFalse(PsiPolyExpressionUtil.hasStandaloneForm(psiExpression));
assertFalse(PsiPolyExpressionUtil.isPolyExpression(psiExpression));
}
private PsiExpression findExpression(String textWithExpression) {
myFixture.configureByText("Foo.java", "import java.util.*;" +
"class Foo {" +
" void foo(int i) {" +
textWithExpression +
" }" +
"}");
final PsiElement elementAtCaret = myFixture.getFile().findElementAt(myFixture.getCaretOffset());
assertNotNull(elementAtCaret);
return PsiTreeUtil.getParentOfType(elementAtCaret, PsiExpression.class);
}
}