NI: resolve try catch as synthetic function call (#KT-25435 fixed)

This commit is contained in:
Dmitriy Novozhilov
2019-01-31 14:00:18 +03:00
parent 4e64b07727
commit a236ad5686
8 changed files with 224 additions and 44 deletions
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.resolve;
import com.google.common.collect.ImmutableMap;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
import kotlin.annotations.jvm.ReadOnly;
import org.jetbrains.annotations.NotNull;
@@ -15,7 +16,6 @@ import org.jetbrains.kotlin.cfg.LeakingThisDescriptor;
import org.jetbrains.kotlin.cfg.TailRecursionKind;
import org.jetbrains.kotlin.contracts.description.InvocationKind;
import org.jetbrains.kotlin.contracts.model.Computation;
import org.jetbrains.kotlin.contracts.model.Functor;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.kotlin.name.FqName;
@@ -264,6 +264,8 @@ public interface BindingContext {
WritableSlice<KtExpression, PrimitiveNumericComparisonInfo> PRIMITIVE_NUMERIC_COMPARISON_INFO = Slices.createSimpleSlice();
WritableSlice<KtExpression, Ref<VariableDescriptor>> NEW_INFERENCE_CATCH_EXCEPTION_PARAMETER = Slices.createSimpleSlice();
@SuppressWarnings("UnusedDeclaration")
@Deprecated // This field is needed only for the side effects of its initializer
Void _static_initializer = BasicWritableSlice.initSliceDebugNames(BindingContext.class);
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.referenceExpression
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.BindingContext.NEW_INFERENCE_CATCH_EXCEPTION_PARAMETER
import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver
import org.jetbrains.kotlin.resolve.calls.CallTransformer
import org.jetbrains.kotlin.resolve.calls.KotlinCallResolver
@@ -40,9 +41,7 @@ import org.jetbrains.kotlin.resolve.calls.util.CallMaker
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.resolve.scopes.SyntheticScopes
import org.jetbrains.kotlin.resolve.scopes.*
import org.jetbrains.kotlin.resolve.scopes.receivers.*
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.expressions.*
@@ -632,6 +631,7 @@ class PSICallResolver(
val context = outerCallContext.replaceContextDependency(ContextDependency.DEPENDENT)
.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE).replaceDataFlowInfo(startDataFlowInfo)
.expandContextForCatchClause(ktExpression)
if (ktExpression is KtCallableReferenceExpression) {
checkNoSpread(outerCallContext, valueArgument)
@@ -683,4 +683,20 @@ class PSICallResolver(
val typeInfo = expressionTypingServices.getTypeInfo(argumentExpression, context)
return createSimplePSICallArgument(context, valueArgument, typeInfo) ?: parseErrorArgument
}
private fun BasicCallResolutionContext.expandContextForCatchClause(ktExpression: Any): BasicCallResolutionContext {
if (ktExpression !is KtExpression) return this
val variableDescriptorHolder = trace.bindingContext[NEW_INFERENCE_CATCH_EXCEPTION_PARAMETER, ktExpression] ?: return this
val variableDescriptor = variableDescriptorHolder.get() ?: return this
variableDescriptorHolder.set(null)
val redeclarationChecker = expressionTypingServices.createLocalRedeclarationChecker(trace)
val catchScope = with(scope) {
LexicalWritableScope(this, ownerDescriptor, false, redeclarationChecker, LexicalScopeKind.CATCH)
}
catchScope.addVariableDescriptor(variableDescriptor)
return replaceScope(catchScope)
}
}
@@ -22,6 +22,7 @@ import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.util.PsiTreeUtil;
import kotlin.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
@@ -35,6 +36,7 @@ import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl;
import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingContextUtils;
import org.jetbrains.kotlin.resolve.BindingTrace;
import org.jetbrains.kotlin.resolve.calls.CallResolver;
@@ -67,7 +69,7 @@ public class ControlStructureTypingUtils {
private static final Logger LOG = Logger.getInstance(ControlStructureTypingUtils.class);
public enum ResolveConstruct {
IF("if"), ELVIS("elvis"), EXCL_EXCL("ExclExcl"), WHEN("when");
IF("if"), ELVIS("elvis"), EXCL_EXCL("ExclExcl"), WHEN("when"), TRY("try");
private final String name;
private final Name specialFunctionName;
@@ -116,6 +118,39 @@ public class ControlStructureTypingUtils {
) {
SimpleFunctionDescriptorImpl function = createFunctionDescriptorForSpecialConstruction(
construct, argumentNames, isArgumentNullable);
return resolveSpecialConstructionAsCall(call, function, construct, context, dataFlowInfoForArguments);
}
/*package*/ ResolvedCall<FunctionDescriptor> resolveTryAsCall(
@NotNull Call call,
@NotNull KtTryExpression tryExpression,
@NotNull List<Pair<KtExpression, VariableDescriptor>> catchedExceptions,
@NotNull ExpressionTypingContext context,
@Nullable MutableDataFlowInfoForArguments dataFlowInfoForArguments
) {
List<String> argumentNames = Lists.newArrayList("tryBlock");
List<Boolean> argumentsNullability = Lists.newArrayList(false);
for (int i = 0; i < tryExpression.getCatchClauses().size(); i++) {
argumentNames.add("catchBlock" + i);
argumentsNullability.add(false);
}
SimpleFunctionDescriptorImpl function =
createFunctionDescriptorForSpecialConstruction(ResolveConstruct.TRY, argumentNames, argumentsNullability);
for (Pair<KtExpression, VariableDescriptor> descriptorPair : catchedExceptions) {
KtExpression catchBlock = descriptorPair.getFirst();
VariableDescriptor catchedExceptionDescriptor = descriptorPair.getSecond();
context.trace.record(BindingContext.NEW_INFERENCE_CATCH_EXCEPTION_PARAMETER, catchBlock, Ref.create(catchedExceptionDescriptor));
}
return resolveSpecialConstructionAsCall(call, function, ResolveConstruct.TRY, context, dataFlowInfoForArguments);
}
private ResolvedCall<FunctionDescriptor> resolveSpecialConstructionAsCall(
@NotNull Call call,
@NotNull SimpleFunctionDescriptorImpl function,
@NotNull ResolveConstruct construct,
@NotNull ExpressionTypingContext context,
@Nullable MutableDataFlowInfoForArguments dataFlowInfoForArguments
) {
TracingStrategy tracing = createTracingForSpecialConstruction(call, construct.getName(), context);
TypeSubstitutor knownTypeParameterSubstitutor = createKnownTypeParameterSubstitutorForSpecialCall(construct, function, context.expectedType, context.languageVersionSettings);
ResolutionCandidate<FunctionDescriptor> resolutionCandidate =
@@ -252,6 +287,20 @@ public class ControlStructureTypingUtils {
return createIndependentDataFlowInfoForArgumentsForCall(subjectDataFlowInfo, dataFlowInfoForArgumentsMap);
}
public static MutableDataFlowInfoForArguments createDataFlowInfoForArgumentsOfTryCall(
@NotNull Call callForTry,
@NotNull DataFlowInfo dataFlowInfoBeforeTry,
@NotNull DataFlowInfo dataFlowInfoAfterTry
) {
Map<ValueArgument, DataFlowInfo> dataFlowInfoForArgumentsMap = new HashMap<>();
List<? extends ValueArgument> valueArguments = callForTry.getValueArguments();
dataFlowInfoForArgumentsMap.put(valueArguments.get(0), dataFlowInfoBeforeTry);
for (int i = 1; i < valueArguments.size(); i++) {
dataFlowInfoForArgumentsMap.put(valueArguments.get(i), dataFlowInfoAfterTry);
}
return createIndependentDataFlowInfoForArgumentsForCall(dataFlowInfoBeforeTry, dataFlowInfoForArgumentsMap);
}
/*package*/ static Call createCallForSpecialConstruction(
@NotNull KtExpression expression,
@NotNull KtExpression calleeExpression,
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.types.expressions;
import com.google.common.collect.Lists;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement;
import kotlin.collections.CollectionsKt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
@@ -47,13 +48,13 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import static org.jetbrains.kotlin.diagnostics.Errors.*;
import static org.jetbrains.kotlin.resolve.BindingContext.*;
import static org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT;
import static org.jetbrains.kotlin.types.TypeUtils.*;
import static org.jetbrains.kotlin.types.expressions.ControlStructureTypingUtils.createCallForSpecialConstruction;
import static org.jetbrains.kotlin.types.expressions.ControlStructureTypingUtils.createDataFlowInfoForArgumentsForIfCall;
import static org.jetbrains.kotlin.types.expressions.ControlStructureTypingUtils.*;
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.*;
public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
@@ -489,6 +490,9 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
@Override
public KotlinTypeInfo visitTryExpression(@NotNull KtTryExpression expression, ExpressionTypingContext typingContext) {
if (typingContext.languageVersionSettings.supportsFeature(LanguageFeature.NewInference)) {
return resolveTryExpressionWithNewInference(expression, typingContext);
}
ExpressionTypingContext context = typingContext.replaceContextDependency(INDEPENDENT);
KtExpression tryBlock = expression.getTryBlock();
List<KtCatchClause> catchClauses = expression.getCatchClauses();
@@ -500,15 +504,8 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
KtExpression catchBody = catchClause.getCatchBody();
boolean nothingInCatchBranch = false;
if (catchParameter != null) {
checkCatchParameterDeclaration(catchParameter, context);
VariableDescriptor variableDescriptor = resolveAndCheckCatchParameter(catchParameter, context);
VariableDescriptor variableDescriptor = components.descriptorResolver.resolveLocalVariableDescriptor(
context.scope, catchParameter, context.trace);
KotlinType catchParameterType = variableDescriptor.getType();
checkCatchParameterType(catchParameter, catchParameterType, context);
KotlinType throwableType = components.builtIns.getThrowable().getDefaultType();
components.dataFlowAnalyzer.checkType(catchParameterType, catchParameter, context.replaceExpectedType(throwableType));
if (catchBody != null) {
LexicalWritableScope catchScope = newWritableScopeImpl(context, LexicalScopeKind.CATCH, components.overloadChecker);
catchScope.addVariableDescriptor(variableDescriptor);
@@ -527,15 +524,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
}
KotlinTypeInfo tryResult = facade.getTypeInfo(tryBlock, context);
ExpressionTypingContext tryOutputContext = context.replaceExpectedType(NO_EXPECTED_TYPE);
if (!nothingInAllCatchBranches &&
facade.getComponents().languageVersionSettings.supportsFeature(LanguageFeature.SoundSmartCastsAfterTry)) {
PreliminaryLoopVisitor tryVisitor = PreliminaryLoopVisitor.visitTryBlock(expression);
tryOutputContext = tryOutputContext.replaceDataFlowInfo(
tryVisitor.clearDataFlowInfoForAssignedLocalVariables(tryOutputContext.dataFlowInfo,
components.languageVersionSettings)
);
}
ExpressionTypingContext tryOutputContext = getCleanedContextFromTryWithAssignmentsToVar(expression, nothingInAllCatchBranches, context);
KotlinTypeInfo result = TypeInfoFactoryKt.noTypeInfo(tryOutputContext);
if (finallyBlock != null) {
@@ -557,6 +546,129 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
}
}
private KotlinTypeInfo resolveTryExpressionWithNewInference(@NotNull KtTryExpression tryExpression, ExpressionTypingContext tryInputContext) {
// tryInputContext is an ExpressionTypingContext before try/catch expression
KtBlockExpression tryBlock = tryExpression.getTryBlock();
List<KtCatchClause> catchClauses = tryExpression.getCatchClauses();
KtFinallySection finallySection = tryExpression.getFinallyBlock();
DataFlowInfo dataFlowInfoBeforeTry = tryInputContext.dataFlowInfo;
List<KtExpression> catchBlocks = Lists.newArrayList();
List<kotlin.Pair<KtExpression, VariableDescriptor>> catchClausesBlocksAndParameters = Lists.newArrayList();
for (KtCatchClause catchClause : catchClauses) {
KtParameter catchParameter = catchClause.getCatchParameter();
KtExpression catchBody = catchClause.getCatchBody();
if (catchParameter != null) {
VariableDescriptor variableDescriptor = resolveAndCheckCatchParameter(catchParameter, tryInputContext);
if (catchBody != null) {
catchBlocks.add(catchBody);
catchClausesBlocksAndParameters.add(new kotlin.Pair<>(catchBody, variableDescriptor));
}
}
}
KtBlockExpression finallyBlock = null;
if (finallySection != null) {
finallyBlock = finallySection.getFinalExpression();
}
List<KtExpression> arguments = Lists.newArrayList(tryBlock);
arguments.addAll(catchBlocks);
Call callForTry = createCallForSpecialConstruction(tryExpression, tryExpression, arguments);
MutableDataFlowInfoForArguments dataFlowInfoForArguments = createDataFlowInfoForArgumentsOfTryCall(callForTry, dataFlowInfoBeforeTry, dataFlowInfoBeforeTry);
ResolvedCall<FunctionDescriptor> resolvedCall = components.controlStructureTypingUtils
.resolveTryAsCall(callForTry, tryExpression, catchClausesBlocksAndParameters, tryInputContext, dataFlowInfoForArguments);
KotlinType resultType = resolvedCall.getResultingDescriptor().getReturnType();
BindingContext bindingContext = tryInputContext.trace.getBindingContext();
return processTryBranches(tryExpression, tryBlock, tryInputContext, catchBlocks, finallyBlock, bindingContext, resultType);
}
@NotNull
private KotlinTypeInfo processTryBranches(
@NotNull KtTryExpression tryExpression,
KtBlockExpression tryBlock,
ExpressionTypingContext context,
List<KtExpression> catchBlocks,
KtBlockExpression finallyBlock,
BindingContext bindingContext,
KotlinType resultType
) {
KotlinTypeInfo tryInfo = BindingContextUtils.getRecordedTypeInfo(tryBlock, bindingContext);
DataFlowInfo dataFlowInfoAfterTry;
if (tryInfo != null) {
dataFlowInfoAfterTry = tryInfo.getDataFlowInfo();
}
else {
dataFlowInfoAfterTry = DataFlowInfo.Companion.getEMPTY();
}
boolean nothingInAllCatchBranches = isCatchBranchesReturnsNothing(catchBlocks, bindingContext);
// it is not actually correct way (#KT-28370) of computing context, but it's how was in OI
ExpressionTypingContext tryOutputContext = getCleanedContextFromTryWithAssignmentsToVar(tryExpression, nothingInAllCatchBranches, context);
KotlinTypeInfo result = TypeInfoFactoryKt.createTypeInfo(resultType, tryOutputContext);
if (finallyBlock != null) {
return facade.getTypeInfo(finallyBlock, tryOutputContext).replaceType(resultType);
} else if (!nothingInAllCatchBranches || tryInfo == null) {
return result;
} else {
return TypeInfoFactoryKt.createTypeInfo(
components.dataFlowAnalyzer.checkType(resultType, tryExpression, tryOutputContext),
dataFlowInfoAfterTry
);
}
}
private static boolean isCatchBranchesReturnsNothing(List<KtExpression> catchBlocks, BindingContext bindingContext) {
return CollectionsKt.all(whichCatchBranchesReturnNothing(catchBlocks, bindingContext), it -> it);
}
private static List<Boolean> whichCatchBranchesReturnNothing(List<KtExpression> catchBlocks, BindingContext bindingContext) {
return catchBlocks.stream()
.map(catchBlock -> BindingContextUtils.getRecordedTypeInfo(catchBlock, bindingContext))
.map(catchTypeInfo -> {
if (catchTypeInfo == null) return true;
KotlinType catchType = catchTypeInfo.getType();
return catchType == null || KotlinBuiltIns.isNothing(catchType);
})
.collect(Collectors.toList());
}
private VariableDescriptor resolveAndCheckCatchParameter(@NotNull KtParameter catchParameter, ExpressionTypingContext context) {
checkCatchParameterDeclaration(catchParameter, context);
VariableDescriptor variableDescriptor = components.descriptorResolver
.resolveLocalVariableDescriptor(context.scope, catchParameter, context.trace);
KotlinType catchParameterType = variableDescriptor.getType();
checkCatchParameterType(catchParameter, catchParameterType, context);
KotlinType throwableType = components.builtIns.getThrowable().getDefaultType();
components.dataFlowAnalyzer.checkType(catchParameterType, catchParameter, context.replaceExpectedType(throwableType));
return variableDescriptor;
}
private ExpressionTypingContext getCleanedContextFromTryWithAssignmentsToVar(
KtTryExpression tryExpression,
boolean nothingInAllCatchBranches,
ExpressionTypingContext context
) {
context = context.replaceExpectedType(NO_EXPECTED_TYPE);
if (!nothingInAllCatchBranches && facade.getComponents().languageVersionSettings.supportsFeature(LanguageFeature.SoundSmartCastsAfterTry)) {
PreliminaryLoopVisitor tryVisitor = PreliminaryLoopVisitor.visitTryBlock(tryExpression);
context = context.replaceDataFlowInfo(
tryVisitor.clearDataFlowInfoForAssignedLocalVariables(context.dataFlowInfo, components.languageVersionSettings)
);
}
return context;
}
private static void checkCatchParameterType(KtParameter catchParameter, KotlinType catchParameterType, ExpressionTypingContext context) {
TypeParameterDescriptor typeParameterDescriptor = TypeUtils.getTypeParameterDescriptorOrNull(catchParameterType);
if (typeParameterDescriptor != null) {
@@ -679,7 +791,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
} else {
KotlinTypeInfo result = facade
.getTypeInfo(returnedExpression, context.replaceExpectedType(newInferenceLambdaInfo.getExpectedType())
.replaceContextDependency(newInferenceLambdaInfo.getContextDependency()));
.replaceContextDependency(newInferenceLambdaInfo.getContextDependency()));
contextInfo = new LambdaContextInfo(result, null, context.scope, context.trace);
}
newInferenceLambdaInfo.getReturnStatements().add(new kotlin.Pair<>(expression, contextInfo));
@@ -24,10 +24,7 @@ import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue;
import org.jetbrains.kotlin.resolve.calls.tower.KotlinResolutionCallbacksImpl;
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind;
import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope;
import org.jetbrains.kotlin.resolve.scopes.TraceBasedLocalRedeclarationChecker;
import org.jetbrains.kotlin.resolve.scopes.*;
import org.jetbrains.kotlin.types.ErrorUtils;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryKt;
@@ -380,4 +377,8 @@ public class ExpressionTypingServices {
return slice == BindingContext.EXPRESSION_EFFECTS;
}
}
public LocalRedeclarationChecker createLocalRedeclarationChecker(BindingTrace trace) {
return new TraceBasedLocalRedeclarationChecker(trace, expressionTypingComponents.overloadChecker);
}
}
@@ -14,15 +14,15 @@ fun foo() : Int {
}
fun bar() : Int =
try {
<!NI;TYPE_MISMATCH, TYPE_MISMATCH!>doSmth()<!>
<!NI;TYPE_MISMATCH, NI;TYPE_MISMATCH!>try {
<!OI;TYPE_MISMATCH!>doSmth()<!>
}
catch (e: Exception) {
<!TYPE_MISMATCH!>""<!>
<!OI;TYPE_MISMATCH!>""<!>
}
finally {
<!UNUSED_EXPRESSION!>""<!>
}
}<!>
fun doSmth() {}
@@ -5,15 +5,15 @@ class ExcA : Exception()
class ExcB : Exception()
fun test2() {
val s: String? = try {
val s: String? = <!NI;TYPE_MISMATCH!>try {
""
}
catch (e: ExcA) {
null
}
catch (e: ExcB) {
<!CONSTANT_EXPECTED_TYPE_MISMATCH!>10<!>
}
<!OI;CONSTANT_EXPECTED_TYPE_MISMATCH!>10<!>
}<!>
s<!UNSAFE_CALL!>.<!>length
}
@@ -13,8 +13,8 @@ fun test0(): List<Int> = run {
}
}
fun test1(): Map<Int, Int> = <!NI;TYPE_MISMATCH!>run {
<!NI;TYPE_MISMATCH!>try {
fun test1(): Map<Int, Int> = run {
try {
emptyMap()
} catch (e: ExcA) {
emptyMap()
@@ -22,13 +22,13 @@ fun test1(): Map<Int, Int> = <!NI;TYPE_MISMATCH!>run {
e.map
} finally {
<!UNUSED_EXPRESSION!>""<!>
}<!>
}<!>
}
}
fun test2(): Map<Int, Int> = <!NI;TYPE_MISMATCH!>run {
<!NI;TYPE_MISMATCH!>try {
fun test2(): Map<Int, Int> = run {
<!NI;TYPE_MISMATCH, NI;TYPE_MISMATCH, NI;TYPE_MISMATCH, NI;TYPE_MISMATCH, NI;TYPE_MISMATCH, NI;TYPE_MISMATCH, NI;TYPE_MISMATCH, NI;TYPE_MISMATCH!>try {
emptyMap()
} catch (e: ExcA) {
<!OI;TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>mapOf("" to "")<!>
<!NI;TYPE_MISMATCH, NI;TYPE_MISMATCH, OI;TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>mapOf(<!NI;TYPE_MISMATCH, NI;TYPE_MISMATCH!>"" to ""<!>)<!>
}<!>
}<!>
}