[FIR] Implement PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL

^KT-59382 Fixed
This commit is contained in:
Nikolay Lunyak
2023-07-07 17:00:51 +03:00
committed by Space Team
parent 00fb927624
commit 4bba93f633
16 changed files with 79 additions and 82 deletions
@@ -724,6 +724,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL) { firDiagnostic ->
ProtectedConstructorNotInSuperCallImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a),
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.SUPERTYPE_NOT_INITIALIZED) { firDiagnostic ->
SupertypeNotInitializedImpl(
firDiagnostic as KtPsiDiagnostic,
@@ -542,6 +542,11 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = PrimaryConstructorDelegationCallExpected::class
}
interface ProtectedConstructorNotInSuperCall : KtFirDiagnostic<KtExpression> {
override val diagnosticClass get() = ProtectedConstructorNotInSuperCall::class
val symbol: KtSymbol
}
interface SupertypeNotInitialized : KtFirDiagnostic<KtTypeReference> {
override val diagnosticClass get() = SupertypeNotInitialized::class
}
@@ -640,6 +640,12 @@ internal class PrimaryConstructorDelegationCallExpectedImpl(
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.PrimaryConstructorDelegationCallExpected
internal class ProtectedConstructorNotInSuperCallImpl(
override val symbol: KtSymbol,
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<KtExpression>(firDiagnostic, token), KtFirDiagnostic.ProtectedConstructorNotInSuperCall
internal class SupertypeNotInitializedImpl(
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
@@ -259,6 +259,9 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val NON_PRIVATE_OR_PROTECTED_CONSTRUCTOR_IN_SEALED by error<PsiElement>()
val CYCLIC_CONSTRUCTOR_DELEGATION_CALL by error<PsiElement>()
val PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED by error<PsiElement>(PositioningStrategy.SECONDARY_CONSTRUCTOR_DELEGATION_CALL)
val PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL by error<KtExpression> {
parameter<FirBasedSymbol<*>>("symbol")
}
// TODO: change it to KtSuperTypeEntry when possible (after re-targeter implementation)
val SUPERTYPE_NOT_INITIALIZED by error<KtTypeReference>()
@@ -224,6 +224,7 @@ object FirErrors {
val NON_PRIVATE_OR_PROTECTED_CONSTRUCTOR_IN_SEALED by error0<PsiElement>()
val CYCLIC_CONSTRUCTOR_DELEGATION_CALL by error0<PsiElement>()
val PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED by error0<PsiElement>(SourceElementPositioningStrategies.SECONDARY_CONSTRUCTOR_DELEGATION_CALL)
val PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL by error1<KtExpression, FirBasedSymbol<*>>()
val SUPERTYPE_NOT_INITIALIZED by error0<KtTypeReference>()
val SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR by error0<PsiElement>()
val DELEGATION_SUPER_CALL_IN_ENUM_CONSTRUCTOR by error0<PsiElement>()
@@ -70,6 +70,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
FirNamedVarargChecker,
FirUnderscoredTypeArgumentSyntaxChecker,
FirContractNotFirstStatementChecker,
FirProtectedConstructorNotInSuperCallChecker,
)
override val propertyAccessExpressionCheckers: Set<FirPropertyAccessExpressionChecker>
@@ -0,0 +1,48 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.analysis.checkers.expression
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.getConstructedClass
import org.jetbrains.kotlin.fir.declarations.isJavaOrEnhancement
import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.originalIfFakeOverride
import org.jetbrains.kotlin.fir.packageFqName
import org.jetbrains.kotlin.fir.references.isError
import org.jetbrains.kotlin.fir.references.resolved
import org.jetbrains.kotlin.fir.references.toResolvedConstructorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
import org.jetbrains.kotlin.util.PrivateForInline
object FirProtectedConstructorNotInSuperCallChecker : FirFunctionCallChecker() {
override fun check(expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter) {
val reference = expression.calleeReference.resolved ?: return
val symbol = reference.toResolvedConstructorSymbol() ?: return
val constructedClass = symbol.getConstructedClass(context.session)
if (
!shouldAllowSuchCallNonetheless(symbol, context) &&
symbol.visibility.normalize() == Visibilities.Protected &&
// Prevent reporting for already invisible references
!reference.isError() &&
context.containingDeclarations.none { it.symbol == constructedClass }
) {
reporter.reportOn(expression.calleeReference.source, FirErrors.PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL, symbol, context)
}
}
@OptIn(PrivateForInline::class)
private fun shouldAllowSuchCallNonetheless(symbol: FirConstructorSymbol, context: CheckerContext): Boolean {
val containingFile = context.containingFile ?: return false
val original = symbol.originalIfFakeOverride() ?: symbol
return original.origin.isJavaOrEnhancement && original.callableId.packageName == containingFile.packageFqName
}
}
@@ -476,6 +476,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROPERTY_WITH_NO_
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROTECTED_CALL_FROM_PUBLIC_INLINE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROTECTED_CALL_FROM_PUBLIC_INLINE_ERROR
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROTECTED_CONSTRUCTOR_CALL_FROM_PUBLIC_INLINE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.QUALIFIED_SUPERTYPE_EXTENDED_BY_OTHER_SUPERTYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RECURSION_IN_IMPLICIT_TYPES
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RECURSION_IN_INLINE
@@ -841,6 +842,7 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
map.put(NON_PRIVATE_OR_PROTECTED_CONSTRUCTOR_IN_SEALED, "Constructor must be private or protected in sealed class")
map.put(CYCLIC_CONSTRUCTOR_DELEGATION_CALL, "There's a cycle in the delegation calls chain")
map.put(PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED, "Primary constructor call expected")
map.put(PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL, "Protected constructor ''{0}'' from other classes can only be used in super-call", SYMBOL)
map.put(SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR, "Supertype initialization is impossible without primary constructor")
map.put(DELEGATION_SUPER_CALL_IN_ENUM_CONSTRUCTOR, "Call to super is not allowed in enum constructor")
map.put(PRIMARY_CONSTRUCTOR_REQUIRED_FOR_DATA_CLASS, "Primary constructor required for data class")
@@ -1,21 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
// FILE: GA.kt
package test.x
open class GA<T> protected constructor()
// FILE: Main.kt
package test
import test.x.GA
class C : GA<Any>() {
companion object {
fun bar() = GA<Any>() // Should be error
}
}
fun main(args: Array<String>) {
C.bar()
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER
// FILE: GA.kt
+1 -1
View File
@@ -5,5 +5,5 @@ open class Foo protected constructor()
inline fun foo(f: () -> Unit) = object: Foo() {}
class A : Foo() {
inline fun foo(f: () -> Unit) = <!PROTECTED_CONSTRUCTOR_CALL_FROM_PUBLIC_INLINE!>Foo<!>()
inline fun foo(f: () -> Unit) = <!PROTECTED_CONSTRUCTOR_CALL_FROM_PUBLIC_INLINE, PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL!>Foo<!>()
}
@@ -1,26 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
open class A protected constructor(x: Int) {
protected constructor() : this(1)
public constructor(x: Double) : this(3)
}
class B4 : A(1) {
init {
A()
A(1)
A(5.0)
}
fun foo() {
A()
A(1)
A(5.0)
object : A() {}
object : A(1) {}
object : A(5.0) {}
class Local : A()
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER
open class A protected constructor(x: Int) {
@@ -1,32 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
// FILE: abc/A.java
package abc;
public class A {
protected A() {}
protected A(int x) {}
public A(double x) {}
}
// FILE: main.kt
import abc.*
class B4 : A(1) {
init {
A()
A(1)
A(5.0)
}
fun foo() {
A()
A(1)
A(5.0)
object : A() {}
object : A(1) {}
object : A(5.0) {}
class Local : A()
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER
// FILE: abc/A.java
@@ -21,8 +21,8 @@ val test3a = MyClass(1.0)
class MyDerived : MyClass(1.0) {
val test4 = <!INVISIBLE_REFERENCE!>MyAlias<!>(1)
val test4a = <!INVISIBLE_REFERENCE!>MyClass<!>(1)
val test5 = MyAlias("")
val test5a = MyClass("")
val test5 = <!PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL!>MyAlias<!>("")
val test5a = <!PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL!>MyClass<!>("")
val test6 = MyAlias(1.0)
val test6a = MyClass(1.0)
}