mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-21 05:51:25 +07:00
lambda/method refs: inference exception variables (initial)
This commit is contained in:
@@ -15,7 +15,9 @@
|
||||
*/
|
||||
package com.intellij.psi.impl.source.resolve;
|
||||
|
||||
import com.intellij.codeInsight.ExceptionUtil;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.JavaSdkVersion;
|
||||
import com.intellij.openapi.projectRoots.JavaVersionService;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
@@ -32,6 +34,7 @@ import com.intellij.psi.scope.processor.MethodCandidatesProcessor;
|
||||
import com.intellij.psi.scope.processor.MethodResolverProcessor;
|
||||
import com.intellij.psi.scope.util.PsiScopesUtil;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
@@ -1147,6 +1150,10 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
final Pair<PsiType, ConstraintType> inferredExceptionTypeConstraint = inferExceptionConstrains(typeParameter, expression, method, resolveResult.getSubstitutor());
|
||||
if (inferredExceptionTypeConstraint != null) {
|
||||
return inferredExceptionTypeConstraint;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1190,6 +1197,55 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Pair<PsiType, ConstraintType> inferExceptionConstrains(PsiTypeParameter typeParameter,
|
||||
PsiExpression expression,
|
||||
PsiMethod method,
|
||||
PsiSubstitutor substitutor) {
|
||||
final PsiClassType[] declaredExceptions = method.getThrowsList().getReferencedTypes();
|
||||
for (PsiClassType exception : declaredExceptions) {
|
||||
final PsiType substitute = substitutor.substitute(exception);
|
||||
if (PsiUtil.resolveClassInType(substitute) == typeParameter) {
|
||||
if (expression instanceof PsiLambdaExpression) {
|
||||
final PsiElement body = ((PsiLambdaExpression)expression).getBody();
|
||||
if (body != null) {
|
||||
final List<PsiClassType> unhandledExceptions = ExceptionUtil.getUnhandledExceptions(body);
|
||||
if (unhandledExceptions.isEmpty()) {
|
||||
return inferUncheckedException(typeParameter, exception, method);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (expression instanceof PsiMethodReferenceExpression) {
|
||||
final PsiElement resolve = ((PsiMethodReferenceExpression)expression).resolve();
|
||||
if (resolve instanceof PsiMethod) {
|
||||
final PsiClassType[] declaredThrowsList = ((PsiMethod)resolve).getThrowsList().getReferencedTypes();
|
||||
for (PsiClassType psiClassType : declaredThrowsList) {
|
||||
if (!ExceptionUtil.isUncheckedException(psiClassType)) return null;
|
||||
}
|
||||
return inferUncheckedException(typeParameter, exception, method);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Pair<PsiType, ConstraintType> inferUncheckedException(PsiTypeParameter typeParameter,
|
||||
PsiClassType exception,
|
||||
PsiMethod method) {
|
||||
final Project project = typeParameter.getProject();
|
||||
final PsiClass runtimeException = JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_LANG_RUNTIME_EXCEPTION, method.getResolveScope());
|
||||
if (runtimeException != null) {
|
||||
for (PsiType superType : exception.getSuperTypes()) {
|
||||
if (!InheritanceUtil.isInheritorOrSelf(runtimeException, PsiUtil.resolveClassInType(superType), true)) {
|
||||
return getFailedInferenceConstraint(typeParameter);
|
||||
}
|
||||
}
|
||||
return Pair.<PsiType, ConstraintType>create(JavaPsiFacade.getElementFactory(project).createType(runtimeException, PsiSubstitutor.EMPTY), ConstraintType.EQUALS);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Pair<PsiType, ConstraintType> inferFromConditionalExpression(PsiElement parent,
|
||||
PsiExpression methodCall,
|
||||
PsiTypeParameter typeParameter,
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
class Test {
|
||||
interface F<T extends Throwable> {
|
||||
void _(T t) throws T;
|
||||
}
|
||||
<K extends Throwable> void foo(F<K> f) throws K { }
|
||||
|
||||
{
|
||||
foo(<error descr="Cyclic inference">(t)->{}</error>);
|
||||
<error descr="Unhandled exception: java.lang.ClassNotFoundException">foo((ClassNotFoundException t)->{});</error>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
class Test {
|
||||
|
||||
interface F<T extends Throwable> {
|
||||
void _() throws T;
|
||||
}
|
||||
|
||||
void m1() { }
|
||||
void m2() throws NullPointerException{ }
|
||||
<K extends Throwable> void foo(F<K> f) throws K { }
|
||||
|
||||
{
|
||||
foo(()->{});
|
||||
foo(()->{throw new NullPointerException();});
|
||||
|
||||
foo(this::m1);
|
||||
foo(this::m2);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
class Test {
|
||||
interface F<T extends ClassNotFoundException> {
|
||||
void _() throws T;
|
||||
}
|
||||
<K extends ClassNotFoundException> void foo(F<K> f) throws K { }
|
||||
|
||||
{
|
||||
<error descr="Unhandled exception: K">foo(() -> {});</error>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
class Test {
|
||||
|
||||
interface F<T extends Throwable> {
|
||||
void _() throws T;
|
||||
}
|
||||
|
||||
void m1() { }
|
||||
void m2() throws ClassNotFoundException { }
|
||||
void m3() throws Exception { }
|
||||
|
||||
<K extends Throwable> void foo1(F<K> f) throws K { }
|
||||
<K extends ClassNotFoundException> void foo2(F<K> f) throws K { }
|
||||
|
||||
{
|
||||
<error descr="Inferred type 'java.lang.RuntimeException' for type parameter 'K' is not within its bound; should extend 'java.lang.ClassNotFoundException'">foo2(()->{})</error>;
|
||||
foo2(()->{ <error descr="Unhandled exception: java.lang.ClassNotFoundException">throw new ClassNotFoundException();</error> });
|
||||
|
||||
<error descr="Inferred type 'java.lang.RuntimeException' for type parameter 'K' is not within its bound; should extend 'java.lang.ClassNotFoundException'">foo2(this::m1)</error>;
|
||||
foo2(<error descr="Unhandled exception: java.lang.ClassNotFoundException">this::m2</error>);
|
||||
|
||||
|
||||
|
||||
foo1(()->{ <error descr="Unhandled exception: java.lang.ClassNotFoundException">throw new ClassNotFoundException();</error> });
|
||||
foo1(()->{ <error descr="Unhandled exception: java.lang.Exception">throw new Exception();</error> });
|
||||
|
||||
foo1(<error descr="Unhandled exception: java.lang.ClassNotFoundException">this::m2</error>);
|
||||
foo1(<error descr="Unhandled exception: java.lang.Exception">this::m3</error>);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 org.jetbrains.annotations.NonNls;
|
||||
|
||||
public class ExceptionVariablesInferenceTest extends LightDaemonAnalyzerTestCase {
|
||||
@NonNls static final String BASE_PATH = "/codeInsight/daemonCodeAnalyzer/lambda/exceptions";
|
||||
|
||||
public void testUnboundExceptions() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testNotThrownLambdaBodyReferencedMethod() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testNotThrownLambdaBodyReferencedMethodContradictBounds() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testLambdaParamExceptionVariable() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTest() throws Exception {
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
private void doTest(final boolean checkWarnings) throws Exception {
|
||||
doTest(BASE_PATH + "/" + getTestName(false) + ".java", checkWarnings, false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user