Minor: fix warnings & refactoring

This commit is contained in:
Dmitry Gridin
2019-03-11 19:05:20 +07:00
parent ba61695a45
commit 3d66147685
11 changed files with 170 additions and 158 deletions
@@ -17,17 +17,15 @@
package org.jetbrains.kotlin.idea.intentions package org.jetbrains.kotlin.idea.intentions
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.psi.* import com.intellij.psi.PsiComment
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.nextStatement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.previousStatement import com.intellij.psi.PsiWhiteSpace
import org.jetbrains.kotlin.idea.refactoring.getLineCount
import org.jetbrains.kotlin.idea.refactoring.getLineNumber import org.jetbrains.kotlin.idea.refactoring.getLineNumber
import org.jetbrains.kotlin.idea.util.CommentSaver import org.jetbrains.kotlin.idea.util.CommentSaver
import org.jetbrains.kotlin.j2k.isInSingleLine import org.jetbrains.kotlin.j2k.isInSingleLine
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
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.resolve.calls.CallExpressionElement
class AddBracesIntention : SelfTargetingIntention<KtElement>(KtElement::class.java, "Add braces") { class AddBracesIntention : SelfTargetingIntention<KtElement>(KtElement::class.java, "Add braces") {
override fun isApplicableTo(element: KtElement, caretOffset: Int): Boolean { override fun isApplicableTo(element: KtElement, caretOffset: Int): Boolean {
@@ -37,7 +35,7 @@ class AddBracesIntention : SelfTargetingIntention<KtElement>(KtElement::class.ja
val parent = expression.parent val parent = expression.parent
return when (parent) { return when (parent) {
is KtContainerNode -> { is KtContainerNode -> {
val description = parent.description()!! val description = parent.description() ?: return false
text = "Add braces to '$description' statement" text = "Add braces to '$description' statement"
true true
} }
@@ -53,7 +51,7 @@ class AddBracesIntention : SelfTargetingIntention<KtElement>(KtElement::class.ja
override fun applyTo(element: KtElement, editor: Editor?) { override fun applyTo(element: KtElement, editor: Editor?) {
if (editor == null) throw IllegalArgumentException("This intention requires an editor") if (editor == null) throw IllegalArgumentException("This intention requires an editor")
val expression = element.getTargetExpression(editor.caretModel.offset)!! val expression = element.getTargetExpression(editor.caretModel.offset) ?: return
var isCommentBeneath = false var isCommentBeneath = false
var isCommentInside = false var isCommentInside = false
val psiFactory = KtPsiFactory(element) val psiFactory = KtPsiFactory(element)
@@ -76,9 +74,9 @@ class AddBracesIntention : SelfTargetingIntention<KtElement>(KtElement::class.ja
// Check if \n before first received comment sibling // Check if \n before first received comment sibling
// if false, the normal procedure of adding braces occurs. // if false, the normal procedure of adding braces occurs.
isCommentBeneath = isCommentBeneath =
sibling.prevSibling is PsiWhiteSpace && sibling.prevSibling is PsiWhiteSpace &&
sibling.prevSibling.textContains('\n') && sibling.prevSibling.textContains('\n') &&
(sibling.prevSibling.prevSibling is PsiComment || sibling.prevSibling.prevSibling is PsiElement) (sibling.prevSibling.prevSibling is PsiComment || sibling.prevSibling.prevSibling is PsiElement)
} }
} }
@@ -117,7 +115,7 @@ class AddBracesIntention : SelfTargetingIntention<KtElement>(KtElement::class.ja
when (element) { when (element) {
is KtDoWhileExpression -> is KtDoWhileExpression ->
// remove new line between '}' and while // remove new line between '}' and while
(element.body!!.parent.nextSibling as? PsiWhiteSpace)?.delete() (element.body?.parent?.nextSibling as? PsiWhiteSpace)?.delete()
is KtIfExpression -> is KtIfExpression ->
(result?.parent?.nextSibling as? PsiWhiteSpace)?.delete() (result?.parent?.nextSibling as? PsiWhiteSpace)?.delete()
} }
@@ -131,7 +129,7 @@ class AddBracesIntention : SelfTargetingIntention<KtElement>(KtElement::class.ja
is KtIfExpression -> { is KtIfExpression -> {
val thenExpr = then ?: return null val thenExpr = then ?: return null
val elseExpr = `else` val elseExpr = `else`
if (elseExpr != null && caretLocation >= elseKeyword!!.startOffset) { if (elseExpr != null && caretLocation >= elseKeyword?.startOffset ?: return null) {
elseExpr elseExpr
} else { } else {
thenExpr thenExpr
@@ -48,8 +48,7 @@ abstract class AbstractChopListIntention<TList : KtElement, TElement : KtElement
val elements = element.elements() val elements = element.elements()
if (!hasLineBreakAfter(elements.last())) { if (!hasLineBreakAfter(elements.last())) {
val rpar = element.allChildren.lastOrNull { it.node.elementType == KtTokens.RPAR } element.allChildren.lastOrNull { it.node.elementType == KtTokens.RPAR }?.startOffset?.let { document.insertString(it, "\n") }
rpar?.startOffset?.let { document.insertString(it, "\n") }
} }
for (e in elements.asReversed()) { for (e in elements.asReversed()) {
@@ -60,8 +59,8 @@ abstract class AbstractChopListIntention<TList : KtElement, TElement : KtElement
val documentManager = PsiDocumentManager.getInstance(project) val documentManager = PsiDocumentManager.getInstance(project)
documentManager.commitDocument(document) documentManager.commitDocument(document)
val psiFile = documentManager.getPsiFile(document)!! val psiFile = documentManager.getPsiFile(document) ?: return
val newList = PsiTreeUtil.getParentOfType(psiFile.findElementAt(startOffset)!!, listClass)!! val newList = PsiTreeUtil.getParentOfType(psiFile.findElementAt(startOffset) ?: return, listClass) ?: return
CodeStyleManager.getInstance(project).adjustLineIndent(psiFile, newList.textRange) CodeStyleManager.getInstance(project).adjustLineIndent(psiFile, newList.textRange)
} }
@@ -20,7 +20,6 @@ import com.intellij.codeInsight.CodeInsightBundle
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiFileSystemItem import com.intellij.psi.PsiFileSystemItem
import com.intellij.psi.PsiWhiteSpace import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTreeUtil
@@ -34,18 +33,18 @@ import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isUnit import org.jetbrains.kotlin.types.typeUtil.isUnit
class IntroduceVariableIntention : SelfTargetingRangeIntention<PsiElement>( class IntroduceVariableIntention : SelfTargetingRangeIntention<PsiElement>(
PsiElement::class.java, CodeInsightBundle.message("intention.introduce.variable.text") PsiElement::class.java, CodeInsightBundle.message("intention.introduce.variable.text")
) { ) {
private fun getExpressionToProcess(element: PsiElement): KtExpression? { private fun getExpressionToProcess(element: PsiElement): KtExpression? {
if (element is PsiFileSystemItem) return null if (element is PsiFileSystemItem) return null
val startElement = PsiTreeUtil.skipSiblingsBackward(element, PsiWhiteSpace::class.java) ?: element val startElement = PsiTreeUtil.skipSiblingsBackward(element, PsiWhiteSpace::class.java) ?: element
return startElement.parentsWithSelf return startElement.parentsWithSelf
.filterIsInstance<KtExpression>() .filterIsInstance<KtExpression>()
.takeWhile { it !is KtDeclarationWithBody } .takeWhile { it !is KtDeclarationWithBody }
.firstOrNull { .firstOrNull {
val parent = it.parent val parent = it.parent
parent is KtBlockExpression || parent is KtDeclarationWithBody && !parent.hasBlockBody() && parent.bodyExpression == it parent is KtBlockExpression || parent is KtDeclarationWithBody && !parent.hasBlockBody() && parent.bodyExpression == it
} }
} }
override fun applicabilityRange(element: PsiElement): TextRange? { override fun applicabilityRange(element: PsiElement): TextRange? {
@@ -58,7 +57,7 @@ class IntroduceVariableIntention : SelfTargetingRangeIntention<PsiElement>(
override fun applyTo(element: PsiElement, editor: Editor?) { override fun applyTo(element: PsiElement, editor: Editor?) {
val expression = getExpressionToProcess(element) ?: return val expression = getExpressionToProcess(element) ?: return
KotlinIntroduceVariableHandler.doRefactoring( KotlinIntroduceVariableHandler.doRefactoring(
element.project, editor, expression, isVar = false, occurrencesToReplace = null, onNonInteractiveFinish = null element.project, editor, expression, isVar = false, occurrencesToReplace = null, onNonInteractiveFinish = null
) )
} }
} }
@@ -35,8 +35,7 @@ class RemoveBracesIntention : SelfTargetingIntention<KtElement>(KtElement::class
override fun isApplicableTo(element: KtElement, caretOffset: Int): Boolean { override fun isApplicableTo(element: KtElement, caretOffset: Int): Boolean {
val block = element.findChildBlock() ?: return false val block = element.findChildBlock() ?: return false
val singleStatement = block.statements.singleOrNull() ?: return false val singleStatement = block.statements.singleOrNull() ?: return false
val container = block.parent when (val container = block.parent) {
when (container) {
is KtContainerNode -> { is KtContainerNode -> {
if (singleStatement is KtIfExpression) { if (singleStatement is KtIfExpression) {
val elseExpression = (container.parent as? KtIfExpression)?.`else` val elseExpression = (container.parent as? KtIfExpression)?.`else`
@@ -74,7 +73,8 @@ class RemoveBracesIntention : SelfTargetingIntention<KtElement>(KtElement::class
if (construct is KtIfExpression && if (construct is KtIfExpression &&
container.node.elementType == KtNodeTypes.ELSE && container.node.elementType == KtNodeTypes.ELSE &&
construct.parent is KtExpression && construct.parent is KtExpression &&
construct.parent !is KtStatementExpression) { construct.parent !is KtStatementExpression
) {
construct.replace(factory.createExpressionByPattern("($0)", construct)) construct.replace(factory.createExpressionByPattern("($0)", construct))
} }
} }
@@ -46,25 +46,23 @@ class ReplaceExplicitFunctionLiteralParamWithItIntention : PsiElementBaseIntenti
if (parameter.destructuringDeclaration != null) return false if (parameter.destructuringDeclaration != null) return false
if (functionLiteral.anyDescendantOfType<KtFunctionLiteral> { literal -> if (functionLiteral.anyDescendantOfType<KtFunctionLiteral> { literal ->
literal.usesName(element.text) && literal.usesName(element.text) && (!literal.hasParameterSpecification() || literal.usesName("it"))
(!literal.hasParameterSpecification() || literal.usesName("it")) }) return false
} ) return false
text = "Replace explicit parameter '${parameter.name}' with 'it'" text = "Replace explicit parameter '${parameter.name}' with 'it'"
return true return true
} }
private fun KtFunctionLiteral.usesName(name: String): Boolean = private fun KtFunctionLiteral.usesName(name: String): Boolean = anyDescendantOfType<KtSimpleNameExpression> { nameExpr ->
anyDescendantOfType<KtSimpleNameExpression> { nameExpr.getReferencedName() == name
nameExpr -> nameExpr.getReferencedName() == name }
}
override fun startInWriteAction(): Boolean = false override fun startInWriteAction(): Boolean = false
override fun invoke(project: Project, editor: Editor, element: PsiElement) { override fun invoke(project: Project, editor: Editor, element: PsiElement) {
val caretOffset = editor.caretModel.offset val caretOffset = editor.caretModel.offset
val functionLiteral = targetFunctionLiteral(element, editor.caretModel.offset)!! val functionLiteral = targetFunctionLiteral(element, editor.caretModel.offset) ?: return
val cursorInParameterList = functionLiteral.valueParameterList!!.textRange.containsOffset(caretOffset) val cursorInParameterList = functionLiteral.valueParameterList?.textRange?.containsOffset(caretOffset) ?: return
ParamRenamingProcessor(editor, functionLiteral, cursorInParameterList).run() ParamRenamingProcessor(editor, functionLiteral, cursorInParameterList).run()
} }
@@ -83,22 +81,23 @@ class ReplaceExplicitFunctionLiteralParamWithItIntention : PsiElementBaseIntenti
} }
private class ParamRenamingProcessor( private class ParamRenamingProcessor(
val editor: Editor, val editor: Editor,
val functionLiteral: KtFunctionLiteral, val functionLiteral: KtFunctionLiteral,
val cursorWasInParameterList: Boolean val cursorWasInParameterList: Boolean
) : RenameProcessor(editor.project, ) : RenameProcessor(
functionLiteral.valueParameters.single(), editor.project,
"it", functionLiteral.valueParameters.single(),
false, "it",
false false,
false
) { ) {
override fun performRefactoring(usages: Array<out UsageInfo>) { override fun performRefactoring(usages: Array<out UsageInfo>) {
super.performRefactoring(usages) super.performRefactoring(usages)
functionLiteral.deleteChildRange(functionLiteral.valueParameterList, functionLiteral.arrow!!) functionLiteral.deleteChildRange(functionLiteral.valueParameterList, functionLiteral.arrow ?: return)
if (cursorWasInParameterList) { if (cursorWasInParameterList) {
editor.caretModel.moveToOffset(functionLiteral.bodyExpression!!.textOffset) editor.caretModel.moveToOffset(functionLiteral.bodyExpression?.textOffset ?: return)
} }
val project = functionLiteral.project val project = functionLiteral.project
@@ -28,23 +28,23 @@ import org.jetbrains.kotlin.psi.KtNameReferenceExpression
import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
class ReplaceItWithExplicitFunctionLiteralParamIntention : SelfTargetingOffsetIndependentIntention<KtNameReferenceExpression> ( class ReplaceItWithExplicitFunctionLiteralParamIntention : SelfTargetingOffsetIndependentIntention<KtNameReferenceExpression>(
KtNameReferenceExpression::class.java, "Replace 'it' with explicit parameter" KtNameReferenceExpression::class.java, "Replace 'it' with explicit parameter"
), LowPriorityAction { ), LowPriorityAction {
override fun isApplicableTo(element: KtNameReferenceExpression) override fun isApplicableTo(element: KtNameReferenceExpression) = isAutoCreatedItUsage(element)
= isAutoCreatedItUsage(element)
override fun applyTo(element: KtNameReferenceExpression, editor: Editor?) { override fun applyTo(element: KtNameReferenceExpression, editor: Editor?) {
if (editor == null) throw IllegalArgumentException("This intention requires an editor") if (editor == null) throw IllegalArgumentException("This intention requires an editor")
val target = element.mainReference.resolveToDescriptors(element.analyze()).single() val target = element.mainReference.resolveToDescriptors(element.analyze()).single()
val functionLiteral = DescriptorToSourceUtils.descriptorToDeclaration(target.containingDeclaration!!) as KtFunctionLiteral val functionLiteral = DescriptorToSourceUtils.descriptorToDeclaration(target.containingDeclaration ?: return) as KtFunctionLiteral
val newExpr = KtPsiFactory(element).createExpression("{ it -> }") as KtLambdaExpression val newExpr = KtPsiFactory(element).createExpression("{ it -> }") as KtLambdaExpression
functionLiteral.addRangeAfter( functionLiteral.addRangeAfter(
newExpr.functionLiteral.valueParameterList, newExpr.functionLiteral.valueParameterList,
newExpr.functionLiteral.arrow!!, newExpr.functionLiteral.arrow ?: return,
functionLiteral.lBrace) functionLiteral.lBrace
)
PsiDocumentManager.getInstance(element.project).doPostponedOperationsAndUnblockDocument(editor.document) PsiDocumentManager.getInstance(element.project).doPostponedOperationsAndUnblockDocument(editor.document)
val paramToRename = functionLiteral.valueParameters.single() val paramToRename = functionLiteral.valueParameters.single()
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.idea.intentions package org.jetbrains.kotlin.idea.intentions
import com.intellij.codeInsight.hint.HintManager import com.intellij.codeInsight.hint.HintManager
import com.intellij.codeInsight.intention.LowPriorityAction
import com.intellij.codeInsight.template.* import com.intellij.codeInsight.template.*
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
@@ -47,7 +46,7 @@ import org.jetbrains.kotlin.utils.ifEmpty
class SpecifyTypeExplicitlyIntention : SelfTargetingRangeIntention<KtCallableDeclaration>( class SpecifyTypeExplicitlyIntention : SelfTargetingRangeIntention<KtCallableDeclaration>(
KtCallableDeclaration::class.java, KtCallableDeclaration::class.java,
"Specify type explicitly" "Specify type explicitly"
), LowPriorityAction { ) {
override fun applicabilityRange(element: KtCallableDeclaration): TextRange? { override fun applicabilityRange(element: KtCallableDeclaration): TextRange? {
if (element.containingFile is KtCodeFragment) return null if (element.containingFile is KtCodeFragment) return null
@@ -156,11 +156,7 @@ open class AddModifierFix(
object MakeClassOpenFactory : KotlinSingleIntentionActionFactory() { object MakeClassOpenFactory : KotlinSingleIntentionActionFactory() {
override fun createAction(diagnostic: Diagnostic): IntentionAction? { override fun createAction(diagnostic: Diagnostic): IntentionAction? {
val typeReference = diagnostic.psiElement as KtTypeReference val typeReference = diagnostic.psiElement as KtTypeReference
val bindingContext = typeReference.analyze(BodyResolveMode.PARTIAL) val declaration = typeReference.classForRefactor() ?: return null
val type = bindingContext[BindingContext.TYPE, typeReference] ?: return null
val classDescriptor = type.constructor.declarationDescriptor as? ClassDescriptor ?: return null
val declaration = DescriptorToSourceUtils.descriptorToDeclaration(classDescriptor) as? KtClass ?: return null
if (!declaration.canRefactor()) return null
if (declaration.isEnum() || declaration.isData()) return null if (declaration.isEnum() || declaration.isData()) return null
return AddModifierFix(declaration, KtTokens.OPEN_KEYWORD) return AddModifierFix(declaration, KtTokens.OPEN_KEYWORD)
} }
@@ -181,3 +177,12 @@ open class AddModifierFix(
} }
} }
} }
fun KtTypeReference.classForRefactor(): KtClass? {
val bindingContext = analyze(BodyResolveMode.PARTIAL)
val type = bindingContext[BindingContext.TYPE, this] ?: return null
val classDescriptor = type.constructor.declarationDescriptor as? ClassDescriptor ?: return null
val declaration = DescriptorToSourceUtils.descriptorToDeclaration(classDescriptor) as? KtClass ?: return null
if (!declaration.canRefactor()) return null
return declaration
}
@@ -117,8 +117,7 @@ class AddExclExclCallFix(psiElement: PsiElement, val checkImplicitReceivers: Boo
return when (psiElement) { return when (psiElement) {
is KtArrayAccessExpression -> psiElement.expressionForCall() is KtArrayAccessExpression -> psiElement.expressionForCall()
is KtOperationReferenceExpression -> { is KtOperationReferenceExpression -> {
val parent = psiElement.parent when (val parent = psiElement.parent) {
when (parent) {
is KtUnaryExpression -> parent.baseExpression.expressionForCall() is KtUnaryExpression -> parent.baseExpression.expressionForCall()
is KtBinaryExpression -> { is KtBinaryExpression -> {
val receiver = if (KtPsiUtil.isInOrNotInOperation(parent)) parent.right else parent.left val receiver = if (KtPsiUtil.isInOrNotInOperation(parent)) parent.right else parent.left
@@ -31,7 +31,6 @@ import com.intellij.psi.PsiErrorElement
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.psi.PsiModifier import com.intellij.psi.PsiModifier
import com.intellij.psi.util.PsiModificationTracker import com.intellij.psi.util.PsiModificationTracker
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
@@ -52,11 +51,12 @@ import org.jetbrains.kotlin.idea.core.isVisible
import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport
import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.util.* import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
import org.jetbrains.kotlin.idea.util.ReceiverType
import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.idea.util.receiverTypesWithIndex
import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.js.resolve.JsPlatform
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
@@ -81,8 +81,8 @@ import java.util.*
* Check possibility and perform fix for unresolved references. * Check possibility and perform fix for unresolved references.
*/ */
internal abstract class ImportFixBase<T : KtExpression> protected constructor( internal abstract class ImportFixBase<T : KtExpression> protected constructor(
expression: T, expression: T,
private val factory: Factory private val factory: Factory
) : KotlinQuickFixAction<T>(expression), HighPriorityAction, HintAction { ) : KotlinQuickFixAction<T>(expression), HighPriorityAction, HintAction {
private val project = expression.project private val project = expression.project
@@ -115,8 +115,7 @@ internal abstract class ImportFixBase<T : KtExpression> protected constructor(
override fun getFamilyName() = KotlinBundle.message("import.fix") override fun getFamilyName() = KotlinBundle.message("import.fix")
override fun isAvailable(project: Project, editor: Editor?, file: KtFile) override fun isAvailable(project: Project, editor: Editor?, file: KtFile) = element != null && suggestions.isNotEmpty()
= element != null && suggestions.isNotEmpty()
override fun invoke(project: Project, editor: Editor?, file: KtFile) { override fun invoke(project: Project, editor: Editor?, file: KtFile) {
val element = element ?: return val element = element ?: return
@@ -197,21 +196,22 @@ internal abstract class ImportFixBase<T : KtExpression> protected constructor(
private fun checkErrorStillPresent(bindingContext: BindingContext): Boolean { private fun checkErrorStillPresent(bindingContext: BindingContext): Boolean {
return elementsToCheckDiagnostics() return elementsToCheckDiagnostics()
.flatMap { bindingContext.diagnostics.forElement(it) } .flatMap { bindingContext.diagnostics.forElement(it) }
.any { diagnostic -> diagnostic.factory in getSupportedErrors() } .any { diagnostic -> diagnostic.factory in getSupportedErrors() }
} }
protected open fun elementsToCheckDiagnostics(): Collection<PsiElement> = listOfNotNull(element) protected open fun elementsToCheckDiagnostics(): Collection<PsiElement> = listOfNotNull(element)
abstract fun fillCandidates( abstract fun fillCandidates(
name: String, name: String,
callTypeAndReceiver: CallTypeAndReceiver<*, *>, callTypeAndReceiver: CallTypeAndReceiver<*, *>,
bindingContext: BindingContext, bindingContext: BindingContext,
indicesHelper: KotlinIndicesHelper indicesHelper: KotlinIndicesHelper
): List<DeclarationDescriptor> ): List<DeclarationDescriptor>
private fun reduceCandidatesBasedOnDependencyRuleViolation( private fun reduceCandidatesBasedOnDependencyRuleViolation(
candidates: Collection<DeclarationDescriptor>, file: PsiFile): Collection<DeclarationDescriptor> { candidates: Collection<DeclarationDescriptor>, file: PsiFile
): Collection<DeclarationDescriptor> {
val project = file.project val project = file.project
val validationManager = DependencyValidationManager.getInstance(project) val validationManager = DependencyValidationManager.getInstance(project)
return candidates.filter { return candidates.filter {
@@ -229,11 +229,11 @@ internal abstract class ImportFixBase<T : KtExpression> protected constructor(
open fun createImportActionsForAllProblems(sameTypeDiagnostics: Collection<Diagnostic>): List<ImportFixBase<*>> = emptyList() open fun createImportActionsForAllProblems(sameTypeDiagnostics: Collection<Diagnostic>): List<ImportFixBase<*>> = emptyList()
override final fun createAction(diagnostic: Diagnostic): IntentionAction? { final override fun createAction(diagnostic: Diagnostic): IntentionAction? {
return createImportAction(diagnostic)?.apply { computeSuggestions() } return createImportAction(diagnostic)?.apply { computeSuggestions() }
} }
override final fun doCreateActionsForAllProblems(sameTypeDiagnostics: Collection<Diagnostic>): List<IntentionAction> { final override fun doCreateActionsForAllProblems(sameTypeDiagnostics: Collection<Diagnostic>): List<IntentionAction> {
return createImportActionsForAllProblems(sameTypeDiagnostics).onEach { it.computeSuggestions() } return createImportActionsForAllProblems(sameTypeDiagnostics).onEach { it.computeSuggestions() }
} }
} }
@@ -242,10 +242,10 @@ internal abstract class ImportFixBase<T : KtExpression> protected constructor(
internal abstract class OrdinaryImportFixBase<T : KtExpression>(expression: T, factory: Factory) : ImportFixBase<T>(expression, factory) { internal abstract class OrdinaryImportFixBase<T : KtExpression>(expression: T, factory: Factory) : ImportFixBase<T>(expression, factory) {
override fun fillCandidates( override fun fillCandidates(
name: String, name: String,
callTypeAndReceiver: CallTypeAndReceiver<*, *>, callTypeAndReceiver: CallTypeAndReceiver<*, *>,
bindingContext: BindingContext, bindingContext: BindingContext,
indicesHelper: KotlinIndicesHelper indicesHelper: KotlinIndicesHelper
): List<DeclarationDescriptor> { ): List<DeclarationDescriptor> {
val expression = element ?: return emptyList() val expression = element ?: return emptyList()
@@ -284,13 +284,14 @@ internal class ImportFix(expression: KtSimpleNameExpression) : OrdinaryImportFix
return emptyList() return emptyList()
} }
override val importNames: Collection<Name> = ((element?.mainReference?.resolvesByNames ?: emptyList()) + importNamesForMembers()).distinct() override val importNames: Collection<Name> =
((element?.mainReference?.resolvesByNames ?: emptyList()) + importNamesForMembers()).distinct()
private fun collectMemberCandidates( private fun collectMemberCandidates(
name: String, name: String,
callTypeAndReceiver: CallTypeAndReceiver<*, *>, callTypeAndReceiver: CallTypeAndReceiver<*, *>,
bindingContext: BindingContext, bindingContext: BindingContext,
indicesHelper: KotlinIndicesHelper indicesHelper: KotlinIndicesHelper
): List<DeclarationDescriptor> { ): List<DeclarationDescriptor> {
val element = element ?: return emptyList() val element = element ?: return emptyList()
@@ -314,78 +315,92 @@ internal class ImportFix(expression: KtSimpleNameExpression) : OrdinaryImportFix
val explicitReceiverTypes = actualReceiverTypes.filterNot { it.implicit } val explicitReceiverTypes = actualReceiverTypes.filterNot { it.implicit }
val checkDispatchReceiver = when(callTypeAndReceiver) { val checkDispatchReceiver = when (callTypeAndReceiver) {
is CallTypeAndReceiver.OPERATOR, is CallTypeAndReceiver.INFIX -> true is CallTypeAndReceiver.OPERATOR, is CallTypeAndReceiver.INFIX -> true
else -> false else -> false
} }
val processor = { descriptor: CallableDescriptor -> val processor = { descriptor: CallableDescriptor ->
if (descriptor.canBeReferencedViaImport() && filterByCallType(descriptor) if (descriptor.canBeReferencedViaImport() && filterByCallType(descriptor)
&& descriptor.isValidByReceiversFor(explicitReceiverTypes, actualReceiverTypes, checkDispatchReceiver)) { && descriptor.isValidByReceiversFor(explicitReceiverTypes, actualReceiverTypes, checkDispatchReceiver)
) {
result.add(descriptor) result.add(descriptor)
} }
} }
indicesHelper.processKotlinCallablesByName( indicesHelper.processKotlinCallablesByName(
name, name,
filter = { declaration -> (declaration.parent as? KtClassBody)?.parent is KtObjectDeclaration }, filter = { declaration -> (declaration.parent as? KtClassBody)?.parent is KtObjectDeclaration },
processor = processor processor = processor
) )
if (TargetPlatformDetector.getPlatform(element.containingKtFile) == JvmPlatform) { if (TargetPlatformDetector.getPlatform(element.containingKtFile) == JvmPlatform) {
indicesHelper.processJvmCallablesByName( indicesHelper.processJvmCallablesByName(
name, name,
filter = { it.hasModifierProperty(PsiModifier.STATIC) }, filter = { it.hasModifierProperty(PsiModifier.STATIC) },
processor = processor processor = processor
) )
} }
return result return result
} }
private fun CallableDescriptor.isValidByReceiversFor(explicitReceiverTypes: Collection<ReceiverType>, private fun CallableDescriptor.isValidByReceiversFor(
allReceiverTypes: Collection<ReceiverType>, explicitReceiverTypes: Collection<ReceiverType>,
checkDispatchReceiver: Boolean): Boolean { allReceiverTypes: Collection<ReceiverType>,
checkDispatchReceiver: Boolean
): Boolean {
val bothReceivers = listOfNotNull(extensionReceiverParameter, dispatchReceiverParameter.takeIf { checkDispatchReceiver }) val bothReceivers = listOfNotNull(extensionReceiverParameter, dispatchReceiverParameter.takeIf { checkDispatchReceiver })
val receiverTypesPerReceiver = generateSequence(explicitReceiverTypes.ifEmpty { allReceiverTypes }) { allReceiverTypes } val receiverTypesPerReceiver = generateSequence(explicitReceiverTypes.ifEmpty { allReceiverTypes }) { allReceiverTypes }
return bothReceivers return bothReceivers
.zip(receiverTypesPerReceiver.asIterable()) .zip(receiverTypesPerReceiver.asIterable())
.all { (receiver, possibleTypes) -> possibleTypes.any { it.type.isSubtypeOf(receiver.type) } } .all { (receiver, possibleTypes) -> possibleTypes.any { it.type.isSubtypeOf(receiver.type) } }
} }
override fun fillCandidates( override fun fillCandidates(
name: String, name: String,
callTypeAndReceiver: CallTypeAndReceiver<*, *>, callTypeAndReceiver: CallTypeAndReceiver<*, *>,
bindingContext: BindingContext, bindingContext: BindingContext,
indicesHelper: KotlinIndicesHelper indicesHelper: KotlinIndicesHelper
): List<DeclarationDescriptor> { ): List<DeclarationDescriptor> {
return super.fillCandidates(name, callTypeAndReceiver, bindingContext, indicesHelper) + collectMemberCandidates(name, callTypeAndReceiver, bindingContext, indicesHelper) return super.fillCandidates(name, callTypeAndReceiver, bindingContext, indicesHelper) + collectMemberCandidates(
name,
callTypeAndReceiver,
bindingContext,
indicesHelper
)
} }
companion object MyFactory : Factory() { companion object MyFactory : Factory() {
override fun createImportAction(diagnostic: Diagnostic) = override fun createImportAction(diagnostic: Diagnostic) =
(diagnostic.psiElement as? KtSimpleNameExpression)?.let(::ImportFix) (diagnostic.psiElement as? KtSimpleNameExpression)?.let(::ImportFix)
} }
} }
internal class ImportConstructorReferenceFix(expression: KtSimpleNameExpression) : ImportFixBase<KtSimpleNameExpression>(expression, MyFactory) { internal class ImportConstructorReferenceFix(expression: KtSimpleNameExpression) :
ImportFixBase<KtSimpleNameExpression>(expression, MyFactory) {
override fun getCallTypeAndReceiver() = element?.let { override fun getCallTypeAndReceiver() = element?.let {
CallTypeAndReceiver.detect(it) as? CallTypeAndReceiver.CALLABLE_REFERENCE CallTypeAndReceiver.detect(it) as? CallTypeAndReceiver.CALLABLE_REFERENCE
} }
override fun fillCandidates(name: String, callTypeAndReceiver: CallTypeAndReceiver<*, *>, bindingContext: BindingContext, indicesHelper: KotlinIndicesHelper): List<DeclarationDescriptor> { override fun fillCandidates(
name: String,
callTypeAndReceiver: CallTypeAndReceiver<*, *>,
bindingContext: BindingContext,
indicesHelper: KotlinIndicesHelper
): List<DeclarationDescriptor> {
val expression = element ?: return emptyList() val expression = element ?: return emptyList()
val filterByCallType = callTypeAndReceiver.toFilter() val filterByCallType = callTypeAndReceiver.toFilter()
// TODO Type-aliases // TODO Type-aliases
return indicesHelper.getClassesByName(expression, name) return indicesHelper.getClassesByName(expression, name)
.asSequence() .asSequence()
.map { it.constructors }.flatten() .map { it.constructors }.flatten()
.filter { it.importableFqName != null } .filter { it.importableFqName != null }
.filter(filterByCallType) .filter(filterByCallType)
.toList() .toList()
} }
override fun createAction(project: Project, editor: Editor, element: KtExpression): KotlinAddImportAction { override fun createAction(project: Project, editor: Editor, element: KtExpression): KotlinAddImportAction {
@@ -396,7 +411,7 @@ internal class ImportConstructorReferenceFix(expression: KtSimpleNameExpression)
companion object MyFactory : Factory() { companion object MyFactory : Factory() {
override fun createImportAction(diagnostic: Diagnostic) = override fun createImportAction(diagnostic: Diagnostic) =
(diagnostic.psiElement as? KtSimpleNameExpression)?.let(::ImportConstructorReferenceFix) (diagnostic.psiElement as? KtSimpleNameExpression)?.let(::ImportConstructorReferenceFix)
} }
} }
@@ -407,14 +422,14 @@ internal class InvokeImportFix(expression: KtExpression) : OrdinaryImportFixBase
companion object MyFactory : Factory() { companion object MyFactory : Factory() {
override fun createImportAction(diagnostic: Diagnostic) = override fun createImportAction(diagnostic: Diagnostic) =
(diagnostic.psiElement as? KtExpression)?.let(::InvokeImportFix) (diagnostic.psiElement as? KtExpression)?.let(::InvokeImportFix)
} }
} }
internal open class ArrayAccessorImportFix( internal open class ArrayAccessorImportFix(
element: KtArrayAccessExpression, element: KtArrayAccessExpression,
override val importNames: Collection<Name>, override val importNames: Collection<Name>,
private val showHint: Boolean private val showHint: Boolean
) : OrdinaryImportFixBase<KtArrayAccessExpression>(element, MyFactory) { ) : OrdinaryImportFixBase<KtArrayAccessExpression>(element, MyFactory) {
override fun getCallTypeAndReceiver() = element?.let { CallTypeAndReceiver.OPERATOR(it.arrayExpression!!) } override fun getCallTypeAndReceiver() = element?.let { CallTypeAndReceiver.OPERATOR(it.arrayExpression!!) }
@@ -445,9 +460,9 @@ internal open class ArrayAccessorImportFix(
} }
internal class DelegateAccessorsImportFix( internal class DelegateAccessorsImportFix(
element: KtExpression, element: KtExpression,
override val importNames: Collection<Name>, override val importNames: Collection<Name>,
private val solveSeveralProblems: Boolean private val solveSeveralProblems: Boolean
) : OrdinaryImportFixBase<KtExpression>(element, MyFactory) { ) : OrdinaryImportFixBase<KtExpression>(element, MyFactory) {
override fun getCallTypeAndReceiver() = CallTypeAndReceiver.DELEGATE(element) override fun getCallTypeAndReceiver() = CallTypeAndReceiver.DELEGATE(element)
@@ -472,9 +487,9 @@ internal class DelegateAccessorsImportFix(
} }
override fun createImportAction(diagnostic: Diagnostic) = override fun createImportAction(diagnostic: Diagnostic) =
(diagnostic.psiElement as? KtExpression)?.let { (diagnostic.psiElement as? KtExpression)?.let {
DelegateAccessorsImportFix(it, importNames(listOf(diagnostic)), false) DelegateAccessorsImportFix(it, importNames(listOf(diagnostic)), false)
} }
override fun createImportActionsForAllProblems(sameTypeDiagnostics: Collection<Diagnostic>): List<DelegateAccessorsImportFix> { override fun createImportActionsForAllProblems(sameTypeDiagnostics: Collection<Diagnostic>): List<DelegateAccessorsImportFix> {
@@ -486,9 +501,9 @@ internal class DelegateAccessorsImportFix(
} }
internal class ComponentsImportFix( internal class ComponentsImportFix(
element: KtExpression, element: KtExpression,
override val importNames: Collection<Name>, override val importNames: Collection<Name>,
private val solveSeveralProblems: Boolean private val solveSeveralProblems: Boolean
) : OrdinaryImportFixBase<KtExpression>(element, MyFactory) { ) : OrdinaryImportFixBase<KtExpression>(element, MyFactory) {
override fun getCallTypeAndReceiver() = element?.let { CallTypeAndReceiver.OPERATOR(it) } override fun getCallTypeAndReceiver() = element?.let { CallTypeAndReceiver.OPERATOR(it) }
@@ -503,12 +518,12 @@ internal class ComponentsImportFix(
companion object MyFactory : Factory() { companion object MyFactory : Factory() {
private fun importNames(diagnostics: Collection<Diagnostic>) = private fun importNames(diagnostics: Collection<Diagnostic>) =
diagnostics.map { Name.identifier(Errors.COMPONENT_FUNCTION_MISSING.cast(it).a.identifier) } diagnostics.map { Name.identifier(Errors.COMPONENT_FUNCTION_MISSING.cast(it).a.identifier) }
override fun createImportAction(diagnostic: Diagnostic) = override fun createImportAction(diagnostic: Diagnostic) =
(diagnostic.psiElement as? KtExpression)?.let { (diagnostic.psiElement as? KtExpression)?.let {
ComponentsImportFix(it, importNames(listOf(diagnostic)), false) ComponentsImportFix(it, importNames(listOf(diagnostic)), false)
} }
override fun createImportActionsForAllProblems(sameTypeDiagnostics: Collection<Diagnostic>): List<ComponentsImportFix> { override fun createImportActionsForAllProblems(sameTypeDiagnostics: Collection<Diagnostic>): List<ComponentsImportFix> {
val element = sameTypeDiagnostics.first().psiElement val element = sameTypeDiagnostics.first().psiElement
@@ -520,7 +535,7 @@ internal class ComponentsImportFix(
} }
internal class ImportForMismatchingArgumentsFix( internal class ImportForMismatchingArgumentsFix(
expression: KtSimpleNameExpression expression: KtSimpleNameExpression
) : ImportFixBase<KtSimpleNameExpression>(expression, MyFactory) { ) : ImportFixBase<KtSimpleNameExpression>(expression, MyFactory) {
override fun getCallTypeAndReceiver() = element?.let { CallTypeAndReceiver.detect(it) } override fun getCallTypeAndReceiver() = element?.let { CallTypeAndReceiver.detect(it) }
@@ -530,17 +545,19 @@ internal class ImportForMismatchingArgumentsFix(
val element = element ?: return emptyList() val element = element ?: return emptyList()
val callExpression = element.parent as? KtCallExpression ?: return emptyList() val callExpression = element.parent as? KtCallExpression ?: return emptyList()
return callExpression.valueArguments + return callExpression.valueArguments +
callExpression.valueArguments.mapNotNull { it.getArgumentExpression() } + callExpression.valueArguments.mapNotNull { it.getArgumentExpression() } +
callExpression.valueArguments.mapNotNull { it.getArgumentName()?.referenceExpression } + callExpression.valueArguments.mapNotNull { it.getArgumentName()?.referenceExpression } +
listOfNotNull(callExpression.valueArgumentList, listOfNotNull(
callExpression.referenceExpression()) callExpression.valueArgumentList,
callExpression.referenceExpression()
)
} }
override fun fillCandidates( override fun fillCandidates(
name: String, name: String,
callTypeAndReceiver: CallTypeAndReceiver<*, *>, callTypeAndReceiver: CallTypeAndReceiver<*, *>,
bindingContext: BindingContext, bindingContext: BindingContext,
indicesHelper: KotlinIndicesHelper indicesHelper: KotlinIndicesHelper
): List<DeclarationDescriptor> { ): List<DeclarationDescriptor> {
val element = element ?: return emptyList() val element = element ?: return emptyList()
@@ -567,9 +584,9 @@ internal class ImportForMismatchingArgumentsFix(
val resolutionScopeWithAddedImport = resolutionScope.addImportingScope(ExplicitImportsScope(listOf(descriptor))) val resolutionScopeWithAddedImport = resolutionScope.addImportingScope(ExplicitImportsScope(listOf(descriptor)))
val dataFlowInfo = bindingContext.getDataFlowInfoBefore(elementToAnalyze) val dataFlowInfo = bindingContext.getDataFlowInfoBefore(elementToAnalyze)
val newBindingContext = elementToAnalyze.analyzeInContext( val newBindingContext = elementToAnalyze.analyzeInContext(
resolutionScopeWithAddedImport, resolutionScopeWithAddedImport,
dataFlowInfo = dataFlowInfo, dataFlowInfo = dataFlowInfo,
contextDependency = ContextDependency.DEPENDENT // to not check complete inference contextDependency = ContextDependency.DEPENDENT // to not check complete inference
) )
return newBindingContext.diagnostics.none { it.severity == Severity.ERROR } return newBindingContext.diagnostics.none { it.severity == Severity.ERROR }
} }
@@ -583,13 +600,13 @@ internal class ImportForMismatchingArgumentsFix(
} }
indicesHelper indicesHelper
.getCallableTopLevelExtensions(callTypeAndReceiver, element, bindingContext) { it == name } .getCallableTopLevelExtensions(callTypeAndReceiver, element, bindingContext) { it == name }
.forEach(::processDescriptor) .forEach(::processDescriptor)
if (!isSelectorInQualified(element)) { if (!isSelectorInQualified(element)) {
indicesHelper indicesHelper
.getTopLevelCallablesByName(name) .getTopLevelCallablesByName(name)
.forEach(::processDescriptor) .forEach(::processDescriptor)
} }
return result return result
@@ -609,8 +626,7 @@ internal object ImportForMissingOperatorFactory : ImportFixBase.Factory() {
override fun createImportAction(diagnostic: Diagnostic): ImportFixBase<*>? { override fun createImportAction(diagnostic: Diagnostic): ImportFixBase<*>? {
val element = diagnostic.psiElement as? KtExpression ?: return null val element = diagnostic.psiElement as? KtExpression ?: return null
val operatorDescriptor = Errors.OPERATOR_MODIFIER_REQUIRED.cast(diagnostic).a val operatorDescriptor = Errors.OPERATOR_MODIFIER_REQUIRED.cast(diagnostic).a
val name = operatorDescriptor.name when (val name = operatorDescriptor.name) {
when (name) {
OperatorNameConventions.GET, OperatorNameConventions.SET -> { OperatorNameConventions.GET, OperatorNameConventions.SET -> {
if (element is KtArrayAccessExpression) { if (element is KtArrayAccessExpression) {
return object : ArrayAccessorImportFix(element, listOf(name), false) { return object : ArrayAccessorImportFix(element, listOf(name), false) {
@@ -634,4 +650,6 @@ private fun KotlinIndicesHelper.getClassesByName(expressionForPlatform: KtExpres
kindFilter = { kind -> kind != ClassKind.ENUM_ENTRY }) kindFilter = { kind -> kind != ClassKind.ENUM_ENTRY })
} }
private fun CallTypeAndReceiver<*, *>.toFilter() = { descriptor: DeclarationDescriptor -> this.callType.descriptorKindFilter.accepts(descriptor) } private fun CallTypeAndReceiver<*, *>.toFilter() = { descriptor: DeclarationDescriptor ->
callType.descriptorKindFilter.accepts(descriptor)
}
@@ -45,11 +45,7 @@ class MakeClassAnAnnotationClassFix(annotationClass: KtClass) : KotlinQuickFixAc
companion object : KotlinSingleIntentionActionFactory() { companion object : KotlinSingleIntentionActionFactory() {
override fun createAction(diagnostic: Diagnostic): IntentionAction? { override fun createAction(diagnostic: Diagnostic): IntentionAction? {
val typeReference = diagnostic.psiElement.getNonStrictParentOfType<KtAnnotationEntry>()?.typeReference ?: return null val typeReference = diagnostic.psiElement.getNonStrictParentOfType<KtAnnotationEntry>()?.typeReference ?: return null
val bindingContext = typeReference.analyze(BodyResolveMode.PARTIAL) val klass = typeReference.classForRefactor() ?: return null
val type = bindingContext[BindingContext.TYPE, typeReference] ?: return null
val classDescriptor = type.constructor.declarationDescriptor as? ClassDescriptor ?: return null
val klass = DescriptorToSourceUtils.descriptorToDeclaration(classDescriptor) as? KtClass ?: return null
if (!klass.canRefactor()) return null
return MakeClassAnAnnotationClassFix(klass) return MakeClassAnAnnotationClassFix(klass)
} }
} }