FIR: introduce FunInterfaceDeclarationChecker

This commit is contained in:
Артём
2021-03-05 12:53:23 +03:00
committed by Mikhail Glukhikh
parent 867d7b5bca
commit 393a19db54
20 changed files with 389 additions and 25 deletions
@@ -179,6 +179,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract
runTest("compiler/fir/analysis-tests/testData/resolve/ft.kt");
}
@TestMetadata("funInterfaceDeclaration.kt")
public void testFunInterfaceDeclaration() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/funInterfaceDeclaration.kt");
}
@TestMetadata("functionTypeAlias.kt")
public void testFunctionTypeAlias() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/functionTypeAlias.kt");
@@ -0,0 +1,55 @@
FILE: funInterfaceDeclaration.kt
public abstract interface Test1 : R|kotlin/Any| {
public abstract fun foo(): R|kotlin/Unit|
public abstract fun boo(): R|kotlin/Unit|
}
public abstract interface Test2 : R|kotlin/Any| {
}
public abstract interface Test3 : R|kotlin/Any| {
public abstract val a: R|kotlin/Int|
public get(): R|kotlin/Int|
public abstract fun foo(): R|kotlin/Unit|
}
public abstract interface Test4 : R|kotlin/Any| {
public abstract fun <T> foo(a: R|T|): R|kotlin/Unit|
}
public abstract interface Test5 : R|kotlin/Any| {
public abstract fun foo(a: R|kotlin/Int| = Int(5)): R|kotlin/Unit|
}
public abstract interface Test6 : R|kotlin/Any| {
public abstract suspend fun foo(): R|kotlin/Unit|
}
public abstract interface Test7 : R|kotlin/Any| {
public abstract fun foo(): R|kotlin/Unit|
}
public abstract interface Test8 : R|Test7| {
public abstract fun boo(): R|kotlin/Unit|
}
public abstract interface Test9 : R|kotlin/Any| {
public open fun num(m: R|kotlin/Int|): R|kotlin/Int| {
^num R|<local>/m|.R|kotlin/Int.times|(R|<local>/m|)
}
}
public abstract interface Test10 : R|Test9| {
public abstract fun test(): R|kotlin/Unit|
}
public abstract interface Test11 : R|kotlin/Any| {
public abstract val a: R|kotlin/Int|
public get(): R|kotlin/Int|
}
public abstract interface Test12 : R|Test11| {
public abstract fun test(): R|kotlin/Unit|
}
@@ -0,0 +1,38 @@
<!FUN_INTERFACE_WRONG_COUNT_OF_ABSTRACT_MEMBERS!>fun<!> interface Test1{
fun foo()
fun boo()
}
<!FUN_INTERFACE_WRONG_COUNT_OF_ABSTRACT_MEMBERS!>fun<!> interface Test2 {}
fun interface Test3 {
<!FUN_INTERFACE_CANNOT_HAVE_ABSTRACT_PROPERTIES!>val<!> a: Int
fun foo()
}
fun interface Test4{
fun <<!FUN_INTERFACE_ABSTRACT_METHOD_WITH_TYPE_PARAMETERS!>T<!>> foo(a: T)
}
fun interface Test5{
fun foo(<!FUN_INTERFACE_ABSTRACT_METHOD_WITH_DEFAULT_VALUE!>a: Int = 5<!>)
}
fun interface Test6{
suspend fun foo()
}
fun interface Test7{
fun foo()
}
<!FUN_INTERFACE_WRONG_COUNT_OF_ABSTRACT_MEMBERS!>fun<!> interface Test8: Test7{
fun boo()
}
interface Test9 {
fun num(m: Int): Int {
return m * m
}
}
fun interface Test10 : Test9 {
fun test()
}
interface Test11 {
val a: Int
}
<!FUN_INTERFACE_CANNOT_HAVE_ABSTRACT_PROPERTIES!>fun<!> interface Test12 : Test11 {
fun test()
}
@@ -2,11 +2,11 @@ fun interface MyRunnable {
fun foo(x: Int): Boolean
}
fun interface WithProperty {
val x: Int
<!FUN_INTERFACE_WRONG_COUNT_OF_ABSTRACT_MEMBERS!>fun<!> interface WithProperty {
<!FUN_INTERFACE_CANNOT_HAVE_ABSTRACT_PROPERTIES!>val<!> x: Int
}
fun interface TwoAbstract : MyRunnable {
<!FUN_INTERFACE_WRONG_COUNT_OF_ABSTRACT_MEMBERS!>fun<!> interface TwoAbstract : MyRunnable {
fun bar()
}
@@ -206,6 +206,12 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest {
runTest("compiler/fir/analysis-tests/testData/resolve/ft.kt");
}
@Test
@TestMetadata("funInterfaceDeclaration.kt")
public void testFunInterfaceDeclaration() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/funInterfaceDeclaration.kt");
}
@Test
@TestMetadata("functionTypeAlias.kt")
public void testFunctionTypeAlias() throws Exception {
@@ -206,6 +206,12 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
runTest("compiler/fir/analysis-tests/testData/resolve/ft.kt");
}
@Test
@TestMetadata("funInterfaceDeclaration.kt")
public void testFunInterfaceDeclaration() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/funInterfaceDeclaration.kt");
}
@Test
@TestMetadata("functionTypeAlias.kt")
public void testFunctionTypeAlias() throws Exception {
@@ -64,6 +64,9 @@ enum class PositioningStrategy(private val strategy: String? = null) {
LONG_LITERAL_SUFFIX,
REIFIED_MODIFIER,
TYPE_PARAMETERS_LIST,
FUN_MODIFIER,
SUSPEND_MODIFIER,
FUN_INTERFACE_ABSTRACT_PROPERTY,
;
@@ -14,7 +14,6 @@ import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.descriptors.EffectiveVisibility
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.PrivateForInline
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirExpression
@@ -500,6 +499,11 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
val FUN_INTERFACES by object : DiagnosticGroup("Fun interfaces") {
val FUN_INTERFACE_CONSTRUCTOR_REFERENCE by error<KtExpression>(PositioningStrategy.REFERENCE_BY_QUALIFIED)
val FUN_INTERFACE_WRONG_COUNT_OF_ABSTRACT_MEMBERS by error<KtClass>(PositioningStrategy.FUN_MODIFIER)
val FUN_INTERFACE_CANNOT_HAVE_ABSTRACT_PROPERTIES by error<KtDeclaration>(PositioningStrategy.FUN_INTERFACE_ABSTRACT_PROPERTY)
val FUN_INTERFACE_ABSTRACT_METHOD_WITH_TYPE_PARAMETERS by error<PsiElement>()
val FUN_INTERFACE_ABSTRACT_METHOD_WITH_DEFAULT_VALUE by error<PsiElement>()
val FUN_INTERFACE_WITH_SUSPEND_FUNCTION by error<KtFunction>(PositioningStrategy.SUSPEND_MODIFIER)
}
val PROPERTIES_AND_ACCESSORS by object : DiagnosticGroup("Properties & accessors") {
@@ -310,6 +310,11 @@ object FirErrors {
// Fun interfaces
val FUN_INTERFACE_CONSTRUCTOR_REFERENCE by error0<KtExpression>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
val FUN_INTERFACE_WRONG_COUNT_OF_ABSTRACT_MEMBERS by error0<KtClass>(SourceElementPositioningStrategies.FUN_MODIFIER)
val FUN_INTERFACE_CANNOT_HAVE_ABSTRACT_PROPERTIES by error0<KtDeclaration>(SourceElementPositioningStrategies.FUN_INTERFACE_ABSTRACT_PROPERTY)
val FUN_INTERFACE_ABSTRACT_METHOD_WITH_TYPE_PARAMETERS by error0<PsiElement>()
val FUN_INTERFACE_ABSTRACT_METHOD_WITH_DEFAULT_VALUE by error0<PsiElement>()
val FUN_INTERFACE_WITH_SUSPEND_FUNCTION by error0<KtFunction>(SourceElementPositioningStrategies.SUSPEND_MODIFIER)
// Properties & accessors
val ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS by error2<KtModifierListOwner, FirMemberDeclaration, FirMemberDeclaration>(SourceElementPositioningStrategies.MODALITY_MODIFIER)
@@ -0,0 +1,87 @@
/*
* Copyright 2010-2021 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.config.LanguageFeature
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClass
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_ABSTRACT_METHOD_WITH_DEFAULT_VALUE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_ABSTRACT_METHOD_WITH_TYPE_PARAMETERS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_CANNOT_HAVE_ABSTRACT_PROPERTIES
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_WITH_SUSPEND_FUNCTION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_WRONG_COUNT_OF_ABSTRACT_MEMBERS
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.scopes.getFunctions
import org.jetbrains.kotlin.fir.scopes.getProperties
object FirFunInterfaceDeclarationChecker : FirRegularClassChecker() {
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.isInterface || !declaration.isFun) return
val scope = declaration.unsubstitutedScope(context)
var abstractFunction: FirSimpleFunction? = null
for (name in scope.getCallableNames()) {
val functions = scope.getFunctions(name)
val properties = scope.getProperties(name)
for (function in functions) {
if (function.fir.isAbstract) {
if (abstractFunction == null) {
abstractFunction = function.fir
} else {
reporter.reportOn(declaration.source, FUN_INTERFACE_WRONG_COUNT_OF_ABSTRACT_MEMBERS, context)
}
}
}
for (property in properties) {
val firProperty = property.fir as? FirProperty ?: continue
if (firProperty.isAbstract) {
val source =
if (firProperty.getContainingClass(context) != declaration)
declaration.source
else
firProperty.source
reporter.reportOn(source, FUN_INTERFACE_CANNOT_HAVE_ABSTRACT_PROPERTIES, context)
}
}
}
if (abstractFunction == null) {
reporter.reportOn(declaration.source, FUN_INTERFACE_WRONG_COUNT_OF_ABSTRACT_MEMBERS, context)
return
}
when {
abstractFunction.typeParameters.isNotEmpty() ->
reporter.reportOn(
abstractFunction.typeParameters.first().source,
FUN_INTERFACE_ABSTRACT_METHOD_WITH_TYPE_PARAMETERS,
context
)
abstractFunction.isSuspend ->
if (!context.session.languageVersionSettings.supportsFeature(LanguageFeature.SuspendFunctionsInFunInterfaces)) {
reporter.reportOn(abstractFunction.source, FUN_INTERFACE_WITH_SUSPEND_FUNCTION, context)
}
}
abstractFunction.valueParameters.forEach {
if (it.defaultValue != null) {
reporter.reportOn(it.source, FUN_INTERFACE_ABSTRACT_METHOD_WITH_DEFAULT_VALUE, context)
}
}
}
}
@@ -111,8 +111,13 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXTENSION_PROPERT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXTENSION_PROPERTY_WITH_BACKING_FIELD
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FINAL_UPPER_BOUND
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FORBIDDEN_VARARG_PARAMETER_TYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUNCTION_DECLARATION_WITH_NO_NAME
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_CONSTRUCTOR_REFERENCE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_ABSTRACT_METHOD_WITH_DEFAULT_VALUE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_ABSTRACT_METHOD_WITH_TYPE_PARAMETERS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_CANNOT_HAVE_ABSTRACT_PROPERTIES
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_WITH_SUSPEND_FUNCTION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_WRONG_COUNT_OF_ABSTRACT_MEMBERS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUNCTION_DECLARATION_WITH_NO_NAME
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.GENERIC_THROWABLE_SUBCLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.GETTER_VISIBILITY_DIFFERS_FROM_PROPERTY_VISIBILITY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.HAS_NEXT_FUNCTION_AMBIGUITY
@@ -698,6 +703,15 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
"An anonymous function is not allowed to specify default values for its parameters"
)
map.put(USELESS_VARARG_ON_PARAMETER, "Vararg on this parameter is useless")
map.put(
FUN_INTERFACE_WRONG_COUNT_OF_ABSTRACT_MEMBERS,
"Fun interface must have exactly one abstract function"
)
map.put(FUN_INTERFACE_CANNOT_HAVE_ABSTRACT_PROPERTIES, "Fun interface can not have abstract properties")
map.put(FUN_INTERFACE_ABSTRACT_METHOD_WITH_TYPE_PARAMETERS, "Fun interface can not have abstract metod with type parameters")
map.put(FUN_INTERFACE_ABSTRACT_METHOD_WITH_DEFAULT_VALUE, "Fun interface abstract method can not have default value")
map.put(FUN_INTERFACE_WITH_SUSPEND_FUNCTION, "Fun interface abstract method can not be suspend")
map.put(MULTIPLE_VARARG_PARAMETERS, "Multiple vararg-parameters are prohibited")
map.put(FORBIDDEN_VARARG_PARAMETER_TYPE, "Forbidden vararg parameter type: {0}", RENDER_TYPE)
map.put(VALUE_PARAMETER_WITH_NO_TYPE_ANNOTATION, "A type annotation is required on a value parameter")
@@ -340,6 +340,12 @@ object LightTreePositioningStrategies {
val CONST_MODIFIER: LightTreePositioningStrategy =
ModifierSetBasedLightTreePositioningStrategy(TokenSet.create(KtTokens.CONST_KEYWORD))
val FUN_MODIFIER: LightTreePositioningStrategy =
ModifierSetBasedLightTreePositioningStrategy(TokenSet.create(KtTokens.FUN_KEYWORD))
val SUSPEND_MODIFIER: LightTreePositioningStrategy =
ModifierSetBasedLightTreePositioningStrategy(TokenSet.create(KtTokens.SUSPEND_KEYWORD))
val INLINE_OR_VALUE_MODIFIER: LightTreePositioningStrategy =
ModifierSetBasedLightTreePositioningStrategy(TokenSet.create(KtTokens.INLINE_KEYWORD, KtTokens.VALUE_KEYWORD))
@@ -443,6 +449,22 @@ object LightTreePositioningStrategies {
}
}
val FUN_INTERFACE_ABSTRACT_PROPERTY: LightTreePositioningStrategy = object : LightTreePositioningStrategy() {
override fun mark(
node: LighterASTNode,
startOffset: Int,
endOffset: Int,
tree: FlyweightCapableTreeStructure<LighterASTNode>
): List<TextRange> {
return when (node.tokenType) {
KtNodeTypes.CLASS -> FUN_MODIFIER.mark(node, startOffset, endOffset, tree)
KtNodeTypes.PROPERTY -> VAL_OR_VAR_NODE.mark(node, startOffset, endOffset, tree)
else -> DEFAULT.mark(node, startOffset, endOffset, tree)
}
}
}
val REFERENCE_BY_QUALIFIED: LightTreePositioningStrategy = FindReferencePositioningStrategy(false)
val REFERENCED_NAME_BY_QUALIFIED: LightTreePositioningStrategy = FindReferencePositioningStrategy(true)
@@ -18,6 +18,11 @@ object SourceElementPositioningStrategies {
PositioningStrategies.VAL_OR_VAR_NODE
)
val FUN_INTERFACE_ABSTRACT_PROPERTY = SourceElementPositioningStrategy(
LightTreePositioningStrategies.FUN_INTERFACE_ABSTRACT_PROPERTY,
PositioningStrategies.FUN_INTERFACE_ABSTRACT_PROPERTY
)
val COMPANION_OBJECT = SourceElementPositioningStrategy(
LightTreePositioningStrategies.COMPANION_OBJECT,
PositioningStrategies.COMPANION_OBJECT
@@ -103,6 +108,17 @@ object SourceElementPositioningStrategies {
PositioningStrategies.INNER_MODIFIER
)
val FUN_MODIFIER = SourceElementPositioningStrategy(
LightTreePositioningStrategies.FUN_MODIFIER,
PositioningStrategies.FUN_MODIFIER
)
val SUSPEND_MODIFIER = SourceElementPositioningStrategy(
LightTreePositioningStrategies.SUSPEND_MODIFIER,
PositioningStrategies.SUSPEND_MODIFIER
)
val OPERATOR = SourceElementPositioningStrategy(
LightTreePositioningStrategies.OPERATOR,
PositioningStrategies.OPERATOR
@@ -78,11 +78,12 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
FirDataClassPrimaryConstructorChecker,
FirPrimaryConstructorSuperTypeChecker,
FirTypeParametersInObjectChecker,
FirMemberFunctionsChecker,
FirMemberPropertiesChecker,
FirNestedClassChecker,
FirInlineClassDeclarationChecker,
)
FirFunInterfaceDeclarationChecker,
FirMemberFunctionsChecker,
FirMemberPropertiesChecker,
FirNestedClassChecker,
FirInlineClassDeclarationChecker,
)
override val constructorCheckers: Set<FirConstructorChecker>
get() = setOf(