[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,
)
}
add(FirErrors.UNSUPPORTED_CLASS_LITERALS_WITH_EMPTY_LHS) { firDiagnostic ->
UnsupportedClassLiteralsWithEmptyLhsImpl(
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.NOTHING_TO_OVERRIDE) { firDiagnostic ->
NothingToOverrideImpl(
firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.a),
@@ -1730,6 +1730,10 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
val lhsType: KtType
}
interface UnsupportedClassLiteralsWithEmptyLhs : KtFirDiagnostic<KtElement> {
override val diagnosticClass get() = UnsupportedClassLiteralsWithEmptyLhs::class
}
interface NothingToOverride : KtFirDiagnostic<KtModifierListOwner> {
override val diagnosticClass get() = NothingToOverride::class
val declaration: KtCallableSymbol
@@ -2080,6 +2080,11 @@ internal class ExpressionOfNullableTypeInClassLiteralLhsImpl(
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.ExpressionOfNullableTypeInClassLiteralLhs
internal class UnsupportedClassLiteralsWithEmptyLhsImpl(
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<KtElement>(firDiagnostic, token), KtFirDiagnostic.UnsupportedClassLiteralsWithEmptyLhs
internal class NothingToOverrideImpl(
override val declaration: KtCallableSymbol,
firDiagnostic: KtPsiDiagnostic,
@@ -297,6 +297,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia
runTest("compiler/testData/diagnostics/tests/Dollar.kt");
}
@Test
@TestMetadata("emptyLhsClassLiterals.kt")
public void testEmptyLhsClassLiterals() throws Exception {
runTest("compiler/testData/diagnostics/tests/emptyLhsClassLiterals.kt");
}
@Test
@TestMetadata("EnumEntryAsType.kt")
public void testEnumEntryAsType() throws Exception {
@@ -297,6 +297,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated
runTest("compiler/testData/diagnostics/tests/Dollar.kt");
}
@Test
@TestMetadata("emptyLhsClassLiterals.kt")
public void testEmptyLhsClassLiterals() throws Exception {
runTest("compiler/testData/diagnostics/tests/emptyLhsClassLiterals.kt");
}
@Test
@TestMetadata("EnumEntryAsType.kt")
public void testEnumEntryAsType() throws Exception {
@@ -297,6 +297,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
runTest("compiler/testData/diagnostics/tests/Dollar.kt");
}
@Test
@TestMetadata("emptyLhsClassLiterals.kt")
public void testEmptyLhsClassLiterals() throws Exception {
runTest("compiler/testData/diagnostics/tests/emptyLhsClassLiterals.kt");
}
@Test
@TestMetadata("EnumEntryAsType.kt")
public void testEnumEntryAsType() throws Exception {
@@ -297,6 +297,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
runTest("compiler/testData/diagnostics/tests/Dollar.kt");
}
@Test
@TestMetadata("emptyLhsClassLiterals.kt")
public void testEmptyLhsClassLiterals() throws Exception {
runTest("compiler/testData/diagnostics/tests/emptyLhsClassLiterals.kt");
}
@Test
@TestMetadata("EnumEntryAsType.kt")
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> {
parameter<ConeKotlinType>("lhsType")
}
val UNSUPPORTED_CLASS_LITERALS_WITH_EMPTY_LHS by error<KtElement>()
}
val OVERRIDES by object : DiagnosticGroup("overrides") {
@@ -463,6 +463,7 @@ object FirErrors {
val CLASS_LITERAL_LHS_NOT_A_CLASS 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 UNSUPPORTED_CLASS_LITERALS_WITH_EMPTY_LHS by error0<KtElement>()
// overrides
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.UNSIGNED_LITERAL_WITHOUT_DECLARATIONS_ON_CLASSPATH
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_FEATURE
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",
RENDER_TYPE
)
map.put(UNSUPPORTED_CLASS_LITERALS_WITH_EMPTY_LHS, "Class literals with empty left hand side are unsupported")
// Value classes
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(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(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(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 ->
FirErrors.AMBIGUOUS_ANNOTATION_ARGUMENT.createOn(source, listOfNotNull(symbolFromCompilerPhase, symbolFromAnnotationArgumentsPhase))
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}")
}
@@ -463,11 +463,13 @@ class LightTreeRawFirExpressionBuilder(
if (it.isExpression()) firReceiverExpression = getAsFirExpression(it, "No receiver in class literal")
}
val classLiteralSource = classLiteralExpression.toFirSourceElement()
return buildGetClassCall {
source = classLiteralExpression.toFirSourceElement()
source = classLiteralSource
argumentList = buildUnaryArgumentList(
firReceiverExpression
?: buildErrorExpression(null, ConeSyntaxDiagnostic("No receiver in class literal"))
?: buildErrorExpression(classLiteralSource, ConeUnsupportedClassLiteralsWithEmptyLhs)
)
}
}
@@ -276,9 +276,12 @@ open class PsiRawFirBuilder(
kind: DiagnosticKind = DiagnosticKind.ExpressionExpected
): 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) {
return buildErrorExpression(source = null, diagnosticFn())
return buildErrorExpression(source = sourceWhenThisIsNull?.toFirSourceElement(), diagnosticFn())
}
val result = when (val fir = convertElement(this, null)) {
@@ -2929,7 +2932,11 @@ open class PsiRawFirBuilder(
override fun visitClassLiteralExpression(expression: KtClassLiteralExpression, data: FirElement?): FirElement {
return buildGetClassCall {
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"
}
object ConeUnsupportedClassLiteralsWithEmptyLhs : ConeDiagnostic {
override val reason: String get() = "No receiver in class literal"
}
enum class DiagnosticKind {
ExpressionExpected,
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() {
<!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");
}
@Test
@TestMetadata("emptyLhsClassLiterals.kt")
public void testEmptyLhsClassLiterals() throws Exception {
runTest("compiler/testData/diagnostics/tests/emptyLhsClassLiterals.kt");
}
@Test
@TestMetadata("EnumEntryAsType.kt")
public void testEnumEntryAsType() throws Exception {