[FIR JS] Report CALL_TO_DEFINED_EXTERNALLY_FROM_NON_EXTERNAL_DECLARATION
This commit is contained in:
committed by
Space Team
parent
4cf8d9ffb9
commit
56285bb2d6
+6
@@ -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,
|
||||
|
||||
+4
@@ -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
|
||||
}
|
||||
|
||||
+5
@@ -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,
|
||||
|
||||
+2
@@ -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") {
|
||||
|
||||
+2
@@ -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>()
|
||||
|
||||
+2
@@ -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)
|
||||
}
|
||||
|
||||
+30
-1
@@ -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
-1
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
+34
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+9
-7
@@ -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))
|
||||
|
||||
@@ -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"))
|
||||
|
||||
Reference in New Issue
Block a user