diff --git a/compiler/fir/analysis-tests/testData/resolve/arguments/operatorsOverLiterals.fir.txt b/compiler/fir/analysis-tests/testData/resolve/arguments/operatorsOverLiterals.fir.txt index 6879a2928fe..3636c3d0df2 100644 --- a/compiler/fir/analysis-tests/testData/resolve/arguments/operatorsOverLiterals.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/arguments/operatorsOverLiterals.fir.txt @@ -19,7 +19,8 @@ FILE: operatorsOverLiterals.kt } public final fun R|kotlin/Int|.bar(): R|kotlin/Int| { } - public final fun R|kotlin/Int|.baz(): R|kotlin/Int| + public final fun R|kotlin/Int|.baz(): R|kotlin/Int| { + } public final fun R|kotlin/Byte|.baz(): R|kotlin/Byte| { } public final fun test_3(): R|kotlin/Unit| { diff --git a/compiler/fir/analysis-tests/testData/resolve/arguments/operatorsOverLiterals.kt b/compiler/fir/analysis-tests/testData/resolve/arguments/operatorsOverLiterals.kt index 4e6622f6fcc..445ecf3ff4d 100644 --- a/compiler/fir/analysis-tests/testData/resolve/arguments/operatorsOverLiterals.kt +++ b/compiler/fir/analysis-tests/testData/resolve/arguments/operatorsOverLiterals.kt @@ -20,7 +20,7 @@ fun test_2(n: Int) { fun Int.bar(): Int {} -fun Int.baz(): Int +fun Int.baz(): Int {} fun Byte.baz(): Byte {} fun test_3() { diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/conflictingOverloads.fir.txt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/conflictingOverloads.fir.txt index 3e429144200..b732440a451 100644 --- a/compiler/fir/analysis-tests/testData/resolve/diagnostics/conflictingOverloads.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/conflictingOverloads.fir.txt @@ -111,7 +111,8 @@ FILE: conflictingOverloads.kt } } - public final fun mest(): R|kotlin/Unit| + public final fun mest(): R|kotlin/Unit| { + } public final class mest : R|kotlin/Any| { public constructor(): R|mest| { super() diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/conflictingOverloads.kt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/conflictingOverloads.kt index d123aa55763..737159ad2f5 100644 --- a/compiler/fir/analysis-tests/testData/resolve/diagnostics/conflictingOverloads.kt +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/conflictingOverloads.kt @@ -59,7 +59,7 @@ class L { fun B.foo() {} } -fun mest() +fun mest() {} class mest diff --git a/compiler/fir/analysis-tests/testData/resolve/intersectionTypes.kt b/compiler/fir/analysis-tests/testData/resolve/intersectionTypes.kt index 961cd6bacff..40e71a1d06a 100644 --- a/compiler/fir/analysis-tests/testData/resolve/intersectionTypes.kt +++ b/compiler/fir/analysis-tests/testData/resolve/intersectionTypes.kt @@ -5,7 +5,7 @@ interface B class Clazz1 : A, B class Clazz2 : A, B -fun select(x: K, y: K): K +fun select(x: K, y: K): K fun test() = select(Clazz1(), Clazz2()) diff --git a/compiler/fir/analysis-tests/testData/resolve/multifile/simpleStarImport.kt b/compiler/fir/analysis-tests/testData/resolve/multifile/simpleStarImport.kt index b442bd93bac..9531ede6c8d 100644 --- a/compiler/fir/analysis-tests/testData/resolve/multifile/simpleStarImport.kt +++ b/compiler/fir/analysis-tests/testData/resolve/multifile/simpleStarImport.kt @@ -14,7 +14,7 @@ package a.d import b.d.* -fun foo(arg: Other): Another +fun foo(arg: Other): Another fun bar() { baz() diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTopLevelFunctionChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTopLevelFunctionChecker.kt new file mode 100644 index 00000000000..ec8b47ec082 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTopLevelFunctionChecker.kt @@ -0,0 +1,39 @@ +/* + * 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.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 FirTopLevelFunctionChecker : FirFileChecker() { + override fun check(declaration: FirFile, context: CheckerContext, reporter: DiagnosticReporter) { + for (topLevelDeclaration in declaration.declarations) { + if (topLevelDeclaration is FirSimpleFunction) { + checkFunction(topLevelDeclaration, reporter) + } + } + } + + private fun checkFunction(function: FirSimpleFunction, 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 hasAbstractModifier = modifierList?.modifiers?.any { it.token == KtTokens.ABSTRACT_KEYWORD } == true + val isExternal = function.isExternal || modifierList?.modifiers?.any { it.token == KtTokens.EXTERNAL_KEYWORD } == true + val isExpect = function.isExpect || modifierList?.modifiers?.any { it.token == KtTokens.EXPECT_KEYWORD } == true + if (!function.hasBody && !hasAbstractModifier && !isExternal && !isExpect) { + reporter.report(FirErrors.NON_MEMBER_FUNCTION_NO_BODY.on(source, function)) + } + } +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index 04f2c986336..3d795c8ecac 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -80,6 +80,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_COMPANION_OB import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISSING_VAL_ON_ANNOTATION_PARAMETER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NONE_APPLICABLE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_ABSTRACT_FUNCTION_WITH_NO_BODY +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_MEMBER_FUNCTION_NO_BODY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_PRIVATE_CONSTRUCTOR_IN_ENUM import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_PRIVATE_CONSTRUCTOR_IN_SEALED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_AN_ANNOTATION_CLASS @@ -359,6 +360,7 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension { map.put(ABSTRACT_FUNCTION_WITH_BODY, "A function ''{0}'' with body cannot be abstract", DECLARATION_NAME) map.put(NON_ABSTRACT_FUNCTION_WITH_NO_BODY, "Function ''{0}'' without a body must be abstract", DECLARATION_NAME) map.put(PRIVATE_FUNCTION_WITH_NO_BODY, "Function ''{0}'' without body cannot be private", DECLARATION_NAME) + map.put(NON_MEMBER_FUNCTION_NO_BODY, "Function ''{0}'' must have a body", DECLARATION_NAME) map.put(FUNCTION_DECLARATION_WITH_NO_NAME, "Function declaration must have a name") diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index ad2406f1548..7450b82105e 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -155,6 +155,7 @@ object FirErrors { val ABSTRACT_FUNCTION_WITH_BODY by error1(SourceElementPositioningStrategies.MODALITY_MODIFIER) val NON_ABSTRACT_FUNCTION_WITH_NO_BODY by error1(SourceElementPositioningStrategies.DECLARATION_SIGNATURE) val PRIVATE_FUNCTION_WITH_NO_BODY by error1(SourceElementPositioningStrategies.VISIBILITY_MODIFIER) + val NON_MEMBER_FUNCTION_NO_BODY by error1(SourceElementPositioningStrategies.DECLARATION_SIGNATURE) val FUNCTION_DECLARATION_WITH_NO_NAME by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE) diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt index 099f3ffbf0c..0c3fe1f985a 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt @@ -59,6 +59,10 @@ object CommonDeclarationCheckers : DeclarationCheckers() { FirConstructorAllowedChecker, ) + override val fileCheckers: Set = setOf( + FirTopLevelFunctionChecker, + ) + override val controlFlowAnalyserCheckers: Set = setOf( FirCallsEffectAnalyzer, FirReturnsImpliesAnalyzer, diff --git a/compiler/testData/diagnostics/tests/Abstract.fir.kt b/compiler/testData/diagnostics/tests/Abstract.fir.kt index fa1c36427a2..7fcfaf02691 100644 --- a/compiler/testData/diagnostics/tests/Abstract.fir.kt +++ b/compiler/testData/diagnostics/tests/Abstract.fir.kt @@ -22,7 +22,7 @@ package MyPackage abstract val e3: Int = 0; get() = a //methods - fun f() + fun f() fun g() {} abstract fun h() abstract fun j() {} diff --git a/compiler/testData/diagnostics/tests/DefaultValuesCheckWithoutBody.fir.kt b/compiler/testData/diagnostics/tests/DefaultValuesCheckWithoutBody.fir.kt index 635ae083091..b508a1436dd 100644 --- a/compiler/testData/diagnostics/tests/DefaultValuesCheckWithoutBody.fir.kt +++ b/compiler/testData/diagnostics/tests/DefaultValuesCheckWithoutBody.fir.kt @@ -6,4 +6,4 @@ abstract class Abst { abstract fun foo(x: Int = y, y: Int = x) } -fun extraDiagnostics(x: Int = y, y: Int) +fun extraDiagnostics(x: Int = y, y: Int) diff --git a/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/namedFun.fir.kt b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/namedFun.fir.kt deleted file mode 100644 index b0c6be61c67..00000000000 --- a/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/namedFun.fir.kt +++ /dev/null @@ -1 +0,0 @@ -fun bar() diff --git a/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/namedFun.kt b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/namedFun.kt index 2a484039cfe..ce85fb96e16 100644 --- a/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/namedFun.kt +++ b/compiler/testData/diagnostics/tests/incompleteCode/diagnosticWithSyntaxError/namedFun.kt @@ -1 +1,2 @@ +// FIR_IDENTICAL fun bar() diff --git a/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/implDeclarationWithoutBody.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/implDeclarationWithoutBody.fir.kt index e0a91283d62..dcf84740c0d 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/implDeclarationWithoutBody.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/implDeclarationWithoutBody.fir.kt @@ -7,6 +7,6 @@ expect fun foo() // MODULE: m2-jvm(m1-common) // FILE: jvm.kt -actual fun foo() +actual fun foo() -actual fun bar() +actual fun bar()