#KT-9009 fixed
This commit is contained in:
committed by
Valentin Kipyatkov
parent
ce1844a6c9
commit
9d56716897
@@ -16,6 +16,7 @@ remove.unnecessary.non.null.assertion=Remove unnecessary non-null assertion (!!)
|
||||
change.to.backing.field=Change reference to backing field
|
||||
implement.members=Implement members
|
||||
import.fix=Import
|
||||
import.static.fix=Static import
|
||||
imports.chooser.title=Imports
|
||||
rename.kotlin.package.class.error=Can't rename Kotlin package facade class
|
||||
add.semicolon.after.invocation=Add semicolon after invocation of ''{0}''
|
||||
|
||||
@@ -19,19 +19,18 @@ package org.jetbrains.kotlin.idea.core
|
||||
import com.intellij.codeInsight.JavaProjectCodeInsightSettings
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiMember
|
||||
import com.intellij.psi.PsiModifier
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.search.PsiShortNamesCache
|
||||
import com.intellij.psi.stubs.StringStubIndexExtension
|
||||
import com.intellij.util.indexing.IdFilter
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getJavaFieldDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getJavaMethodDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.*
|
||||
import org.jetbrains.kotlin.idea.core.extension.KotlinIndicesHelperExtension
|
||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
|
||||
import org.jetbrains.kotlin.idea.stubindex.*
|
||||
import org.jetbrains.kotlin.idea.util.CallType
|
||||
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
|
||||
@@ -154,7 +153,7 @@ class KotlinIndicesHelper(
|
||||
.filter {
|
||||
ProgressManager.checkCanceled()
|
||||
KotlinTopLevelExtensionsByReceiverTypeIndex.receiverTypeNameFromKey(it) in receiverTypeNames
|
||||
&& nameFilter(KotlinTopLevelExtensionsByReceiverTypeIndex.callableNameFromKey(it))
|
||||
&& nameFilter(KotlinTopLevelExtensionsByReceiverTypeIndex.callableNameFromKey(it))
|
||||
}
|
||||
.flatMap { index.get(it, project, scope).asSequence() }
|
||||
|
||||
@@ -206,6 +205,32 @@ class KotlinIndicesHelper(
|
||||
.toSet()
|
||||
}
|
||||
|
||||
|
||||
fun getJvmStatics(name: String): Collection<DeclarationDescriptor> {
|
||||
return sequenceOf(PsiShortNamesCache.getInstance(project).getFieldsByName(name, scope),
|
||||
PsiShortNamesCache.getInstance(project).getMethodsByName(name, scope))
|
||||
.flatMap { it.asSequence() }
|
||||
.mapNotNull { (it as PsiMember).getJavaMemberDescriptor(resolutionFacade) }
|
||||
.filter(descriptorFilter)
|
||||
.toSet()
|
||||
}
|
||||
|
||||
fun getKotlinStatics(name: String): Collection<DeclarationDescriptor> {
|
||||
return sequenceOf(KotlinFunctionShortNameIndex.getInstance().get(name, project, scope),
|
||||
KotlinPropertyShortNameIndex.getInstance().get(name, project, scope)).
|
||||
flatMap { it.asSequence() }
|
||||
.mapNotNull {
|
||||
when (it) {
|
||||
is KtNamedFunction -> it.descriptor
|
||||
is KtProperty -> it.descriptor
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
.filter(descriptorFilter)
|
||||
.toSet()
|
||||
}
|
||||
|
||||
|
||||
fun getKotlinClasses(nameFilter: (String) -> Boolean, kindFilter: (ClassKind) -> Boolean): Collection<ClassDescriptor> {
|
||||
return KotlinFullClassNameIndex.getInstance().getAllKeys(project).asSequence()
|
||||
.map { FqName(it) }
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* 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.quickfix
|
||||
|
||||
import com.intellij.codeInsight.hint.HintManager
|
||||
import com.intellij.codeInsight.intention.HighPriorityAction
|
||||
import com.intellij.codeInspection.HintAction
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.command.CommandProcessor
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.packageDependencies.DependencyValidationManager
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.util.PsiModificationTracker
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.actions.KotlinAddImportAction
|
||||
import org.jetbrains.kotlin.idea.actions.createSingleImportAction
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolveScope
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.core.KotlinIndicesHelper
|
||||
import org.jetbrains.kotlin.idea.core.isVisible
|
||||
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil
|
||||
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isImportDirectiveExpression
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
import org.jetbrains.kotlin.utils.CachedValueProperty
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by Semoro on 08.07.16.
|
||||
* ©XCodersTeam, 2016
|
||||
*/
|
||||
|
||||
class AutoStaticImportFix(expression: KtSimpleNameExpression) :
|
||||
KotlinQuickFixAction<KtSimpleNameExpression>(expression), HighPriorityAction, HintAction {
|
||||
|
||||
private val modificationCountOnCreate = PsiModificationTracker.SERVICE.getInstance(element.project).modificationCount
|
||||
|
||||
private val suggestionCount: Int by CachedValueProperty(
|
||||
calculator = { computeSuggestions().size },
|
||||
timestampCalculator = { PsiModificationTracker.SERVICE.getInstance(element.project).modificationCount }
|
||||
)
|
||||
|
||||
|
||||
override fun showHint(editor: Editor): Boolean {
|
||||
if (!element.isValid || isOutdated()) return false
|
||||
|
||||
if (ApplicationManager.getApplication().isUnitTestMode && HintManager.getInstance().hasShownHintsThatWillHideByOtherHint(true)) return false
|
||||
|
||||
if (suggestionCount == 0) return false
|
||||
|
||||
return createAction(element.project, editor).showHint()
|
||||
}
|
||||
|
||||
override fun getText() = KotlinBundle.message("import.static.fix")
|
||||
|
||||
override fun getFamilyName() = KotlinBundle.message("import.fix")
|
||||
|
||||
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile)
|
||||
= super.isAvailable(project, editor, file) && suggestionCount > 0
|
||||
|
||||
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||
CommandProcessor.getInstance().runUndoTransparentAction {
|
||||
createAction(project, editor!!).execute()
|
||||
}
|
||||
}
|
||||
|
||||
override fun startInWriteAction() = true
|
||||
|
||||
private fun isOutdated() = modificationCountOnCreate != PsiModificationTracker.SERVICE.getInstance(element.project).modificationCount
|
||||
|
||||
private fun createAction(project: Project, editor: Editor): KotlinAddImportAction {
|
||||
return createSingleImportAction(project, editor, element, computeSuggestions())
|
||||
}
|
||||
|
||||
fun computeSuggestions(): Collection<DeclarationDescriptor> {
|
||||
if (!element.isValid) return listOf()
|
||||
if (element.containingFile !is KtFile) return emptyList()
|
||||
|
||||
val callTypeAndReceiver = getCallTypeAndReceiver()
|
||||
|
||||
if (callTypeAndReceiver is CallTypeAndReceiver.UNKNOWN) return emptyList()
|
||||
|
||||
if (importNames.isEmpty()) return emptyList()
|
||||
|
||||
return importNames.flatMapTo(LinkedHashSet()) {
|
||||
computeSuggestionsForName(it, callTypeAndReceiver)
|
||||
}
|
||||
}
|
||||
|
||||
fun computeSuggestionsForName(name: Name, callTypeAndReceiver: CallTypeAndReceiver<*, *>):
|
||||
Collection<DeclarationDescriptor> {
|
||||
val nameStr = name.asString()
|
||||
if (nameStr.isEmpty()) return emptyList()
|
||||
|
||||
val file = element.containingFile as KtFile
|
||||
|
||||
fun filterByCallType(descriptor: DeclarationDescriptor) = callTypeAndReceiver.callType.descriptorKindFilter.accepts(descriptor)
|
||||
|
||||
val searchScope = getResolveScope(file)
|
||||
|
||||
val bindingContext = element.analyze(BodyResolveMode.PARTIAL)
|
||||
|
||||
val diagnostics = bindingContext.diagnostics.forElement(element)
|
||||
|
||||
if (!diagnostics.any { it.factory in getSupportedErrors() }) return emptyList()
|
||||
|
||||
val resolutionFacade = element.getResolutionFacade()
|
||||
|
||||
fun isVisible(descriptor: DeclarationDescriptor): Boolean {
|
||||
if (descriptor is DeclarationDescriptorWithVisibility) {
|
||||
return descriptor.isVisible(element, callTypeAndReceiver.receiver as? KtExpression, bindingContext, resolutionFacade)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
val result = ArrayList<DeclarationDescriptor>()
|
||||
|
||||
val indicesHelper = KotlinIndicesHelper(resolutionFacade, searchScope, ::isVisible)
|
||||
|
||||
val expression = element
|
||||
if (expression is KtSimpleNameExpression) {
|
||||
if (!expression.isImportDirectiveExpression() && !KtPsiUtil.isSelectorInQualified(expression)) {
|
||||
indicesHelper.getKotlinStatics(nameStr).filterTo(result, ::filterByCallType)
|
||||
if (!(ProjectStructureUtil.isJsKotlinModule(file))) {
|
||||
indicesHelper.getJvmStatics(nameStr).filterTo(result, ::filterByCallType)
|
||||
}
|
||||
}
|
||||
}
|
||||
return if (result.size > 1)
|
||||
reduceCandidatesBasedOnDependencyRuleViolation(result, file)
|
||||
else
|
||||
result
|
||||
}
|
||||
|
||||
private fun reduceCandidatesBasedOnDependencyRuleViolation(
|
||||
candidates: Collection<DeclarationDescriptor>, file: PsiFile): Collection<DeclarationDescriptor> {
|
||||
val project = file.project
|
||||
val validationManager = DependencyValidationManager.getInstance(project)
|
||||
return candidates.filter {
|
||||
val targetFile = DescriptorToSourceUtilsIde.getAnyDeclaration(project, it)?.containingFile ?: return@filter true
|
||||
validationManager.getViolatorDependencyRules(file, targetFile).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
fun getCallTypeAndReceiver() = CallTypeAndReceiver.detect(element)
|
||||
|
||||
val importNames: Collection<Name> = run {
|
||||
if (element.getIdentifier() == null) {
|
||||
val conventionName = KtPsiUtil.getConventionName(element)
|
||||
if (conventionName != null) {
|
||||
if (element is KtOperationReferenceExpression) {
|
||||
val elementType = element.firstChild.node.elementType
|
||||
if (elementType in OperatorConventions.ASSIGNMENT_OPERATIONS) {
|
||||
val counterpart = OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS[elementType]
|
||||
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())) {
|
||||
return@run Name.identifier(element.getReferencedName()).singletonOrEmptyList()
|
||||
}
|
||||
|
||||
emptyList<Name>()
|
||||
}
|
||||
|
||||
fun getSupportedErrors(): Collection<DiagnosticFactory<*>> = ERRORS
|
||||
|
||||
companion object : KotlinSingleIntentionActionFactory() {
|
||||
override fun createAction(diagnostic: Diagnostic) =
|
||||
(diagnostic.psiElement as? KtSimpleNameExpression)?.let { AutoStaticImportFix(it) }
|
||||
|
||||
override fun isApplicableForCodeFragment() = true
|
||||
|
||||
private val ERRORS: Collection<DiagnosticFactory<*>> by lazy(LazyThreadSafetyMode.PUBLICATION) { QuickFixes.getInstance().getDiagnostics(this) }
|
||||
}
|
||||
}
|
||||
@@ -87,9 +87,9 @@ class QuickFixRegistrar : QuickFixContributor {
|
||||
|
||||
NON_MEMBER_FUNCTION_NO_BODY.registerFactory(AddFunctionBodyFix)
|
||||
|
||||
NOTHING_TO_OVERRIDE.registerFactory( RemoveModifierFix.createRemoveModifierFromListOwnerFactory(OVERRIDE_KEYWORD),
|
||||
ChangeMemberFunctionSignatureFix,
|
||||
AddFunctionToSupertypeFix)
|
||||
NOTHING_TO_OVERRIDE.registerFactory(RemoveModifierFix.createRemoveModifierFromListOwnerFactory(OVERRIDE_KEYWORD),
|
||||
ChangeMemberFunctionSignatureFix,
|
||||
AddFunctionToSupertypeFix)
|
||||
VIRTUAL_MEMBER_HIDDEN.registerFactory(AddModifierFix.createFactory(OVERRIDE_KEYWORD))
|
||||
|
||||
USELESS_CAST.registerFactory(RemoveUselessCastFix)
|
||||
@@ -128,7 +128,9 @@ class QuickFixRegistrar : QuickFixContributor {
|
||||
NON_PRIVATE_CONSTRUCTOR_IN_ENUM.registerFactory(removeModifierFactory)
|
||||
NON_PRIVATE_CONSTRUCTOR_IN_SEALED.registerFactory(removeModifierFactory)
|
||||
|
||||
UNRESOLVED_REFERENCE.registerFactory(AutoStaticImportFix)
|
||||
UNRESOLVED_REFERENCE.registerFactory(AutoImportFix)
|
||||
|
||||
UNRESOLVED_REFERENCE.registerFactory(AddTestLibQuickFix)
|
||||
|
||||
UNRESOLVED_REFERENCE_WRONG_RECEIVER.registerFactory(AutoImportFix)
|
||||
@@ -337,8 +339,8 @@ class QuickFixRegistrar : QuickFixContributor {
|
||||
DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE.registerFactory(CreatePropertyDelegateAccessorsActionFactory)
|
||||
|
||||
UNRESOLVED_REFERENCE.registerFactory(CreateClassFromTypeReferenceActionFactory,
|
||||
CreateClassFromReferenceExpressionActionFactory,
|
||||
CreateClassFromCallWithConstructorCalleeActionFactory,
|
||||
CreateClassFromReferenceExpressionActionFactory,
|
||||
CreateClassFromCallWithConstructorCalleeActionFactory,
|
||||
PlatformUnresolvedProvider)
|
||||
|
||||
PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED.registerFactory(InsertDelegationCallQuickfix.InsertThisDelegationCallFactory)
|
||||
@@ -347,12 +349,12 @@ class QuickFixRegistrar : QuickFixContributor {
|
||||
EXPLICIT_DELEGATION_CALL_REQUIRED.registerFactory(InsertDelegationCallQuickfix.InsertSuperDelegationCallFactory)
|
||||
|
||||
MISSING_CONSTRUCTOR_KEYWORD.registerFactory(MissingConstructorKeywordFix,
|
||||
MissingConstructorKeywordFix.createWholeProjectFixFactory())
|
||||
MissingConstructorKeywordFix.createWholeProjectFixFactory())
|
||||
|
||||
ANONYMOUS_FUNCTION_WITH_NAME.registerFactory(RemoveNameFromFunctionExpressionFix)
|
||||
|
||||
UNRESOLVED_REFERENCE.registerFactory(ReplaceObsoleteLabelSyntaxFix,
|
||||
ReplaceObsoleteLabelSyntaxFix.createWholeProjectFixFactory())
|
||||
ReplaceObsoleteLabelSyntaxFix.createWholeProjectFixFactory())
|
||||
|
||||
DEPRECATION.registerFactory(DeprecatedSymbolUsageFix, DeprecatedSymbolUsageInWholeProjectFix)
|
||||
DEPRECATION_ERROR.registerFactory(DeprecatedSymbolUsageFix, DeprecatedSymbolUsageInWholeProjectFix)
|
||||
|
||||
Reference in New Issue
Block a user