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<KtTypeArgumentList> TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtParameter> REIFIED_TYPE_IN_CATCH_CLAUSE = 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<KtTypeParameterList> GENERIC_THROWABLE_SUBCLASS = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtTypeAlias> TOPLEVEL_TYPEALIASES_ONLY = 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_VARARG_PARAMETER = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtParameter> DATA_CLASS_NOT_PROPERTY_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 // Multi-platform projects
DiagnosticFactory0<KtDeclaration> HEADER_DECLARATION_WITH_BODY = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); 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(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(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(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, 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_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(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(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, 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); "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.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.diagnostics.Errors;
import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingContextUtils; import org.jetbrains.kotlin.resolve.BindingContextUtils;
@@ -486,17 +487,12 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
KtExpression catchBody = catchClause.getCatchBody(); KtExpression catchBody = catchClause.getCatchBody();
boolean nothingInCatchBranch = false; boolean nothingInCatchBranch = false;
if (catchParameter != null) { if (catchParameter != null) {
components.identifierChecker.checkDeclaration(catchParameter, context.trace); checkCatchParameterDeclaration(catchParameter, context);
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);
VariableDescriptor variableDescriptor = components.descriptorResolver.resolveLocalVariableDescriptor( VariableDescriptor variableDescriptor = components.descriptorResolver.resolveLocalVariableDescriptor(
context.scope, catchParameter, context.trace); context.scope, catchParameter, context.trace);
KotlinType catchParameterType = variableDescriptor.getType(); KotlinType catchParameterType = variableDescriptor.getType();
if (TypeUtils.isReifiedTypeParameter(catchParameterType)) { checkCatchParameterType(catchParameter, catchParameterType, context);
context.trace.report(REIFIED_TYPE_IN_CATCH_CLAUSE.on(catchParameter));
}
KotlinType throwableType = components.builtIns.getThrowable().getDefaultType(); KotlinType throwableType = components.builtIns.getThrowable().getDefaultType();
components.dataFlowAnalyzer.checkType(catchParameterType, catchParameter, context.replaceExpectedType(throwableType)); 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 @Override
public KotlinTypeInfo visitThrowExpression(@NotNull KtThrowExpression expression, ExpressionTypingContext context) { public KotlinTypeInfo visitThrowExpression(@NotNull KtThrowExpression expression, ExpressionTypingContext context) {
KtExpression thrownExpression = expression.getThrownExpression(); 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<!>) { } catch (<!REIFIED_TYPE_IN_CATCH_CLAUSE!>e: E<!>) {
failure(e) failure(e)
} }
fun <T : Throwable> tryCatch() {
try { } catch (<!TYPE_PARAMETER_IN_CATCH_CLAUSE!>e: T<!>) { }
}
@@ -1,6 +1,7 @@
package package
public fun bar(): kotlin.Unit 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 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 { 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); 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") @TestMetadata("commonSupertypeOfT.kt")
public void testCommonSupertypeOfT() throws Exception { public void testCommonSupertypeOfT() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/commonSupertypeOfT.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/commonSupertypeOfT.kt");
+3 -3
View File
@@ -2,12 +2,12 @@
package foo package foo
public fun <T : Throwable> failsWith(block: () -> Any): T { public inline fun <reified T : Throwable> failsWith(block: () -> Any): T {
try { try {
block() block()
} }
catch (e: T) { catch (e: Throwable) {
return e if (e is T) return e
} }
throw Exception("Should have failed") throw Exception("Should have failed")