[FIR] Prohibit class literals with empty left-hand side

^KT-59165 Fixed
This commit is contained in:
Nikolay Lunyak
2023-07-26 13:41:04 +03:00
committed by Space Team
parent d748fe7696
commit 713c2136cc
18 changed files with 76 additions and 23 deletions
@@ -2440,6 +2440,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token, token,
) )
} }
add(FirErrors.UNSUPPORTED_CLASS_LITERALS_WITH_EMPTY_LHS) { firDiagnostic ->
UnsupportedClassLiteralsWithEmptyLhsImpl(
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.NOTHING_TO_OVERRIDE) { firDiagnostic -> add(FirErrors.NOTHING_TO_OVERRIDE) { firDiagnostic ->
NothingToOverrideImpl( NothingToOverrideImpl(
firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.a), firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.a),
@@ -1730,6 +1730,10 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
val lhsType: KtType val lhsType: KtType
} }
interface UnsupportedClassLiteralsWithEmptyLhs : KtFirDiagnostic<KtElement> {
override val diagnosticClass get() = UnsupportedClassLiteralsWithEmptyLhs::class
}
interface NothingToOverride : KtFirDiagnostic<KtModifierListOwner> { interface NothingToOverride : KtFirDiagnostic<KtModifierListOwner> {
override val diagnosticClass get() = NothingToOverride::class override val diagnosticClass get() = NothingToOverride::class
val declaration: KtCallableSymbol val declaration: KtCallableSymbol
@@ -2080,6 +2080,11 @@ internal class ExpressionOfNullableTypeInClassLiteralLhsImpl(
token: KtLifetimeToken, token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.ExpressionOfNullableTypeInClassLiteralLhs ) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.ExpressionOfNullableTypeInClassLiteralLhs
internal class UnsupportedClassLiteralsWithEmptyLhsImpl(
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<KtElement>(firDiagnostic, token), KtFirDiagnostic.UnsupportedClassLiteralsWithEmptyLhs
internal class NothingToOverrideImpl( internal class NothingToOverrideImpl(
override val declaration: KtCallableSymbol, override val declaration: KtCallableSymbol,
firDiagnostic: KtPsiDiagnostic, firDiagnostic: KtPsiDiagnostic,
@@ -297,6 +297,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia
runTest("compiler/testData/diagnostics/tests/Dollar.kt"); runTest("compiler/testData/diagnostics/tests/Dollar.kt");
} }
@Test
@TestMetadata("emptyLhsClassLiterals.kt")
public void testEmptyLhsClassLiterals() throws Exception {
runTest("compiler/testData/diagnostics/tests/emptyLhsClassLiterals.kt");
}
@Test @Test
@TestMetadata("EnumEntryAsType.kt") @TestMetadata("EnumEntryAsType.kt")
public void testEnumEntryAsType() throws Exception { public void testEnumEntryAsType() throws Exception {
@@ -297,6 +297,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated
runTest("compiler/testData/diagnostics/tests/Dollar.kt"); runTest("compiler/testData/diagnostics/tests/Dollar.kt");
} }
@Test
@TestMetadata("emptyLhsClassLiterals.kt")
public void testEmptyLhsClassLiterals() throws Exception {
runTest("compiler/testData/diagnostics/tests/emptyLhsClassLiterals.kt");
}
@Test @Test
@TestMetadata("EnumEntryAsType.kt") @TestMetadata("EnumEntryAsType.kt")
public void testEnumEntryAsType() throws Exception { public void testEnumEntryAsType() throws Exception {
@@ -297,6 +297,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
runTest("compiler/testData/diagnostics/tests/Dollar.kt"); runTest("compiler/testData/diagnostics/tests/Dollar.kt");
} }
@Test
@TestMetadata("emptyLhsClassLiterals.kt")
public void testEmptyLhsClassLiterals() throws Exception {
runTest("compiler/testData/diagnostics/tests/emptyLhsClassLiterals.kt");
}
@Test @Test
@TestMetadata("EnumEntryAsType.kt") @TestMetadata("EnumEntryAsType.kt")
public void testEnumEntryAsType() throws Exception { public void testEnumEntryAsType() throws Exception {
@@ -297,6 +297,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
runTest("compiler/testData/diagnostics/tests/Dollar.kt"); runTest("compiler/testData/diagnostics/tests/Dollar.kt");
} }
@Test
@TestMetadata("emptyLhsClassLiterals.kt")
public void testEmptyLhsClassLiterals() throws Exception {
runTest("compiler/testData/diagnostics/tests/emptyLhsClassLiterals.kt");
}
@Test @Test
@TestMetadata("EnumEntryAsType.kt") @TestMetadata("EnumEntryAsType.kt")
public void testEnumEntryAsType() throws Exception { public void testEnumEntryAsType() throws Exception {
@@ -806,6 +806,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val EXPRESSION_OF_NULLABLE_TYPE_IN_CLASS_LITERAL_LHS by error<PsiElement> { val EXPRESSION_OF_NULLABLE_TYPE_IN_CLASS_LITERAL_LHS by error<PsiElement> {
parameter<ConeKotlinType>("lhsType") parameter<ConeKotlinType>("lhsType")
} }
val UNSUPPORTED_CLASS_LITERALS_WITH_EMPTY_LHS by error<KtElement>()
} }
val OVERRIDES by object : DiagnosticGroup("overrides") { val OVERRIDES by object : DiagnosticGroup("overrides") {
@@ -463,6 +463,7 @@ object FirErrors {
val CLASS_LITERAL_LHS_NOT_A_CLASS by error0<KtExpression>() val CLASS_LITERAL_LHS_NOT_A_CLASS by error0<KtExpression>()
val NULLABLE_TYPE_IN_CLASS_LITERAL_LHS by error0<KtExpression>() val NULLABLE_TYPE_IN_CLASS_LITERAL_LHS by error0<KtExpression>()
val EXPRESSION_OF_NULLABLE_TYPE_IN_CLASS_LITERAL_LHS by error1<PsiElement, ConeKotlinType>() val EXPRESSION_OF_NULLABLE_TYPE_IN_CLASS_LITERAL_LHS by error1<PsiElement, ConeKotlinType>()
val UNSUPPORTED_CLASS_LITERALS_WITH_EMPTY_LHS by error0<KtElement>()
// overrides // overrides
val NOTHING_TO_OVERRIDE by error1<KtModifierListOwner, FirCallableSymbol<*>>(SourceElementPositioningStrategies.OVERRIDE_MODIFIER) val NOTHING_TO_OVERRIDE by error1<KtModifierListOwner, FirCallableSymbol<*>>(SourceElementPositioningStrategies.OVERRIDE_MODIFIER)
@@ -595,6 +595,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSAFE_INFIX_CALL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSAFE_OPERATOR_CALL import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSAFE_OPERATOR_CALL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSIGNED_LITERAL_WITHOUT_DECLARATIONS_ON_CLASSPATH import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSIGNED_LITERAL_WITHOUT_DECLARATIONS_ON_CLASSPATH
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSUPPORTED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSUPPORTED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSUPPORTED_CLASS_LITERALS_WITH_EMPTY_LHS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSUPPORTED_CONTEXTUAL_DECLARATION_CALL import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSUPPORTED_CONTEXTUAL_DECLARATION_CALL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSUPPORTED_FEATURE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSUPPORTED_FEATURE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNUSED_VARIABLE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNUSED_VARIABLE
@@ -1359,6 +1360,7 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
"Expression in a class literal has a nullable type ''{0}'', use !! to make the type non-nullable", "Expression in a class literal has a nullable type ''{0}'', use !! to make the type non-nullable",
RENDER_TYPE RENDER_TYPE
) )
map.put(UNSUPPORTED_CLASS_LITERALS_WITH_EMPTY_LHS, "Class literals with empty left hand side are unsupported")
// Value classes // Value classes
map.put(VALUE_CLASS_NOT_TOP_LEVEL, "Value class cannot be local or inner") map.put(VALUE_CLASS_NOT_TOP_LEVEL, "Value class cannot be local or inner")
@@ -2002,7 +2004,7 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
map.put(USELESS_CAST, "No cast needed") map.put(USELESS_CAST, "No cast needed")
map.put(UNCHECKED_CAST, "Unchecked cast: ''{0}'' to ''{1}''", RENDER_TYPE, RENDER_TYPE) map.put(UNCHECKED_CAST, "Unchecked cast: ''{0}'' to ''{1}''", RENDER_TYPE, RENDER_TYPE)
map.put(USELESS_IS_CHECK, "Check for instance is always ''{0}''", TO_STRING) map.put(USELESS_IS_CHECK, "Check for instance is always ''{0}''", TO_STRING)
map.put(DYNAMIC_NOT_ALLOWED, "Dynamic types are not allowed in this position"); map.put(DYNAMIC_NOT_ALLOWED, "Dynamic types are not allowed in this position")
map.put(IS_ENUM_ENTRY, "'is' over enum entry is not allowed, use comparison instead") map.put(IS_ENUM_ENTRY, "'is' over enum entry is not allowed, use comparison instead")
map.put(ENUM_ENTRY_AS_TYPE, "Use of enum entry names as types is not allowed, use enum type instead") map.put(ENUM_ENTRY_AS_TYPE, "Use of enum entry names as types is not allowed, use enum type instead")
@@ -151,6 +151,7 @@ private fun ConeDiagnostic.toKtDiagnostic(
is ConeAmbiguouslyResolvedAnnotationArgument -> is ConeAmbiguouslyResolvedAnnotationArgument ->
FirErrors.AMBIGUOUS_ANNOTATION_ARGUMENT.createOn(source, listOfNotNull(symbolFromCompilerPhase, symbolFromAnnotationArgumentsPhase)) FirErrors.AMBIGUOUS_ANNOTATION_ARGUMENT.createOn(source, listOfNotNull(symbolFromCompilerPhase, symbolFromAnnotationArgumentsPhase))
is ConeAmbiguousFunctionTypeKinds -> FirErrors.AMBIGUOUS_FUNCTION_TYPE_KIND.createOn(source, kinds) is ConeAmbiguousFunctionTypeKinds -> FirErrors.AMBIGUOUS_FUNCTION_TYPE_KIND.createOn(source, kinds)
is ConeUnsupportedClassLiteralsWithEmptyLhs -> FirErrors.UNSUPPORTED_CLASS_LITERALS_WITH_EMPTY_LHS.createOn(source)
else -> throw IllegalArgumentException("Unsupported diagnostic type: ${this.javaClass}") else -> throw IllegalArgumentException("Unsupported diagnostic type: ${this.javaClass}")
} }
@@ -463,11 +463,13 @@ class LightTreeRawFirExpressionBuilder(
if (it.isExpression()) firReceiverExpression = getAsFirExpression(it, "No receiver in class literal") if (it.isExpression()) firReceiverExpression = getAsFirExpression(it, "No receiver in class literal")
} }
val classLiteralSource = classLiteralExpression.toFirSourceElement()
return buildGetClassCall { return buildGetClassCall {
source = classLiteralExpression.toFirSourceElement() source = classLiteralSource
argumentList = buildUnaryArgumentList( argumentList = buildUnaryArgumentList(
firReceiverExpression firReceiverExpression
?: buildErrorExpression(null, ConeSyntaxDiagnostic("No receiver in class literal")) ?: buildErrorExpression(classLiteralSource, ConeUnsupportedClassLiteralsWithEmptyLhs)
) )
} }
} }
@@ -276,9 +276,12 @@ open class PsiRawFirBuilder(
kind: DiagnosticKind = DiagnosticKind.ExpressionExpected kind: DiagnosticKind = DiagnosticKind.ExpressionExpected
): FirExpression = toFirExpression { ConeSimpleDiagnostic(errorReason, kind) } ): FirExpression = toFirExpression { ConeSimpleDiagnostic(errorReason, kind) }
private inline fun KtElement?.toFirExpression(diagnosticFn: () -> ConeDiagnostic): FirExpression { private inline fun KtElement?.toFirExpression(
sourceWhenThisIsNull: PsiElement? = null,
diagnosticFn: () -> ConeDiagnostic,
): FirExpression {
if (this == null) { if (this == null) {
return buildErrorExpression(source = null, diagnosticFn()) return buildErrorExpression(source = sourceWhenThisIsNull?.toFirSourceElement(), diagnosticFn())
} }
val result = when (val fir = convertElement(this, null)) { val result = when (val fir = convertElement(this, null)) {
@@ -2929,7 +2932,11 @@ open class PsiRawFirBuilder(
override fun visitClassLiteralExpression(expression: KtClassLiteralExpression, data: FirElement?): FirElement { override fun visitClassLiteralExpression(expression: KtClassLiteralExpression, data: FirElement?): FirElement {
return buildGetClassCall { return buildGetClassCall {
source = expression.toFirSourceElement() source = expression.toFirSourceElement()
argumentList = buildUnaryArgumentList(expression.receiverExpression.toFirExpression("No receiver in class literal")) argumentList = buildUnaryArgumentList(
expression.receiverExpression.toFirExpression(sourceWhenThisIsNull = expression) {
ConeUnsupportedClassLiteralsWithEmptyLhs
}
)
} }
} }
@@ -71,6 +71,10 @@ class ConeAmbiguousFunctionTypeKinds(val kinds: List<FunctionTypeKind>) : ConeDi
get() = "There are multiple function kinds for functional type ref" get() = "There are multiple function kinds for functional type ref"
} }
object ConeUnsupportedClassLiteralsWithEmptyLhs : ConeDiagnostic {
override val reason: String get() = "No receiver in class literal"
}
enum class DiagnosticKind { enum class DiagnosticKind {
ExpressionExpected, ExpressionExpected,
NotLoopLabel, NotLoopLabel,
@@ -1,17 +0,0 @@
fun regular() {
::class
with(Any()) {
::class
}
}
fun Any.extension() {
::class
}
class A {
fun member() {
::class
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
fun regular() { fun regular() {
<!UNSUPPORTED_CLASS_LITERALS_WITH_EMPTY_LHS!>::class<!> <!UNSUPPORTED_CLASS_LITERALS_WITH_EMPTY_LHS!>::class<!>
@@ -0,0 +1,6 @@
// FIR_IDENTICAL
// ISSUE: KT-59165
fun regular() {
<!UNSUPPORTED_CLASS_LITERALS_WITH_EMPTY_LHS!>::class<!> // K1: Error, K2: compiles
}
@@ -297,6 +297,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/Dollar.kt"); runTest("compiler/testData/diagnostics/tests/Dollar.kt");
} }
@Test
@TestMetadata("emptyLhsClassLiterals.kt")
public void testEmptyLhsClassLiterals() throws Exception {
runTest("compiler/testData/diagnostics/tests/emptyLhsClassLiterals.kt");
}
@Test @Test
@TestMetadata("EnumEntryAsType.kt") @TestMetadata("EnumEntryAsType.kt")
public void testEnumEntryAsType() throws Exception { public void testEnumEntryAsType() throws Exception {