Get Kotlin references through Kotlin contributors only (KT-31129)

Speed up getting references and protect Kotlin from slow
references contributors.

 #KT-31129 Fixed
This commit is contained in:
Nikolay Krasko
2019-04-23 17:25:03 +03:00
parent b5918f4b5d
commit 78200dd38e
12 changed files with 214 additions and 84 deletions
@@ -18,10 +18,7 @@ package org.jetbrains.kotlin.kdoc.psi.impl
import com.intellij.lang.ASTNode
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiReference
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry
import org.jetbrains.kotlin.psi.KtElementImpl
import org.jetbrains.kotlin.kdoc.psi.api.KDoc
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
class KDocLink(node: ASTNode) : KtElementImpl(node) {
@@ -42,7 +39,4 @@ class KDocLink(node: ASTNode) : KtElementImpl(node) {
val tag = getStrictParentOfType<KDocTag>()
return if (tag != null && tag.getSubjectLink() == this) tag else null
}
override fun getReferences(): Array<out PsiReference> =
ReferenceProvidersRegistry.getReferencesFromProviders(this)
}
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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.psi
import com.intellij.openapi.components.ServiceManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
open class KotlinReferenceProvidersService {
open fun getReferences(psiElement: PsiElement): Array<PsiReference> = PsiReference.EMPTY_ARRAY
companion object {
private val NO_REFERENCES_SERVICE = KotlinReferenceProvidersService()
@JvmStatic
fun getInstance(): KotlinReferenceProvidersService {
return ServiceManager.getService(KotlinReferenceProvidersService::class.java) ?: NO_REFERENCES_SERVICE
}
@JvmStatic
fun getReferencesFromProviders(psiElement: PsiElement): Array<PsiReference> {
return getInstance().getReferences(psiElement)
}
}
}
@@ -18,8 +18,10 @@ package org.jetbrains.kotlin.psi;
import com.intellij.lang.Language;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.PsiModifiableCodeBlock;
import com.intellij.psi.PsiReference;
import com.intellij.psi.impl.source.tree.CompositeElement;
import com.intellij.psi.impl.source.tree.LazyParseablePsiElement;
import com.intellij.psi.util.PsiUtilCore;
@@ -121,7 +123,7 @@ public class KtBlockExpression extends LazyParseablePsiElement implements KtElem
@NotNull
@Override
public PsiReference[] getReferences() {
return ReferenceProvidersRegistry.getReferencesFromProviders(this, PsiReferenceService.Hints.NO_HINTS);
return KotlinReferenceProvidersService.getReferencesFromProviders(this);
}
@NotNull
@@ -19,8 +19,10 @@ package org.jetbrains.kotlin.psi;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode;
import com.intellij.lang.Language;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.idea.KotlinLanguage;
@@ -92,7 +94,7 @@ public class KtElementImpl extends ASTWrapperPsiElement implements KtElement {
@NotNull
@Override
public PsiReference[] getReferences() {
return ReferenceProvidersRegistry.getReferencesFromProviders(this, PsiReferenceService.Hints.NO_HINTS);
return KotlinReferenceProvidersService.getReferencesFromProviders(this);
}
@NotNull
@@ -20,7 +20,6 @@ import com.intellij.extapi.psi.StubBasedPsiElementBase;
import com.intellij.lang.ASTNode;
import com.intellij.lang.Language;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry;
import com.intellij.psi.stubs.IStubElementType;
import com.intellij.psi.stubs.StubElement;
import com.intellij.util.IncorrectOperationException;
@@ -109,7 +108,7 @@ public class KtElementImplStub<T extends StubElement<?>> extends StubBasedPsiEle
@NotNull
@Override
public PsiReference[] getReferences() {
return ReferenceProvidersRegistry.getReferencesFromProviders(this, PsiReferenceService.Hints.NO_HINTS);
return KotlinReferenceProvidersService.getReferencesFromProviders(this);
}
@NotNull
@@ -17,10 +17,7 @@
package org.jetbrains.kotlin.psi;
import com.intellij.lang.ASTNode;
import com.intellij.psi.ElementManipulators;
import com.intellij.psi.LiteralTextEscaper;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiLanguageInjectionHost;
import com.intellij.psi.*;
import com.intellij.psi.tree.TokenSet;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
@@ -28,7 +25,8 @@ import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub;
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes;
public class KtStringTemplateExpression extends KtElementImplStub<KotlinPlaceHolderStub<KtStringTemplateExpression>> implements KtExpression, PsiLanguageInjectionHost {
public class KtStringTemplateExpression extends KtElementImplStub<KotlinPlaceHolderStub<KtStringTemplateExpression>>
implements KtExpression, PsiLanguageInjectionHost, ContributedReferenceHost {
private static final TokenSet CLOSE_QUOTE_TOKEN_SET = TokenSet.create(KtTokens.CLOSING_QUOTE);
public KtStringTemplateExpression(@NotNull ASTNode node) {
@@ -43,8 +43,6 @@ import com.intellij.pom.core.impl.PomModelImpl;
import com.intellij.pom.tree.TreeAspect;
import com.intellij.psi.*;
import com.intellij.psi.impl.*;
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry;
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistryImpl;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.testFramework.LightVirtualFile;
import com.intellij.testFramework.TestDataFile;
@@ -109,7 +107,6 @@ public abstract class KtParsingTestCase extends KtPlatformLiteFixture {
registerComponentInstance(appContainer, PsiDocumentManager.class, new MockPsiDocumentManager());
registerApplicationService(PsiBuilderFactory.class, new PsiBuilderFactoryImpl());
registerApplicationService(DefaultASTFactory.class, new CoreASTFactory());
registerApplicationService(ReferenceProvidersRegistry.class, new ReferenceProvidersRegistryImpl());
registerApplicationService(ProgressManager.class, new CoreProgressManager());
@@ -16,58 +16,58 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
class KotlinDefaultAnnotationMethodImplicitReferenceContributor : AbstractKotlinReferenceContributor() {
class ReferenceImpl(private val argument: KtValueArgument) : PsiReference {
private fun resolveAnnotationCallee(): PsiElement? = argument.getStrictParentOfType<KtAnnotationEntry>()
?.calleeExpression
?.constructorReferenceExpression
?.mainReference
?.resolve()
class ReferenceImpl(private val argument: KtValueArgument) : PsiReference {
private fun resolveAnnotationCallee(): PsiElement? = argument.getStrictParentOfType<KtAnnotationEntry>()
?.calleeExpression
?.constructorReferenceExpression
?.mainReference
?.resolve()
override fun getElement() = argument
override fun getElement() = argument
override fun getRangeInElement() = TextRange.EMPTY_RANGE
override fun getRangeInElement() = TextRange.EMPTY_RANGE
override fun resolve(): PsiElement? {
val annotationPsi = resolveAnnotationCallee() ?: return null
val name = PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME
return when (annotationPsi) {
is PsiClass -> {
val signature = MethodSignatureUtil.createMethodSignature(
name, PsiType.EMPTY_ARRAY, PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY
)
MethodSignatureUtil.findMethodBySignature(annotationPsi, signature, false)
}
is KtPrimaryConstructor -> annotationPsi.containingClassOrObject?.findPropertyByName(name) as? KtParameter
else -> null
override fun resolve(): PsiElement? {
val annotationPsi = resolveAnnotationCallee() ?: return null
val name = PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME
return when (annotationPsi) {
is PsiClass -> {
val signature = MethodSignatureUtil.createMethodSignature(
name, PsiType.EMPTY_ARRAY, PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY
)
MethodSignatureUtil.findMethodBySignature(annotationPsi, signature, false)
}
is KtPrimaryConstructor -> annotationPsi.containingClassOrObject?.findPropertyByName(name) as? KtParameter
else -> null
}
override fun getCanonicalText() = PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME
override fun handleElementRename(newElementName: String): PsiElement {
val psiFactory = KtPsiFactory(argument)
val newArgument = psiFactory.createArgument(
argument.getArgumentExpression(),
Name.identifier(newElementName.quoteIfNeeded()),
argument.getSpreadElement() != null
)
return argument.replaced(newArgument)
}
override fun bindToElement(element: PsiElement) = throw IncorrectOperationException("Not implemented")
override fun isReferenceTo(element: PsiElement): Boolean {
val unwrapped = element.unwrapped
return (unwrapped is PsiMethod || unwrapped is KtParameter) && unwrapped == resolve()
}
override fun getVariants() = ArrayUtil.EMPTY_OBJECT_ARRAY
override fun isSoft() = false
}
override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {
override fun getCanonicalText() = PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME
override fun handleElementRename(newElementName: String): PsiElement {
val psiFactory = KtPsiFactory(argument)
val newArgument = psiFactory.createArgument(
argument.getArgumentExpression(),
Name.identifier(newElementName.quoteIfNeeded()),
argument.getSpreadElement() != null
)
return argument.replaced(newArgument)
}
override fun bindToElement(element: PsiElement) = throw IncorrectOperationException("Not implemented")
override fun isReferenceTo(element: PsiElement): Boolean {
val unwrapped = element.unwrapped
return (unwrapped is PsiMethod || unwrapped is KtParameter) && unwrapped == resolve()
}
override fun getVariants() = ArrayUtil.EMPTY_OBJECT_ARRAY
override fun isSoft() = false
}
internal class KotlinDefaultAnnotationMethodImplicitReferenceContributor : KotlinReferenceProviderContributor {
override fun registerReferenceProviders(registrar: KotlinPsiReferenceRegistrar) {
registrar.registerProvider<KtValueArgument> {
if (it.isNamed()) return@registerProvider null
val annotationEntry = it.getParentOfTypeAndBranch<KtAnnotationEntry> { valueArgumentList }
@@ -16,8 +16,6 @@
package org.jetbrains.kotlin.idea.references
import com.intellij.psi.PsiReference
import com.intellij.psi.PsiReferenceRegistrar
import org.jetbrains.kotlin.idea.kdoc.KDocReference
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtImportDirective
@@ -26,24 +24,27 @@ import org.jetbrains.kotlin.psi.KtPackageDirective
import org.jetbrains.kotlin.psi.KtUserType
import org.jetbrains.kotlin.psi.psiUtil.parents
class KotlinReferenceContributor() : AbstractKotlinReferenceContributor() {
override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {
internal class KotlinReferenceContributor : KotlinReferenceProviderContributor {
override fun registerReferenceProviders(registrar: KotlinPsiReferenceRegistrar) {
with(registrar) {
registerProvider(factory = ::KtSimpleNameReference)
registerMultiProvider<KtNameReferenceExpression> {
if (it.getReferencedNameElementType() != KtTokens.IDENTIFIER) return@registerMultiProvider emptyArray()
if (it.parents.any { it is KtImportDirective || it is KtPackageDirective || it is KtUserType }) {
registerMultiProvider<KtNameReferenceExpression> { nameReferenceExpression ->
if (nameReferenceExpression.getReferencedNameElementType() != KtTokens.IDENTIFIER) return@registerMultiProvider emptyArray()
if (nameReferenceExpression.parents.any { it is KtImportDirective || it is KtPackageDirective || it is KtUserType }) {
return@registerMultiProvider emptyArray()
}
when (it.readWriteAccess(useResolveForReadWrite = false)) {
when (nameReferenceExpression.readWriteAccess(useResolveForReadWrite = false)) {
ReferenceAccess.READ ->
arrayOf<PsiReference>(SyntheticPropertyAccessorReference.Getter(it))
arrayOf(SyntheticPropertyAccessorReference.Getter(nameReferenceExpression))
ReferenceAccess.WRITE ->
arrayOf<PsiReference>(SyntheticPropertyAccessorReference.Setter(it))
arrayOf(SyntheticPropertyAccessorReference.Setter(nameReferenceExpression))
ReferenceAccess.READ_WRITE ->
arrayOf<PsiReference>(SyntheticPropertyAccessorReference.Getter(it), SyntheticPropertyAccessorReference.Setter(it))
arrayOf(
SyntheticPropertyAccessorReference.Getter(nameReferenceExpression),
SyntheticPropertyAccessorReference.Setter(nameReferenceExpression)
)
}
}
@@ -0,0 +1,111 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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.idea.references
import com.intellij.openapi.project.IndexNotReadyException
import com.intellij.psi.ContributedReferenceHost
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
import com.intellij.psi.PsiReferenceService
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.PsiModificationTracker
import com.intellij.util.containers.ConcurrentFactoryMap
import com.intellij.util.containers.ContainerUtil
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.psi.KotlinReferenceProvidersService
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.utils.SmartList
internal interface KotlinPsiReferenceProvider {
fun getReferencesByElement(element: PsiElement): Array<PsiReference>
}
internal interface KotlinReferenceProviderContributor {
fun registerReferenceProviders(registrar: KotlinPsiReferenceRegistrar)
}
internal class KotlinPsiReferenceRegistrar {
val providers: MultiMap<Class<out PsiElement>, KotlinPsiReferenceProvider> = MultiMap.create()
inline fun <reified E : KtElement> registerProvider(crossinline factory: (E) -> PsiReference?) {
registerMultiProvider<E> { element ->
factory(element)?.let { reference -> arrayOf(reference) } ?: PsiReference.EMPTY_ARRAY
}
}
inline fun <reified E : KtElement> registerMultiProvider(crossinline factory: (E) -> Array<PsiReference>) {
val provider: KotlinPsiReferenceProvider = object : KotlinPsiReferenceProvider {
override fun getReferencesByElement(element: PsiElement): Array<PsiReference> {
@Suppress("UNCHECKED_CAST")
return factory(element as E)
}
}
registerMultiProvider(E::class.java, provider)
}
fun registerMultiProvider(klass: Class<out PsiElement>, provider: KotlinPsiReferenceProvider) {
providers.putValue(klass, provider)
}
}
class KtIdeReferenceProviderService : KotlinReferenceProvidersService() {
private val originalProvidersBinding: MultiMap<Class<out PsiElement>, KotlinPsiReferenceProvider>
private val providersBindingCache: Map<Class<out PsiElement>, List<KotlinPsiReferenceProvider>>
private val referenceContributors = listOf(KotlinReferenceContributor(), KotlinDefaultAnnotationMethodImplicitReferenceContributor())
init {
val registrar = KotlinPsiReferenceRegistrar()
referenceContributors.forEach { it.registerReferenceProviders(registrar) }
originalProvidersBinding = registrar.providers
providersBindingCache = ConcurrentFactoryMap.createMap<Class<out PsiElement>, List<KotlinPsiReferenceProvider>> { klass ->
val result = ContainerUtil.newSmartList<KotlinPsiReferenceProvider>()
for (bindingClass in originalProvidersBinding.keySet()) {
if (bindingClass.isAssignableFrom(klass)) {
result.addAll(originalProvidersBinding.get(bindingClass))
}
}
result
}
}
private fun doGetKotlinReferencesFromProviders(context: PsiElement): Array<PsiReference> {
val providers: List<KotlinPsiReferenceProvider>? = providersBindingCache[context.javaClass]
if (providers.isNullOrEmpty()) return PsiReference.EMPTY_ARRAY
val result = SmartList<PsiReference>()
for (provider in providers) {
try {
result.addAll(provider.getReferencesByElement(context))
} catch (ignored: IndexNotReadyException) {
// Ignore and continue to next provider
}
}
if (result.isEmpty()) {
return PsiReference.EMPTY_ARRAY
}
return result.toTypedArray()
}
override fun getReferences(psiElement: PsiElement): Array<PsiReference> {
if (psiElement is ContributedReferenceHost) {
return ReferenceProvidersRegistry.getReferencesFromProviders(psiElement, PsiReferenceService.Hints.NO_HINTS)
}
return CachedValuesManager.getCachedValue(psiElement) {
CachedValueProvider.Result.create(
doGetKotlinReferencesFromProviders(psiElement),
PsiModificationTracker.MODIFICATION_COUNT
)
}
}
}
+3 -5
View File
@@ -224,6 +224,9 @@
<applicationService serviceInterface="org.jetbrains.kotlin.psi.stubs.elements.StubIndexService"
serviceImplementation="org.jetbrains.kotlin.idea.stubindex.IdeStubIndexService"/>
<applicationService serviceInterface="org.jetbrains.kotlin.psi.KotlinReferenceProvidersService"
serviceImplementation="org.jetbrains.kotlin.idea.references.KtIdeReferenceProviderService"/>
<applicationService serviceInterface="org.jetbrains.kotlin.load.kotlin.KotlinBinaryClassCache"
serviceImplementation="org.jetbrains.kotlin.load.kotlin.KotlinBinaryClassCache"/>
@@ -523,15 +526,10 @@
<weigher key="completion" implementationClass="org.jetbrains.kotlin.idea.completion.KotlinLookupElementProximityWeigher" id="kotlin.proximity" order="after proximity"/>
<psi.referenceContributor language="kotlin" implementation="org.jetbrains.kotlin.idea.references.KotlinReferenceContributor"/>
<psi.referenceContributor
id="kotlinFilePathReferenceContributor"
language="kotlin"
implementation="org.jetbrains.kotlin.idea.references.KotlinFilePathReferenceContributor"/>
<psi.referenceContributor
id="kotlinDefaultAnnotationMethodImplicitReferenceContributor"
language="kotlin"
implementation="org.jetbrains.kotlin.idea.references.KotlinDefaultAnnotationMethodImplicitReferenceContributor"/>
<psi.treeChangePreprocessor implementation="org.jetbrains.kotlin.idea.caches.KotlinPackageStatementPsiTreeChangePreprocessor"/>
@@ -32,7 +32,7 @@ import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtValueArgument
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.idea.references.KotlinDefaultAnnotationMethodImplicitReferenceContributor.ReferenceImpl as ImplicitReference
import org.jetbrains.kotlin.idea.references.ReferenceImpl as ImplicitReference
class DefaultAnnotationMethodKotlinImplicitReferenceSearcher :
QueryExecutorBase<PsiReference, MethodReferencesSearch.SearchParameters>(true) {