Remove explicit type intention should not be available on public

This commit is contained in:
Valentin Kipyatkov
2015-05-05 16:22:14 +03:00
parent 2d90ee5cad
commit b0ce16eada
8 changed files with 62 additions and 10 deletions
@@ -75,16 +75,13 @@ public class ConvertToExpressionBodyIntention : JetSelfTargetingOffsetIndependen
editor.getCaretModel().moveToOffset(range.getEndOffset())
}
else {
(declaration : PsiElement).deleteChildRange(colon, typeRef)
(declaration as PsiElement).deleteChildRange(colon, typeRef)
}
}
}
private fun canOmitType(declaration: JetCallableDeclaration, expression: JetExpression): Boolean {
if (declaration.getModifierList()?.hasModifier(JetTokens.OVERRIDE_KEYWORD) ?: false) return true
val descriptor = declaration.resolveToDescriptor()
if ((descriptor as? DeclarationDescriptorWithVisibility)?.getVisibility()?.isPublicAPI() ?: false) return false
if (!declaration.canRemoveTypeSpecificationByVisibility()) return false
// Workaround for anonymous objects and similar expressions without resolution scope
// TODO: This should probably be fixed in front-end so that resolution scope is recorded for anonymous objects as well
@@ -92,7 +89,7 @@ public class ConvertToExpressionBodyIntention : JetSelfTargetingOffsetIndependen
?.getStatements()?.singleOrNull() as? JetExpression
?: return false
val declaredType = (descriptor as? CallableDescriptor)?.getReturnType() ?: return false
val declaredType = (declaration.resolveToDescriptor() as? CallableDescriptor)?.getReturnType() ?: return false
val scope = scopeExpression.analyze()[BindingContext.RESOLUTION_SCOPE, scopeExpression] ?: return false
val expressionType = expression.analyzeInContext(scope).getType(expression) ?: return false
return expressionType.isSubtypeOf(declaredType)
@@ -39,16 +39,17 @@ import java.util.*
public class RemoveExplicitTypeIntention : JetSelfTargetingIntention<JetCallableDeclaration>(javaClass(), "Remove explicit type specification") {
override fun isApplicableTo(element: JetCallableDeclaration, caretOffset: Int): Boolean {
//TODO: check for public API
if (element.getContainingFile() is JetCodeFragment) return false
if (element.getTypeReference() == null) return false
val initializer = (element as? JetWithExpressionInitializer)?.getInitializer()
if (initializer != null && initializer.getTextRange().containsOffset(caretOffset)) return false
if (!element.canRemoveTypeSpecificationByVisibility()) return false
return when (element) {
is JetProperty -> true
is JetNamedFunction -> !element.hasBlockBody() && element.getBodyExpression() != null
is JetProperty -> initializer != null
is JetNamedFunction -> !element.hasBlockBody() && initializer != null
is JetParameter -> element.isLoopParameter()
else -> false
}
@@ -18,15 +18,20 @@ package org.jetbrains.kotlin.idea.intentions
import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.JetNodeTypes
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.analyzer.analyzeInContext
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.references.JetReference
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.ShortenReferences
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
fun JetCallableDeclaration.setType(type: JetType) {
if (type.isError()) return
@@ -59,3 +64,10 @@ fun isAutoCreatedItUsage(expression: JetSimpleNameExpression): Boolean {
val target = reference?.resolveToDescriptors(context)?.singleOrNull() as? ValueParameterDescriptor? ?: return false
return context[BindingContext.AUTO_CREATED_IT, target]
}
fun JetCallableDeclaration.canRemoveTypeSpecificationByVisibility(): Boolean {
val descriptor = resolveToDescriptor()
val isOverride = getModifierList()?.hasModifier(JetTokens.OVERRIDE_KEYWORD) ?: false
if (!isOverride && (descriptor as? DeclarationDescriptorWithVisibility)?.getVisibility()?.isPublicAPI() ?: false) return false
return true
}
@@ -0,0 +1,2 @@
// IS_APPLICABLE: false
public var x: String? = null
@@ -0,0 +1,7 @@
public trait I {
public fun foo(): String
}
public class C : I {
override fun foo(): <caret>String = ""
}
@@ -0,0 +1,7 @@
public trait I {
public fun foo(): String
}
public class C : I {
override fun foo() = ""
}
@@ -0,0 +1,8 @@
// IS_APPLICABLE: false
public trait I {
public val v: String?
}
public trait I1 : I {
override val v: String<caret>
}
@@ -5050,6 +5050,24 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/removeExplicitType"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("notOnPublic.kt")
public void testNotOnPublic() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitType/notOnPublic.kt");
doTest(fileName);
}
@TestMetadata("onOverride.kt")
public void testOnOverride() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitType/onOverride.kt");
doTest(fileName);
}
@TestMetadata("onOverrideInTrait.kt")
public void testOnOverrideInTrait() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitType/onOverrideInTrait.kt");
doTest(fileName);
}
@TestMetadata("onType.kt")
public void testOnType() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/removeExplicitType/onType.kt");