mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-21 05:51:25 +07:00
lambda: inference param types for assignment and cast context
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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"); };
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
};
|
||||
}
|
||||
@@ -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>
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user