Changes on review of pull request

This commit is contained in:
Valentin Kipyatkov
2016-08-17 16:37:06 +03:00
parent 939b4768f5
commit 0072bf079d
5 changed files with 103 additions and 58 deletions
@@ -16,7 +16,6 @@ remove.unnecessary.non.null.assertion=Remove unnecessary non-null assertion (!!)
change.to.backing.field=Change reference to backing field change.to.backing.field=Change reference to backing field
implement.members=Implement members implement.members=Implement members
import.fix=Import import.fix=Import
import.static.fix=Static import
imports.chooser.title=Imports imports.chooser.title=Imports
rename.kotlin.package.class.error=Can't rename Kotlin package facade class rename.kotlin.package.class.error=Can't rename Kotlin package facade class
add.semicolon.after.invocation=Add semicolon after invocation of ''{0}'' add.semicolon.after.invocation=Add semicolon after invocation of ''{0}''
@@ -205,32 +205,29 @@ class KotlinIndicesHelper(
.toSet() .toSet()
} }
fun getJvmCallablesByName(name: String): Collection<CallableDescriptor> {
fun getJvmStatics(name: String): Collection<DeclarationDescriptor> { val javaDeclarations = PsiShortNamesCache.getInstance(project).getFieldsByName(name, scope).asSequence() +
return sequenceOf(PsiShortNamesCache.getInstance(project).getFieldsByName(name, scope), PsiShortNamesCache.getInstance(project).getMethodsByName(name, scope).asSequence()
PsiShortNamesCache.getInstance(project).getMethodsByName(name, scope)) return javaDeclarations
.flatMap { it.asSequence() } .mapNotNull { (it as PsiMember).getJavaMemberDescriptor(resolutionFacade) as? CallableDescriptor }
.mapNotNull { (it as PsiMember).getJavaMemberDescriptor(resolutionFacade) }
.filter(descriptorFilter) .filter(descriptorFilter)
.toSet() .toSet()
} }
fun getKotlinStatics(name: String): Collection<DeclarationDescriptor> { fun getKotlinCallablesByName(name: String): Collection<CallableDescriptor> {
return sequenceOf(KotlinFunctionShortNameIndex.getInstance().get(name, project, scope), val functions = KotlinFunctionShortNameIndex.getInstance().get(name, project, scope)
KotlinPropertyShortNameIndex.getInstance().get(name, project, scope)) .asSequence()
.flatMap { it.asSequence() } .map { it.descriptor as? CallableDescriptor }
.mapNotNull { val properties = KotlinPropertyShortNameIndex.getInstance().get(name, project, scope)
when (it) { .asSequence()
is KtNamedFunction -> it.descriptor .map { it.descriptor as? CallableDescriptor }
is KtProperty -> it.descriptor
else -> null return (functions + properties)
} .filterNotNull()
}.filterNot { it.importableFqName == null }
.filter(descriptorFilter) .filter(descriptorFilter)
.toSet() .toSet()
} }
fun getKotlinClasses(nameFilter: (String) -> Boolean, kindFilter: (ClassKind) -> Boolean): Collection<ClassDescriptor> { fun getKotlinClasses(nameFilter: (String) -> Boolean, kindFilter: (ClassKind) -> Boolean): Collection<ClassDescriptor> {
return KotlinFullClassNameIndex.getInstance().getAllKeys(project).asSequence() return KotlinFullClassNameIndex.getInstance().getAllKeys(project).asSequence()
.map { FqName(it) } .map { FqName(it) }
@@ -29,6 +29,7 @@ import com.intellij.psi.PsiFile
import com.intellij.psi.util.PsiModificationTracker import com.intellij.psi.util.PsiModificationTracker
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors
@@ -42,6 +43,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.getResolveScope
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.core.KotlinIndicesHelper import org.jetbrains.kotlin.idea.core.KotlinIndicesHelper
import org.jetbrains.kotlin.idea.core.isVisible import org.jetbrains.kotlin.idea.core.isVisible
import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil import org.jetbrains.kotlin.idea.project.ProjectStructureUtil
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
@@ -147,8 +149,7 @@ internal abstract class AutoImportFixBase<T : KtExpression>(expression: T) :
val indicesHelper = KotlinIndicesHelper(resolutionFacade, searchScope, ::isVisible) val indicesHelper = KotlinIndicesHelper(resolutionFacade, searchScope, ::isVisible)
val result = fillCandidates(callTypeAndReceiver, file, bindingContext, val result = fillCandidates(nameStr, callTypeAndReceiver, bindingContext, indicesHelper)
indicesHelper, nameStr, filterByCallType = { callTypeAndReceiver.callType.descriptorKindFilter.accepts(it) })
return if (result.size > 1) return if (result.size > 1)
reduceCandidatesBasedOnDependencyRuleViolation(result, file) reduceCandidatesBasedOnDependencyRuleViolation(result, file)
@@ -156,9 +157,12 @@ internal abstract class AutoImportFixBase<T : KtExpression>(expression: T) :
result result
} }
abstract fun fillCandidates(callTypeAndReceiver: CallTypeAndReceiver<*, *>, file: KtFile, abstract fun fillCandidates(
bindingContext: BindingContext, indicesHelper: KotlinIndicesHelper, name: String,
nameStr: String, filterByCallType: (descriptor: DeclarationDescriptor) -> (Boolean)): List<DeclarationDescriptor> callTypeAndReceiver: CallTypeAndReceiver<*, *>,
bindingContext: BindingContext,
indicesHelper: KotlinIndicesHelper
): List<DeclarationDescriptor>
private fun reduceCandidatesBasedOnDependencyRuleViolation( private fun reduceCandidatesBasedOnDependencyRuleViolation(
candidates: Collection<DeclarationDescriptor>, file: PsiFile): Collection<DeclarationDescriptor> { candidates: Collection<DeclarationDescriptor>, file: PsiFile): Collection<DeclarationDescriptor> {
@@ -172,25 +176,32 @@ internal abstract class AutoImportFixBase<T : KtExpression>(expression: T) :
} }
internal abstract class AutoImportFixBaseDynamicContext<T : KtExpression>(expression: T) : AutoImportFixBase<T>(expression) { internal abstract class AutoImportFixBaseDynamicContext<T : KtExpression>(expression: T) : AutoImportFixBase<T>(expression) {
override fun fillCandidates(callTypeAndReceiver: CallTypeAndReceiver<*, *>, file: KtFile, bindingContext: BindingContext, indicesHelper: KotlinIndicesHelper, nameStr: String, filterByCallType: (DeclarationDescriptor) -> Boolean): List<DeclarationDescriptor> { override fun fillCandidates(
name: String,
callTypeAndReceiver: CallTypeAndReceiver<*, *>,
bindingContext: BindingContext,
indicesHelper: KotlinIndicesHelper
): List<DeclarationDescriptor> {
val result = ArrayList<DeclarationDescriptor>() val result = ArrayList<DeclarationDescriptor>()
val expression = element val expression = element
if (expression is KtSimpleNameExpression) { if (expression is KtSimpleNameExpression) {
if (!expression.isImportDirectiveExpression() && !KtPsiUtil.isSelectorInQualified(expression)) { if (!expression.isImportDirectiveExpression() && !KtPsiUtil.isSelectorInQualified(expression)) {
if (ProjectStructureUtil.isJsKotlinModule(file)) { val filterByCallType = { descriptor: DeclarationDescriptor -> callTypeAndReceiver.callType.descriptorKindFilter.accepts(descriptor) }
indicesHelper.getKotlinClasses({ it == nameStr }, { true }).filterTo(result, filterByCallType)
if (ProjectStructureUtil.isJsKotlinModule(expression.getContainingKtFile())) {
indicesHelper.getKotlinClasses({ it == name }, { true }).filterTo(result, filterByCallType)
} }
else { else {
indicesHelper.getJvmClassesByName(nameStr).filterTo(result, filterByCallType) indicesHelper.getJvmClassesByName(name).filterTo(result, filterByCallType)
} }
indicesHelper.getTopLevelCallablesByName(nameStr).filterTo(result, filterByCallType) indicesHelper.getTopLevelCallablesByName(name).filterTo(result, filterByCallType)
} }
} }
result.addAll(indicesHelper.getCallableTopLevelExtensions(callTypeAndReceiver, expression, bindingContext) { it == nameStr }) result.addAll(indicesHelper.getCallableTopLevelExtensions(callTypeAndReceiver, expression, bindingContext) { it == name })
return result return result
} }
@@ -366,16 +377,28 @@ internal class MissingComponentsAutoImportFix(element: KtExpression, override va
internal class AutoImportStaticFix(expression: KtSimpleNameExpression) : AutoImportFixBase<KtSimpleNameExpression>(expression) { internal class AutoImportStaticFix(expression: KtSimpleNameExpression) : AutoImportFixBase<KtSimpleNameExpression>(expression) {
override fun getText() = KotlinBundle.message("import.static.fix") override fun getText() = "Static import"
override fun fillCandidates(callTypeAndReceiver: CallTypeAndReceiver<*, *>, file: KtFile, bindingContext: BindingContext, indicesHelper: KotlinIndicesHelper, nameStr: String, filterByCallType: (DeclarationDescriptor) -> Boolean): List<DeclarationDescriptor> { override fun fillCandidates(
name: String,
callTypeAndReceiver: CallTypeAndReceiver<*, *>,
bindingContext: BindingContext,
indicesHelper: KotlinIndicesHelper
): List<DeclarationDescriptor> {
val result = ArrayList<DeclarationDescriptor>() val result = ArrayList<DeclarationDescriptor>()
val expression = element val expression = element
if (expression is KtSimpleNameExpression) { if (expression is KtSimpleNameExpression) {
if (!expression.isImportDirectiveExpression() && !KtPsiUtil.isSelectorInQualified(expression)) { if (!expression.isImportDirectiveExpression() && !KtPsiUtil.isSelectorInQualified(expression)) {
indicesHelper.getKotlinStatics(nameStr).filterTo(result, filterByCallType) val filterByCallType = { descriptor: DeclarationDescriptor -> callTypeAndReceiver.callType.descriptorKindFilter.accepts(descriptor) }
if (!(ProjectStructureUtil.isJsKotlinModule(file))) {
indicesHelper.getJvmStatics(nameStr).filterTo(result, filterByCallType) indicesHelper.getKotlinCallablesByName(name)
.filter { it.canBeReferencedViaImport() && it.containingDeclaration !is PackageFragmentDescriptor }
.filterTo(result, filterByCallType)
if (!ProjectStructureUtil.isJsKotlinModule(expression.getContainingKtFile())) {
indicesHelper.getJvmCallablesByName(name)
.filter { it.canBeReferencedViaImport() }
.filterTo(result, filterByCallType)
} }
} }
} }
@@ -385,28 +408,18 @@ internal class AutoImportStaticFix(expression: KtSimpleNameExpression) : AutoImp
override fun getCallTypeAndReceiver() = CallTypeAndReceiver.detect(element) override fun getCallTypeAndReceiver() = CallTypeAndReceiver.detect(element)
override val importNames: Collection<Name> = run { override val importNames: Collection<Name> = run {
if (element.getIdentifier() == null) { if (element.getIdentifier() != null) {
val conventionName = KtPsiUtil.getConventionName(element) val name = element.getReferencedName()
if (conventionName != null) { if (Name.isValidIdentifier(name)) {
if (element is KtOperationReferenceExpression) { Name.identifier(name).singletonList()
val elementType = element.firstChild.node.elementType }
if (elementType in OperatorConventions.ASSIGNMENT_OPERATIONS) { else {
val counterpart = OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS[elementType] emptyList<Name>()
val counterpartName = OperatorConventions.BINARY_OPERATION_NAMES[counterpart]
if (counterpartName != null) {
return@run listOf(conventionName, counterpartName)
}
}
}
return@run conventionName.singletonOrEmptyList()
} }
} }
else if (Name.isValidIdentifier(element.getReferencedName())) { else {
return@run Name.identifier(element.getReferencedName()).singletonOrEmptyList() emptyList<Name>()
} }
emptyList<Name>()
} }
override fun getSupportedErrors(): Collection<DiagnosticFactory<*>> = ERRORS override fun getSupportedErrors(): Collection<DiagnosticFactory<*>> = ERRORS
@@ -0,0 +1,32 @@
// FILE: main.before.kt
// "Static import" "false"
// ERROR: Unresolved reference: foobar
// ACTION: Create function 'foobar'
// ACTION: Import
// ACTION: Rename reference
//KT-9009
fun f() {
foobar<caret>()
}
//FILE: dependency.before.kt
package bar
fun foobar() {
}
//FILE: main.after.kt
// "Static import" "false"
// ERROR: Unresolved reference: foobar
// ACTION: Create function 'foobar'
// ACTION: Rename reference
//KT-9009
fun f() {
foobar<caret>()
}
@@ -367,15 +367,19 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
@TestMetadata("noStaticImportForJavaNonStaticField.test") @TestMetadata("noStaticImportForJavaNonStaticField.test")
public void testNoStaticImportForJavaNonStaticField() throws Exception { public void testNoStaticImportForJavaNonStaticField() throws Exception {
String fileName = String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/noStaticImportForJavaNonStaticField.test");
KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/noStaticImportForJavaNonStaticField.test");
doTestWithExtraFile(fileName); doTestWithExtraFile(fileName);
} }
@TestMetadata("noStaticImportForJavaNonStaticMethod.test") @TestMetadata("noStaticImportForJavaNonStaticMethod.test")
public void testNoStaticImportForJavaNonStaticMethod() throws Exception { public void testNoStaticImportForJavaNonStaticMethod() throws Exception {
String fileName = String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/noStaticImportForJavaNonStaticMethod.test");
KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/noStaticImportForJavaNonStaticMethod.test"); doTestWithExtraFile(fileName);
}
@TestMetadata("noStaticImportForTopLevelFunction.test")
public void testNoStaticImportForTopLevelFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/noStaticImportForTopLevelFunction.test");
doTestWithExtraFile(fileName); doTestWithExtraFile(fileName);
} }