Support "::foo" as a short-hand for "this::foo"

#KT-15667 Fixed
This commit is contained in:
Alexander Udalov
2017-08-24 17:15:42 +03:00
parent d2ff821a3b
commit 2877314313
24 changed files with 195 additions and 116 deletions
@@ -659,7 +659,6 @@ public interface Errors {
DiagnosticFactory1<KtExpression, CallableMemberDescriptor> EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<KtExpression> CALLABLE_REFERENCE_LHS_NOT_A_CLASS = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtExpression> CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtExpression> CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtExpression> CLASS_LITERAL_LHS_NOT_A_CLASS = DiagnosticFactory0.create(ERROR);
@@ -839,9 +839,6 @@ public class DefaultErrorMessages {
MAP.put(EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED,
"''{0}'' is a member and an extension at the same time. References to such elements are not allowed", NAME);
MAP.put(CALLABLE_REFERENCE_LHS_NOT_A_CLASS, "Left-hand side of a callable reference cannot be a type parameter");
MAP.put(CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS,
"Left-hand side of a callable reference with a receiver parameter cannot be empty. " +
"Please specify the type of the receiver before '::' explicitly");
MAP.put(CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR, "Annotation class cannot be instantiated");
MAP.put(CLASS_LITERAL_LHS_NOT_A_CLASS, "Only classes are allowed on the left hand side of a class literal");
@@ -550,9 +550,13 @@ class DoubleColonExpressionResolver(
descriptor: CallableDescriptor, trace: BindingTrace, expression: KtCallableReferenceExpression
) {
val simpleName = expression.callableReference
if (expression.isEmptyLHS &&
(descriptor.dispatchReceiverParameter != null || descriptor.extensionReceiverParameter != null)) {
trace.report(CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS.on(simpleName))
if (!languageVersionSettings.supportsFeature(LanguageFeature.CallableReferencesToClassMembersWithEmptyLHS)) {
if (expression.isEmptyLHS &&
(descriptor.dispatchReceiverParameter != null || descriptor.extensionReceiverParameter != null)) {
trace.report(UNSUPPORTED_FEATURE.on(
simpleName, LanguageFeature.CallableReferencesToClassMembersWithEmptyLHS to languageVersionSettings
))
}
}
if (descriptor is ConstructorDescriptor && DescriptorUtils.isAnnotationClass(descriptor.containingDeclaration)) {
trace.report(CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR.on(simpleName))
@@ -0,0 +1,58 @@
// IGNORE_BACKEND: JS
// LANGUAGE_VERSION: 1.2
var result = ""
class A {
fun memberFunction() { result += "A.mf," }
fun aMemberFunction() { result += "A.amf," }
val memberProperty: Int get() = 42.also { result += "A.mp," }
val aMemberProperty: Int get() = 42.also { result += "A.amp," }
fun test(): String {
(::memberFunction)()
(::aExtensionFunction)()
(::memberProperty)()
(::aExtensionProperty)()
return result
}
inner class B {
fun memberFunction() { result += "B.mf," }
val memberProperty: Int get() = 42.also { result += "B.mp," }
fun test(): String {
(::aMemberFunction)()
(::aExtensionFunction)()
(::aMemberProperty)()
(::aExtensionProperty)()
(::memberFunction)()
(::memberProperty)()
(::bExtensionFunction)()
(::bExtensionProperty)()
return result
}
}
}
fun A.aExtensionFunction() { result += "A.ef," }
val A.aExtensionProperty: Int get() = 42.also { result += "A.ep," }
fun A.B.bExtensionFunction() { result += "B.ef," }
val A.B.bExtensionProperty: Int get() = 42.also { result += "B.ep," }
fun box(): String {
val a = A().test()
if (a != "A.mf,A.ef,A.mp,A.ep,") return "Fail $a"
result = ""
val b = A().B().test()
if (b != "A.amf,A.ef,A.amp,A.ep,B.mf,B.mp,B.ef,B.ep,") return "Fail $b"
return "OK"
}
@@ -1,4 +1,5 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION, -EXTENSION_SHADOWED_BY_MEMBER
// !LANGUAGE: +CallableReferencesToClassMembersWithEmptyLHS
val topLevelVal = 1
fun topLevelFun() = 2
@@ -14,13 +15,13 @@ class A {
val ok2 = ::topLevelFun
fun fail1() {
::<!CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS!>extensionVal<!>
::<!CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS!>extensionFun<!>
::extensionVal
::extensionFun
}
fun fail2() {
::<!CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS!>memberVal<!>
::<!CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS!>memberFun<!>
::memberVal
::memberFun
}
}
@@ -30,11 +31,11 @@ val ok1 = ::topLevelVal
val ok2 = ::topLevelFun
fun A.fail1() {
::<!CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS!>extensionVal<!>
::<!CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS!>extensionFun<!>
::extensionVal
::extensionFun
}
fun A.fail2() {
::<!CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS!>memberVal<!>
::<!CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS!>memberFun<!>
::memberVal
::memberFun
}
@@ -1,12 +1,14 @@
// !CHECK_TYPE
// !DIAGNOSTICS: -UNUSED_EXPRESSION
// !LANGUAGE: +CallableReferencesToClassMembersWithEmptyLHS
import kotlin.reflect.KFunction1
class A {
inner class Inner
fun main() {
::<!CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS!>Inner<!>
::Inner
val y = A::Inner
checkSubtype<KFunction1<A, Inner>>(y)
@@ -1,5 +1,7 @@
// !CHECK_TYPE
// !DIAGNOSTICS: -UNUSED_EXPRESSION
// !LANGUAGE: +CallableReferencesToClassMembersWithEmptyLHS
import kotlin.reflect.KFunction1
class A {
@@ -7,7 +9,7 @@ class A {
}
fun A.main() {
::<!CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS!>Inner<!>
::Inner
val y = A::Inner
checkSubtype<KFunction1<A, A.Inner>>(y)
@@ -1,24 +1,31 @@
// !CHECK_TYPE
// !LANGUAGE: +CallableReferencesToClassMembersWithEmptyLHS
import kotlin.reflect.KFunction0
fun explicitlyExpectFunction0(f: () -> Unit) = f
fun explicitlyExpectFunction1(f: (A) -> Unit) = f
fun expectFunction0Unit(f: () -> Unit) = f
fun expectFunction0String(f: () -> String) = f
fun expectFunction1Unit(f: (A) -> Unit) = f
fun expectFunction1String(f: (A) -> String) = f
fun foo() {}
fun foo(): String = ""
class A {
fun foo() {}
fun main() {
val x = ::<!CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS!>foo<!>
val x = ::foo
checkSubtype<KFunction0<Unit>>(x)
explicitlyExpectFunction0(x)
explicitlyExpectFunction1(<!TYPE_MISMATCH!>x<!>)
expectFunction0Unit(x)
expectFunction0String(<!TYPE_MISMATCH!>x<!>)
expectFunction1Unit(<!TYPE_MISMATCH!>x<!>)
expectFunction1String(<!TYPE_MISMATCH!>x<!>)
explicitlyExpectFunction0(::<!CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS!>foo<!>)
explicitlyExpectFunction1(<!TYPE_MISMATCH!>::<!CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS!>foo<!><!>)
expectFunction0Unit(::foo)
expectFunction0String(::foo)
expectFunction1Unit(<!TYPE_MISMATCH!>::foo<!>)
expectFunction1String(<!TYPE_MISMATCH!>::foo<!>)
}
}
@@ -1,8 +1,10 @@
package
public fun explicitlyExpectFunction0(/*0*/ f: () -> kotlin.Unit): () -> kotlin.Unit
public fun explicitlyExpectFunction1(/*0*/ f: (A) -> kotlin.Unit): (A) -> kotlin.Unit
public fun foo(): kotlin.Unit
public fun expectFunction0String(/*0*/ f: () -> kotlin.String): () -> kotlin.String
public fun expectFunction0Unit(/*0*/ f: () -> kotlin.Unit): () -> kotlin.Unit
public fun expectFunction1String(/*0*/ f: (A) -> kotlin.String): (A) -> kotlin.String
public fun expectFunction1Unit(/*0*/ f: (A) -> kotlin.Unit): (A) -> kotlin.Unit
public fun foo(): kotlin.String
public final class A {
public constructor A()
@@ -0,0 +1,28 @@
// !LANGUAGE: -CallableReferencesToClassMembersWithEmptyLHS
class A {
fun memberFunction() {}
val memberProperty: Int get() = 42
fun test() {
(::<!UNSUPPORTED_FEATURE!>memberFunction<!>)()
(::<!UNSUPPORTED_FEATURE!>extensionFunction<!>)()
(::<!UNSUPPORTED_FEATURE!>memberProperty<!>)()
(::<!UNSUPPORTED_FEATURE!>extensionProperty<!>)()
}
inner class B {
fun memberFunction() { }
val memberProperty: Int get() = 43
fun test() {
(::<!UNSUPPORTED_FEATURE!>memberFunction<!>)()
(::<!UNSUPPORTED_FEATURE!>extensionFunction<!>)()
(::<!UNSUPPORTED_FEATURE!>memberProperty<!>)()
(::<!UNSUPPORTED_FEATURE!>extensionProperty<!>)()
}
}
}
fun A.extensionFunction() {}
val A.extensionProperty: Int get() = 44
@@ -0,0 +1,24 @@
package
public val A.extensionProperty: kotlin.Int
public fun A.extensionFunction(): kotlin.Unit
public final class A {
public constructor A()
public final val memberProperty: kotlin.Int
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final fun memberFunction(): kotlin.Unit
public final fun test(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public final inner class B {
public constructor B()
public final val memberProperty: kotlin.Int
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final fun memberFunction(): kotlin.Unit
public final fun test(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
@@ -1669,6 +1669,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName);
}
@TestMetadata("emptyLHS.kt")
public void testEmptyLHS() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/emptyLHS.kt");
doTest(fileName);
}
@TestMetadata("enumEntryMember.kt")
public void testEnumEntryMember() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/enumEntryMember.kt");
@@ -21877,6 +21877,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("noCallableReferencesWithEmptyLHS.kt")
public void testNoCallableReferencesWithEmptyLHS() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sourceCompatibility/noCallableReferencesWithEmptyLHS.kt");
doTest(fileName);
}
@TestMetadata("noDataClassInheritance.kt")
public void testNoDataClassInheritance() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sourceCompatibility/noDataClassInheritance.kt");
@@ -21877,6 +21877,12 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
doTest(fileName);
}
@TestMetadata("noCallableReferencesWithEmptyLHS.kt")
public void testNoCallableReferencesWithEmptyLHS() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sourceCompatibility/noCallableReferencesWithEmptyLHS.kt");
doTest(fileName);
}
@TestMetadata("noDataClassInheritance.kt")
public void testNoDataClassInheritance() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sourceCompatibility/noDataClassInheritance.kt");
@@ -1669,6 +1669,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("emptyLHS.kt")
public void testEmptyLHS() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/emptyLHS.kt");
doTest(fileName);
}
@TestMetadata("enumEntryMember.kt")
public void testEnumEntryMember() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/enumEntryMember.kt");
@@ -1669,6 +1669,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
doTest(fileName);
}
@TestMetadata("emptyLHS.kt")
public void testEmptyLHS() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/emptyLHS.kt");
doTest(fileName);
}
@TestMetadata("enumEntryMember.kt")
public void testEnumEntryMember() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/enumEntryMember.kt");
@@ -59,6 +59,7 @@ enum class LanguageFeature(
CapturedInClosureSmartCasts(KOTLIN_1_2),
LateinitTopLevelProperties(KOTLIN_1_2),
LateinitLocalVariables(KOTLIN_1_2),
CallableReferencesToClassMembersWithEmptyLHS(KOTLIN_1_2),
RestrictionOfValReassignmentViaBackingField(KOTLIN_1_3),
@@ -94,7 +94,6 @@ class KotlinCleanupInspection : LocalInspectionTool(), CleanupLocalInspectionToo
Errors.NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION,
Errors.OPERATOR_MODIFIER_REQUIRED,
Errors.INFIX_MODIFIER_REQUIRED,
Errors.CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS,
Errors.DEPRECATED_TYPE_PARAMETER_SYNTAX,
Errors.MISPLACED_TYPE_PARAMETER_CONSTRAINTS,
Errors.COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT,
@@ -1,54 +0,0 @@
/*
* Copyright 2010-2016 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.idea.quickfix
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class AddTypeToLHSOfCallableReferenceFix(
expression: KtCallableReferenceExpression
) : KotlinQuickFixAction<KtCallableReferenceExpression>(expression), CleanupFix {
override fun getFamilyName() = "Add type to left-hand side"
override fun getText() = familyName
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
val element = element ?: return
val resolvedCall = element.callableReference.getResolvedCall(element.analyze(BodyResolveMode.PARTIAL)) ?: return
val receiver = with(resolvedCall) {
dispatchReceiver ?: extensionReceiver ?: return
}
val expression = KtPsiFactory(project).createExpression(IdeDescriptorRenderers.SOURCE_CODE.renderType(receiver.type))
element.setReceiverExpression(expression)
ShortenReferences.DEFAULT.process(element)
}
companion object : KotlinSingleIntentionActionFactory() {
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
return AddTypeToLHSOfCallableReferenceFix(diagnostic.psiElement.parent as KtCallableReferenceExpression)
}
}
}
@@ -421,8 +421,6 @@ class QuickFixRegistrar : QuickFixContributor {
UNDERSCORE_IS_RESERVED.registerFactory(RenameUnderscoreFix)
CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS.registerFactory(AddTypeToLHSOfCallableReferenceFix)
DEPRECATED_TYPE_PARAMETER_SYNTAX.registerFactory(MigrateTypeParameterListFix)
UNRESOLVED_REFERENCE.registerFactory(KotlinAddOrderEntryActionFactory)
@@ -1,8 +0,0 @@
// "Add type to left-hand side" "true"
package foo.bar
class A {
fun foo() {}
fun bar() = ::fo<caret>o
}
@@ -1,8 +0,0 @@
// "Add type to left-hand side" "true"
package foo.bar
class A {
fun foo() {}
fun bar() = A::foo
}
@@ -6764,21 +6764,6 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/migration"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("idea/testData/quickfix/migration/addTypeToLHSOfCallableReference")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class AddTypeToLHSOfCallableReference extends AbstractQuickFixTest {
public void testAllFilesPresentInAddTypeToLHSOfCallableReference() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/migration/addTypeToLHSOfCallableReference"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("member.kt")
public void testMember() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/migration/addTypeToLHSOfCallableReference/member.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/quickfix/migration/commasInWhenWithoutArgument")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -1957,6 +1957,18 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
doTest(fileName);
}
@TestMetadata("emptyLHS.kt")
public void testEmptyLHS() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/emptyLHS.kt");
try {
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
}
@TestMetadata("enumEntryMember.kt")
public void testEnumEntryMember() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/enumEntryMember.kt");