Implemented fast search for invoke operator calls
#KT-13643 Fixed
This commit is contained in:
+8
-4
@@ -37,22 +37,21 @@ import org.jetbrains.kotlin.idea.search.KOTLIN_NAMED_ARGUMENT_SEARCH_CONTEXT
|
|||||||
import org.jetbrains.kotlin.idea.search.allScope
|
import org.jetbrains.kotlin.idea.search.allScope
|
||||||
import org.jetbrains.kotlin.idea.search.effectiveSearchScope
|
import org.jetbrains.kotlin.idea.search.effectiveSearchScope
|
||||||
import org.jetbrains.kotlin.idea.search.unionSafe
|
import org.jetbrains.kotlin.idea.search.unionSafe
|
||||||
import org.jetbrains.kotlin.idea.search.usagesSearch.dataClassComponentFunction
|
import org.jetbrains.kotlin.idea.search.usagesSearch.*
|
||||||
import org.jetbrains.kotlin.idea.search.usagesSearch.findDestructuringDeclarationUsages
|
|
||||||
import org.jetbrains.kotlin.idea.search.usagesSearch.getClassNameForCompanionObject
|
|
||||||
import org.jetbrains.kotlin.idea.search.usagesSearch.getSpecialNamesToSearch
|
|
||||||
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
|
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parents
|
import org.jetbrains.kotlin.psi.psiUtil.parents
|
||||||
import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike
|
import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike
|
||||||
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
|
|
||||||
data class KotlinReferencesSearchOptions(val acceptCallableOverrides: Boolean = false,
|
data class KotlinReferencesSearchOptions(val acceptCallableOverrides: Boolean = false,
|
||||||
val acceptOverloads: Boolean = false,
|
val acceptOverloads: Boolean = false,
|
||||||
val acceptExtensionsOfDeclarationClass: Boolean = false,
|
val acceptExtensionsOfDeclarationClass: Boolean = false,
|
||||||
val acceptCompanionObjectMembers: Boolean = false,
|
val acceptCompanionObjectMembers: Boolean = false,
|
||||||
val searchForComponentConventions: Boolean = true,
|
val searchForComponentConventions: Boolean = true,
|
||||||
|
val searchInvokeOperator: Boolean = true,
|
||||||
val searchNamedArguments: Boolean = true) {
|
val searchNamedArguments: Boolean = true) {
|
||||||
fun anyEnabled(): Boolean = acceptCallableOverrides || acceptOverloads || acceptExtensionsOfDeclarationClass
|
fun anyEnabled(): Boolean = acceptCallableOverrides || acceptOverloads || acceptExtensionsOfDeclarationClass
|
||||||
|
|
||||||
@@ -144,6 +143,11 @@ class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, ReferencesSearc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: Java invoke's
|
||||||
|
if (kotlinOptions.searchInvokeOperator && element is KtFunction && name == OperatorNameConventions.INVOKE.asString()) {
|
||||||
|
findInvokeOperatorUsages(element, effectiveSearchScope, consumer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun searchNamedArguments(parameter: KtParameter, queryParameters: ReferencesSearch.SearchParameters) {
|
private fun searchNamedArguments(parameter: KtParameter, queryParameters: ReferencesSearch.SearchParameters) {
|
||||||
|
|||||||
+2
-1
@@ -51,6 +51,7 @@ import org.jetbrains.kotlin.load.java.sam.SingleAbstractMethodUtils
|
|||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
//TODO: check if smart search is too expensive
|
//TODO: check if smart search is too expensive
|
||||||
@@ -592,7 +593,7 @@ class ExpressionsOfTypeProcessor(
|
|||||||
private fun PsiElement.isOperatorExpensiveToSearch(): Boolean {
|
private fun PsiElement.isOperatorExpensiveToSearch(): Boolean {
|
||||||
when (this) {
|
when (this) {
|
||||||
is KtFunction -> {
|
is KtFunction -> {
|
||||||
if (name?.startsWith("component") == true) return false // component functions are not so expensive to search
|
if (name?.startsWith("component") == true || name == OperatorNameConventions.INVOKE.asString()) return false
|
||||||
return hasModifier(KtTokens.OPERATOR_KEYWORD)
|
return hasModifier(KtTokens.OPERATOR_KEYWORD)
|
||||||
|| hasModifier(KtTokens.OVERRIDE_KEYWORD) && (resolveToDescriptorIfAny() as? FunctionDescriptor)?.isOperator == true
|
|| hasModifier(KtTokens.OVERRIDE_KEYWORD) && (resolveToDescriptorIfAny() as? FunctionDescriptor)?.isOperator == true
|
||||||
}
|
}
|
||||||
|
|||||||
+136
@@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.search.usagesSearch
|
||||||
|
|
||||||
|
import com.intellij.openapi.roots.ProjectRootManager
|
||||||
|
import com.intellij.psi.PsiManager
|
||||||
|
import com.intellij.psi.PsiMethod
|
||||||
|
import com.intellij.psi.PsiReference
|
||||||
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import com.intellij.psi.search.LocalSearchScope
|
||||||
|
import com.intellij.psi.search.SearchScope
|
||||||
|
import com.intellij.util.Processor
|
||||||
|
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
|
||||||
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.references.KtInvokeFunctionReference
|
||||||
|
import org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor.Companion.testLog
|
||||||
|
import org.jetbrains.kotlin.idea.util.fuzzyExtensionReceiverType
|
||||||
|
import org.jetbrains.kotlin.idea.util.toFuzzyType
|
||||||
|
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||||
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||||
|
import org.jetbrains.kotlin.util.isValidOperator
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||||
|
|
||||||
|
//TODO: problem with infinite recursion not solved
|
||||||
|
|
||||||
|
//TODO: code duplication
|
||||||
|
|
||||||
|
fun findInvokeOperatorUsages(
|
||||||
|
invokeFunction: PsiMethod,
|
||||||
|
scope: SearchScope,
|
||||||
|
consumer: Processor<PsiReference>
|
||||||
|
) {
|
||||||
|
if (invokeFunction !is KtLightMethod) return //TODO
|
||||||
|
val ktDeclarationTarget = invokeFunction.kotlinOrigin as? KtDeclaration ?: return //TODO?
|
||||||
|
findInvokeOperatorUsages(ktDeclarationTarget, scope, consumer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findInvokeOperatorUsages(
|
||||||
|
targetDeclaration: KtDeclaration,
|
||||||
|
searchScope: SearchScope,
|
||||||
|
consumer: Processor<PsiReference>
|
||||||
|
) {
|
||||||
|
val usePlainSearch = when (ExpressionsOfTypeProcessor.mode) {
|
||||||
|
ExpressionsOfTypeProcessor.Mode.ALWAYS_SMART -> false
|
||||||
|
ExpressionsOfTypeProcessor.Mode.ALWAYS_PLAIN -> true
|
||||||
|
ExpressionsOfTypeProcessor.Mode.PLAIN_WHEN_NEEDED -> searchScope is LocalSearchScope // for local scope it's faster to use plain search
|
||||||
|
}
|
||||||
|
if (usePlainSearch) {
|
||||||
|
doPlainSearch(targetDeclaration, searchScope, consumer)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val descriptor = targetDeclaration.resolveToDescriptor() as? CallableDescriptor ?: return
|
||||||
|
|
||||||
|
if (descriptor is FunctionDescriptor && !descriptor.isValidOperator()) return
|
||||||
|
|
||||||
|
val dataType = if (descriptor.isExtension) {
|
||||||
|
descriptor.fuzzyExtensionReceiverType()!!
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val classDescriptor = descriptor.containingDeclaration as? ClassDescriptor ?: return
|
||||||
|
classDescriptor.defaultType.toFuzzyType(classDescriptor.typeConstructor.parameters)
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpressionsOfTypeProcessor(
|
||||||
|
dataType,
|
||||||
|
searchScope,
|
||||||
|
suspiciousExpressionHandler = { expression -> processSuspiciousExpression(expression, targetDeclaration, consumer) },
|
||||||
|
suspiciousScopeHandler = { searchScope -> doPlainSearch(targetDeclaration, searchScope, consumer) },
|
||||||
|
resolutionFacade = targetDeclaration.getResolutionFacade()
|
||||||
|
).run()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processSuspiciousExpression(expression: KtExpression, targetDeclaration: KtDeclaration, consumer: Processor<PsiReference>) {
|
||||||
|
val callExpression = expression.parent as? KtCallExpression ?: return
|
||||||
|
testLog?.add("Resolving call ${callExpression.text}")
|
||||||
|
processCallExpression(callExpression, targetDeclaration, consumer)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doPlainSearch(ktDeclaration: KtDeclaration, scope: SearchScope, consumer: Processor<PsiReference>) {
|
||||||
|
if (scope is LocalSearchScope) {
|
||||||
|
for (element in scope.scope) {
|
||||||
|
val stop = element.anyDescendantOfType<KtCallExpression> { !processCallExpression(it, ktDeclaration, consumer) }
|
||||||
|
if (stop) break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
scope as GlobalSearchScope
|
||||||
|
val project = ktDeclaration.project
|
||||||
|
val psiManager = PsiManager.getInstance(project)
|
||||||
|
ProjectRootManager.getInstance(project).fileIndex.iterateContent { file ->
|
||||||
|
if (file in scope) {
|
||||||
|
val ktFile = psiManager.findFile(file) as? KtFile
|
||||||
|
if (ktFile != null) {
|
||||||
|
val stop = ktFile.anyDescendantOfType<KtCallExpression> {
|
||||||
|
!processCallExpression(it, ktDeclaration, consumer)
|
||||||
|
}
|
||||||
|
if (stop) return@iterateContent false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processCallExpression(callExpression: KtCallExpression, targetDeclaration: KtDeclaration, consumer: Processor<PsiReference>): Boolean {
|
||||||
|
val reference = callExpression.references.firstIsInstance<KtInvokeFunctionReference>()
|
||||||
|
if (reference.isReferenceTo(targetDeclaration)) {
|
||||||
|
return consumer.process(reference)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -131,9 +131,9 @@ abstract class KotlinFindMemberUsagesHandler<T : KtNamedDeclaration>
|
|||||||
val uniqueProcessor = CommonProcessors.UniqueProcessor(processor)
|
val uniqueProcessor = CommonProcessors.UniqueProcessor(processor)
|
||||||
|
|
||||||
if (options.isUsages) {
|
if (options.isUsages) {
|
||||||
// we disable searchForComponentConventions for ReferencesSearch because they will be searched by MethodReferencesSearch
|
// we disable searchForComponentConventions and searchInvokeOperator for ReferencesSearch because they will be searched by MethodReferencesSearch
|
||||||
val kotlinSearchOptions = createKotlinReferencesSearchOptions(options)
|
val kotlinSearchOptions = createKotlinReferencesSearchOptions(options)
|
||||||
.copy(searchForComponentConventions = false)
|
.copy(searchForComponentConventions = false, searchInvokeOperator = false)
|
||||||
val searchParameters = KotlinReferencesSearchParameters(element, options.searchScope, kotlinOptions = kotlinSearchOptions)
|
val searchParameters = KotlinReferencesSearchParameters(element, options.searchScope, kotlinOptions = kotlinSearchOptions)
|
||||||
|
|
||||||
with(applyQueryFilters(element, options, ReferencesSearch.search(searchParameters))) {
|
with(applyQueryFilters(element, options, ReferencesSearch.search(searchParameters))) {
|
||||||
|
|||||||
+5
@@ -23,11 +23,13 @@ import com.intellij.psi.search.searches.MethodReferencesSearch
|
|||||||
import com.intellij.util.Processor
|
import com.intellij.util.Processor
|
||||||
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
|
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
|
||||||
import org.jetbrains.kotlin.idea.search.usagesSearch.findDestructuringDeclarationUsages
|
import org.jetbrains.kotlin.idea.search.usagesSearch.findDestructuringDeclarationUsages
|
||||||
|
import org.jetbrains.kotlin.idea.search.usagesSearch.findInvokeOperatorUsages
|
||||||
import org.jetbrains.kotlin.idea.search.usagesSearch.getOperationSymbolsToSearch
|
import org.jetbrains.kotlin.idea.search.usagesSearch.getOperationSymbolsToSearch
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
import org.jetbrains.kotlin.lexer.KtSingleValueToken
|
import org.jetbrains.kotlin.lexer.KtSingleValueToken
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike
|
import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike
|
||||||
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
|
|
||||||
class KotlinConventionMethodReferencesSearcher() : QueryExecutorBase<PsiReference, MethodReferencesSearch.SearchParameters>(true) {
|
class KotlinConventionMethodReferencesSearcher() : QueryExecutorBase<PsiReference, MethodReferencesSearch.SearchParameters>(true) {
|
||||||
override fun processQuery(queryParameters: MethodReferencesSearch.SearchParameters, consumer: Processor<PsiReference>) {
|
override fun processQuery(queryParameters: MethodReferencesSearch.SearchParameters, consumer: Processor<PsiReference>) {
|
||||||
@@ -39,6 +41,9 @@ class KotlinConventionMethodReferencesSearcher() : QueryExecutorBase<PsiReferenc
|
|||||||
if (isComponentLike(identifier)) {
|
if (isComponentLike(identifier)) {
|
||||||
findDestructuringDeclarationUsages(method, queryParameters.effectiveSearchScope, consumer, queryParameters.optimizer)
|
findDestructuringDeclarationUsages(method, queryParameters.effectiveSearchScope, consumer, queryParameters.optimizer)
|
||||||
}
|
}
|
||||||
|
else if (identifier == OperatorNameConventions.INVOKE) {
|
||||||
|
findInvokeOperatorUsages(method, queryParameters.effectiveSearchScope, consumer)
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
val operationSymbolsToSearch = identifier.getOperationSymbolsToSearch()
|
val operationSymbolsToSearch = identifier.getOperationSymbolsToSearch()
|
||||||
val wordsToSearch = operationSymbolsToSearch.first.map { (it as KtSingleValueToken).value }
|
val wordsToSearch = operationSymbolsToSearch.first.map { (it as KtSingleValueToken).value }
|
||||||
|
|||||||
+5
-3
@@ -2,10 +2,12 @@
|
|||||||
// OPTIONS: usages
|
// OPTIONS: usages
|
||||||
|
|
||||||
class B(val n: Int) {
|
class B(val n: Int) {
|
||||||
fun <caret>invoke(i: Int): B = B(i)
|
operator fun <caret>invoke(i: Int){}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun f() = B(1)
|
||||||
|
|
||||||
fun test() {
|
fun test() {
|
||||||
B(1).invoke(2)
|
f(1).invoke(2)
|
||||||
B(1)(2)
|
f(2)(2)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
Checked type of f()
|
||||||
|
Resolving call f(1)
|
||||||
|
Resolving call f(2)
|
||||||
|
Resolving call f(2)(2)
|
||||||
|
Searched references to B
|
||||||
|
Searched references to f() in Kotlin files
|
||||||
|
Used plain search in LocalSearchScope:
|
||||||
|
CLASS:B
|
||||||
@@ -1 +1,2 @@
|
|||||||
Function call 9 B(1).invoke(2)
|
Function call 11 f(1).invoke(2)
|
||||||
|
Implicit 'invoke' 12 f(2)(2)
|
||||||
@@ -4,5 +4,5 @@ class A(val n: Int) {
|
|||||||
|
|
||||||
fun test() {
|
fun test() {
|
||||||
A(1).foo(2)
|
A(1).foo(2)
|
||||||
A(1)(2)
|
A(1).foo(2)
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
class B(val n: Int) {
|
||||||
|
operator fun <caret>invoke(i: Int){}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun f() = B(1)
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
f(1).invoke(2)
|
||||||
|
f(2)(2)
|
||||||
|
}
|
||||||
@@ -478,6 +478,16 @@ public abstract class AbstractFindUsagesTest extends KotlinLightCodeInsightFixtu
|
|||||||
|
|
||||||
if (log != null) {
|
if (log != null) {
|
||||||
KotlinTestUtils.assertEqualsToFile(new File(rootPath, prefix + "log"), log);
|
KotlinTestUtils.assertEqualsToFile(new File(rootPath, prefix + "log"), log);
|
||||||
|
|
||||||
|
// if log is empty then compare results with plain search
|
||||||
|
try {
|
||||||
|
ExpressionsOfTypeProcessor.Companion.setMode(ExpressionsOfTypeProcessor.Mode.ALWAYS_PLAIN);
|
||||||
|
|
||||||
|
findUsagesAndCheckResults(mainFileText, prefix, rootPath, caretElement, options);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
ExpressionsOfTypeProcessor.Companion.setMode(ExpressionsOfTypeProcessor.Mode.ALWAYS_SMART);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import com.intellij.psi.search.LocalSearchScope
|
|||||||
import com.intellij.psi.search.searches.ReferencesSearch
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
|
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
|
||||||
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
|
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
|
||||||
|
import org.jetbrains.kotlin.idea.references.KtInvokeFunctionReference
|
||||||
import org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor
|
import org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor
|
||||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||||
import org.jetbrains.kotlin.psi.KtFunction
|
import org.jetbrains.kotlin.psi.KtFunction
|
||||||
@@ -58,6 +59,13 @@ class KotlinReferencesSearchTest(): AbstractSearcherTest() {
|
|||||||
Assert.assertTrue(refs[1] is KtDestructuringDeclarationReference)
|
Assert.assertTrue(refs[1] is KtDestructuringDeclarationReference)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun testInvokeFun() {
|
||||||
|
val refs = doTest<KtFunction>()
|
||||||
|
Assert.assertEquals(2, refs.size)
|
||||||
|
Assert.assertEquals("invoke", refs[0].canonicalText)
|
||||||
|
Assert.assertTrue(refs[1] is KtInvokeFunctionReference)
|
||||||
|
}
|
||||||
|
|
||||||
// workaround for KT-9788 AssertionError from backand when we read field from inline function
|
// workaround for KT-9788 AssertionError from backand when we read field from inline function
|
||||||
private val myFixtureProxy: JavaCodeInsightTestFixture get() = myFixture
|
private val myFixtureProxy: JavaCodeInsightTestFixture get() = myFixture
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user