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.lang.ASTNode;
|
||||||
import com.intellij.psi.PsiElement;
|
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.IElementType;
|
||||||
import com.intellij.psi.tree.TokenSet;
|
import com.intellij.psi.tree.TokenSet;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|||||||
@@ -214,13 +214,13 @@ public class JetShortNamesCache extends PsiShortNamesCache {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public Collection<FunctionDescriptor> getTopLevelFunctionDescriptorsByName(
|
public Collection<FunctionDescriptor> getTopLevelFunctionDescriptorsByName(
|
||||||
@NotNull final String name,
|
@NotNull String name,
|
||||||
@NotNull JetSimpleNameExpression expression,
|
@NotNull JetSimpleNameExpression expression,
|
||||||
@NotNull CancelableResolveSession resolveSession,
|
@NotNull CancelableResolveSession resolveSession,
|
||||||
@NotNull GlobalSearchScope scope
|
@NotNull GlobalSearchScope scope
|
||||||
) {
|
) {
|
||||||
// name parameter can differ from expression.getReferenceName() when expression contains completion suffix
|
// 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()) {
|
if (referenceName == null || referenceName.toString().isEmpty()) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
@@ -238,7 +238,7 @@ public class JetShortNamesCache extends PsiShortNamesCache {
|
|||||||
ContainerUtil.filter(getTopLevelFunctionFqNames(project, scope, false), new Condition<FqName>() {
|
ContainerUtil.filter(getTopLevelFunctionFqNames(project, scope, false), new Condition<FqName>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean value(FqName fqName) {
|
public boolean value(FqName fqName) {
|
||||||
return fqName.lastSegmentIs(Name.identifier(name));
|
return fqName.lastSegmentIs(referenceName);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
for (FqName fqName : topLevelFunctionFqNames) {
|
for (FqName fqName : topLevelFunctionFqNames) {
|
||||||
@@ -292,7 +292,7 @@ public class JetShortNamesCache extends PsiShortNamesCache {
|
|||||||
@NotNull GlobalSearchScope searchScope
|
@NotNull GlobalSearchScope searchScope
|
||||||
) {
|
) {
|
||||||
BindingContext context = resolveSession.resolveToElement(expression);
|
BindingContext context = resolveSession.resolveToElement(expression);
|
||||||
JetExpression receiverExpression = expression.getReceiverExpression();
|
JetExpression receiverExpression = getReceiverForExtensionCall(expression);
|
||||||
|
|
||||||
if (receiverExpression == null) {
|
if (receiverExpression == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
@@ -325,6 +325,18 @@ public class JetShortNamesCache extends PsiShortNamesCache {
|
|||||||
return resultDescriptors;
|
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
|
@NotNull
|
||||||
private Set<FqName> extensionFunctionsFromSourceFqNames(
|
private Set<FqName> extensionFunctionsFromSourceFqNames(
|
||||||
@NotNull Condition<String> acceptedNameCondition,
|
@NotNull Condition<String> acceptedNameCondition,
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import com.intellij.openapi.command.CommandProcessor;
|
|||||||
import com.intellij.openapi.editor.Editor;
|
import com.intellij.openapi.editor.Editor;
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
import com.intellij.openapi.util.Condition;
|
import com.intellij.openapi.util.Condition;
|
||||||
import com.intellij.openapi.util.text.StringUtil;
|
|
||||||
import com.intellij.psi.PsiClass;
|
import com.intellij.psi.PsiClass;
|
||||||
import com.intellij.psi.PsiFile;
|
import com.intellij.psi.PsiFile;
|
||||||
import com.intellij.psi.search.GlobalSearchScope;
|
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.ImportPath;
|
||||||
import org.jetbrains.jet.lang.resolve.lazy.KotlinCodeAnalyzer;
|
import org.jetbrains.jet.lang.resolve.lazy.KotlinCodeAnalyzer;
|
||||||
import org.jetbrains.jet.lang.resolve.name.FqName;
|
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.JetBundle;
|
||||||
import org.jetbrains.jet.plugin.actions.JetAddImportAction;
|
import org.jetbrains.jet.plugin.actions.JetAddImportAction;
|
||||||
import org.jetbrains.jet.plugin.caches.JetShortNamesCache;
|
import org.jetbrains.jet.plugin.caches.JetShortNamesCache;
|
||||||
@@ -81,8 +81,14 @@ public class AutoImportFix extends JetHintAction<JetSimpleNameExpression> implem
|
|||||||
}
|
}
|
||||||
|
|
||||||
String referenceName = element.getReferencedName();
|
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();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ public class QuickFixes {
|
|||||||
|
|
||||||
JetSingleIntentionActionFactory unresolvedReferenceFactory = AutoImportFix.createFactory();
|
JetSingleIntentionActionFactory unresolvedReferenceFactory = AutoImportFix.createFactory();
|
||||||
factories.put(UNRESOLVED_REFERENCE, unresolvedReferenceFactory);
|
factories.put(UNRESOLVED_REFERENCE, unresolvedReferenceFactory);
|
||||||
|
factories.put(UNRESOLVED_REFERENCE_WRONG_RECEIVER, unresolvedReferenceFactory);
|
||||||
|
|
||||||
JetSingleIntentionActionFactory removeImportFixFactory = RemovePsiElementSimpleFix.createRemoveImportFactory();
|
JetSingleIntentionActionFactory removeImportFixFactory = RemovePsiElementSimpleFix.createRemoveImportFactory();
|
||||||
factories.put(USELESS_SIMPLE_IMPORT, removeImportFixFactory);
|
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");
|
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")
|
@TestMetadata("extensionFunctionImport.before.Main.kt")
|
||||||
public void testExtensionFunctionImport() throws Exception {
|
public void testExtensionFunctionImport() throws Exception {
|
||||||
doTestWithExtraFile("idea/testData/quickfix/autoImports/extensionFunctionImport.before.Main.kt");
|
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");
|
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")
|
@TestMetadata("noImportForFunInQualifiedNotFirst.before.Main.kt")
|
||||||
public void testNoImportForFunInQualifiedNotFirst() throws Exception {
|
public void testNoImportForFunInQualifiedNotFirst() throws Exception {
|
||||||
doTestWithExtraFile("idea/testData/quickfix/autoImports/noImportForFunInQualifiedNotFirst.before.Main.kt");
|
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");
|
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")
|
@TestMetadata("idea/testData/quickfix/createFromUsage")
|
||||||
|
|||||||
Reference in New Issue
Block a user