lambda: inference param types for assignment and cast context

This commit is contained in:
anna
2012-07-19 12:15:41 +02:00
parent 6b99ef5e9e
commit 5bb4a61bb9
9 changed files with 156 additions and 38 deletions

View File

@@ -2630,8 +2630,11 @@ public class HighlightUtil {
public static HighlightInfo checkLambdaFeature(final PsiLambdaExpression expression) {
final HighlightInfo info = checkFeature(expression, Feature.LAMBDA_EXPRESSIONS);
if (info != null) return info;
// todo[r.sh] stub; remove after implementing support in TypeConversionUtil
final String message = "Lambda expressions type check is not yet implemented";
return HighlightInfo.createHighlightInfo(HighlightInfoType.WEAK_WARNING, expression, message);
if (expression.getParent() instanceof PsiExpressionList) {
// todo[r.sh] stub; remove after implementing support in TypeConversionUtil
final String message = "Lambda expressions type check is not yet implemented";
return HighlightInfo.createHighlightInfo(HighlightInfoType.WEAK_WARNING, expression, message);
}
return null;
}
}

View File

@@ -36,13 +36,12 @@ public class JavaHighlightInfoFilter implements HighlightInfoFilter {
final PsiElement element = file.findElementAt(info.getStartOffset());
if (element == null) return true;
return !isInsideLambda(element) &&
!isLambdaInAmbiguousCall(info, element);
}
private static boolean isInsideLambda(final PsiElement element) {
return PsiTreeUtil.getParentOfType(element, PsiLambdaExpression.class, false) != null;
final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(element, PsiLambdaExpression.class, false);
if (lambdaExpression != null) {
if (lambdaExpression.getParent() instanceof PsiExpressionList) return false;
if (isLambdaInAmbiguousCall(info, element)) return false;
}
return true;
}
private static boolean isLambdaInAmbiguousCall(final HighlightInfo info, final PsiElement element) {

View File

@@ -29,6 +29,7 @@ import com.intellij.psi.impl.java.stubs.PsiParameterStub;
import com.intellij.psi.impl.source.tree.ChildRole;
import com.intellij.psi.impl.source.tree.CompositeElement;
import com.intellij.psi.impl.source.tree.JavaSharedImplUtil;
import com.intellij.psi.impl.source.tree.java.LambdaUtil;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.SearchScope;
import com.intellij.ui.RowIcon;
@@ -121,7 +122,7 @@ public class PsiParameterImpl extends JavaStubPsiElement<PsiParameterStub> imple
final PsiTypeElement typeElement = getTypeElement();
if (typeElement == null && isLambdaParameter()) {
return new PsiLambdaParameterType(this);
return LambdaUtil.getLambdaParameterType(this);
}
return JavaSharedImplUtil.getType(typeElement, getNameIdentifier(), this);

View File

@@ -16,36 +16,25 @@
package com.intellij.psi.impl.source.tree.java;
import com.intellij.psi.*;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.MethodSignatureUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.psi.util.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* User: anna
* Date: 7/17/12
*/
public class FunctionalInterfaceUtil {
public class LambdaUtil {
@Nullable
public static String checkInterfaceFunctional(@NotNull PsiClass psiClass) {
if (psiClass.isInterface()) {
final List<MethodSignature> methods = new ArrayList<MethodSignature>();
final PsiMethod[] psiClassMethods = psiClass.getAllMethods();
for (PsiMethod psiMethod : psiClassMethods) {
if (!psiMethod.hasModifierProperty(PsiModifier.ABSTRACT)) continue;
final PsiClass methodContainingClass = psiMethod.getContainingClass();
if (!overridesPublicObjectMethod(psiMethod)) {
methods.add(getMethodSignature(psiMethod, psiClass, methodContainingClass));
}
}
return hasSubsignature(methods);
}
return "Target type of a lambda conversion must be an interface";
final List<MethodSignature> signatures = findFunctionCandidates(psiClass);
if (signatures == null) return "Target type of a lambda conversion must be an interface";
if (signatures.isEmpty()) return "No target method found";
return signatures.size() == 1 ? null : "Multiple non-overriding abstract methods found";
}
private static boolean overridesPublicObjectMethod(PsiMethod psiMethod) {
@@ -74,7 +63,7 @@ public class FunctionalInterfaceUtil {
}
@Nullable
private static String hasSubsignature(List<MethodSignature> signatures) {
private static List<MethodSignature> hasSubsignature(List<MethodSignature> signatures) {
for (MethodSignature signature : signatures) {
boolean subsignature = true;
for (MethodSignature methodSignature : signatures) {
@@ -85,9 +74,66 @@ public class FunctionalInterfaceUtil {
}
}
}
if (subsignature) return null;
if (subsignature) return Collections.singletonList(signature);
}
if (signatures.isEmpty()) return "No target method found";
return signatures.size() == 1 ? null : "Multiple non-overriding abstract methods found";
return signatures;
}
@Nullable
private static List<MethodSignature> findFunctionCandidates(PsiClass psiClass) {
if (psiClass.isInterface()) {
final List<MethodSignature> methods = new ArrayList<MethodSignature>();
final PsiMethod[] psiClassMethods = psiClass.getAllMethods();
for (PsiMethod psiMethod : psiClassMethods) {
if (!psiMethod.hasModifierProperty(PsiModifier.ABSTRACT)) continue;
final PsiClass methodContainingClass = psiMethod.getContainingClass();
if (!overridesPublicObjectMethod(psiMethod)) {
methods.add(getMethodSignature(psiMethod, psiClass, methodContainingClass));
}
}
return hasSubsignature(methods);
}
return null;
}
@Nullable
public static MethodSignature getFunction(PsiClass psiClass) {
final List<MethodSignature> functions = findFunctionCandidates(psiClass);
if (functions != null && functions.size() == 1) {
return functions.get(0);
}
return null;
}
public static PsiType getLambdaParameterType(PsiParameter param) {
final PsiElement paramParent = param.getParent();
if (paramParent instanceof PsiParameterList) {
final int parameterIndex = ((PsiParameterList)paramParent).getParameterIndex(param);
if (parameterIndex > -1) {
final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(param, PsiLambdaExpression.class);
if (lambdaExpression != null) {
final PsiElement parent = lambdaExpression.getParent();
PsiType type = null;
if (parent instanceof PsiTypeCastExpression) {
type = ((PsiTypeCastExpression)parent).getType();
} else if (parent instanceof PsiVariable) {
type = ((PsiVariable)parent).getType();
}
if (type instanceof PsiClassType) {
final PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)type).resolveGenerics();
final PsiClass psiClass = resolveResult.getElement();
if (psiClass != null) {
final MethodSignature methodSignature = getFunction(psiClass);
if (methodSignature != null) {
return resolveResult.getSubstitutor().substitute(methodSignature.getParameterTypes()[parameterIndex]);
}
}
}
}
}
}
return new PsiLambdaParameterType(param);
}
}

View File

@@ -27,13 +27,13 @@ class C {
}
void test() {
Simplest simplest = <weak_warning descr="Lambda expressions type check is not yet implemented">() -> { }</weak_warning>;
Simplest simplest = () -> { };
use(<weak_warning descr="Lambda expressions type check is not yet implemented">() -> { }</weak_warning>);
IntParser intParser = <weak_warning descr="Lambda expressions type check is not yet implemented">(String s) -> Integer.parseInt(s)</weak_warning>;
IntParser intParser = (String s) -> Integer.parseInt(s);
}
Runnable foo() {
return <weak_warning descr="Lambda expressions type check is not yet implemented">() -> { System.out.println("foo"); }</weak_warning>;
return () -> { System.out.println("foo"); };
}
}

