java 8 initial graph inference: allow to infer type information from siblings and parent calls

This commit is contained in:
anna
2013-02-08 11:27:04 +01:00
parent 0ac39239b2
commit a9c4a9c5ab
11 changed files with 226 additions and 9 deletions

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}

View File

@@ -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));
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}