Intention to replace a shorthand type with a reconstructed one

This commit is contained in:
Andrey Breslav
2013-09-13 00:30:13 +02:00
parent a9134f8eff
commit 8bf1ab85ab
15 changed files with 166 additions and 2 deletions
@@ -375,6 +375,7 @@ public class GenerateTests {
testModel("idea/testData/intentions/declarations/split", "doTestSplitProperty"),
testModel("idea/testData/intentions/declarations/join", "doTestJoinProperty"),
testModel("idea/testData/intentions/declarations/convertMemberToExtension", "doTestConvertMemberToExtension"),
testModel("idea/testData/intentions/reconstructedType", "doTestReconstructType"),
testModel("idea/testData/intentions/removeUnnecessaryParentheses", "doTestRemoveUnnecessaryParentheses")
);
@@ -0,0 +1 @@
foo as <spot>Map<String, String></spot>
@@ -0,0 +1 @@
foo as <spot>Map</spot>
@@ -0,0 +1,5 @@
<html>
<body>
This intention replaces a shortened type on the right-hand side of as/as?/is/!is by its complete version
</body>
</html>
+5
View File
@@ -426,6 +426,11 @@
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.jet.plugin.intentions.ReconstructTypeInCastOrIsAction</className>
<category>Kotlin</category>
</intentionAction>
<project.converterProvider implementation="org.jetbrains.jet.plugin.converters.JetRunConfigurationSettingsFormatConverterProvider"/>
</extensions>
@@ -276,4 +276,6 @@ find.what.constructor.usages.checkbox=Usages of &constructor
find.what.derived.traits.checkbox=&Derived traits
find.what.derived.classes.checkbox=&Derived classes
convert.to.extension=Convert to extension
convert.to.extension=Convert to extension
replace.by.reconstructed.type.family.name=Replace by Reconstructed Type
replace.by.reconstructed.type=Replace by ''{0}''
@@ -0,0 +1,89 @@
/*
* Copyright 2010-2013 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.jet.plugin.intentions;
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.types.ErrorUtils;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.plugin.JetBundle;
import org.jetbrains.jet.plugin.codeInsight.ReferenceToClassesShortening;
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
import org.jetbrains.jet.renderer.DescriptorRenderer;
import java.util.Collections;
public class ReconstructTypeInCastOrIsAction extends PsiElementBaseIntentionAction {
@NotNull
@Override
public String getFamilyName() {
return JetBundle.message("replace.by.reconstructed.type.family.name");
}
@Override
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
JetTypeReference typeRef = PsiTreeUtil.getTopmostParentOfType(element, JetTypeReference.class);
assert typeRef != null : "Must be checked by isAvailable(): " + element;
JetType type = getReconstructedType(typeRef);
JetTypeReference newType = JetPsiFactory.createType(project, DescriptorRenderer.SOURCE_CODE.renderType(type));
JetTypeReference replaced = (JetTypeReference) typeRef.replace(newType);
ReferenceToClassesShortening.compactReferenceToClasses(Collections.singletonList(replaced));
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
JetTypeReference typeRef = PsiTreeUtil.getTopmostParentOfType(element, JetTypeReference.class);
if (typeRef == null) return false;
// Only user types (like Foo) are interesting
JetTypeElement typeElement = typeRef.getTypeElement();
if (!(typeElement instanceof JetUserType)) return false;
// If there are generic arguments already, there's nothing to reconstruct
if (!((JetUserType) typeElement).getTypeArguments().isEmpty()) return false;
// We must be on the RHS of as/as?/is/!is or inside an is/!is-condition in when()
JetExpression outerExpression = PsiTreeUtil.getParentOfType(typeRef, JetExpression.class);
if (!(outerExpression instanceof JetBinaryExpressionWithTypeRHS)) {
JetWhenConditionIsPattern outerIsCondition = PsiTreeUtil.getParentOfType(typeRef, JetWhenConditionIsPattern.class);
if (outerIsCondition == null) return false;
}
JetType type = getReconstructedType(typeRef);
if (type == null) return false;
if (ErrorUtils.isErrorType(type)) return false;
// No type parameters expected => nothing to reconstruct
if (type.getConstructor().getParameters().isEmpty()) return false;
setText(JetBundle.message("replace.by.reconstructed.type", type));
return true;
}
private static JetType getReconstructedType(JetTypeReference typeRef) {
BindingContext bindingContext = AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) typeRef.getContainingFile()).getBindingContext();
return bindingContext.get(BindingContext.TYPE, typeRef);
}
}
@@ -0,0 +1,4 @@
// IS_APPLICABLE: false
class G<T>
fun foo(a: Any) = a as <caret>G<*>
@@ -0,0 +1,4 @@
// IS_APPLICABLE: false
class G
fun foo(a: Any) = a as <caret>G
@@ -0,0 +1,2 @@
// IS_APPLICABLE: false
fun foo(a: Any) = a as <caret>G
@@ -0,0 +1,5 @@
// IS_APPLICABLE: true
class B<T>
class G<T>: B<T>
fun foo(a: B<String>) = a as <caret>G
@@ -0,0 +1,5 @@
// IS_APPLICABLE: true
class B<T>
class G<T>: B<T>
fun foo(a: B<String>) = a as <caret>G<String>
@@ -0,0 +1,2 @@
// IS_APPLICABLE: false
fun foo(a: <caret>Any) = 1
@@ -110,6 +110,10 @@ public abstract class AbstractCodeTransformationTest extends LightCodeInsightTes
doTestIntention(path, new ConvertMemberToExtension());
}
public void doTestReconstructType(@NotNull String path) throws Exception {
doTestIntention(path, new ReconstructTypeInCastOrIsAction());
}
private void doTestIntention(@NotNull String path, @NotNull IntentionAction intentionAction) throws Exception {
configureByFile(path);
@@ -30,7 +30,7 @@ import org.jetbrains.jet.plugin.intentions.AbstractCodeTransformationTest;
/** This class is generated by {@link org.jetbrains.jet.generators.tests.GenerateTests}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@InnerTestClasses({CodeTransformationsTestGenerated.IfToAssignment.class, CodeTransformationsTestGenerated.IfToReturn.class, CodeTransformationsTestGenerated.IfToReturnAsymmetrically.class, CodeTransformationsTestGenerated.WhenToAssignment.class, CodeTransformationsTestGenerated.WhenToReturn.class, CodeTransformationsTestGenerated.AssignmentToIf.class, CodeTransformationsTestGenerated.AssignmentToWhen.class, CodeTransformationsTestGenerated.PropertyToIf.class, CodeTransformationsTestGenerated.PropertyToWhen.class, CodeTransformationsTestGenerated.ReturnToIf.class, CodeTransformationsTestGenerated.ReturnToWhen.class, CodeTransformationsTestGenerated.IfToWhen.class, CodeTransformationsTestGenerated.WhenToIf.class, CodeTransformationsTestGenerated.Flatten.class, CodeTransformationsTestGenerated.IntroduceSubject.class, CodeTransformationsTestGenerated.EliminateSubject.class, CodeTransformationsTestGenerated.Split.class, CodeTransformationsTestGenerated.Join.class, CodeTransformationsTestGenerated.ConvertMemberToExtension.class, CodeTransformationsTestGenerated.RemoveUnnecessaryParentheses.class})
@InnerTestClasses({CodeTransformationsTestGenerated.IfToAssignment.class, CodeTransformationsTestGenerated.IfToReturn.class, CodeTransformationsTestGenerated.IfToReturnAsymmetrically.class, CodeTransformationsTestGenerated.WhenToAssignment.class, CodeTransformationsTestGenerated.WhenToReturn.class, CodeTransformationsTestGenerated.AssignmentToIf.class, CodeTransformationsTestGenerated.AssignmentToWhen.class, CodeTransformationsTestGenerated.PropertyToIf.class, CodeTransformationsTestGenerated.PropertyToWhen.class, CodeTransformationsTestGenerated.ReturnToIf.class, CodeTransformationsTestGenerated.ReturnToWhen.class, CodeTransformationsTestGenerated.IfToWhen.class, CodeTransformationsTestGenerated.WhenToIf.class, CodeTransformationsTestGenerated.Flatten.class, CodeTransformationsTestGenerated.IntroduceSubject.class, CodeTransformationsTestGenerated.EliminateSubject.class, CodeTransformationsTestGenerated.Split.class, CodeTransformationsTestGenerated.Join.class, CodeTransformationsTestGenerated.ConvertMemberToExtension.class, CodeTransformationsTestGenerated.ReconstructedType.class, CodeTransformationsTestGenerated.RemoveUnnecessaryParentheses.class})
public class CodeTransformationsTestGenerated extends AbstractCodeTransformationTest {
@TestMetadata("idea/testData/intentions/branched/folding/ifToAssignment")
public static class IfToAssignment extends AbstractCodeTransformationTest {
@@ -974,6 +974,39 @@ public class CodeTransformationsTestGenerated extends AbstractCodeTransformation
}
@TestMetadata("idea/testData/intentions/reconstructedType")
public static class ReconstructedType extends AbstractCodeTransformationTest {
public void testAllFilesPresentInReconstructedType() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("idea/testData/intentions/reconstructedType"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("completeGenericType.kt")
public void testCompleteGenericType() throws Exception {
doTestReconstructType("idea/testData/intentions/reconstructedType/completeGenericType.kt");
}
@TestMetadata("completeType.kt")
public void testCompleteType() throws Exception {
doTestReconstructType("idea/testData/intentions/reconstructedType/completeType.kt");
}
@TestMetadata("errorType.kt")
public void testErrorType() throws Exception {
doTestReconstructType("idea/testData/intentions/reconstructedType/errorType.kt");
}
@TestMetadata("reconstructableType.kt")
public void testReconstructableType() throws Exception {
doTestReconstructType("idea/testData/intentions/reconstructedType/reconstructableType.kt");
}
@TestMetadata("typeInDeclaration.kt")
public void testTypeInDeclaration() throws Exception {
doTestReconstructType("idea/testData/intentions/reconstructedType/typeInDeclaration.kt");
}
}
@TestMetadata("idea/testData/intentions/removeUnnecessaryParentheses")
public static class RemoveUnnecessaryParentheses extends AbstractCodeTransformationTest {
public void testAllFilesPresentInRemoveUnnecessaryParentheses() throws Exception {
@@ -1063,6 +1096,7 @@ public class CodeTransformationsTestGenerated extends AbstractCodeTransformation
suite.addTestSuite(Split.class);
suite.addTestSuite(Join.class);
suite.addTestSuite(ConvertMemberToExtension.class);
suite.addTestSuite(ReconstructedType.class);
suite.addTestSuite(RemoveUnnecessaryParentheses.class);
return suite;
}