提交 39df3e2b 编写于 作者: J Jinseong Jeon 提交者: Mikhail Glukhikh

FIR checker: introduce member function checker

上级 5594af0d
......@@ -13,7 +13,7 @@ interface Your<R> {
object My : Your<Double> {
fun <T> T.bar() {}
fun baz()
<!NON_ABSTRACT_FUNCTION_WITH_NO_BODY!>fun baz()<!>
fun Boolean.gau() {}
}
......
......@@ -38,7 +38,7 @@ private class Private {
fun withLocals() {
class Local {
private fun bar()
<!NON_ABSTRACT_FUNCTION_WITH_NO_BODY!>private fun bar()<!>
fun baz() {
bar()
......
......@@ -16,8 +16,7 @@ FILE: RedundantModalityModifierChecker.kt
public abstract fun foo(): R|kotlin/Unit|
private final fun bar(): R|kotlin/Unit| {
}
private final fun bar(): R|kotlin/Unit|
public open fun goo(): R|kotlin/Unit| {
}
......
......@@ -9,14 +9,14 @@ interface Interface {
get() = 42<!>
<!REDUNDANT_MODALITY_MODIFIER{LT}!>// Redundant
<!REDUNDANT_MODALITY_MODIFIER{PSI}!>abstract<!> fun foo()<!>
// error
private final fun bar() {}
<!PRIVATE_FUNCTION_WITH_NO_BODY{LT}!>// error
<!PRIVATE_FUNCTION_WITH_NO_BODY{PSI}!>private<!> final fun bar()<!>
<!REDUNDANT_MODALITY_MODIFIER{LT}!><!REDUNDANT_MODALITY_MODIFIER{PSI}!>open<!> fun goo() {}<!>
<!REDUNDANT_MODALITY_MODIFIER{LT}!><!REDUNDANT_MODALITY_MODIFIER{PSI}!>abstract<!> fun tar()<!>
// error
abstract fun too() {}
<!ABSTRACT_FUNCTION_WITH_BODY{LT}!>// error
<!ABSTRACT_FUNCTION_WITH_BODY{PSI}!>abstract<!> fun too() {}<!>
}
interface B {
<!REDUNDANT_MODALITY_MODIFIER{LT}!><!REDUNDANT_MODALITY_MODIFIER{PSI}!>abstract<!> var bar: Unit<!>
......
......@@ -5,7 +5,7 @@ class A {
}
abstract class B {
<!EXPOSED_FUNCTION_RETURN_TYPE{LT}!>fun <!EXPOSED_FUNCTION_RETURN_TYPE{PSI}!>foo<!>(str: String): A.InnerA<!>
<!EXPOSED_FUNCTION_RETURN_TYPE{LT}, NON_ABSTRACT_FUNCTION_WITH_NO_BODY!>fun <!EXPOSED_FUNCTION_RETURN_TYPE{PSI}!>foo<!>(str: String): A.InnerA<!>
}
private enum class Some {
......
/*
* 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.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.extended.report
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.lexer.KtTokens
// See old FE's [DeclarationsChecker]
object FirMemberFunctionChecker : FirRegularClassChecker() {
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
for (member in declaration.declarations) {
if (member is FirSimpleFunction) {
checkFunction(declaration, member, context, reporter)
}
}
}
private fun checkFunction(
containingDeclaration: FirRegularClass,
function: FirSimpleFunction,
context: CheckerContext,
reporter: DiagnosticReporter
) {
val source = function.source ?: return
if (source.kind is FirFakeSourceElementKind) return
// If multiple (potentially conflicting) modality modifiers are specified, not all modifiers are recorded at `status`.
// So, our source of truth should be the full modifier list retrieved from the source.
val modifierList = with(FirModifierList) { source.getModifierList() }
val isAbstract = function.isAbstract || modifierList?.modifiers?.any { it.token == KtTokens.ABSTRACT_KEYWORD } == true
if (isAbstract) {
if (!containingDeclaration.canHaveAbstractDeclaration) {
reporter.report(source, FirErrors.ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS)
}
if (function.hasBody) {
reporter.report(source, FirErrors.ABSTRACT_FUNCTION_WITH_BODY)
}
}
val isInsideExpectClass = isInsideExpectClass(containingDeclaration, context)
val isOpen = function.isOpen || modifierList?.modifiers?.any { it.token == KtTokens.OPEN_KEYWORD } == true
val isExternal = function.isExternal || modifierList?.modifiers?.any { it.token == KtTokens.EXTERNAL_KEYWORD } == true
if (!function.hasBody) {
if (containingDeclaration.isInterface) {
if (Visibilities.isPrivate(function.visibility)) {
reporter.report(source, FirErrors.PRIVATE_FUNCTION_WITH_NO_BODY)
}
if (!isInsideExpectClass && !isAbstract && isOpen) {
reporter.report(source, FirErrors.REDUNDANT_OPEN_IN_INTERFACE)
}
} else if (!isInsideExpectClass && !isAbstract && !isExternal) {
// TODO: we need to check if modifiers of the function already get some errors, e.g., INCOMPATIBLE_MODIFIERS
reporter.report(FirErrors.NON_ABSTRACT_FUNCTION_WITH_NO_BODY.on(source, function.symbol))
}
}
}
private fun isInsideExpectClass(containingDeclaration: FirRegularClass, context: CheckerContext): Boolean =
// Note that the class that contains the currently visiting function is *not* in the context's containing declarations *yet*.
containingDeclaration.isExpect || context.containingDeclarations.asReversed().any { it is FirRegularClass && it.isExpect }
}
......@@ -14,10 +14,7 @@ import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertyAccessorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.name.Name
......@@ -111,6 +108,7 @@ object FirErrors {
val REDUNDANT_MODIFIER by error2<FirSourceElement, PsiElement, KtModifierKeywordToken, KtModifierKeywordToken>()
val DEPRECATED_MODIFIER_PAIR by error2<FirSourceElement, PsiElement, KtModifierKeywordToken, KtModifierKeywordToken>()
val INCOMPATIBLE_MODIFIERS by error2<FirSourceElement, PsiElement, KtModifierKeywordToken, KtModifierKeywordToken>()
val REDUNDANT_OPEN_IN_INTERFACE by warning0<FirSourceElement, KtModifierListOwner>(SourceElementPositioningStrategies.MODALITY_MODIFIER)
// Applicability
val NONE_APPLICABLE by error1<FirSourceElement, PsiElement, Collection<AbstractFirBasedSymbol<*>>>()
......@@ -149,6 +147,12 @@ object FirErrors {
val LOCAL_OBJECT_NOT_ALLOWED by error1<FirSourceElement, KtNamedDeclaration, Name>(SourceElementPositioningStrategies.DECLARATION_NAME)
val LOCAL_INTERFACE_NOT_ALLOWED by error1<FirSourceElement, KtNamedDeclaration, Name>(SourceElementPositioningStrategies.DECLARATION_NAME)
// Functions
val ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS by error0<FirSourceElement, KtModifierListOwner>(SourceElementPositioningStrategies.MODALITY_MODIFIER)
val ABSTRACT_FUNCTION_WITH_BODY by error0<FirSourceElement, KtModifierListOwner>(SourceElementPositioningStrategies.MODALITY_MODIFIER)
val NON_ABSTRACT_FUNCTION_WITH_NO_BODY by error1<FirSourceElement, PsiElement, FirFunctionSymbol<*>>()
val PRIVATE_FUNCTION_WITH_NO_BODY by error0<FirSourceElement, KtModifierListOwner>(SourceElementPositioningStrategies.VISIBILITY_MODIFIER)
// Properties & accessors
val ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS by error0<FirSourceElement, KtModifierListOwner>(SourceElementPositioningStrategies.MODALITY_MODIFIER)
val PRIVATE_PROPERTY_IN_INTERFACE by error0<FirSourceElement, KtModifierListOwner>(SourceElementPositioningStrategies.VISIBILITY_MODIFIER)
......
......@@ -47,6 +47,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
FirSupertypeInitializedWithoutPrimaryConstructor,
FirTypeParametersInObjectChecker,
FirTypeMismatchOnOverrideChecker,
FirMemberFunctionChecker,
FirMemberPropertyChecker,
)
......
......@@ -23,10 +23,10 @@ abstract class MyAbstractClass() {
abstract val e3: Int = <!ABSTRACT_PROPERTY_WITH_INITIALIZER!>0<!>; <!ABSTRACT_PROPERTY_WITH_GETTER!>get() = a<!>
//methods
fun f()
<!NON_ABSTRACT_FUNCTION_WITH_NO_BODY!>fun f()<!>
fun g() {}
abstract fun h()
abstract fun j() {}
<!ABSTRACT_FUNCTION_WITH_BODY!>abstract<!> fun j() {}
//property accessors
var i: Int abstract get abstract set
......
......@@ -23,10 +23,10 @@ class MyClass() {
<!ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS!>abstract<!> val e3: Int = 0; get() = a
//methods
fun f()
<!NON_ABSTRACT_FUNCTION_WITH_NO_BODY!>fun f()<!>
fun g() {}
abstract fun h()
abstract fun j() {}
<!ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS!>abstract<!> fun h()
<!ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS, ABSTRACT_FUNCTION_WITH_BODY!>abstract<!> fun j() {}
//property accessors
var i: Int abstract get abstract set
......
......@@ -26,7 +26,7 @@ interface MyTrait {
fun f()
fun g() {}
abstract fun h()
abstract fun j() {}
<!ABSTRACT_FUNCTION_WITH_BODY!>abstract<!> fun j() {}
//property accessors
var i: Int abstract get abstract set
......
......@@ -13,5 +13,5 @@ interface T {
}
class A {
final fun foo()
<!NON_ABSTRACT_FUNCTION_WITH_NO_BODY!>final fun foo()<!>
}
\ No newline at end of file
......@@ -25,10 +25,10 @@ enum class MyEnum() {
abstract val e3: Int = <!ABSTRACT_PROPERTY_WITH_INITIALIZER!>0<!>; <!ABSTRACT_PROPERTY_WITH_GETTER!>get() = a<!>
//methods
fun f()
<!NON_ABSTRACT_FUNCTION_WITH_NO_BODY!>fun f()<!>
fun g() {}
abstract fun h()
abstract fun j() {}
<!ABSTRACT_FUNCTION_WITH_BODY!>abstract<!> fun j() {}
//property accessors
var i: Int abstract get abstract set
......
......@@ -6,7 +6,7 @@ interface My {
final val y: Int
final val yy: Int
get() = 1
private fun foo(): Int
<!PRIVATE_FUNCTION_WITH_NO_BODY!>private<!> fun foo(): Int
// ok
private fun bar() = 42
}
// !LANGUAGE: +MultiPlatformProjects
// MODULE: m1-common
// FILE: common.kt
interface Foo {
fun foo()
}
expect class NonAbstractClass : Foo {
abstract fun bar()
<!ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS!>abstract<!> val baz: Int
abstract override fun foo()
}
expect abstract class AbstractClass : Foo {
abstract fun bar()
abstract val baz: Int
abstract override fun foo()
}
\ No newline at end of file
// FIR_IDENTICAL
// !LANGUAGE: +MultiPlatformProjects
// MODULE: m1-common
// FILE: common.kt
......
......@@ -3,7 +3,7 @@
// FILE: common.kt
class Foo {
expect fun bar(): String
<!NON_ABSTRACT_FUNCTION_WITH_NO_BODY!>expect fun bar(): String<!>
}
// MODULE: m1-jvm(m1-common)
......
......@@ -9,7 +9,7 @@ class Outer expect constructor() {
expect init {}
expect fun foo()
<!NON_ABSTRACT_FUNCTION_WITH_NO_BODY!>expect fun foo()<!>
expect val bar: Int
}
......
......@@ -16,5 +16,5 @@ interface Test<in I, out O> {
fun internal_fun(i: O) : I
public fun public_fun(i: O) : I
protected fun protected_fun(i: O) : I
private fun private_fun(i: O) : I
<!PRIVATE_FUNCTION_WITH_NO_BODY!>private<!> fun private_fun(i: O) : I
}
\ No newline at end of file
......@@ -5,8 +5,8 @@
// TESTCASE NUMBER: 1
abstract class Base() {
abstract fun foo() = {}
fun boo() : Unit
<!ABSTRACT_FUNCTION_WITH_BODY!>abstract<!> fun foo() = {}
<!NON_ABSTRACT_FUNCTION_WITH_NO_BODY!>fun boo() : Unit<!>
abstract val a = <!ABSTRACT_PROPERTY_WITH_INITIALIZER!>""<!>
val b
var d
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册