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:
@@ -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);
|
||||||
|
|||||||
+3
@@ -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);
|
||||||
|
|||||||
+26
-7
@@ -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
@@ -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")
|
||||||
|
|||||||
Reference in New Issue
Block a user