[FIR JS] Report CALL_TO_DEFINED_EXTERNALLY_FROM_NON_EXTERNAL_DECLARATION

This commit is contained in:
Nikolay Lunyak
2023-01-04 16:23:42 +02:00
committed by Space Team
parent 4cf8d9ffb9
commit 56285bb2d6
16 changed files with 128 additions and 38 deletions
@@ -4667,6 +4667,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirJsErrors.CALL_TO_DEFINED_EXTERNALLY_FROM_NON_EXTERNAL_DECLARATION) { firDiagnostic ->
CallToDefinedExternallyFromNonExternalDeclarationImpl(
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirJsErrors.DELEGATION_BY_DYNAMIC) { firDiagnostic ->
DelegationByDynamicImpl(
firDiagnostic as KtPsiDiagnostic,
@@ -3246,6 +3246,10 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract val function: KtFunctionLikeSymbol
}
abstract class CallToDefinedExternallyFromNonExternalDeclaration : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = CallToDefinedExternallyFromNonExternalDeclaration::class
}
abstract class DelegationByDynamic : KtFirDiagnostic<KtElement>() {
override val diagnosticClass get() = DelegationByDynamic::class
}
@@ -3927,6 +3927,11 @@ internal class OverridingExternalFunWithOptionalParamsWithFakeImpl(
override val token: KtLifetimeToken,
) : KtFirDiagnostic.OverridingExternalFunWithOptionalParamsWithFake(), KtAbstractFirDiagnostic<KtElement>
internal class CallToDefinedExternallyFromNonExternalDeclarationImpl(
override val firDiagnostic: KtPsiDiagnostic,
override val token: KtLifetimeToken,
) : KtFirDiagnostic.CallToDefinedExternallyFromNonExternalDeclaration(), KtAbstractFirDiagnostic<PsiElement>
internal class DelegationByDynamicImpl(
override val firDiagnostic: KtPsiDiagnostic,
override val token: KtLifetimeToken,
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.checkers.generator.diagnostics
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.fir.PrivateForInline
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticList
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.PositioningStrategy
@@ -38,6 +39,7 @@ object JS_DIAGNOSTICS_LIST : DiagnosticList("FirJsErrors") {
val OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS_WITH_FAKE by error<KtElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) {
parameter<FirNamedFunctionSymbol>("function")
}
val CALL_TO_DEFINED_EXTERNALLY_FROM_NON_EXTERNAL_DECLARATION by error<PsiElement>()
}
val DYNAMICS by object : DiagnosticGroup("Dynamics") {
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.analysis.diagnostics.js
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.diagnostics.*
import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies
import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory
@@ -35,6 +36,7 @@ object FirJsErrors {
// External
val OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS by error0<KtElement>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS_WITH_FAKE by error1<KtElement, FirNamedFunctionSymbol>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val CALL_TO_DEFINED_EXTERNALLY_FROM_NON_EXTERNAL_DECLARATION by error0<PsiElement>()
// Dynamics
val DELEGATION_BY_DYNAMIC by error0<KtElement>()
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryToRendererMap
import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers
import org.jetbrains.kotlin.fir.analysis.diagnostics.checkMissingMessages
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.CALL_TO_DEFINED_EXTERNALLY_FROM_NON_EXTERNAL_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.DELEGATION_BY_DYNAMIC
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.IMPLEMENTING_FUNCTION_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.JS_MODULE_PROHIBITED_ON_NON_NATIVE
@@ -42,6 +43,7 @@ object FirJsErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
"Overriding `external` function with optional parameters by declaration from superclass: {0}",
FirDiagnosticRenderers.SYMBOL
)
map.put(CALL_TO_DEFINED_EXTERNALLY_FROM_NON_EXTERNAL_DECLARATION, "This property can only be used from external declarations")
map.checkMissingMessages(FirJsErrors)
}
@@ -14,12 +14,14 @@ import org.jetbrains.kotlin.fir.analysis.checkers.directOverriddenFunctions
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
import org.jetbrains.kotlin.fir.analysis.checkers.hasAnnotationOrInsideAnnotatedClass
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.declarations.utils.isExternal
import org.jetbrains.kotlin.fir.declarations.utils.modality
import org.jetbrains.kotlin.fir.isSubstitutionOrIntersectionOverride
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.js.PredefinedAnnotation
import org.jetbrains.kotlin.name.JsStandardClassIds
private val FirBasedSymbol<*>.isExternal
@@ -46,6 +48,10 @@ fun FirBasedSymbol<*>.isEffectivelyExternal(session: FirSession): Boolean {
return getContainingClassSymbol(session)?.isEffectivelyExternal(session) == true
}
fun FirBasedSymbol<*>.isEffectivelyExternalMember(session: FirSession): Boolean {
return fir is FirMemberDeclaration && isEffectivelyExternal(session)
}
fun FirBasedSymbol<*>.isEffectivelyExternal(context: CheckerContext) = isEffectivelyExternal(context.session)
fun FirFunctionSymbol<*>.isOverridingExternalWithOptionalParams(context: CheckerContext): Boolean {
@@ -66,10 +72,33 @@ fun FirBasedSymbol<*>.isNativeObject(session: FirSession): Boolean {
}
if (this is FirPropertyAccessorSymbol) {
return propertySymbol.hasAnnotationOrInsideAnnotatedClass(JsStandardClassIds.Annotations.JsNative, session)
val property = propertySymbol
return property.hasAnnotationOrInsideAnnotatedClass(JsStandardClassIds.Annotations.JsNative, session)
}
return false
}
private val FirBasedSymbol<*>.isExpect
get() = when (this) {
is FirCallableSymbol<*> -> isExpect
is FirClassSymbol<*> -> isExpect
else -> false
}
fun FirBasedSymbol<*>.isPredefinedObject(session: FirSession): Boolean {
if (fir is FirMemberDeclaration && isExpect) return true
if (isEffectivelyExternalMember(session)) return true
for (annotation in PredefinedAnnotation.values()) {
if (hasAnnotationOrInsideAnnotatedClass(annotation.classId, session)) {
return true
}
}
return false
}
fun FirBasedSymbol<*>.isNativeObject(context: CheckerContext) = isNativeObject(context.session)
fun FirBasedSymbol<*>.isPredefinedObject(context: CheckerContext) = isPredefinedObject(context.session)
@@ -6,11 +6,16 @@
package org.jetbrains.kotlin.fir.analysis.js.checkers
import org.jetbrains.kotlin.fir.analysis.checkers.expression.*
import org.jetbrains.kotlin.fir.analysis.js.checkers.expression.FirJsQualifierChecker
import org.jetbrains.kotlin.fir.analysis.js.checkers.expression.*
object JsExpressionCheckers : ExpressionCheckers() {
override val annotationCallCheckers: Set<FirAnnotationCallChecker>
get() = setOf(
FirJsQualifierChecker,
)
override val qualifiedAccessCheckers: Set<FirQualifiedAccessChecker>
get() = setOf(
FirJsDefinedExternallyCallChecker,
)
}
@@ -0,0 +1,34 @@
/*
* 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.js.checkers.expression
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.closestNonLocal
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirQualifiedAccessChecker
import org.jetbrains.kotlin.fir.analysis.js.checkers.isNativeObject
import org.jetbrains.kotlin.fir.analysis.js.checkers.isPredefinedObject
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess
import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
import org.jetbrains.kotlin.name.JsStandardClassIds
object FirJsDefinedExternallyCallChecker : FirQualifiedAccessChecker() {
override fun check(expression: FirQualifiedAccess, context: CheckerContext, reporter: DiagnosticReporter) {
val symbol = expression.calleeReference.toResolvedCallableSymbol() ?: return
if (symbol.callableId !in JsStandardClassIds.Callables.definedExternallyPropertyNames) {
return
}
val container = context.closestNonLocal?.symbol ?: return
if (!container.isNativeObject(context) && !container.isPredefinedObject(context)) {
reporter.reportOn(expression.source, FirJsErrors.CALL_TO_DEFINED_EXTERNALLY_FROM_NON_EXTERNAL_DECLARATION, context)
}
}
}
@@ -713,6 +713,8 @@ fun FirNamedFunctionSymbol.directOverriddenFunctions(session: FirSession, scopeS
fun FirNamedFunctionSymbol.directOverriddenFunctions(context: CheckerContext) =
directOverriddenFunctions(context.session, context.sessionHolder.scopeSession)
val CheckerContext.closestNonLocal get() = containingDeclarations.takeWhile { it.isNonLocal }.lastOrNull()
fun CheckerContext.closestNonLocalWith(declaration: FirDeclaration) =
(containingDeclarations + declaration).takeWhile { it.isNonLocal }.lastOrNull()
@@ -1,29 +0,0 @@
// !DIAGNOSTICS: -UNUSED_ANONYMOUS_PARAMETER -UNUSED_PARAMETER, -UNREACHABLE_CODE
val prop: String = definedExternally
val prop2: String
get() = definedExternally
fun foo(x: Int, y: String = definedExternally) {
println("Hello")
println("world")
object {
fun bar(): Any = definedExternally
}
listOf<String>()
.map<String, String> { definedExternally }
.filter(fun(x: String): Boolean { definedExternally })
definedExternally
}
open class A(val x: Int)
open class B() : A(definedExternally) {
constructor(y: String) : this()
constructor(y: String, z: String) : this(y + z + definedExternally)
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_ANONYMOUS_PARAMETER -UNUSED_PARAMETER, -UNREACHABLE_CODE
val prop: String = <!CALL_TO_DEFINED_EXTERNALLY_FROM_NON_EXTERNAL_DECLARATION!>definedExternally<!>
@@ -27,3 +28,12 @@ open class B() : A(<!CALL_TO_DEFINED_EXTERNALLY_FROM_NON_EXTERNAL_DECLARATION!>d
constructor(y: String, z: String) : this(y + z + <!CALL_TO_DEFINED_EXTERNALLY_FROM_NON_EXTERNAL_DECLARATION!>definedExternally<!>)
}
private fun exposeClassS() = run {
class S {
inner class W {
val v: String = <!CALL_TO_DEFINED_EXTERNALLY_FROM_NON_EXTERNAL_DECLARATION!>definedExternally<!>
}
}
S()
}
@@ -2,6 +2,7 @@ package
public val prop: kotlin.String
public val prop2: kotlin.String
private fun exposeClassS(): exposeClassS.`<anonymous>`.S
public fun foo(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.String = ...): kotlin.Unit
public open class A {
@@ -21,3 +22,4 @@ public open class B : A {
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -16,16 +16,18 @@
package org.jetbrains.kotlin.js
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.JsStandardClassIds
enum class PredefinedAnnotation(fqName: String) {
LIBRARY("kotlin.js.library"),
NATIVE("kotlin.js.native"),
NATIVE_INVOKE("kotlin.js.nativeInvoke"),
NATIVE_GETTER("kotlin.js.nativeGetter"),
NATIVE_SETTER("kotlin.js.nativeSetter");
enum class PredefinedAnnotation(val classId: ClassId) {
LIBRARY(JsStandardClassIds.Annotations.JsLibrary),
NATIVE(JsStandardClassIds.Annotations.JsNative),
NATIVE_INVOKE(JsStandardClassIds.Annotations.JsNativeInvoke),
NATIVE_GETTER(JsStandardClassIds.Annotations.JsNativeGetter),
NATIVE_SETTER(JsStandardClassIds.Annotations.JsNativeSetter);
val fqName: FqName = FqName(fqName)
val fqName: FqName = classId.asSingleFqName()
companion object {
val WITH_CUSTOM_NAME = setOf(LIBRARY, NATIVE)
@@ -16,7 +16,20 @@ object JsStandardClassIds {
val JsNonModule = "JsNonModule".jsId()
val JsNative = "native".jsId()
val JsLibrary = "library".jsId()
val JsNativeInvoke = "nativeInvoke".jsId()
val JsNativeGetter = "nativeGetter".jsId()
val JsNativeSetter = "nativeSetter".jsId()
}
object Callables {
val JsDefinedExternally = "definedExternally".callableId(BASE_JS_PACKAGE)
val JsNoImpl = "noImpl".callableId(BASE_JS_PACKAGE)
val definedExternallyPropertyNames = setOf(JsNoImpl, JsDefinedExternally)
}
}
private fun String.jsId() = ClassId(JsStandardClassIds.BASE_JS_PACKAGE, Name.identifier(this))
private fun String.callableId(packageName: FqName) = CallableId(packageName, Name.identifier(this))
+1
View File
@@ -6,6 +6,7 @@ plugins {
dependencies {
api(project(":compiler:util"))
api(project(":compiler:frontend"))
api(project(":core:compiler.common.js"))
api(project(":js:js.ast"))
api(project(":js:js.parser"))
api(project(":js:js.serializer"))