Switch to 181 platform

This commit is contained in:
Vyacheslav Gerasimov
2018-04-27 18:25:17 +03:00
parent df59af8ee8
commit bc403ce744
673 changed files with 25853 additions and 922 deletions
@@ -22,9 +22,7 @@ import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.ClassFileViewProvider
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiManager
import com.intellij.psi.impl.compiled.ClsFileImpl
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl
@@ -80,10 +78,7 @@ private fun createJavaFileStub(project: Project, packageFqName: FqName, files: C
val javaFileStub = PsiJavaFileStubImpl(packageFqName.asString(), /*compiled = */true)
javaFileStub.psiFactory = ClsWrapperStubPsiFactory.INSTANCE
val manager = PsiManager.getInstance(project)
val virtualFile = getRepresentativeVirtualFile(files)
val fakeFile = object : ClsFileImpl(ClassFileViewProvider(manager, virtualFile)) {
val fakeFile = object : ClsFileImpl(files.first().viewProvider) {
override fun getStub() = javaFileStub
override fun getPackageName() = packageFqName.asString()
@@ -121,10 +116,6 @@ private fun createJavaFileStub(project: Project, packageFqName: FqName, files: C
return javaFileStub
}
private fun getRepresentativeVirtualFile(files: Collection<KtFile>): VirtualFile {
return files.first().viewProvider.virtualFile
}
private fun logErrorWithOSInfo(cause: Throwable?, fqName: FqName, virtualFile: VirtualFile?) {
val path = if (virtualFile == null) "<null>" else virtualFile.path
LOG.error(
@@ -0,0 +1,137 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.asJava.builder
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.ClassFileViewProvider
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiManager
import com.intellij.psi.impl.compiled.ClsFileImpl
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl
import com.intellij.psi.impl.source.tree.TreeElement
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import java.lang.StringBuilder
data class LightClassBuilderResult(val stub: PsiJavaFileStub, val bindingContext: BindingContext, val diagnostics: Diagnostics)
fun buildLightClass(
packageFqName: FqName,
files: Collection<KtFile>,
generateClassFilter: GenerationState.GenerateClassFilter,
context: LightClassConstructionContext,
generate: (state: GenerationState, files: Collection<KtFile>) -> Unit
): LightClassBuilderResult {
val project = files.first().project
try {
val classBuilderFactory = KotlinLightClassBuilderFactory(createJavaFileStub(project, packageFqName, files))
val state = GenerationState.Builder(
project,
classBuilderFactory,
context.module,
context.bindingContext,
files.toList(),
CompilerConfiguration.EMPTY
).generateDeclaredClassFilter(generateClassFilter).wantsDiagnostics(false).build()
state.beforeCompile()
generate(state, files)
val javaFileStub = classBuilderFactory.result()
ServiceManager.getService(project, StubComputationTracker::class.java)?.onStubComputed(javaFileStub, context)
return LightClassBuilderResult(javaFileStub, context.bindingContext, state.collectedExtraJvmDiagnostics)
}
catch (e: ProcessCanceledException) {
throw e
}
catch (e: RuntimeException) {
logErrorWithOSInfo(e, packageFqName, null)
throw e
}
}
private fun createJavaFileStub(project: Project, packageFqName: FqName, files: Collection<KtFile>): PsiJavaFileStub {
val javaFileStub = PsiJavaFileStubImpl(packageFqName.asString(), /*compiled = */true)
javaFileStub.psiFactory = ClsWrapperStubPsiFactory.INSTANCE
val manager = PsiManager.getInstance(project)
val virtualFile = getRepresentativeVirtualFile(files)
val fakeFile = object : ClsFileImpl(ClassFileViewProvider(manager, virtualFile)) {
override fun getStub() = javaFileStub
override fun getPackageName() = packageFqName.asString()
override fun isPhysical() = false
override fun appendMirrorText(indentLevel: Int, buffer: StringBuilder) {
if (files.size == 1) {
LOG.error("Mirror text should never be calculated for light classes generated from a single file")
}
super.appendMirrorText(indentLevel, buffer)
}
override fun setMirror(element: TreeElement) {
if (files.size == 1) {
LOG.error("Mirror element should never be set for light classes generated from a single file")
}
super.setMirror(element)
}
override fun getMirror(): PsiElement {
if (files.size == 1) {
LOG.error("Mirror element should never be calculated for light classes generated from a single file")
}
return super.getMirror()
}
override fun getText(): String {
return files.singleOrNull()?.text ?: super.getText()
}
}
javaFileStub.psi = fakeFile
return javaFileStub
}
private fun getRepresentativeVirtualFile(files: Collection<KtFile>): VirtualFile {
return files.first().viewProvider.virtualFile
}
private fun logErrorWithOSInfo(cause: Throwable?, fqName: FqName, virtualFile: VirtualFile?) {
val path = if (virtualFile == null) "<null>" else virtualFile.path
LOG.error(
"Could not generate LightClass for $fqName declared in $path\n" +
"System: ${SystemInfo.OS_NAME} ${SystemInfo.OS_VERSION} Java Runtime: ${SystemInfo.JAVA_RUNTIME_VERSION}",
cause
)
}
private val LOG = Logger.getInstance(LightClassBuilderResult::class.java)
@@ -16,8 +16,8 @@
package org.jetbrains.kotlin.asJava.elements
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.pom.java.LanguageLevel
import com.intellij.psi.ClassFileViewProvider
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.impl.compiled.ClsFileImpl
@@ -33,9 +33,11 @@ open class FakeFileForLightClass(
private val lightClass: () -> KtLightClass,
private val stub: () -> PsiClassHolderFileStub<*>,
private val packageFqName: FqName = ktFile.packageFqName
) : ClsFileImpl(ClassFileViewProvider(ktFile.manager, ktFile.virtualFile ?:
ktFile.originalFile.virtualFile ?:
ktFile.viewProvider.virtualFile)) {
) : ClsFileImpl(ktFile.viewProvider) {
override fun getVirtualFile(): VirtualFile =
ktFile.virtualFile ?: ktFile.originalFile.virtualFile ?: super.getVirtualFile()
override fun getPackageName() = packageFqName.asString()
override fun getStub() = stub()
@@ -83,4 +85,4 @@ open class FakeFileForLightClass(
}
override fun isPhysical() = false
}
}
@@ -0,0 +1,86 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.asJava.elements
import com.intellij.pom.java.LanguageLevel
import com.intellij.psi.ClassFileViewProvider
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.impl.compiled.ClsFileImpl
import com.intellij.psi.stubs.PsiClassHolderFileStub
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile
open class FakeFileForLightClass(
val ktFile: KtFile,
private val lightClass: () -> KtLightClass,
private val stub: () -> PsiClassHolderFileStub<*>,
private val packageFqName: FqName = ktFile.packageFqName
) : ClsFileImpl(ClassFileViewProvider(ktFile.manager, ktFile.virtualFile ?:
ktFile.originalFile.virtualFile ?:
ktFile.viewProvider.virtualFile)) {
override fun getPackageName() = packageFqName.asString()
override fun getStub() = stub()
override fun getClasses() = arrayOf(lightClass())
override fun getNavigationElement() = ktFile
override fun accept(visitor: PsiElementVisitor) {
// Prevent access to compiled PSI
// TODO: More complex traversal logic may be implemented when necessary
}
// this should be equal to current compiler target language level
override fun getLanguageLevel() = LanguageLevel.JDK_1_6
override fun hashCode(): Int {
val thisClass = lightClass()
if (thisClass is KtLightClassForSourceDeclaration) return ktFile.hashCode()
return thisClass.hashCode()
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is FakeFileForLightClass) return false
val thisClass = lightClass()
val anotherClass = other.lightClass()
if (thisClass is KtLightClassForSourceDeclaration) {
return anotherClass is KtLightClassForSourceDeclaration && ktFile == other.ktFile
}
return thisClass == anotherClass
}
override fun isEquivalentTo(another: PsiElement?) = this == another
override fun setPackageName(packageName: String) {
if (lightClass() is KtLightClassForFacade) {
ktFile.packageDirective?.fqName = FqName(packageName)
}
else {
super.setPackageName(packageName)
}
}
override fun isPhysical() = false
}
@@ -95,12 +95,13 @@ class KtLightAnnotationForSourceEntry(
override fun getReferences() = originalExpression?.references.orEmpty()
override fun getLanguage() = KotlinLanguage.INSTANCE
override fun getNavigationElement() = originalExpression
override fun isPhysical(): Boolean = originalExpression?.containingFile == kotlinOrigin.containingFile
override fun isPhysical(): Boolean = false
override fun getTextRange() = originalExpression?.textRange ?: TextRange.EMPTY_RANGE
override fun getStartOffsetInParent() = originalExpression?.startOffsetInParent ?: 0
override fun getParent() = parent
override fun getText() = originalExpression?.text.orEmpty()
override fun getContainingFile(): PsiFile? = if (isPhysical) kotlinOrigin.containingFile else delegate.containingFile
override fun getContainingFile(): PsiFile? = if (originalExpression?.containingFile == kotlinOrigin.containingFile)
kotlinOrigin.containingFile else delegate.containingFile
override fun replace(newElement: PsiElement): PsiElement {
val value = (newElement as? PsiLiteral)?.value as? String ?: return this
@@ -250,8 +251,6 @@ class KtLightAnnotationForSourceEntry(
else -> LightElementValue(value, parent, ktOrigin)
}
override fun isPhysical() = true
override fun getName() = null
private fun wrapAnnotationValue(value: PsiAnnotationMemberValue): PsiAnnotationMemberValue = wrapAnnotationValue(value, this, {
@@ -0,0 +1,380 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.asJava.elements
import com.intellij.openapi.diagnostic.Attachment
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.TextRange
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.*
import org.jetbrains.annotations.NotNull
import org.jetbrains.annotations.Nullable
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.classes.cannotModify
import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.TypeUtils
private val LOG = Logger.getInstance("#org.jetbrains.kotlin.asJava.elements.lightAnnotations")
abstract class KtLightAbstractAnnotation(parent: PsiElement, computeDelegate: () -> PsiAnnotation) :
KtLightElementBase(parent), PsiAnnotation, KtLightElement<KtCallElement, PsiAnnotation> {
override val clsDelegate by lazyPub(computeDelegate)
override fun getNameReferenceElement() = clsDelegate.nameReferenceElement
override fun getOwner() = parent as? PsiAnnotationOwner
override fun getMetaData() = clsDelegate.metaData
override fun getParameterList() = clsDelegate.parameterList
override fun canNavigate(): Boolean = super<KtLightElementBase>.canNavigate()
override fun canNavigateToSource(): Boolean = super<KtLightElementBase>.canNavigateToSource()
override fun navigate(requestFocus: Boolean) = super<KtLightElementBase>.navigate(requestFocus)
open fun fqNameMatches(fqName: String): Boolean = qualifiedName == fqName
}
private typealias AnnotationValueOrigin = () -> PsiElement?
class KtLightAnnotationForSourceEntry(
private val qualifiedName: String,
override val kotlinOrigin: KtCallElement,
parent: PsiElement,
computeDelegate: () -> PsiAnnotation
) : KtLightAbstractAnnotation(parent, computeDelegate) {
override fun getQualifiedName() = qualifiedName
open inner class LightElementValue<out D : PsiElement>(
val delegate: D,
private val parent: PsiElement,
valueOrigin: AnnotationValueOrigin
) : PsiAnnotationMemberValue, PsiCompiledElement, PsiElement by delegate {
override fun getMirror(): PsiElement = delegate
val originalExpression: PsiElement? by lazyPub(valueOrigin)
fun getConstantValue(): Any? {
val expression = originalExpression as? KtExpression ?: return null
val annotationEntry = this@KtLightAnnotationForSourceEntry.kotlinOrigin
val context = LightClassGenerationSupport.getInstance(project).analyze(annotationEntry)
return context[BindingContext.COMPILE_TIME_VALUE, expression]?.getValue(TypeUtils.NO_EXPECTED_TYPE)
}
override fun getReference() = references.singleOrNull()
override fun getReferences() = originalExpression?.references.orEmpty()
override fun getLanguage() = KotlinLanguage.INSTANCE
override fun getNavigationElement() = originalExpression
override fun isPhysical(): Boolean = originalExpression?.containingFile == kotlinOrigin.containingFile
override fun getTextRange() = originalExpression?.textRange ?: TextRange.EMPTY_RANGE
override fun getStartOffsetInParent() = originalExpression?.startOffsetInParent ?: 0
override fun getParent() = parent
override fun getText() = originalExpression?.text.orEmpty()
override fun getContainingFile(): PsiFile? = if (isPhysical) kotlinOrigin.containingFile else delegate.containingFile
override fun replace(newElement: PsiElement): PsiElement {
val value = (newElement as? PsiLiteral)?.value as? String ?: return this
val origin = originalExpression
val exprToReplace =
if (origin is KtCallExpression /*arrayOf*/) {
unwrapArray(origin.valueArguments)
}
else {
origin as? KtExpression
} ?: return this
exprToReplace.replace(KtPsiFactory(this).createExpression("\"${StringUtil.escapeStringCharacters(value)}\""))
return this
}
}
private fun getMemberValueAsCallArgument(memberValue: PsiElement, callHolder: KtCallElement): PsiElement? {
val resolvedCall = callHolder.getResolvedCall() ?: return null
val annotationConstructor = resolvedCall.resultingDescriptor
val parameterName =
memberValue.getNonStrictParentOfType<PsiNameValuePair>()?.name ?:
memberValue.getNonStrictParentOfType<PsiAnnotationMethod>()?.takeIf { it.containingClass?.isAnnotationType == true }?.name ?:
"value"
val parameter = annotationConstructor.valueParameters.singleOrNull { it.name.asString() == parameterName } ?: return null
val resolvedArgument = resolvedCall.valueArguments[parameter] ?: return null
return when (resolvedArgument) {
is DefaultValueArgument -> {
val psi = parameter.source.getPsi()
when (psi) {
is KtParameter -> psi.defaultValue
is PsiAnnotationMethod -> psi.defaultValue
else -> error("$psi of type ${psi?.javaClass}")
}
}
is ExpressionValueArgument -> {
val argExpression = resolvedArgument.valueArgument?.getArgumentExpression()
argExpression?.asKtCall()
?: argExpression
?: error("resolvedArgument ($resolvedArgument) has no arg expression")
}
is VarargValueArgument ->
memberValue.unwrapArray(resolvedArgument.arguments)
?: resolvedArgument.arguments.first().asElement().let {
(it as? KtValueArgument)
?.takeIf {
it.getSpreadElement() != null ||
it.getArgumentName() != null ||
it.getArgumentExpression() is KtCollectionLiteralExpression
}
?.getArgumentExpression() ?: it.parent
}
else -> error("resolvedArgument: ${resolvedArgument.javaClass} cant be processed")
}
}
private fun PsiElement.unwrapArray(arguments: List<ValueArgument>): PsiElement? {
val arrayInitializer = parent as? PsiArrayInitializerMemberValue ?: return null
val exprIndex = arrayInitializer.initializers.indexOf(this)
if (exprIndex < 0 || exprIndex >= arguments.size) return null
return arguments[exprIndex].getArgumentExpression()
}
open inner class LightExpressionValue<out D : PsiExpression>(
delegate: D,
parent: PsiElement,
valueOrigin: AnnotationValueOrigin
) : LightElementValue<D>(delegate, parent, valueOrigin), PsiExpression {
override fun getType(): PsiType? = delegate.type
}
inner class LightPsiLiteral(
delegate: PsiLiteralExpression,
parent: PsiElement,
valueOrigin: AnnotationValueOrigin
) : LightExpressionValue<PsiLiteralExpression>(delegate, parent, valueOrigin), PsiLiteralExpression {
override fun getValue() = delegate.value
}
inner class LightClassLiteral(
delegate: PsiClassObjectAccessExpression,
parent: PsiElement,
valueOrigin: AnnotationValueOrigin
) : LightExpressionValue<PsiClassObjectAccessExpression>(delegate, parent, valueOrigin), PsiClassObjectAccessExpression {
override fun getType() = delegate.type
override fun getOperand(): PsiTypeElement = delegate.operand
}
inner class LightArrayInitializerValue(
delegate: PsiArrayInitializerMemberValue,
parent: PsiElement,
valueOrigin: AnnotationValueOrigin
) : LightElementValue<PsiArrayInitializerMemberValue>(delegate, parent, valueOrigin), PsiArrayInitializerMemberValue {
private val _initializers by lazyPub {
delegate.initializers.mapIndexed { i, memberValue ->
wrapAnnotationValue(memberValue, this, {
originalExpression.let { ktOrigin ->
when {
ktOrigin is KtCallExpression
&& memberValue is PsiAnnotation
&& isAnnotationConstructorCall(ktOrigin, memberValue) -> ktOrigin
ktOrigin is KtValueArgumentList -> ktOrigin.arguments.getOrNull(i)?.getArgumentExpression()
ktOrigin is KtCallElement -> ktOrigin.valueArguments.getOrNull(i)?.getArgumentExpression()
ktOrigin is KtCollectionLiteralExpression -> ktOrigin.getInnerExpressions().getOrNull(i)
delegate.initializers.size == 1 -> ktOrigin
else -> null
}.also {
if (it == null)
LOG.error("error wrapping ${memberValue.javaClass} for ${ktOrigin?.javaClass} in ${ktOrigin?.containingFile}",
Attachment(
"source_fragments.txt",
"origin: '${psiReport(ktOrigin)}', delegate: ${psiReport(delegate)}, parent: ${psiReport(parent)}"
)
)
}
}
})
}.toTypedArray()
}
override fun getInitializers() = _initializers
}
private fun wrapAnnotationValue(value: PsiAnnotationMemberValue, parent: PsiElement, ktOrigin: AnnotationValueOrigin): PsiAnnotationMemberValue =
when {
value is PsiLiteralExpression -> LightPsiLiteral(value, parent, ktOrigin)
value is PsiClassObjectAccessExpression -> LightClassLiteral(value, parent, ktOrigin)
value is PsiExpression -> LightExpressionValue(value, parent, ktOrigin)
value is PsiArrayInitializerMemberValue -> LightArrayInitializerValue(value, parent, ktOrigin)
value is PsiAnnotation -> {
val origin = ktOrigin()
val ktCallElement = origin?.asKtCall()
val qualifiedName = value.qualifiedName
if (qualifiedName != null && ktCallElement != null)
KtLightAnnotationForSourceEntry(qualifiedName, ktCallElement, parent, { value })
else {
LOG.error("can't convert ${origin?.javaClass} to KtCallElement in ${origin?.containingFile} (value = ${value.javaClass})",
Attachment("source_fragments.txt", "origin: '${psiReport(origin)}', value: '${psiReport(value)}'"))
LightElementValue(value, parent, ktOrigin) // or maybe create a LightErrorAnnotationMemberValue instead?
}
}
else -> LightElementValue(value, parent, ktOrigin)
}
override fun isPhysical() = true
override fun getName() = null
private fun wrapAnnotationValue(value: PsiAnnotationMemberValue): PsiAnnotationMemberValue = wrapAnnotationValue(value, this, {
getMemberValueAsCallArgument(value, kotlinOrigin)
})
override fun findAttributeValue(name: String?) = clsDelegate.findAttributeValue(name)?.let { wrapAnnotationValue(it) }
override fun findDeclaredAttributeValue(name: String?) = clsDelegate.findDeclaredAttributeValue(name)?.let { wrapAnnotationValue(it) }
override fun getParameterList(): PsiAnnotationParameterList = KtLightAnnotationParameterList(super.getParameterList())
inner class KtLightAnnotationParameterList(private val list: PsiAnnotationParameterList) : KtLightElementBase(this),
PsiAnnotationParameterList {
override val kotlinOrigin get() = null
override fun getAttributes(): Array<PsiNameValuePair> = list.attributes.map { KtLightPsiNameValuePair(it) }.toTypedArray()
inner class KtLightPsiNameValuePair(private val psiNameValuePair: PsiNameValuePair) : KtLightElementBase(this),
PsiNameValuePair {
override fun setValue(newValue: PsiAnnotationMemberValue): PsiAnnotationMemberValue = psiNameValuePair.setValue(newValue)
override fun getNameIdentifier(): PsiIdentifier? = psiNameValuePair.nameIdentifier
override fun getLiteralValue(): String? = (value as? PsiLiteralValue)?.value?.toString()
override val kotlinOrigin: KtElement? = null
override fun getValue(): PsiAnnotationMemberValue? = psiNameValuePair.value?.let { wrapAnnotationValue(it) }
}
}
override fun delete() = kotlinOrigin.delete()
override fun toString() = "@$qualifiedName"
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || other::class.java != this::class.java) return false
return kotlinOrigin == (other as KtLightAnnotationForSourceEntry).kotlinOrigin
}
override fun hashCode() = kotlinOrigin.hashCode()
override fun <T : PsiAnnotationMemberValue?> setDeclaredAttributeValue(attributeName: String?, value: T?) = cannotModify()
}
class KtLightNonSourceAnnotation(
parent: PsiElement, clsDelegate: PsiAnnotation
) : KtLightAbstractAnnotation(parent, { clsDelegate }) {
override val kotlinOrigin: KtAnnotationEntry? get() = null
override fun getQualifiedName() = clsDelegate.qualifiedName
override fun <T : PsiAnnotationMemberValue?> setDeclaredAttributeValue(attributeName: String?, value: T?) = cannotModify()
override fun findAttributeValue(attributeName: String?) = clsDelegate.findAttributeValue(attributeName)
override fun findDeclaredAttributeValue(attributeName: String?) = clsDelegate.findDeclaredAttributeValue(attributeName)
}
class KtLightNonExistentAnnotation(parent: KtLightElement<*, *>) : KtLightElementBase(parent), PsiAnnotation {
override val kotlinOrigin get() = null
override fun toString() = this.javaClass.name
override fun <T : PsiAnnotationMemberValue?> setDeclaredAttributeValue(attributeName: String?, value: T?) = cannotModify()
override fun getNameReferenceElement() = null
override fun findAttributeValue(attributeName: String?) = null
override fun getQualifiedName() = null
override fun getOwner() = parent as? PsiAnnotationOwner
override fun findDeclaredAttributeValue(attributeName: String?) = null
override fun getMetaData() = null
override fun getParameterList() = KtLightEmptyAnnotationParameterList(this)
override fun canNavigate(): Boolean = super<KtLightElementBase>.canNavigate()
override fun canNavigateToSource(): Boolean = super<KtLightElementBase>.canNavigateToSource()
override fun navigate(requestFocus: Boolean) = super<KtLightElementBase>.navigate(requestFocus)
}
class KtLightEmptyAnnotationParameterList(parent: PsiElement) : KtLightElementBase(parent), PsiAnnotationParameterList {
override val kotlinOrigin get() = null
override fun getAttributes(): Array<PsiNameValuePair> = emptyArray()
}
class KtLightNullabilityAnnotation(member: KtLightElement<*, PsiModifierListOwner>, parent: PsiElement) : KtLightAbstractAnnotation(parent, {
// searching for last because nullability annotations are generated after backend generates source annotations
member.clsDelegate.modifierList?.annotations?.findLast {
isNullabilityAnnotation(it.qualifiedName)
} ?: KtLightNonExistentAnnotation(member)
}) {
override fun fqNameMatches(fqName: String): Boolean {
if (!isNullabilityAnnotation(fqName)) return false
return super.fqNameMatches(fqName)
}
override val kotlinOrigin get() = null
override fun <T : PsiAnnotationMemberValue?> setDeclaredAttributeValue(attributeName: String?, value: T?) = cannotModify()
override fun findAttributeValue(attributeName: String?) = null
override fun getQualifiedName(): String? = clsDelegate.qualifiedName
override fun findDeclaredAttributeValue(attributeName: String?) = null
}
internal fun isNullabilityAnnotation(qualifiedName: String?) = qualifiedName in backendNullabilityAnnotations
private val backendNullabilityAnnotations = arrayOf(Nullable::class.java.name, NotNull::class.java.name)
private fun KtElement.getResolvedCall(): ResolvedCall<out CallableDescriptor>? {
val context = LightClassGenerationSupport.getInstance(this.project).analyze(this)
return this.getResolvedCall(context)
}
private fun isAnnotationConstructorCall(callExpression: KtCallExpression, psiAnnotation: PsiAnnotation) =
(callExpression.getResolvedCall()?.resultingDescriptor as? ClassConstructorDescriptor)?.constructedClass?.fqNameUnsafe?.toString() == psiAnnotation.qualifiedName
private fun PsiElement.asKtCall(): KtCallElement? = (this as? KtElement)?.getResolvedCall()?.call?.callElement as? KtCallElement
private fun psiReport(psiElement: PsiElement?): String {
if (psiElement == null) return "null"
val text = try {
psiElement.text
}
catch (e: Exception) {
"${e.javaClass.simpleName}:${e.message}"
}
return "${psiElement.javaClass.canonicalName}[$text]"
}