diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/symbolAnnotationsUtils.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/symbolAnnotationsUtils.kt index 9a039f1e3c8..94145fe591c 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/symbolAnnotationsUtils.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/symbolAnnotationsUtils.kt @@ -9,18 +9,21 @@ import com.intellij.psi.CommonClassNames.JAVA_LANG_ANNOTATION_RETENTION import com.intellij.psi.PsiAnnotation import com.intellij.psi.PsiClass import com.intellij.psi.PsiModifierList +import com.intellij.psi.PsiClassType +import com.intellij.psi.PsiElement +import com.intellij.psi.impl.light.LightReferenceListBuilder import org.jetbrains.annotations.NotNull import org.jetbrains.annotations.Nullable import org.jetbrains.kotlin.analysis.api.KtAnalysisSession -import org.jetbrains.kotlin.analysis.api.annotations.KtConstantAnnotationValue -import org.jetbrains.kotlin.analysis.api.annotations.KtEnumEntryAnnotationValue -import org.jetbrains.kotlin.analysis.api.annotations.KtNamedAnnotationValue -import org.jetbrains.kotlin.analysis.api.annotations.annotations +import org.jetbrains.kotlin.analysis.api.annotations.* +import org.jetbrains.kotlin.analysis.api.components.buildClassType import org.jetbrains.kotlin.analysis.api.symbols.KtFileSymbol import org.jetbrains.kotlin.analysis.api.symbols.markers.KtAnnotatedSymbol +import org.jetbrains.kotlin.analysis.api.types.KtTypeMappingMode import org.jetbrains.kotlin.builtins.StandardNames.DEFAULT_VALUE_PARAMETER import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.light.classes.symbol.NullabilityType +import org.jetbrains.kotlin.light.classes.symbol.classes.SymbolLightClassBase import org.jetbrains.kotlin.light.classes.symbol.methods.SymbolLightMethod import org.jetbrains.kotlin.load.java.JvmAbi.JVM_FIELD_ANNOTATION_CLASS_ID import org.jetbrains.kotlin.load.java.JvmAnnotationNames.RETENTION_POLICY_ENUM @@ -30,6 +33,7 @@ import org.jetbrains.kotlin.name.JvmNames.JVM_NAME_CLASS_ID import org.jetbrains.kotlin.name.JvmNames.JVM_OVERLOADS_CLASS_ID import org.jetbrains.kotlin.name.JvmNames.JVM_SYNTHETIC_ANNOTATION_CLASS_ID import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_CLASS_ID +import org.jetbrains.kotlin.resolve.annotations.JVM_THROWS_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.resolve.deprecation.DeprecationLevelValue import org.jetbrains.kotlin.resolve.inline.INLINE_ONLY_ANNOTATION_FQ_NAME @@ -92,19 +96,33 @@ internal fun KtAnnotatedSymbol.hasAnnotation( classId: ClassId, annotationUseSiteTarget: AnnotationUseSiteTarget?, strictUseSite: Boolean = true, -): Boolean = annotations.any { - val useSiteTarget = it.useSiteTarget - (useSiteTarget == annotationUseSiteTarget || !strictUseSite && useSiteTarget == null) && it.classId == classId -} +): Boolean = findAnnotation(classId, annotationUseSiteTarget, strictUseSite) != null + +internal fun KtAnnotatedSymbol.findAnnotation( + classId: ClassId, + annotationUseSiteTarget: AnnotationUseSiteTarget?, + strictUseSite: Boolean = true, +): KtAnnotationApplication? = + annotations.find { + val useSiteTarget = it.useSiteTarget + (useSiteTarget == annotationUseSiteTarget || !strictUseSite && useSiteTarget == null) && it.classId == classId + } internal fun KtAnnotatedSymbol.hasAnnotation( fqName: FqName, annotationUseSiteTarget: AnnotationUseSiteTarget?, strictUseSite: Boolean = true, -): Boolean = annotations.any { - val useSiteTarget = it.useSiteTarget - (useSiteTarget == annotationUseSiteTarget || !strictUseSite && useSiteTarget == null) && it.classId?.asSingleFqName() == fqName -} +): Boolean = findAnnotation(fqName, annotationUseSiteTarget, strictUseSite) != null + +internal fun KtAnnotatedSymbol.findAnnotation( + fqName: FqName, + annotationUseSiteTarget: AnnotationUseSiteTarget?, + strictUseSite: Boolean = true, +): KtAnnotationApplication? = + annotations.find { + val useSiteTarget = it.useSiteTarget + (useSiteTarget == annotationUseSiteTarget || !strictUseSite && useSiteTarget == null) && it.classId?.asSingleFqName() == fqName + } internal fun NullabilityType.computeNullabilityAnnotation(parent: PsiModifierList): SymbolLightSimpleAnnotation? { return when (this) { @@ -192,3 +210,33 @@ private fun createRetentionRuntimeAnnotation(modifierList: PsiModifierList, rete ) ) ) + +context(KtAnalysisSession) +internal fun KtAnnotatedSymbol.computeThrowsList( + builder: LightReferenceListBuilder, + annotationUseSiteTarget: AnnotationUseSiteTarget?, + useSitePosition: PsiElement, + containingClass: SymbolLightClassBase, + strictUseSite: Boolean = true, +) { + val annoApp = findAnnotation(JVM_THROWS_ANNOTATION_FQ_NAME, annotationUseSiteTarget, strictUseSite) ?: return + + fun handleAnnotationValue(annotationValue: KtAnnotationValue) = when (annotationValue) { + is KtArrayAnnotationValue -> { + annotationValue.values.forEach(::handleAnnotationValue) + } + is KtKClassAnnotationValue.KtNonLocalKClassAnnotationValue -> { + val psiType = buildClassType(annotationValue.classId).asPsiType( + useSitePosition, + KtTypeMappingMode.DEFAULT, + containingClass.isAnnotationType + ) + (psiType as? PsiClassType)?.let { + builder.addReference(it) + } + } + else -> {} + } + + annoApp.arguments.forEach { handleAnnotationValue(it.expression) } +} diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/methods/SymbolLightAccessorMethod.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/methods/SymbolLightAccessorMethod.kt index 4eff8a5df39..794fe21d117 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/methods/SymbolLightAccessorMethod.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/methods/SymbolLightAccessorMethod.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.light.classes.symbol.methods import com.intellij.openapi.util.TextRange import com.intellij.psi.* import com.intellij.psi.impl.light.LightParameterListBuilder +import com.intellij.psi.impl.light.LightReferenceListBuilder import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.KtConstantInitializerValue import org.jetbrains.kotlin.analysis.api.KtConstantValueForAnnotation @@ -22,6 +23,7 @@ import org.jetbrains.kotlin.asJava.classes.lazyPub import org.jetbrains.kotlin.asJava.elements.KtLightIdentifier import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.light.classes.symbol.* +import org.jetbrains.kotlin.light.classes.symbol.annotations.* import org.jetbrains.kotlin.light.classes.symbol.annotations.computeAnnotations import org.jetbrains.kotlin.light.classes.symbol.annotations.getJvmNameFromAnnotation import org.jetbrains.kotlin.light.classes.symbol.annotations.hasDeprecatedAnnotation @@ -131,6 +133,18 @@ internal class SymbolLightAccessorMethod private constructor( //TODO Fix it when SymbolConstructorValueParameter be ready private val isParameter: Boolean get() = containingPropertyDeclaration == null || containingPropertyDeclaration is KtParameter + override fun computeThrowsList(builder: LightReferenceListBuilder) { + analyzeForLightClasses(ktModule) { + propertyAccessorSymbol().computeThrowsList( + builder, + accessorSite, + this@SymbolLightAccessorMethod, + containingClass, + strictUseSite = false + ) + } + } + private fun computeAnnotations(modifierList: PsiModifierList): List = analyzeForLightClasses(ktModule) { val nullabilityApplicable = isGetter && !modifierList.hasModifierProperty(PsiModifier.PRIVATE) && diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/methods/SymbolLightMethod.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/methods/SymbolLightMethod.kt index a840b4c84a7..1de7c0db5c1 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/methods/SymbolLightMethod.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/methods/SymbolLightMethod.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.light.classes.symbol.methods import com.intellij.psi.PsiIdentifier import com.intellij.psi.PsiParameterList +import com.intellij.psi.impl.light.LightReferenceListBuilder import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionLikeSymbol import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol @@ -16,6 +17,7 @@ import org.jetbrains.kotlin.analysis.api.symbols.sourcePsiSafe import org.jetbrains.kotlin.asJava.builder.LightMemberOrigin import org.jetbrains.kotlin.asJava.classes.lazyPub import org.jetbrains.kotlin.asJava.elements.KtLightIdentifier +import org.jetbrains.kotlin.light.classes.symbol.annotations.computeThrowsList import org.jetbrains.kotlin.light.classes.symbol.annotations.hasDeprecatedAnnotation import org.jetbrains.kotlin.light.classes.symbol.classes.SymbolLightClassBase import org.jetbrains.kotlin.light.classes.symbol.compareSymbolPointers @@ -120,6 +122,17 @@ internal abstract class SymbolLightMethod private override fun getParameterList(): PsiParameterList = _parametersList + override fun computeThrowsList(builder: LightReferenceListBuilder) { + withFunctionSymbol { functionSymbol -> + functionSymbol.computeThrowsList( + builder, + annotationUseSiteTarget = null, + this@SymbolLightMethod, + containingClass + ) + } + } + override fun isValid(): Boolean = super.isValid() && functionDeclaration?.isValid ?: functionSymbolPointer.isValid(ktModule) override fun isOverride(): Boolean = withFunctionSymbol { it.getDirectlyOverriddenSymbols().isNotEmpty() } diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/methods/SymbolLightMethodBase.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/methods/SymbolLightMethodBase.kt index d0bc27e563e..75135b8c530 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/methods/SymbolLightMethodBase.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/methods/SymbolLightMethodBase.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.light.classes.symbol.methods import com.intellij.psi.* import com.intellij.psi.impl.PsiImplUtil import com.intellij.psi.impl.PsiSuperMethodImplUtil +import com.intellij.psi.impl.light.LightReferenceListBuilder import com.intellij.psi.util.MethodSignature import com.intellij.psi.util.MethodSignatureBackedByPsiMethod import org.jetbrains.kotlin.analysis.api.KtAnalysisSession @@ -19,6 +20,7 @@ import org.jetbrains.kotlin.asJava.builder.LightMemberOrigin import org.jetbrains.kotlin.asJava.classes.KotlinLightReferenceListBuilder import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade import org.jetbrains.kotlin.asJava.classes.cannotModify +import org.jetbrains.kotlin.asJava.classes.lazyPub import org.jetbrains.kotlin.asJava.elements.KtLightMethod import org.jetbrains.kotlin.asJava.mangleInternalName import org.jetbrains.kotlin.descriptors.Visibilities @@ -79,8 +81,23 @@ internal abstract class SymbolLightMethodBase( abstract override fun hasTypeParameters(): Boolean abstract override fun getTypeParameterList(): PsiTypeParameterList? - override fun getThrowsList(): PsiReferenceList = - KotlinLightReferenceListBuilder(manager, language, PsiReferenceList.Role.THROWS_LIST) //TODO() + private class SymbolLightThrowsReferencesListBuilder( + private val parentMethod: PsiMethod + ) : KotlinLightReferenceListBuilder(parentMethod.manager, parentMethod.language, PsiReferenceList.Role.THROWS_LIST) { + override fun getParent(): PsiElement = parentMethod + + override fun getContainingFile(): PsiFile = parentMethod.containingFile + } + + private val _throwsList by lazyPub { + val builder = SymbolLightThrowsReferencesListBuilder(this) + computeThrowsList(builder) + builder + } + + protected open fun computeThrowsList(builder: LightReferenceListBuilder) {} + + override fun getThrowsList(): PsiReferenceList = _throwsList override fun getDefaultValue(): PsiAnnotationMemberValue? = null diff --git a/compiler/testData/asJava/ultraLightClasses/throwsAnnotation.fir.java b/compiler/testData/asJava/ultraLightClasses/throwsAnnotation.fir.java index c32d1dd7cee..36d13db6850 100644 --- a/compiler/testData/asJava/ultraLightClasses/throwsAnnotation.fir.java +++ b/compiler/testData/asJava/ultraLightClasses/throwsAnnotation.fir.java @@ -6,13 +6,13 @@ public final class MyException /* MyException*/ extends java.lang.Exception { public final class C /* C*/ { @kotlin.jvm.Throws(exceptionClasses = {java.io.IOException.class, MyException.class}) @org.jetbrains.annotations.NotNull() - public final java.lang.String readFile(@org.jetbrains.annotations.NotNull() java.lang.String);// readFile(java.lang.String) + public final java.lang.String readFile(@org.jetbrains.annotations.NotNull() java.lang.String) throws java.io.IOException, MyException;// readFile(java.lang.String) @kotlin.jvm.Throws(exceptionClasses = {java.lang.Exception.class}) - public C(int);// .ctor(int) + public C(int) throws java.lang.Exception;// .ctor(int) @kotlin.jvm.Throws(exceptionClasses = {java.lang.Throwable.class}) - public final void baz();// baz() + public final void baz() throws java.lang.Throwable;// baz() public C();// .ctor() diff --git a/compiler/testData/asJava/ultraLightFacades/throwsAnnotation.fir.java b/compiler/testData/asJava/ultraLightFacades/throwsAnnotation.fir.java index 231c04fc8d3..65c5f8ed677 100644 --- a/compiler/testData/asJava/ultraLightFacades/throwsAnnotation.fir.java +++ b/compiler/testData/asJava/ultraLightFacades/throwsAnnotation.fir.java @@ -1,9 +1,9 @@ public final class ThrowsAnnotationKt /* ThrowsAnnotationKt*/ { @kotlin.jvm.Throws(exceptionClasses = {java.io.IOException.class, MyException.class}) @org.jetbrains.annotations.NotNull() - public static final java.lang.String readFile(@org.jetbrains.annotations.NotNull() java.lang.String);// readFile(java.lang.String) + public static final java.lang.String readFile(@org.jetbrains.annotations.NotNull() java.lang.String) throws java.io.IOException, MyException;// readFile(java.lang.String) @kotlin.jvm.Throws(exceptionClasses = {java.lang.Throwable.class}) - public static final void baz();// baz() + public static final void baz() throws java.lang.Throwable;// baz() }