KT-15153 Support typeAlias extensions in completion and add import

#KT-15153 fixed
This commit is contained in:
Simon Ogorodnik
2017-01-19 18:49:22 +03:00
parent 9bc45a9c91
commit 3948c1e007
30 changed files with 432 additions and 13 deletions
@@ -47,6 +47,7 @@ import org.jetbrains.kotlin.formatter.AbstractFormatterTest
import org.jetbrains.kotlin.formatter.AbstractTypingIndentationTestBase
import org.jetbrains.kotlin.generators.tests.generator.*
import org.jetbrains.kotlin.idea.AbstractExpressionSelectionTest
import org.jetbrains.kotlin.idea.AbstractKotlinTypeAliasByExpansionShortNameIndexTest
import org.jetbrains.kotlin.idea.AbstractSmartSelectionTest
import org.jetbrains.kotlin.idea.actions.AbstractGotoTestOrCodeActionTest
import org.jetbrains.kotlin.idea.caches.resolve.AbstractIdeCompiledLightClassTest
@@ -529,6 +530,10 @@ fun main(args: Array<String>) {
model("quickfix", pattern = """^(\w+)\.((before\.Main\.\w+)|(test))$""", testMethod = "doTestWithExtraFile")
}
testClass<AbstractKotlinTypeAliasByExpansionShortNameIndexTest> {
model("typealiasExpansionIndex")
}
testClass<AbstractHighlightingTest> {
model("highlighter")
}
@@ -177,6 +177,8 @@ public class IdeStubIndexService extends StubIndexService {
sink.occurrence(KotlinTypeAliasShortNameIndex.getInstance().getKey(), name);
}
IndexUtilsKt.indexTypeAliasExpansion(stub, sink);
if (stub.isTopLevel()) {
FqName fqName = stub.getFqName();
if (fqName != null) {
@@ -20,34 +20,44 @@ import com.intellij.psi.stubs.IndexSink
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.stubs.KotlinCallableStubBase
import org.jetbrains.kotlin.psi.stubs.KotlinTypeAliasStub
import org.jetbrains.kotlin.util.aliasImportMap
fun <TDeclaration : KtCallableDeclaration> indexTopLevelExtension(stub: KotlinCallableStubBase<TDeclaration>, sink: IndexSink) {
if (stub.isExtension()) {
val declaration = stub.psi
val containingTypeReference = declaration.receiverTypeReference!!
containingTypeReference.typeElement?.index(declaration, sink, containingTypeReference)
containingTypeReference.typeElement?.index(declaration, containingTypeReference) { typeName ->
val name = declaration.name ?: return@index
sink.occurrence(KotlinTopLevelExtensionsByReceiverTypeIndex.INSTANCE.key,
KotlinTopLevelExtensionsByReceiverTypeIndex.buildKey(typeName, name))
}
}
}
private fun <TDeclaration : KtCallableDeclaration> KtTypeElement.index(
declaration: TDeclaration, sink: IndexSink, containingTypeReference: KtTypeReference
) {
fun occurrence(typeName: String) {
val name = declaration.name ?: return
sink.occurrence(KotlinTopLevelExtensionsByReceiverTypeIndex.INSTANCE.key,
KotlinTopLevelExtensionsByReceiverTypeIndex.buildKey(typeName, name))
fun indexTypeAliasExpansion(stub: KotlinTypeAliasStub, sink: IndexSink) {
val declaration = stub.psi
val typeReference = declaration.getTypeReference() ?: return
val typeElement = typeReference.typeElement ?: return
typeElement.index(declaration, typeReference) { typeName ->
sink.occurrence(KotlinTypeAliasByExpansionShortNameIndex.KEY, typeName)
}
}
private fun KtTypeElement.index(
declaration: KtTypeParameterListOwner,
containingTypeReference: KtTypeReference,
occurrence: (String) -> Unit
) {
when (this) {
is KtUserType -> {
var referenceName = referencedName ?: return
val referenceName = referencedName ?: return
val typeParameter = declaration.typeParameters.firstOrNull { it.name == referenceName }
if (typeParameter != null) {
val bound = typeParameter.extendsBound
if (bound != null) {
bound.typeElement?.index(declaration, sink, containingTypeReference)
bound.typeElement?.index(declaration, containingTypeReference, occurrence)
}
else {
occurrence("Any")
@@ -60,7 +70,7 @@ private fun <TDeclaration : KtCallableDeclaration> KtTypeElement.index(
aliasImportMap()[referenceName].forEach { occurrence(it) }
}
is KtNullableType -> innerType?.index(declaration, sink, containingTypeReference)
is KtNullableType -> innerType?.index(declaration, containingTypeReference, occurrence)
is KtFunctionType -> {
val arity = parameters.size + (if (receiverTypeReference != null) 1 else 0)
@@ -0,0 +1,38 @@
/*
* 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.idea.stubindex
import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.stubs.StringStubIndexExtension
import com.intellij.psi.stubs.StubIndex
import com.intellij.psi.stubs.StubIndexKey
import org.jetbrains.kotlin.psi.KtTypeAlias
class KotlinTypeAliasByExpansionShortNameIndex : StringStubIndexExtension<KtTypeAlias>() {
override fun getKey(): StubIndexKey<String, KtTypeAlias> = KEY
override fun get(key: String, project: Project, scope: GlobalSearchScope) =
StubIndex.getElements(KEY, key, project, scope, KtTypeAlias::class.java)!!
companion object {
val KEY = KotlinIndexUtil.createIndexKey(KotlinTypeAliasByExpansionShortNameIndex::class.java)
val INSTANCE = KotlinTypeAliasByExpansionShortNameIndex()
@JvmStatic fun getInstance() = INSTANCE
}
}
@@ -0,0 +1,13 @@
class A
typealias TA = A
typealias TA1 = A
fun TA.ext() {}
fun usage() {
val ta = TA1()
ta.<caret>
}
// EXIST: ext
@@ -158,6 +158,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName);
}
@TestMetadata("ExtensionOnTypeAliasFromOtherTypeAlias.kt")
public void testExtensionOnTypeAliasFromOtherTypeAlias() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/ExtensionOnTypeAliasFromOtherTypeAlias.kt");
doTest(fileName);
}
@TestMetadata("ExtensionToIntInFloatStyle.kt")
public void testExtensionToIntInFloatStyle() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/ExtensionToIntInFloatStyle.kt");
@@ -158,6 +158,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName);
}
@TestMetadata("ExtensionOnTypeAliasFromOtherTypeAlias.kt")
public void testExtensionOnTypeAliasFromOtherTypeAlias() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/ExtensionOnTypeAliasFromOtherTypeAlias.kt");
doTest(fileName);
}
@TestMetadata("ExtensionToIntInFloatStyle.kt")
public void testExtensionToIntInFloatStyle() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/ExtensionToIntInFloatStyle.kt");
@@ -49,7 +49,6 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.isHiddenInResolution
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList
import java.lang.reflect.Field
import java.lang.reflect.Modifier
@@ -175,9 +174,31 @@ class KotlinIndicesHelper(
suitableExtensions
}
private fun resolveTypeAliasesUsingIndex(type: KotlinType, originalTypeName: String, out: MutableCollection<String>) {
val index = KotlinTypeAliasByExpansionShortNameIndex.INSTANCE
fun searchRecursively(typeName: String) {
ProgressManager.checkCanceled()
index[typeName, project, scope].asSequence()
.map { it.resolveToDescriptorIfAny() as? TypeAliasDescriptor }
.filterNotNull()
.filter { it.expandedType == type }
.map { it.name.asString() }
.filter { it !in out }
.onEach(::searchRecursively)
.toCollection(out)
}
searchRecursively(originalTypeName)
}
private fun MutableCollection<String>.addTypeNames(type: KotlinType) {
val constructor = type.constructor
addIfNotNull(constructor.declarationDescriptor?.name?.asString())
constructor.declarationDescriptor?.name?.asString()?.let { typeName ->
add(typeName)
resolveTypeAliasesUsingIndex(type, typeName, this)
}
constructor.supertypes.forEach { addTypeNames(it) }
}
+1
View File
@@ -662,6 +662,7 @@
<stubIndex implementation="org.jetbrains.kotlin.idea.stubindex.KotlinFileFacadeShortNameIndex"/>
<stubIndex implementation="org.jetbrains.kotlin.idea.stubindex.KotlinMultifileClassPartIndex"/>
<stubIndex implementation="org.jetbrains.kotlin.idea.stubindex.KotlinScriptFqnIndex"/>
<stubIndex implementation="org.jetbrains.kotlin.idea.stubindex.KotlinTypeAliasByExpansionShortNameIndex"/>
<psi.classFileDecompiler implementation="org.jetbrains.kotlin.idea.decompiler.classFile.KotlinClassFileDecompiler"/>
<psi.classFileDecompiler implementation="org.jetbrains.kotlin.idea.decompiler.js.KotlinJavaScriptMetaFileDecompiler"/>
@@ -0,0 +1,10 @@
// "Import" "true"
// ERROR: Unresolved reference: ext
import dep.A
import dep.ext
fun use() {
val ta = A()
ta.ext<caret>
}
@@ -0,0 +1,7 @@
package dep
class A
typealias TA = A
val TA.ext: String get() = "ext"
@@ -0,0 +1,9 @@
// "Import" "true"
// ERROR: Unresolved reference: ext
import dep.A
fun use() {
val ta = A()
ta.ext<caret>
}
@@ -0,0 +1,10 @@
// "Import" "true"
// ERROR: Unresolved reference: ext
import dep.TA1
import dep.ext
fun use() {
val ta = TA1()
ta.ext<caret>
}
@@ -0,0 +1,8 @@
package dep
class A
typealias TA = A
typealias TA1 = A
val TA.ext: String get() = "ext"
@@ -0,0 +1,9 @@
// "Import" "true"
// ERROR: Unresolved reference: ext
import dep.TA1
fun use() {
val ta = TA1()
ta.ext<caret>
}
@@ -0,0 +1,10 @@
// "Import" "true"
// ERROR: Unresolved reference: ext
import dep.TA
import dep.ext
fun use() {
val ta = TA()
ta.ext<caret>()
}
@@ -0,0 +1,9 @@
package dep
class A
typealias TA = A
fun TA.ext() {
}
@@ -0,0 +1,9 @@
// "Import" "true"
// ERROR: Unresolved reference: ext
import dep.TA
fun use() {
val ta = TA()
ta.ext<caret>()
}
@@ -0,0 +1,9 @@
// "Import" "true"
// ERROR: Unresolved reference: ext
import dep.TTA
import dep.ext
fun use(taa: TTA) {
taa.ext<caret>()
}
@@ -0,0 +1,10 @@
package dep
class A
typealias TA = A
typealias TTA = TA
fun TA.ext() {
}
@@ -0,0 +1,8 @@
// "Import" "true"
// ERROR: Unresolved reference: ext
import dep.TTA
fun use(taa: TTA) {
taa.ext<caret>()
}
@@ -0,0 +1,10 @@
// "Import" "true"
// ERROR: Unresolved reference: ext
import dep.TA
import dep.ext
fun use() {
val ta = TA()
ta.ext<caret>
}
@@ -0,0 +1,7 @@
package dep
class A
typealias TA = A
val TA.ext: String get() = "ext"
@@ -0,0 +1,9 @@
// "Import" "true"
// ERROR: Unresolved reference: ext
import dep.TA
fun use() {
val ta = TA()
ta.ext<caret>
}
@@ -0,0 +1,17 @@
typealias TA = () -> Unit
// CONTAINS (key="Function0", value="TA")
typealias TB = () -> String
// CONTAINS (key="Function0", value="TB")
typealias TC = (String) -> Unit
// CONTAINS (key="Function1", value="TC")
typealias TD = String.() -> Unit
// CONTAINS (key="Function1", value="TD")
typealias TE = (String, String) -> Unit
// CONTAINS (key="Function2", value="TE")
typealias TF = String.(String) -> Unit
// CONTAINS (key="Function2", value="TF")
+7
View File
@@ -0,0 +1,7 @@
class A<T>
typealias TA<T> = A<T>
// CONTAINS (key="A", value="TA")
typealias TB = A<Any>
// CONTAINS (key="A", value="TB")
+3
View File
@@ -0,0 +1,3 @@
class A
typealias TA = A
// CONTAINS (key="A", value="TA")
@@ -0,0 +1,70 @@
/*
* 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.idea
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.idea.stubindex.KotlinTypeAliasByExpansionShortNameIndex
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.junit.Assert
abstract class AbstractKotlinTypeAliasByExpansionShortNameIndexTest : KotlinLightCodeInsightFixtureTestCase() {
override fun getTestDataPath(): String {
return KotlinTestUtils.getHomeDirectory() + "/"
}
lateinit var scope: GlobalSearchScope
override fun setUp() {
super.setUp()
scope = GlobalSearchScope.allScope(project)
}
override fun getProjectDescriptor() = super.getProjectDescriptorFromTestName()
fun doTest(file: String) {
myFixture.configureByFile(file)
val fileText = myFixture.file.text
InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, "CONTAINS").forEach {
assertIndexContains(it)
}
}
private val regex = "\\(key=\"(.*?)\"[, ]*value=\"(.*?)\"\\)".toRegex()
fun assertIndexContains(record: String) {
val index = KotlinTypeAliasByExpansionShortNameIndex.INSTANCE
val (_, key, value) = regex.find(record)!!.groupValues
val result = index.get(key, project, scope)
if (value !in result.map { it.name }) {
Assert.fail(buildString {
appendln("Record $record not found in index")
appendln("Index contents:")
index.getAllKeys(project).asSequence().forEach {
appendln("KEY: $it")
index.get(it, project, scope).forEach {
appendln(" ${it.name}")
}
}
})
}
}
}
@@ -0,0 +1,56 @@
/*
* 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.idea;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TargetBackend;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("idea/testData/typealiasExpansionIndex")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class KotlinTypeAliasByExpansionShortNameIndexTestGenerated extends AbstractKotlinTypeAliasByExpansionShortNameIndexTest {
public void testAllFilesPresentInTypealiasExpansionIndex() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/typealiasExpansionIndex"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("functionalTypes.kt")
public void testFunctionalTypes() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/typealiasExpansionIndex/functionalTypes.kt");
doTest(fileName);
}
@TestMetadata("generics.kt")
public void testGenerics() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/typealiasExpansionIndex/generics.kt");
doTest(fileName);
}
@TestMetadata("simpleType.kt")
public void testSimpleType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/typealiasExpansionIndex/simpleType.kt");
doTest(fileName);
}
}
@@ -150,6 +150,18 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
doTestWithExtraFile(fileName);
}
@TestMetadata("extensionPropertyOnTypeAliasFromExpansion.before.Main.kt")
public void testExtensionPropertyOnTypeAliasFromExpansion() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/extensionPropertyOnTypeAliasFromExpansion.before.Main.kt");
doTestWithExtraFile(fileName);
}
@TestMetadata("extensionPropertyOnTypeAliasFromOtherTypeAlias.before.Main.kt")
public void testExtensionPropertyOnTypeAliasFromOtherTypeAlias() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/extensionPropertyOnTypeAliasFromOtherTypeAlias.before.Main.kt");
doTestWithExtraFile(fileName);
}
@TestMetadata("factoryFunctionFromLambda.before.Main.kt")
public void testFactoryFunctionFromLambda() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/factoryFunctionFromLambda.before.Main.kt");
@@ -582,6 +594,24 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
doTestWithExtraFile(fileName);
}
@TestMetadata("typeAliasExtensionFunction.before.Main.kt")
public void testTypeAliasExtensionFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/typeAliasExtensionFunction.before.Main.kt");
doTestWithExtraFile(fileName);
}
@TestMetadata("typeAliasExtensionFunctionInTypeAliasChain.before.Main.kt")
public void testTypeAliasExtensionFunctionInTypeAliasChain() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/typeAliasExtensionFunctionInTypeAliasChain.before.Main.kt");
doTestWithExtraFile(fileName);
}
@TestMetadata("typeAliasExtensionProperty.before.Main.kt")
public void testTypeAliasExtensionProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/typeAliasExtensionProperty.before.Main.kt");
doTestWithExtraFile(fileName);
}
@TestMetadata("typeAliasImport.before.Main.kt")
public void testTypeAliasImport() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/typeAliasImport.before.Main.kt");