diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 3c8fd37c2ca..66ec1fc157a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -583,6 +583,7 @@ public interface Errors { DiagnosticFactory1 UNSAFE_CALL = DiagnosticFactory1.create(ERROR); DiagnosticFactory3 UNSAFE_INFIX_CALL = DiagnosticFactory3.create(ERROR); DiagnosticFactory1 UNNECESSARY_SAFE_CALL = DiagnosticFactory1.create(WARNING); + DiagnosticFactory0 UNEXPECTED_SAFE_CALL = DiagnosticFactory0.create(ERROR); DiagnosticFactory1 UNNECESSARY_NOT_NULL_ASSERTION = DiagnosticFactory1.create(WARNING); DiagnosticFactory0 NOT_NULL_ASSERTION_ON_FUNCTION_LITERAL = DiagnosticFactory0.create(WARNING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 04689446d3c..f407e15f471 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -466,6 +466,7 @@ public class DefaultErrorMessages { MAP.put(UNSUPPORTED, "Unsupported [{0}]", STRING); MAP.put(EXCEPTION_FROM_ANALYZER, "Internal Error occurred while analyzing this expression:\n{0}", THROWABLE); MAP.put(UNNECESSARY_SAFE_CALL, "Unnecessary safe call on a non-null receiver of type {0}", RENDER_TYPE); + MAP.put(UNEXPECTED_SAFE_CALL, "Safe-call is not allowed here"); MAP.put(UNNECESSARY_NOT_NULL_ASSERTION, "Unnecessary non-null assertion (!!) on a non-null receiver of type {0}", RENDER_TYPE); MAP.put(NOT_NULL_ASSERTION_ON_FUNCTION_LITERAL, "Non-null assertion (!!) is called on function literal"); MAP.put(NAME_IN_CONSTRAINT_IS_NOT_A_TYPE_PARAMETER, "{0} does not refer to a type parameter of {1}", new Renderer() { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt index ddd4c2e53d1..b584ebc43f9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt @@ -50,7 +50,7 @@ abstract class TargetPlatform( } private val DEFAULT_DECLARATION_CHECKERS = listOf(DataClassAnnotationChecker(), ConstModifierChecker, UnderscoreChecker, OperatorModifierChecker()) -private val DEFAULT_CALL_CHECKERS = listOf(CapturingInClosureChecker(), InlineCheckerWrapper(), ReifiedTypeParameterSubstitutionChecker()) +private val DEFAULT_CALL_CHECKERS = listOf(CapturingInClosureChecker(), InlineCheckerWrapper(), ReifiedTypeParameterSubstitutionChecker(), SafeCallChecker()) private val DEFAULT_TYPE_CHECKERS = emptyList() private val DEFAULT_VALIDATORS = listOf(DeprecatedSymbolValidator(), OperatorValidator()) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/SafeCallChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/SafeCallChecker.kt new file mode 100644 index 00000000000..aff3d8481a4 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/SafeCallChecker.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2010-2015 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 org.jetbrains.kotlin.resolve.calls.checkers + +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.lexer.JetTokens +import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind + +public class SafeCallChecker : CallChecker { + override fun check(resolvedCall: ResolvedCall, context: BasicCallResolutionContext) { + val operationNode = resolvedCall.call.callOperationNode ?: return + + if (operationNode.elementType == JetTokens.SAFE_ACCESS && resolvedCall.explicitReceiverKind == ExplicitReceiverKind.NO_EXPLICIT_RECEIVER) { + context.trace.report(Errors.UNEXPECTED_SAFE_CALL.on(operationNode.psi)) + } + } +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/AbstractTracingStrategy.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/AbstractTracingStrategy.java index 7ba54f0fdf7..98282788035 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/AbstractTracingStrategy.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/AbstractTracingStrategy.java @@ -189,7 +189,13 @@ public abstract class AbstractTracingStrategy implements TracingStrategy { public void unnecessarySafeCall(@NotNull BindingTrace trace, @NotNull JetType type) { ASTNode callOperationNode = call.getCallOperationNode(); assert callOperationNode != null; - trace.report(UNNECESSARY_SAFE_CALL.on(callOperationNode.getPsi(), type)); + ReceiverValue explicitReceiver = call.getExplicitReceiver(); + if (explicitReceiver instanceof ExpressionReceiver && ((ExpressionReceiver)explicitReceiver).getExpression() instanceof JetSuperExpression) { + trace.report(UNEXPECTED_SAFE_CALL.on(callOperationNode.getPsi())); + } + else { + trace.report(UNNECESSARY_SAFE_CALL.on(callOperationNode.getPsi(), type)); + } } @Override diff --git a/compiler/testData/codegen/box/safeCall/staticCall.kt b/compiler/testData/codegen/box/safeCall/staticCall.kt deleted file mode 100644 index 46cd563da4e..00000000000 --- a/compiler/testData/codegen/box/safeCall/staticCall.kt +++ /dev/null @@ -1 +0,0 @@ -fun box(): String = if (Thread?.currentThread() != null) "OK" else "Fail" diff --git a/compiler/testData/diagnostics/tests/SafeCallOnFakePackage.kt b/compiler/testData/diagnostics/tests/SafeCallOnFakePackage.kt index 1a5c3fa4250..ecd81a884b6 100644 --- a/compiler/testData/diagnostics/tests/SafeCallOnFakePackage.kt +++ b/compiler/testData/diagnostics/tests/SafeCallOnFakePackage.kt @@ -10,7 +10,7 @@ val s: String = "test" // FILE: test.kt fun ff() { - val a = Test?.FOO - val b = foo?.s - System?.out.println(a + b) -} + val a = Test?.FOO + val b = foo?.s + System?.out.println(a + b) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/SafeCallOnSuperReceiver.kt b/compiler/testData/diagnostics/tests/SafeCallOnSuperReceiver.kt new file mode 100644 index 00000000000..5af03be990c --- /dev/null +++ b/compiler/testData/diagnostics/tests/SafeCallOnSuperReceiver.kt @@ -0,0 +1,11 @@ +// http://youtrack.jetbrains.net/issue/KT-413 + +open class A { + fun f() {} +} + +class B : A() { + fun g() { + super?.f() + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/SafeCallOnSuperReceiver.txt b/compiler/testData/diagnostics/tests/SafeCallOnSuperReceiver.txt new file mode 100644 index 00000000000..2d573846f2b --- /dev/null +++ b/compiler/testData/diagnostics/tests/SafeCallOnSuperReceiver.txt @@ -0,0 +1,18 @@ +package + +public open class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun f(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class B : A { + public constructor B() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final override /*1*/ /*fake_override*/ fun f(): kotlin.Unit + public final fun g(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index 6b2e27c08cf..79e753a5593 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -487,6 +487,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("SafeCallOnSuperReceiver.kt") + public void testSafeCallOnSuperReceiver() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/SafeCallOnSuperReceiver.kt"); + doTest(fileName); + } + @TestMetadata("SelfTypesUnsupported.kt") public void testSelfTypesUnsupported() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/SelfTypesUnsupported.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java index 8fbd1b77683..060be214ebe 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -6831,12 +6831,6 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/safeCall/safeCallOnLong.kt"); doTest(fileName); } - - @TestMetadata("staticCall.kt") - public void testStaticCall() throws Exception { - String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/safeCall/staticCall.kt"); - doTest(fileName); - } } @TestMetadata("compiler/testData/codegen/box/samConstructors")