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:
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user