Auto-imports for operator calls and infix calls

#KT-1613 Fixed
 #KT-4192 Fixed

Enable auto-import fix for "unresolved reference with wrong receiver" diagnostic
This commit is contained in:
Pavel V. Talanov
2013-12-30 18:15:49 +04:00
parent 63abe860d6
commit e693820fb4
29 changed files with 255 additions and 9 deletions
@@ -18,9 +18,6 @@ package org.jetbrains.jet.lang.psi;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.PsiReferenceService;
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import org.jetbrains.annotations.NotNull;
@@ -214,13 +214,13 @@ public class JetShortNamesCache extends PsiShortNamesCache {
@NotNull
public Collection<FunctionDescriptor> getTopLevelFunctionDescriptorsByName(
@NotNull final String name,
@NotNull String name,
@NotNull JetSimpleNameExpression expression,
@NotNull CancelableResolveSession resolveSession,
@NotNull GlobalSearchScope scope
) {
// name parameter can differ from expression.getReferenceName() when expression contains completion suffix
Name referenceName = expression.getIdentifier() == null ? JetPsiUtil.getConventionName(expression) : Name.identifier(name);
final Name referenceName = expression.getIdentifier() == null ? JetPsiUtil.getConventionName(expression) : Name.identifier(name);
if (referenceName == null || referenceName.toString().isEmpty()) {
return Collections.emptyList();
}
@@ -238,7 +238,7 @@ public class JetShortNamesCache extends PsiShortNamesCache {
ContainerUtil.filter(getTopLevelFunctionFqNames(project, scope, false), new Condition<FqName>() {
@Override
public boolean value(FqName fqName) {
return fqName.lastSegmentIs(Name.identifier(name));
return fqName.lastSegmentIs(referenceName);
}
});
for (FqName fqName : topLevelFunctionFqNames) {
@@ -292,7 +292,7 @@ public class JetShortNamesCache extends PsiShortNamesCache {
@NotNull GlobalSearchScope searchScope
) {
BindingContext context = resolveSession.resolveToElement(expression);
JetExpression receiverExpression = expression.getReceiverExpression();
JetExpression receiverExpression = getReceiverForExtensionCall(expression);
if (receiverExpression == null) {
return Collections.emptyList();
@@ -325,6 +325,18 @@ public class JetShortNamesCache extends PsiShortNamesCache {
return resultDescriptors;
}
@Nullable
private static JetExpression getReceiverForExtensionCall(@NotNull JetSimpleNameExpression expression) {
PsiElement parent = expression.getParent();
if (parent instanceof JetBinaryExpression && ((JetBinaryExpression) parent).getOperationReference() == expression) {
return ((JetBinaryExpression) parent).getLeft();
}
else if (parent instanceof JetUnaryExpression && ((JetUnaryExpression) parent).getOperationReference() == expression) {
return ((JetUnaryExpression) parent).getBaseExpression();
}
return expression.getReceiverExpression();
}
@NotNull
private Set<FqName> extensionFunctionsFromSourceFqNames(
@NotNull Condition<String> acceptedNameCondition,
@@ -29,7 +29,6 @@ import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiFile;
import com.intellij.psi.search.GlobalSearchScope;
@@ -48,6 +47,7 @@ import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.ImportPath;
import org.jetbrains.jet.lang.resolve.lazy.KotlinCodeAnalyzer;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.plugin.JetBundle;
import org.jetbrains.jet.plugin.actions.JetAddImportAction;
import org.jetbrains.jet.plugin.caches.JetShortNamesCache;
@@ -81,8 +81,14 @@ public class AutoImportFix extends JetHintAction<JetSimpleNameExpression> implem
}
String referenceName = element.getReferencedName();
if (element.getIdentifier() == null) {
Name conventionName = JetPsiUtil.getConventionName(element);
if (conventionName != null) {
referenceName = conventionName.asString();
}
}
if (!StringUtil.isNotEmpty(referenceName)) {
if (referenceName.isEmpty()) {
return Collections.emptyList();
}
@@ -132,6 +132,7 @@ public class QuickFixes {
JetSingleIntentionActionFactory unresolvedReferenceFactory = AutoImportFix.createFactory();
factories.put(UNRESOLVED_REFERENCE, unresolvedReferenceFactory);
factories.put(UNRESOLVED_REFERENCE_WRONG_RECEIVER, unresolvedReferenceFactory);
JetSingleIntentionActionFactory removeImportFixFactory = RemovePsiElementSimpleFix.createRemoveImportFactory();
factories.put(USELESS_SIMPLE_IMPORT, removeImportFixFactory);
@@ -0,0 +1,10 @@
// "Import" "true"
// ERROR: Unresolved reference: /
import util.div
trait H
fun f(h: H) {
h / 3
}
@@ -0,0 +1,8 @@
// "Import" "true"
// ERROR: Unresolved reference: /
trait H
fun f(h: H) {
h <caret>/ 3
}
@@ -0,0 +1,3 @@
package util
fun H.div(i: Int) = 3
@@ -0,0 +1,10 @@
// "Import" "true"
// ERROR: Unresolved reference: foo
import util.foo
trait H
fun f(h: H) {
h foo h
}
@@ -0,0 +1,8 @@
// "Import" "true"
// ERROR: Unresolved reference: foo
trait H
fun f(h: H) {
h <caret>foo h
}
@@ -0,0 +1,3 @@
package util
fun H.foo(other: H) = ""
@@ -0,0 +1,15 @@
// "Import" "true"
// ERROR: <html>Unresolved reference. <br/> None of the following candidates is applicable because of receiver type mismatch: <ul><li><b>internal</b> <b>fun</b> jet.String.minus(str: jet.String): jet.String <i>defined in</i> root package</li><li><b>internal</b> <b>fun</b> jet.String.minus(i: java.lang.Integer): jet.String <i>defined in</i> root package</li></ul></html>
import util.minus
trait H
fun f(h: H?) {
h - "other"
}
fun String.minus(str: String) = ""
fun String.minus(i: Integer) = ""
@@ -0,0 +1,13 @@
// "Import" "true"
// ERROR: <html>Unresolved reference. <br/> None of the following candidates is applicable because of receiver type mismatch: <ul><li><b>internal</b> <b>fun</b> jet.String.minus(str: jet.String): jet.String <i>defined in</i> root package</li><li><b>internal</b> <b>fun</b> jet.String.minus(i: java.lang.Integer): jet.String <i>defined in</i> root package</li></ul></html>
trait H
fun f(h: H?) {
h <caret>- "other"
}
fun String.minus(str: String) = ""
fun String.minus(i: Integer) = ""
@@ -0,0 +1,3 @@
package util
fun H?.minus(s: String) = ""
@@ -0,0 +1,10 @@
// "Import" "true"
// ERROR: <html>Unresolved reference. <br/> None of the following candidates is applicable because of receiver type mismatch: <ul><li><b>public</b> <b>fun</b> jet.String?.plus(other: jet.Any?): jet.String <i>defined in</i> jet</li></ul></html>
import util.plus
trait H
fun f(h: H?) {
h + "other"
}
@@ -0,0 +1,8 @@
// "Import" "true"
// ERROR: <html>Unresolved reference. <br/> None of the following candidates is applicable because of receiver type mismatch: <ul><li><b>public</b> <b>fun</b> jet.String?.plus(other: jet.Any?): jet.String <i>defined in</i> jet</li></ul></html>
trait H
fun f(h: H?) {
h <caret>+ "other"
}
@@ -0,0 +1,3 @@
package util
fun H?.plus(s: String) = ""
@@ -0,0 +1,11 @@
// "Import" "true"
// ERROR: Unresolved reference: ++
import util.inc
trait H
fun f(h: H?) {
var h1 = h
h1++
}
@@ -0,0 +1,9 @@
// "Import" "true"
// ERROR: Unresolved reference: ++
trait H
fun f(h: H?) {
var h1 = h
h1<caret>++
}
@@ -0,0 +1,4 @@
package util
fun H.inc(): H {
}
@@ -0,0 +1,10 @@
// "Import" "true"
// ERROR: Unresolved reference: *=
import util.timesAssign
trait H
fun f(h: H) {
h *= 3
}
@@ -0,0 +1,8 @@
// "Import" "true"
// ERROR: Unresolved reference: *=
trait H
fun f(h: H) {
h <caret>*= 3
}
@@ -0,0 +1,4 @@
package util
fun H.div(i: Int) = 3
fun H.timesAssign(i: Int) {}
@@ -0,0 +1,10 @@
// "Import" "true"
// ERROR: Unresolved reference: -
import util.minus
trait H
fun f(h: H?) {
-h
}
@@ -0,0 +1,8 @@
// "Import" "true"
// ERROR: Unresolved reference: -
trait H
fun f(h: H?) {
<caret>-h
}
@@ -0,0 +1,3 @@
package util
fun H?.minus() = ""
@@ -0,0 +1,14 @@
// "Import" "true"
// ERROR: <html>Unresolved reference. <br/> None of the following candidates is applicable because of receiver type mismatch: <ul><li><b>internal</b> <b>fun</b> A.plus(): jet.Int <i>defined in</i> root package</li><li><b>public</b> <b>fun</b> jet.String?.plus(other: jet.Any?): jet.String <i>defined in</i> jet</li></ul></html>
import util.plus
trait H
fun f(h: H?) {
+h
}
class A()
fun A.plus(): Int = 3
@@ -0,0 +1,12 @@
// "Import" "true"
// ERROR: <html>Unresolved reference. <br/> None of the following candidates is applicable because of receiver type mismatch: <ul><li><b>internal</b> <b>fun</b> A.plus(): jet.Int <i>defined in</i> root package</li><li><b>public</b> <b>fun</b> jet.String?.plus(other: jet.Any?): jet.String <i>defined in</i> jet</li></ul></html>
trait H
fun f(h: H?) {
<caret>+h
}
class A()
fun A.plus(): Int = 3
@@ -0,0 +1,3 @@
package util
fun H.plus() = ""
@@ -62,6 +62,11 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
doTestWithExtraFile("idea/testData/quickfix/autoImports/classImport.before.Main.kt");
}
@TestMetadata("divOperator.before.Main.kt")
public void testDivOperator() throws Exception {
doTestWithExtraFile("idea/testData/quickfix/autoImports/divOperator.before.Main.kt");
}
@TestMetadata("extensionFunctionImport.before.Main.kt")
public void testExtensionFunctionImport() throws Exception {
doTestWithExtraFile("idea/testData/quickfix/autoImports/extensionFunctionImport.before.Main.kt");
@@ -87,6 +92,16 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
doTestWithExtraFile("idea/testData/quickfix/autoImports/importTrait.before.Main.kt");
}
@TestMetadata("infixCall.before.Main.kt")
public void testInfixCall() throws Exception {
doTestWithExtraFile("idea/testData/quickfix/autoImports/infixCall.before.Main.kt");
}
@TestMetadata("minusOperator.before.Main.kt")
public void testMinusOperator() throws Exception {
doTestWithExtraFile("idea/testData/quickfix/autoImports/minusOperator.before.Main.kt");
}
@TestMetadata("noImportForFunInQualifiedNotFirst.before.Main.kt")
public void testNoImportForFunInQualifiedNotFirst() throws Exception {
doTestWithExtraFile("idea/testData/quickfix/autoImports/noImportForFunInQualifiedNotFirst.before.Main.kt");
@@ -127,6 +142,31 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
doTestWithExtraFile("idea/testData/quickfix/autoImports/packageClass.before.Main.kt");
}
@TestMetadata("plusOperator.before.Main.kt")
public void testPlusOperator() throws Exception {
doTestWithExtraFile("idea/testData/quickfix/autoImports/plusOperator.before.Main.kt");
}
@TestMetadata("postfixOperator.before.Main.kt")
public void testPostfixOperator() throws Exception {
doTestWithExtraFile("idea/testData/quickfix/autoImports/postfixOperator.before.Main.kt");
}
@TestMetadata("timesAssign.before.Main.kt")
public void testTimesAssign() throws Exception {
doTestWithExtraFile("idea/testData/quickfix/autoImports/timesAssign.before.Main.kt");
}
@TestMetadata("unaryMinusOperator.before.Main.kt")
public void testUnaryMinusOperator() throws Exception {
doTestWithExtraFile("idea/testData/quickfix/autoImports/unaryMinusOperator.before.Main.kt");
}
@TestMetadata("unaryPlusOperator.before.Main.kt")
public void testUnaryPlusOperator() throws Exception {
doTestWithExtraFile("idea/testData/quickfix/autoImports/unaryPlusOperator.before.Main.kt");
}
}
@TestMetadata("idea/testData/quickfix/createFromUsage")