Perform additional checks on catch parameter declaration

KT-8320 It should not be possible to catch a type parameter type
KT-7645 Prohibit default value for `catch`-block parameter
This commit is contained in:
Dmitry Petrov
2017-04-13 13:01:30 +03:00
parent 5ccfbcbe22
commit e0ebaac70c
9 changed files with 52 additions and 10 deletions
@@ -152,6 +152,7 @@ public interface Errors {
DiagnosticFactory0<KtTypeArgumentList> TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtParameter> REIFIED_TYPE_IN_CATCH_CLAUSE = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtParameter> TYPE_PARAMETER_IN_CATCH_CLAUSE = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtTypeParameterList> GENERIC_THROWABLE_SUBCLASS = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtTypeAlias> TOPLEVEL_TYPEALIASES_ONLY = DiagnosticFactory0.create(ERROR);
@@ -527,6 +528,8 @@ public interface Errors {
DiagnosticFactory0<KtParameter> DATA_CLASS_VARARG_PARAMETER = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtParameter> DATA_CLASS_NOT_PROPERTY_PARAMETER = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtParameter> CATCH_PARAMETER_WITH_DEFAULT_VALUE = DiagnosticFactory0.create(ERROR);
// Multi-platform projects
DiagnosticFactory0<KtDeclaration> HEADER_DECLARATION_WITH_BODY = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
@@ -621,6 +621,7 @@ public class DefaultErrorMessages {
MAP.put(TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED, "Type arguments for outer class are redundant when nested class is referenced");
MAP.put(REIFIED_TYPE_IN_CATCH_CLAUSE, "Reified type is forbidden for catch parameter");
MAP.put(TYPE_PARAMETER_IN_CATCH_CLAUSE, "Type parameter is forbidden for catch parameter");
MAP.put(GENERIC_THROWABLE_SUBCLASS, "Subclass of 'Throwable' may not have type parameters");
MAP.put(TYPE_MISMATCH_IN_FOR_LOOP, "The loop iterates over values of type {0} but the parameter is declared to be {1}", RENDER_TYPE,
@@ -797,6 +798,8 @@ public class DefaultErrorMessages {
MAP.put(DATA_CLASS_VARARG_PARAMETER, "Primary constructor vararg parameters are forbidden for data classes");
MAP.put(DATA_CLASS_NOT_PROPERTY_PARAMETER, "Data class primary constructor must have only property (val / var) parameters");
MAP.put(CATCH_PARAMETER_WITH_DEFAULT_VALUE, "Catch clause parameter may not have a default value");
MAP.put(AMBIGUOUS_ANONYMOUS_TYPE_INFERRED, "Right-hand side has anonymous type. Please specify type explicitly", TO_STRING);
MAP.put(KCLASS_WITH_NULLABLE_TYPE_PARAMETER_IN_SIGNATURE,
"Declaration has an inconsistent return type. Please add upper bound Any for type parameter ''{0}'' or specify return type explicitly", NAME);
@@ -23,6 +23,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.diagnostics.Errors;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingContextUtils;
@@ -486,17 +487,12 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
KtExpression catchBody = catchClause.getCatchBody();
boolean nothingInCatchBranch = false;
if (catchParameter != null) {
components.identifierChecker.checkDeclaration(catchParameter, context.trace);
ModifiersChecker.ModifiersCheckingProcedure modifiersChecking = components.modifiersChecker.withTrace(context.trace);
modifiersChecking.checkParameterHasNoValOrVar(catchParameter, VAL_OR_VAR_ON_CATCH_PARAMETER);
ModifierCheckerCore.INSTANCE.check(catchParameter, context.trace, null, components.languageVersionSettings);
checkCatchParameterDeclaration(catchParameter, context);
VariableDescriptor variableDescriptor = components.descriptorResolver.resolveLocalVariableDescriptor(
context.scope, catchParameter, context.trace);
KotlinType catchParameterType = variableDescriptor.getType();
if (TypeUtils.isReifiedTypeParameter(catchParameterType)) {
context.trace.report(REIFIED_TYPE_IN_CATCH_CLAUSE.on(catchParameter));
}
checkCatchParameterType(catchParameter, catchParameterType, context);
KotlinType throwableType = components.builtIns.getThrowable().getDefaultType();
components.dataFlowAnalyzer.checkType(catchParameterType, catchParameter, context.replaceExpectedType(throwableType));
@@ -539,6 +535,29 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
}
}
private static void checkCatchParameterType(KtParameter catchParameter, KotlinType catchParameterType, ExpressionTypingContext context) {
TypeParameterDescriptor typeParameterDescriptor = TypeUtils.getTypeParameterDescriptorOrNull(catchParameterType);
if (typeParameterDescriptor != null) {
if (typeParameterDescriptor.isReified()) {
context.trace.report(REIFIED_TYPE_IN_CATCH_CLAUSE.on(catchParameter));
}
else {
context.trace.report(TYPE_PARAMETER_IN_CATCH_CLAUSE.on(catchParameter));
}
}
}
private void checkCatchParameterDeclaration(KtParameter catchParameter, ExpressionTypingContext context) {
components.identifierChecker.checkDeclaration(catchParameter, context.trace);
ModifiersChecker.ModifiersCheckingProcedure modifiersChecking = components.modifiersChecker.withTrace(context.trace);
modifiersChecking.checkParameterHasNoValOrVar(catchParameter, VAL_OR_VAR_ON_CATCH_PARAMETER);
ModifierCheckerCore.INSTANCE.check(catchParameter, context.trace, null, components.languageVersionSettings);
if (catchParameter.hasDefaultValue()) {
context.trace.report(Errors.CATCH_PARAMETER_WITH_DEFAULT_VALUE.on(catchParameter));
}
}
@Override
public KotlinTypeInfo visitThrowExpression(@NotNull KtThrowExpression expression, ExpressionTypingContext context) {
KtExpression thrownExpression = expression.getThrownExpression();
@@ -19,3 +19,7 @@ inline fun <reified E : Exception, R> tryCatch(lazy: () -> R, failure: (E) -> R)
} catch (<!REIFIED_TYPE_IN_CATCH_CLAUSE!>e: E<!>) {
failure(e)
}
fun <T : Throwable> tryCatch() {
try { } catch (<!TYPE_PARAMETER_IN_CATCH_CLAUSE!>e: T<!>) { }
}
@@ -1,6 +1,7 @@
package
public fun bar(): kotlin.Unit
public fun </*0*/ T : kotlin.Throwable> tryCatch(): kotlin.Unit
public inline fun </*0*/ reified E : kotlin.Exception /* = java.lang.Exception */, /*1*/ R> tryCatch(/*0*/ lazy: () -> R, /*1*/ failure: (E) -> R): R
public final class XException</*0*/ T> : kotlin.Throwable {
@@ -0,0 +1,3 @@
fun test() {
try { } catch (<!CATCH_PARAMETER_WITH_DEFAULT_VALUE!>e: Exception = <!DEBUG_INFO_MISSING_UNRESOLVED!>Exception<!>()<!>) { }
}
@@ -0,0 +1,3 @@
package
public fun test(): kotlin.Unit
@@ -4390,6 +4390,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("catchWithDefault.kt")
public void testCatchWithDefault() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/catchWithDefault.kt");
doTest(fileName);
}
@TestMetadata("commonSupertypeOfT.kt")
public void testCommonSupertypeOfT() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/commonSupertypeOfT.kt");
+3 -3
View File
@@ -2,12 +2,12 @@
package foo
public fun <T : Throwable> failsWith(block: () -> Any): T {
public inline fun <reified T : Throwable> failsWith(block: () -> Any): T {
try {
block()
}
catch (e: T) {
return e
catch (e: Throwable) {
if (e is T) return e
}
throw Exception("Should have failed")