View File

@@ -0,0 +1,12 @@
interface I {
void m(int i);
}
class Foo {
I ii = (@<error descr="'@Override' not applicable to type use">Override</error> final int k)->{
int j = k;
};
I ii1 = (final int k)->{
<error descr="Incompatible types. Found: 'int', required: 'java.lang.String'">String s = k;</error>
};
}

View File

@@ -0,0 +1,23 @@
import java.lang.String;
interface I {
void m(int i);
}
interface A<B> {
void foo(B b);
}
class Foo {
I ii = (final <error descr="Cannot resolve symbol 'k'">k</error><error descr="Identifier expected">)</error>->{};
I ii1 = (k)->{
int i = k;
<error descr="Incompatible types. Found: 'int', required: 'java.lang.String'">String s = k;</error>
};
A<String> a = (ab) -> {
String s = ab;
<error descr="Incompatible types. Found: 'java.lang.String', required: 'int'">int i = ab;</error>
};
}

View File

@@ -16,7 +16,7 @@
package com.intellij.codeInsight.daemon;
import com.intellij.psi.PsiClass;
import com.intellij.psi.impl.source.tree.java.FunctionalInterfaceUtil;
import com.intellij.psi.impl.source.tree.java.LambdaUtil;
import com.intellij.psi.search.GlobalSearchScope;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
@@ -30,7 +30,7 @@ public class FunctionalInterfaceTest extends LightDaemonAnalyzerTestCase {
final PsiClass psiClass = getJavaFacade().findClass("Foo", GlobalSearchScope.projectScope(getProject()));
assertNotNull("Class Foo not found", psiClass);
final String errorMessage = FunctionalInterfaceUtil.checkInterfaceFunctional(psiClass);
final String errorMessage = LambdaUtil.checkInterfaceFunctional(psiClass);
assertEquals(expectedErrorMessage, errorMessage);
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2000-2012 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;
import org.jetbrains.annotations.NonNls;
public class LambdaParamsTest extends LightDaemonAnalyzerTestCase {
@NonNls static final String BASE_PATH = "/codeInsight/daemonCodeAnalyzer/lambda/params";
public void testFormalParams() throws Exception {
doTest();
}
public void testInferredParams() throws Exception {
doTest();
}
private void doTest() throws Exception {
doTest(BASE_PATH + "/" + getTestName(false) + ".java", false, false);
}
}