diff --git a/idea/src/org/jetbrains/jet/plugin/JetBundle.properties b/idea/src/org/jetbrains/jet/plugin/JetBundle.properties index fa7e4f725fe..55e6b2a6951 100644 --- a/idea/src/org/jetbrains/jet/plugin/JetBundle.properties +++ b/idea/src/org/jetbrains/jet/plugin/JetBundle.properties @@ -93,3 +93,4 @@ options.jet.attribute.descriptor.auto.casted=Smart-cast value options.jet.attribute.descriptor.label=Label change.to.function.invocation=Change to function invocation migrate.tuple=Migrate tuples in project\: e.g., \#(,) and \#(,,) will be replaced by Pair and Triple +migrate.sure=Replace sure() calls by !! in project diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/MigrateSureInProjectFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/MigrateSureInProjectFix.java new file mode 100644 index 00000000000..5b22cdbec1e --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/MigrateSureInProjectFix.java @@ -0,0 +1,145 @@ +/* + * Copyright 2010-2012 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.quickfix; + +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.analyzer.AnalyzeExhaust; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.diagnostics.Diagnostic; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.plugin.JetBundle; +import org.jetbrains.jet.plugin.project.PluginJetFilesProvider; + +import java.util.Collection; + +/** + * @author abreslav + */ +public class MigrateSureInProjectFix extends JetIntentionAction { + public MigrateSureInProjectFix(@NotNull PsiElement element) { + super(element); + } + + @NotNull + @Override + public String getText() { + return JetBundle.message("migrate.sure"); + } + + @NotNull + @Override + public String getFamilyName() { + return JetBundle.message("migrate.sure"); + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + return super.isAvailable(project, editor, file) + && (file instanceof JetFile) + && isUnresolvedSure(element); + } + + private static boolean isUnresolvedSure(PsiElement element) { + if (!(element instanceof JetSimpleNameExpression)) return false; + JetSimpleNameExpression calleeExpression = (JetSimpleNameExpression) element; + + // it must be "sure" + if (!"sure".equals(calleeExpression.getReferencedName())) return false; + + // parent must be a call expression, and this must be the callee + PsiElement parent = calleeExpression.getParent(); + if (!(parent instanceof JetCallExpression)) return false; + JetCallExpression callExpression = (JetCallExpression) parent; + if (callExpression.getCalleeExpression() != calleeExpression) return false; + + // it must be a qualified call + PsiElement callParent = callExpression.getParent(); + assert callParent != null; + if (!(callParent instanceof JetDotQualifiedExpression)) return false; + + // not more than one type argument + if (callExpression.getTypeArguments().size() > 1) return false; + + // no value arguments + if (!callExpression.getValueArguments().isEmpty() || !callExpression.getFunctionLiteralArguments().isEmpty()) return false; + + return true; + } + + @Override + public void invoke(@NotNull Project project, Editor editor, final PsiFile file) throws IncorrectOperationException { + JetFile initialFile = (JetFile) file; + Collection files = PluginJetFilesProvider.WHOLE_PROJECT_DECLARATION_PROVIDER.fun(initialFile); + + AnalyzeExhaust analyzeExhaust = MigrateTuplesInProjectFix.analyzeFiles(initialFile, files); + + for (JetFile jetFile : files) { + replaceUnresolvedSure(jetFile, analyzeExhaust.getBindingContext()); + } + } + + private void replaceUnresolvedSure(JetFile file, final BindingContext context) { + for (JetDeclaration declaration : file.getDeclarations()) { + declaration.acceptChildren(new JetVisitorVoid() { + + @Override + public void visitCallExpression(JetCallExpression expression) { + expression.acceptChildren(this); + + if (!isUnresolvedSure(expression.getCalleeExpression())) return; + + PsiElement parent = expression.getParent(); + assert parent != null : "isUnresolvedSure() must be true"; + JetExpression receiver = ((JetDotQualifiedExpression) parent).getReceiverExpression(); + + // sure() must be unresolved + DeclarationDescriptor callee = context.get(BindingContext.REFERENCE_TARGET, + (JetReferenceExpression) expression.getCalleeExpression()); + if (callee != null) return; + + // replace 'foo.sure()' with 'foo!!' + JetExpression newExpression = JetPsiFactory.createExpression(expression.getProject(), receiver.getText() + "!!"); + parent.replace(newExpression); + } + + @Override + public void visitElement(PsiElement element) { + element.acceptChildren(this); + } + }); + } + } + + + public static JetIntentionActionFactory createFactory() { + return new JetIntentionActionFactory() { + @Nullable + @Override + public JetIntentionAction createAction(Diagnostic diagnostic) { + PsiElement element = diagnostic.getPsiElement(); + return new MigrateSureInProjectFix(element); + } + }; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java b/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java index bfe1109201f..172527bced4 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java @@ -125,6 +125,8 @@ public class QuickFixes { factories.put(TUPLES_ARE_NOT_SUPPORTED, MigrateTuplesInProjectFix.createFactory()); + factories.put(UNRESOLVED_REFERENCE, MigrateSureInProjectFix.createFactory()); + ImplementMethodsHandler implementMethodsHandler = new ImplementMethodsHandler(); actions.put(ABSTRACT_MEMBER_NOT_IMPLEMENTED, implementMethodsHandler); actions.put(MANY_IMPL_MEMBER_NOT_IMPLEMENTED, implementMethodsHandler); diff --git a/idea/testData/quickfix/migration/afterBareSure.kt b/idea/testData/quickfix/migration/afterBareSure.kt new file mode 100644 index 00000000000..f14b883f8a8 --- /dev/null +++ b/idea/testData/quickfix/migration/afterBareSure.kt @@ -0,0 +1,6 @@ +// "Replace sure() calls by !! in project" "false" +// ERROR: Unresolved reference: sure + +fun test() { + sure() +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/afterSure.kt b/idea/testData/quickfix/migration/afterSure.kt new file mode 100644 index 00000000000..cc1326aefdd --- /dev/null +++ b/idea/testData/quickfix/migration/afterSure.kt @@ -0,0 +1,38 @@ +// "Replace sure() calls by !! in project" "true" + +package p + +fun println(a: Any) = a + +fun int(): Int? = 1 + +fun test1(a: Int?) { + println(a!! + 1) + println(a!! + 1) + println((a)!! + 1) + println(int()!! + 1) + println(p.int()!! + 1) + println(if (true) { + null + } else { + 2 + }!! + 1) + println(((2 + 1): Int?)!! + 1) + sure() + p.sure() +} + +fun sure() {} + + +class A { + + fun Int?.sure(x : Int) = x + fun String?.sure() {} + + fun test(x: Int?) { + x.sure(1) + "".sure() + } +} + diff --git a/idea/testData/quickfix/migration/beforeBareSure.kt b/idea/testData/quickfix/migration/beforeBareSure.kt new file mode 100644 index 00000000000..120af475699 --- /dev/null +++ b/idea/testData/quickfix/migration/beforeBareSure.kt @@ -0,0 +1,6 @@ +// "Replace sure() calls by !! in project" "false" +// ERROR: Unresolved reference: sure + +fun test() { + sure() +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/beforeSure.kt b/idea/testData/quickfix/migration/beforeSure.kt new file mode 100644 index 00000000000..48aae6133d7 --- /dev/null +++ b/idea/testData/quickfix/migration/beforeSure.kt @@ -0,0 +1,38 @@ +// "Replace sure() calls by !! in project" "true" + +package p + +fun println(a: Any) = a + +fun int(): Int? = 1 + +fun test1(a: Int?) { + println(a.sure() + 1) + println(a.sure() + 1) + println((a).sure() + 1) + println(int().sure() + 1) + println(p.int().sure() + 1) + println(if (true) { + null + } else { + 2 + }.sure() + 1) + println(((2 + 1): Int?).sure() + 1) + sure() + p.sure() +} + +fun sure() {} + + +class A { + + fun Int?.sure(x : Int) = x + fun String?.sure() {} + + fun test(x: Int?) { + x.sure(1) + "".sure() + } +} +