[FIR] Fix incorrect diagnostic behaviour + several enum diagnostics

This commit is contained in:
Nick
2020-08-03 18:01:58 +03:00
committed by Mikhail Glukhikh
parent b76f757d47
commit f74eb07203
18 changed files with 283 additions and 21 deletions
@@ -0,0 +1,6 @@
enum class A {
A(1), B(2), C("test");
constructor(x: Int) : <!DELEGATION_SUPER_CALL_IN_ENUM_CONSTRUCTOR!>super<!>()
constructor(t: String) : this(10)
}
@@ -0,0 +1,38 @@
FILE: delegationSuperCallInEnumConstructor.kt
public final enum class A : R|kotlin/Enum<A>| {
public final static enum entry A: R|A| = object : R|A| {
private constructor(): R|<anonymous>| {
super<R|A|>(Int(1))
}
}
public final static enum entry B: R|A| = object : R|A| {
private constructor(): R|<anonymous>| {
super<R|A|>(Int(2))
}
}
public final static enum entry C: R|A| = object : R|A| {
private constructor(): R|<anonymous>| {
super<R|A|>(String(test))
}
}
private constructor(x: R|kotlin/Int|): R|A| {
super<R|kotlin/Enum<A>|>()
}
private constructor(t: R|kotlin/String|): R|A| {
this<R|A|>(Int(10))
}
public final static fun values(): R|kotlin/Array<A>| {
}
public final static fun valueOf(value: R|kotlin/String|): R|A| {
}
}
@@ -0,0 +1,18 @@
class A(x: Int) {
constructor(z: String) : this(10)
}
class B : A {
<!EXPLICIT_DELEGATION_CALL_REQUIRED, NONE_APPLICABLE!>constructor()<!>
constructor(z: String) : this()
}
<!SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR!>class C : A(20) {
<!EXPLICIT_DELEGATION_CALL_REQUIRED, NONE_APPLICABLE!>constructor()<!>
constructor(z: String) : this()
}<!>
class D() : A(20) {
<!NONE_APPLICABLE, PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>constructor(x: Int)<!>
constructor(z: String) : this()
}
@@ -0,0 +1,45 @@
FILE: explicitDelegationCallRequired.kt
public final class A : R|kotlin/Any| {
public constructor(x: R|kotlin/Int|): R|A| {
super<R|kotlin/Any|>()
}
public constructor(z: R|kotlin/String|): R|A| {
this<R|A|>(Int(10))
}
}
public final class B : R|A| {
public constructor(): R|B| {
super<R|A|>()
}
public constructor(z: R|kotlin/String|): R|B| {
this<R|B|>()
}
}
public final class C : R|A| {
public constructor(): R|C| {
super<R|A|>()
}
public constructor(z: R|kotlin/String|): R|C| {
this<R|C|>()
}
}
public final class D : R|A| {
public constructor(): R|D| {
super<R|A|>(Int(20))
}
public constructor(x: R|kotlin/Int|): R|D| {
super<R|A|>()
}
public constructor(z: R|kotlin/String|): R|D| {
this<R|D|>()
}
}
@@ -0,0 +1,9 @@
data class A {}
<!PRIMARY_CONSTRUCTOR_REQUIRED_FOR_DATA_CLASS!>data class B {
constructor()
}<!>
<!PRIMARY_CONSTRUCTOR_REQUIRED_FOR_DATA_CLASS!>data class C {
constructor(x: Int)
}<!>
@@ -0,0 +1,21 @@
FILE: primaryConstructorRequiredForDataClass.kt
public final data class A : R|kotlin/Any| {
public constructor(): R|A| {
super<R|kotlin/Any|>()
}
public final fun copy(): R|A|
}
public final data class B : R|kotlin/Any| {
public constructor(): R|B| {
super<R|kotlin/Any|>()
}
}
public final data class C : R|kotlin/Any| {
public constructor(x: R|kotlin/Int|): R|C| {
super<R|kotlin/Any|>()
}
}
@@ -921,6 +921,16 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/cyclicConstructorDelegationCall.kt");
}
@TestMetadata("delegationSuperCallInEnumConstructor.kt")
public void testDelegationSuperCallInEnumConstructor() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/delegationSuperCallInEnumConstructor.kt");
}
@TestMetadata("explicitDelegationCallRequired.kt")
public void testExplicitDelegationCallRequired() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/explicitDelegationCallRequired.kt");
}
@TestMetadata("incompatibleModifiers.kt")
public void testIncompatibleModifiers() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/incompatibleModifiers.kt");
@@ -951,6 +961,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/notASupertype.kt");
}
@TestMetadata("primaryConstructorRequiredForDataClass.kt")
public void testPrimaryConstructorRequiredForDataClass() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/primaryConstructorRequiredForDataClass.kt");
}
@TestMetadata("projectionsOnNonClassTypeArguments.kt")
public void testProjectionsOnNonClassTypeArguments() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/projectionsOnNonClassTypeArguments.kt");
@@ -921,6 +921,16 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/cyclicConstructorDelegationCall.kt");
}
@TestMetadata("delegationSuperCallInEnumConstructor.kt")
public void testDelegationSuperCallInEnumConstructor() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/delegationSuperCallInEnumConstructor.kt");
}
@TestMetadata("explicitDelegationCallRequired.kt")
public void testExplicitDelegationCallRequired() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/explicitDelegationCallRequired.kt");
}
@TestMetadata("incompatibleModifiers.kt")
public void testIncompatibleModifiers() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/incompatibleModifiers.kt");
@@ -951,6 +961,11 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/notASupertype.kt");
}
@TestMetadata("primaryConstructorRequiredForDataClass.kt")
public void testPrimaryConstructorRequiredForDataClass() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/primaryConstructorRequiredForDataClass.kt");
}
@TestMetadata("projectionsOnNonClassTypeArguments.kt")
public void testProjectionsOnNonClassTypeArguments() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/projectionsOnNonClassTypeArguments.kt");
@@ -20,8 +20,10 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
override val memberDeclarationCheckers: List<FirMemberDeclarationChecker> = listOf(
FirInfixFunctionDeclarationChecker,
FirExposedVisibilityDeclarationChecker,
FirCyclicConstructorDelegationCallChecker,
FirCommonConstructorDelegationIssuesChecker,
FirSupertypeInitializedWithoutPrimaryConstructor,
FirDelegationSuperCallInEnumConstructorChecker,
FirPrimaryConstructorRequiredForDataClassChecker,
)
override val constructorCheckers: List<FirConstructorChecker> = listOf(
@@ -12,12 +12,12 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.FirConstructor
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeAmbiguityError
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
object FirCyclicConstructorDelegationCallChecker : FirMemberDeclarationChecker() {
object FirCommonConstructorDelegationIssuesChecker : FirMemberDeclarationChecker() {
override fun check(declaration: FirMemberDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration !is FirRegularClass) {
return
@@ -26,9 +26,15 @@ object FirCyclicConstructorDelegationCallChecker : FirMemberDeclarationChecker()
val cyclicConstructors = mutableSetOf<FirConstructor>()
var hasPrimaryConstructor = false
// secondary; non-cyclic;
// candidates for further analysis
val otherConstructors = mutableSetOf<FirConstructor>()
for (it in declaration.declarations) {
if (it is FirConstructor) {
if (!it.isPrimary) {
otherConstructors += it
it.findCycle(cyclicConstructors)?.let { visited ->
cyclicConstructors += visited
}
@@ -38,15 +44,26 @@ object FirCyclicConstructorDelegationCallChecker : FirMemberDeclarationChecker()
}
}
otherConstructors -= cyclicConstructors
if (hasPrimaryConstructor) {
for (it in declaration.declarations) {
if (
it is FirConstructor && !it.isPrimary && it !in cyclicConstructors &&
it.delegatedConstructor?.constructedTypeRef?.getType() != it.returnTypeRef.getType()
) {
for (it in otherConstructors) {
if (it.delegatedConstructor?.isThis == false) {
reporter.reportPrimaryConstructorDelegationCallExpected(it.delegatedConstructor?.source)
}
}
} else {
for (it in otherConstructors) {
val callee = it.delegatedConstructor?.calleeReference
// couldn't find proper super() constructor implicitly
if (
callee is FirErrorNamedReference && callee.diagnostic is ConeAmbiguityError &&
it.delegatedConstructor?.source?.startOffset == it.delegatedConstructor?.source?.endOffset
) {
reporter.reportExplicitDelegationCallRequired(it.source)
}
}
}
cyclicConstructors.forEach {
@@ -78,11 +95,6 @@ object FirCyclicConstructorDelegationCallChecker : FirMemberDeclarationChecker()
?.resolvedSymbol
?.fir.safeAs()
private fun FirTypeRef.getType() = when (this) {
is FirResolvedTypeRef -> type
else -> null
}
private fun DiagnosticReporter.reportCyclicConstructorDelegationCall(source: FirSourceElement?) {
source?.let { report(FirErrors.CYCLIC_CONSTRUCTOR_DELEGATION_CALL.on(it)) }
}
@@ -90,4 +102,8 @@ object FirCyclicConstructorDelegationCallChecker : FirMemberDeclarationChecker()
private fun DiagnosticReporter.reportPrimaryConstructorDelegationCallExpected(source: FirSourceElement?) {
source?.let { report(FirErrors.PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED.on(it)) }
}
private fun DiagnosticReporter.reportExplicitDelegationCallRequired(source: FirSourceElement?) {
source?.let { report(FirErrors.EXPLICIT_DELEGATION_CALL_REQUIRED.on(it)) }
}
}
@@ -0,0 +1,40 @@
/*
* Copyright 2010-2020 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.declaration
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.FirConstructor
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
object FirDelegationSuperCallInEnumConstructorChecker : FirMemberDeclarationChecker() {
override fun check(declaration: FirMemberDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration !is FirRegularClass || declaration.classKind != ClassKind.ENUM_CLASS) {
return
}
for (it in declaration.declarations) {
// checking offsets is needed because we
// still have a real source element ("") here,
// even if the user hasn't written anything
if (
it is FirConstructor && !it.isPrimary &&
it.delegatedConstructor?.isThis == false &&
it.delegatedConstructor?.source?.startOffset != it.delegatedConstructor?.source?.endOffset
) {
reporter.report(it.delegatedConstructor?.source)
}
}
}
private fun DiagnosticReporter.report(source: FirSourceElement?) {
source?.let { report(FirErrors.DELEGATION_SUPER_CALL_IN_ENUM_CONSTRUCTOR.on(it)) }
}
}
@@ -0,0 +1,34 @@
/*
* Copyright 2010-2020 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.declaration
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.FirConstructor
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.isData
object FirPrimaryConstructorRequiredForDataClassChecker : FirMemberDeclarationChecker() {
override fun check(declaration: FirMemberDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration !is FirRegularClass || declaration.classKind != ClassKind.CLASS || !declaration.isData) {
return
}
val hasPrimaryConstructor = declaration.declarations.any { it is FirConstructor && it.isPrimary }
if (!hasPrimaryConstructor) {
reporter.report(declaration.source)
}
}
private fun DiagnosticReporter.report(source: FirSourceElement?) {
source?.let { report(FirErrors.PRIMARY_CONSTRUCTOR_REQUIRED_FOR_DATA_CLASS.on(it)) }
}
}
@@ -67,6 +67,9 @@ object FirErrors {
val CYCLIC_CONSTRUCTOR_DELEGATION_CALL by warning0<FirSourceElement, PsiElement>()
val PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED by warning0<FirSourceElement, PsiElement>()
val SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR by warning0<FirSourceElement, PsiElement>()
val DELEGATION_SUPER_CALL_IN_ENUM_CONSTRUCTOR by warning0<FirSourceElement, PsiElement>()
val PRIMARY_CONSTRUCTOR_REQUIRED_FOR_DATA_CLASS by warning0<FirSourceElement, PsiElement>()
val EXPLICIT_DELEGATION_CALL_REQUIRED by warning0<FirSourceElement, PsiElement>()
// Exposed visibility group
val EXPOSED_TYPEALIAS_EXPANDED_TYPE by error3<FirSourceElement, PsiElement, FirEffectiveVisibility, FirMemberDeclaration, FirEffectiveVisibility>()
@@ -8,7 +8,7 @@ expect open class A {
}
expect class B : A {
<!NONE_APPLICABLE!>constructor(i: Int)<!>
<!EXPLICIT_DELEGATION_CALL_REQUIRED, NONE_APPLICABLE!>constructor(i: Int)<!>
constructor() : super("B")
}
@@ -7,8 +7,8 @@ data class A2(val y: String, val z: Int) {
constructor(x: String): this(x, 0)
}
data class A3 {
<!PRIMARY_CONSTRUCTOR_REQUIRED_FOR_DATA_CLASS!>data class A3 {
constructor()
}
}<!>
data class A4 internal constructor()
@@ -6,7 +6,7 @@ enum class A {
constructor(x: Int)
constructor(x: Int, y: Int): this(x+y)
constructor(x: Double): this(x.toInt(), 1)
constructor(x: String): super(x, 1)
constructor(x: String): <!DELEGATION_SUPER_CALL_IN_ENUM_CONSTRUCTOR!>super<!>(x, 1)
}
enum class B(x: Int) {
@@ -14,7 +14,7 @@ enum class B(x: Int) {
constructor(x: Int, y: Int): this(x+y)
constructor(x: Double): this(x.toInt(), 1)
constructor(x: String): <!PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>super<!>(x, 1)
constructor(x: String): <!DELEGATION_SUPER_CALL_IN_ENUM_CONSTRUCTOR, PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>super<!>(x, 1)
}
enum class C {
@@ -26,7 +26,7 @@ open class B2 {
}
class A2 : B2 {
<!NONE_APPLICABLE!>constructor()<!>
<!EXPLICIT_DELEGATION_CALL_REQUIRED, NONE_APPLICABLE!>constructor()<!>
constructor(x: Int) : <!NONE_APPLICABLE!>super<!>()
}
@@ -6,5 +6,5 @@ open class B(x: Double) {
interface C
class A : B, C {
constructor(): <!NONE_APPLICABLE!>super<!>(' ')
<!NONE_APPLICABLE!>constructor(x: Int)<!>
<!EXPLICIT_DELEGATION_CALL_REQUIRED, NONE_APPLICABLE!>constructor(x: Int)<!>
}