mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-05-01 02:38:59 +07:00
java 8 initial graph inference: allow to infer type information from siblings and parent calls
This commit is contained in:
@@ -27,7 +27,7 @@ public class DefaultParameterTypeInferencePolicy extends ParameterTypeInferenceP
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Pair<PsiType, ConstraintType> inferTypeConstraintFromCallContext(PsiCallExpression innerMethodCall,
|
||||
public Pair<PsiType, ConstraintType> inferTypeConstraintFromCallContext(PsiExpression innerMethodCall,
|
||||
PsiExpressionList parent,
|
||||
PsiCallExpression contextCall,
|
||||
PsiTypeParameter typeParameter) {
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
*/
|
||||
public abstract class ParameterTypeInferencePolicy {
|
||||
@Nullable
|
||||
public abstract Pair<PsiType, ConstraintType> inferTypeConstraintFromCallContext(PsiCallExpression innerMethodCall,
|
||||
public abstract Pair<PsiType, ConstraintType> inferTypeConstraintFromCallContext(PsiExpression innerMethodCall,
|
||||
PsiExpressionList parent,
|
||||
PsiCallExpression contextCall,
|
||||
PsiTypeParameter typeParameter);
|
||||
|
||||
@@ -36,17 +36,18 @@ public class ProcessCandidateParameterTypeInferencePolicy extends DefaultParamet
|
||||
public static final ProcessCandidateParameterTypeInferencePolicy INSTANCE = new ProcessCandidateParameterTypeInferencePolicy();
|
||||
|
||||
@Override
|
||||
public Pair<PsiType, ConstraintType> inferTypeConstraintFromCallContext(PsiCallExpression innerMethodCall,
|
||||
public Pair<PsiType, ConstraintType> inferTypeConstraintFromCallContext(PsiExpression innerMethodCall,
|
||||
PsiExpressionList expressionList,
|
||||
PsiCallExpression contextCall,
|
||||
PsiTypeParameter typeParameter) {
|
||||
PsiExpression[] expressions = expressionList.getExpressions();
|
||||
int i = ArrayUtil.find(expressions, innerMethodCall);
|
||||
if (i < 0) return null;
|
||||
|
||||
final MethodCandidatesProcessor processor = new MethodCandidatesProcessor(contextCall);
|
||||
try {
|
||||
//can't call resolve() since it obtains full substitution, that may result in infinite recursion
|
||||
PsiScopesUtil.setupAndRunProcessor(processor, contextCall, false);
|
||||
PsiExpression[] expressions = expressionList.getExpressions();
|
||||
int i = ArrayUtil.find(expressions, innerMethodCall);
|
||||
if (i < 0) return null;
|
||||
final JavaResolveResult[] results = processor.getResult();
|
||||
PsiMethod owner = (PsiMethod)typeParameter.getOwner();
|
||||
if (owner == null) return null;
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.intellij.psi.impl.source.resolve;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.projectRoots.JavaSdkVersion;
|
||||
import com.intellij.openapi.projectRoots.JavaVersionService;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.*;
|
||||
@@ -32,6 +33,7 @@ import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.ArrayUtilRt;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -45,6 +47,7 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.resolve.PsiResolveHelperImpl");
|
||||
public static final Pair<PsiType,ConstraintType> RAW_INFERENCE = new Pair<PsiType, ConstraintType>(null, ConstraintType.EQUALS);
|
||||
private final PsiManager myManager;
|
||||
public static final Key<PsiCallExpression> CALL_EXPRESSION_KEY = Key.create("methodCall");
|
||||
|
||||
public PsiResolveHelperImpl(PsiManager manager) {
|
||||
myManager = manager;
|
||||
@@ -733,6 +736,8 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
|
||||
} else if (exprType instanceof PsiLambdaExpressionType) {
|
||||
return inferConstraintFromFunctionalInterfaceMethod(typeParam, ((PsiLambdaExpressionType)exprType).getExpression(), returnType,
|
||||
lowerBound);
|
||||
} else if (exprType == null && independent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (exprType == null){
|
||||
@@ -933,6 +938,12 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
|
||||
final PsiTypeParameter typeParameter,
|
||||
PsiSubstitutor substitutor,
|
||||
ParameterTypeInferencePolicy policy) {
|
||||
final PsiCallExpression preparedKey = methodCall.getCopyableUserData(CALL_EXPRESSION_KEY);
|
||||
if (preparedKey != null) {
|
||||
parent = parent.getContext();
|
||||
methodCall = preparedKey;
|
||||
}
|
||||
|
||||
Pair<PsiType, ConstraintType> constraint = null;
|
||||
PsiType expectedType = null;
|
||||
|
||||
@@ -962,9 +973,14 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
|
||||
}
|
||||
else if (parent instanceof PsiExpressionList) {
|
||||
final PsiElement pParent = parent.getParent();
|
||||
if (pParent instanceof PsiCallExpression && parent.equals(((PsiCallExpression)pParent).getArgumentList())) {
|
||||
constraint = policy.inferTypeConstraintFromCallContext(methodCall, (PsiExpressionList)parent, (PsiCallExpression)pParent,
|
||||
typeParameter);
|
||||
if (pParent instanceof PsiCallExpression && parent.equals(((PsiCallExpression)pParent).getArgumentList())) {
|
||||
constraint = policy.inferTypeConstraintFromCallContext(methodCall, (PsiExpressionList)parent, (PsiCallExpression)pParent, typeParameter);
|
||||
if (constraint == null && PsiUtil.isLanguageLevel8OrHigher(methodCall)) {
|
||||
constraint = graphInferenceFromCallContext(methodCall, typeParameter, (PsiCallExpression)pParent);
|
||||
if (constraint != null && constraint.getFirst().equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) {
|
||||
constraint = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (parent instanceof PsiLambdaExpression) {
|
||||
expectedType = LambdaUtil.getFunctionalInterfaceReturnType(((PsiLambdaExpression)parent).getFunctionalInterfaceType());
|
||||
@@ -1025,6 +1041,9 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (preparedKey != null) {
|
||||
return getFailedInferenceConstraint(typeParameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1063,4 +1082,27 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Pair<PsiType, ConstraintType> graphInferenceFromCallContext(@NotNull PsiCallExpression methodCall,
|
||||
@NotNull PsiTypeParameter typeParameter,
|
||||
@NotNull PsiCallExpression parentCall) {
|
||||
final PsiExpressionList argumentList = parentCall.getArgumentList();
|
||||
LOG.assertTrue(argumentList != null);
|
||||
final int exprIdx = ArrayUtilRt.find(argumentList.getExpressions(), PsiUtil.skipParenthesizedExprUp(methodCall));
|
||||
|
||||
if (exprIdx > -1) {
|
||||
final PsiExpression nullPlaceholder = JavaPsiFacade.getElementFactory(methodCall.getProject()).createExpressionFromText("null", methodCall);
|
||||
|
||||
final PsiCallExpression copy = (PsiCallExpression)parentCall.copy();
|
||||
final PsiExpressionList copyArgumentList = copy.getArgumentList();
|
||||
LOG.assertTrue(copyArgumentList != null);
|
||||
final PsiExpression currentCallInCopy = copyArgumentList.getExpressions()[exprIdx];
|
||||
|
||||
final PsiExpression methodCallCopy = (PsiExpression)currentCallInCopy.replace(nullPlaceholder);
|
||||
copy.putCopyableUserData(CALL_EXPRESSION_KEY, parentCall);
|
||||
return ProcessCandidateParameterTypeInferencePolicy.INSTANCE
|
||||
.inferTypeConstraintFromCallContext(methodCallCopy, copyArgumentList, copy, typeParameter);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import java.util.*;
|
||||
|
||||
class Main {
|
||||
|
||||
void foo(List<Integer> list) {
|
||||
bar(list, i -> i.intValue(), i -> i.<error descr="Cannot resolve method 'unknown()'">unknown</error>());
|
||||
bar1(list, i -> i.intValue(), i -> i.<error descr="Cannot resolve method 'unknown()'">unknown</error>());
|
||||
}
|
||||
|
||||
<U, S_IN, S_OUT, R> R bar(List<S_IN> list,
|
||||
Fun<S_IN, S_OUT> f1,
|
||||
Fun<S_OUT, R> f2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
<R, S_IN, S_OUT> R bar1(List<S_IN> list,
|
||||
Fun<S_IN, S_OUT> f1,
|
||||
Fun<S_OUT, R> f2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public interface Fun<T, R> {
|
||||
public R _(T t);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
class Main {
|
||||
|
||||
<R> void bar(Fun<Integer, R> collector) { }
|
||||
|
||||
<T, D> Fun<T, Integer> foo(D d) { return null; }
|
||||
|
||||
public void test() {
|
||||
bar(foo(""));
|
||||
}
|
||||
|
||||
interface Fun<T, R> {
|
||||
R _(T t);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import java.util.*;
|
||||
|
||||
class Main {
|
||||
|
||||
void test(List<Integer> li) {
|
||||
Fun<Stream<Integer>, Stream<Integer>> f = s -> s.substr(0);
|
||||
foo(li, f, Collections.emptyList());
|
||||
|
||||
foo(li, s -> s.substr(0), Collections.emptyList());
|
||||
}
|
||||
|
||||
<T, U, S_OUT extends Stream<U>, It extends Iterable<U>> Collection<U>
|
||||
foo(Collection<T> coll, Fun<Stream<T>, S_OUT> f, It it) {
|
||||
return null;
|
||||
}
|
||||
|
||||
interface Stream<T> {
|
||||
Stream<T> substr(long startingOffset);
|
||||
}
|
||||
|
||||
interface Fun<T, R> {
|
||||
R _(T t);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import java.util.*;
|
||||
public class Main {
|
||||
public static <T> T foo() {return null;}
|
||||
|
||||
public static <B> List<B> bar(B b) {return null;}
|
||||
static {
|
||||
List<String> s = bar(foo());
|
||||
}
|
||||
|
||||
|
||||
public static <B> B bar1(B b) {return null;}
|
||||
static {
|
||||
String s1 = bar1(foo());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
class Main {
|
||||
static <T> T foo(T t) { return null; }
|
||||
|
||||
static {
|
||||
long l1 = foo(foo(1));
|
||||
Integer i = 1;
|
||||
long l2 = foo(foo(i));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import java.util.*;
|
||||
class Main {
|
||||
|
||||
<T, R> Collector<T, R> m(Supplier<? extends R> supplier, BiConsumer<R, T> accumulator) {
|
||||
return null;
|
||||
}
|
||||
|
||||
<T, C extends Collection<T>> Collector<T, C> test1(Supplier<C> collectionFactory) {
|
||||
return m(collectionFactory, Collection::add);
|
||||
}
|
||||
|
||||
Collector<String, StringBuilder> test2(Supplier<StringBuilder> sb) {
|
||||
return m(sb, StringBuilder::append);
|
||||
}
|
||||
|
||||
interface Supplier<T> {
|
||||
public T get();
|
||||
}
|
||||
|
||||
interface Collector<T, R> {
|
||||
}
|
||||
|
||||
interface BiConsumer<T, U> {
|
||||
void accept(T t, U u);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.codeInsight.daemon.LightDaemonAnalyzerTestCase;
|
||||
import com.intellij.openapi.projectRoots.JavaSdkVersion;
|
||||
import com.intellij.openapi.projectRoots.JavaVersionService;
|
||||
import com.intellij.openapi.projectRoots.JavaVersionServiceImpl;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
|
||||
public class GraphInferenceHighlightingTest extends LightDaemonAnalyzerTestCase {
|
||||
@NonNls static final String BASE_PATH = "/codeInsight/daemonCodeAnalyzer/lambda/graphInference";
|
||||
|
||||
public void testNestedCalls() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testNestedCallsSameMethod() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testChainedInference() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testChainedInference1() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testReturnStmt() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testInferenceFromSiblings() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTest() throws Exception {
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
private void doTest(final boolean checkWarnings) throws Exception {
|
||||
((JavaVersionServiceImpl)JavaVersionService.getInstance()).setTestVersion(JavaSdkVersion.JDK_1_8, getTestRootDisposable());
|
||||
doTest(BASE_PATH + "/" + getTestName(false) + ".java", checkWarnings, false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user