KT-4976 Completion ignores smart casts
#KT-4976 Fixed #KT-5718 Fixed
This commit is contained in:
+19
-8
@@ -33,6 +33,8 @@ import org.jetbrains.jet.lang.diagnostics.Errors;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.*;
|
||||
import org.jetbrains.jet.lang.resolve.calls.CallResolver;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastUtils;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage;
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintPosition;
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem;
|
||||
@@ -60,6 +62,7 @@ import static org.jetbrains.jet.lang.diagnostics.Errors.*;
|
||||
import static org.jetbrains.jet.lang.psi.PsiPackage.JetPsiFactory;
|
||||
import static org.jetbrains.jet.lang.resolve.BindingContext.*;
|
||||
import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType;
|
||||
import static org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage.getDataFlowInfo;
|
||||
|
||||
public class ExpressionTypingUtils {
|
||||
|
||||
@@ -199,7 +202,8 @@ public class ExpressionTypingUtils {
|
||||
@NotNull JetExpression receiverExpression,
|
||||
@NotNull JetType receiverType,
|
||||
@NotNull JetScope scope,
|
||||
@NotNull ModuleDescriptor module
|
||||
@NotNull ModuleDescriptor module,
|
||||
@NotNull BindingContext bindingContext
|
||||
) {
|
||||
JetImportDirective importDirective = JetPsiFactory(receiverExpression).createImportDirective(callableFQN.asString());
|
||||
|
||||
@@ -209,11 +213,13 @@ public class ExpressionTypingUtils {
|
||||
List<CallableDescriptor> callableExtensionDescriptors = new ArrayList<CallableDescriptor>();
|
||||
ReceiverValue receiverValue = new ExpressionReceiver(receiverExpression, receiverType);
|
||||
|
||||
DataFlowInfo dataFlowInfo = getDataFlowInfo(bindingContext, receiverExpression);
|
||||
|
||||
for (DeclarationDescriptor declarationDescriptor : declarationDescriptors) {
|
||||
if (declarationDescriptor instanceof CallableDescriptor) {
|
||||
CallableDescriptor callableDescriptor = (CallableDescriptor) declarationDescriptor;
|
||||
|
||||
if (checkIsExtensionCallable(receiverValue, callableDescriptor)) {
|
||||
if (checkIsExtensionCallable(receiverValue, callableDescriptor, bindingContext, dataFlowInfo)) {
|
||||
callableExtensionDescriptors.add(callableDescriptor);
|
||||
}
|
||||
}
|
||||
@@ -227,15 +233,20 @@ public class ExpressionTypingUtils {
|
||||
*/
|
||||
public static boolean checkIsExtensionCallable (
|
||||
@NotNull ReceiverValue receiverArgument,
|
||||
@NotNull CallableDescriptor callableDescriptor
|
||||
@NotNull CallableDescriptor callableDescriptor,
|
||||
@NotNull BindingContext bindingContext,
|
||||
@NotNull DataFlowInfo dataFlowInfo
|
||||
) {
|
||||
JetType type = receiverArgument.getType();
|
||||
List<JetType> types = AutoCastUtils.getAutoCastVariants(receiverArgument, bindingContext, dataFlowInfo);
|
||||
|
||||
if (checkReceiverResolution(receiverArgument, type, callableDescriptor)) return true;
|
||||
if (type.isNullable()) {
|
||||
JetType notNullableType = TypeUtils.makeNotNullable(type);
|
||||
if (checkReceiverResolution(receiverArgument, notNullableType, callableDescriptor)) return true;
|
||||
for (JetType type : types) {
|
||||
if (checkReceiverResolution(receiverArgument, type, callableDescriptor)) return true;
|
||||
if (type.isNullable()) {
|
||||
JetType notNullableType = TypeUtils.makeNotNullable(type);
|
||||
if (checkReceiverResolution(receiverArgument, notNullableType, callableDescriptor)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ public class KotlinIndicesHelper(private val project: Project) {
|
||||
return allFqNames
|
||||
.filter { nameFilter(it.shortName().asString()) }
|
||||
.toSet()
|
||||
.flatMap { ExpressionTypingUtils.canFindSuitableCall(it, receiverExpression, expressionType, jetScope, resolveSession.getModuleDescriptor()) }
|
||||
.flatMap { ExpressionTypingUtils.canFindSuitableCall(it, receiverExpression, expressionType, jetScope, resolveSession.getModuleDescriptor(), context) }
|
||||
}
|
||||
|
||||
public fun getClassDescriptors(nameFilter: (String) -> Boolean, analyzer: KotlinCodeAnalyzer, scope: GlobalSearchScope): Collection<ClassDescriptor> {
|
||||
|
||||
@@ -58,26 +58,29 @@ public final class TipsManager {
|
||||
Set<DeclarationDescriptor> descriptors = new HashSet<DeclarationDescriptor>();
|
||||
// Process as call expression
|
||||
JetScope resolutionScope = context.get(BindingContext.RESOLUTION_SCOPE, expression);
|
||||
if (resolutionScope == null) return descriptors;
|
||||
|
||||
Qualifier qualifier = context.get(BindingContext.QUALIFIER, receiverExpression);
|
||||
if (qualifier != null && resolutionScope != null) {
|
||||
if (qualifier != null) {
|
||||
// It's impossible to add extension function for package or class (if it's class object, expression type is not null)
|
||||
descriptors.addAll(new HashSet<DeclarationDescriptor>(excludePrivateDescriptors(qualifier.getScope().getAllDescriptors())));
|
||||
descriptors.addAll(excludePrivateDescriptors(qualifier.getScope().getAllDescriptors()));
|
||||
}
|
||||
|
||||
JetType expressionType = context.get(BindingContext.EXPRESSION_TYPE, receiverExpression);
|
||||
if (expressionType != null && resolutionScope != null && !expressionType.isError()) {
|
||||
if (expressionType != null && !expressionType.isError()) {
|
||||
ExpressionReceiver receiverValue = new ExpressionReceiver(receiverExpression, expressionType);
|
||||
|
||||
DataFlowInfo info = getDataFlowInfo(context, expression);
|
||||
|
||||
List<JetType> variantsForExplicitReceiver = AutoCastUtils.getAutoCastVariants(receiverValue, context, info);
|
||||
|
||||
for (JetType variant : variantsForExplicitReceiver) {
|
||||
descriptors.addAll(includeExternalCallableExtensions(
|
||||
excludePrivateDescriptors(variant.getMemberScope().getAllDescriptors()),
|
||||
resolutionScope, receiverValue));
|
||||
descriptors.addAll(excludePrivateDescriptors(variant.getMemberScope().getAllDescriptors()));
|
||||
}
|
||||
|
||||
descriptors.addAll(externalCallableExtensions(resolutionScope, receiverValue, context, info));
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
else {
|
||||
@@ -102,7 +105,10 @@ public final class TipsManager {
|
||||
}
|
||||
|
||||
descriptorsSet.addAll(resolutionScope.getAllDescriptors());
|
||||
return excludeNotCallableExtensions(excludePrivateDescriptors(descriptorsSet), resolutionScope);
|
||||
|
||||
DataFlowInfo info = getDataFlowInfo(context, expression);
|
||||
|
||||
return excludeNotCallableExtensions(excludePrivateDescriptors(descriptorsSet), resolutionScope, context, info);
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
@@ -138,7 +144,10 @@ public final class TipsManager {
|
||||
}
|
||||
|
||||
public static Collection<DeclarationDescriptor> excludeNotCallableExtensions(
|
||||
@NotNull Collection<? extends DeclarationDescriptor> descriptors, @NotNull JetScope scope
|
||||
@NotNull Collection<? extends DeclarationDescriptor> descriptors,
|
||||
@NotNull JetScope scope,
|
||||
@NotNull final BindingContext context,
|
||||
@NotNull final DataFlowInfo dataFlowInfo
|
||||
) {
|
||||
Set<DeclarationDescriptor> descriptorsSet = Sets.newHashSet(descriptors);
|
||||
|
||||
@@ -152,7 +161,7 @@ public final class TipsManager {
|
||||
return false;
|
||||
}
|
||||
for (ReceiverParameterDescriptor receiverDescriptor : result) {
|
||||
if (ExpressionTypingUtils.checkIsExtensionCallable(receiverDescriptor.getValue(), callableDescriptor)) {
|
||||
if (ExpressionTypingUtils.checkIsExtensionCallable(receiverDescriptor.getValue(), callableDescriptor, context, dataFlowInfo)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -184,22 +193,18 @@ public final class TipsManager {
|
||||
});
|
||||
}
|
||||
|
||||
private static Set<DeclarationDescriptor> includeExternalCallableExtensions(
|
||||
@NotNull Collection<DeclarationDescriptor> descriptors,
|
||||
private static Collection<CallableDescriptor> externalCallableExtensions(
|
||||
@NotNull JetScope externalScope,
|
||||
@NotNull final ReceiverValue receiverValue
|
||||
@NotNull final ReceiverValue receiverValue,
|
||||
@NotNull final BindingContext context,
|
||||
@NotNull final DataFlowInfo dataFlowInfo
|
||||
) {
|
||||
Set<DeclarationDescriptor> descriptorsSet = Sets.newHashSet(descriptors);
|
||||
|
||||
descriptorsSet.addAll(
|
||||
Collections2.filter(JetScopeUtils.getAllExtensions(externalScope),
|
||||
return Collections2.filter(JetScopeUtils.getAllExtensions(externalScope),
|
||||
new Predicate<CallableDescriptor>() {
|
||||
@Override
|
||||
public boolean apply(CallableDescriptor callableDescriptor) {
|
||||
return ExpressionTypingUtils.checkIsExtensionCallable(receiverValue, callableDescriptor);
|
||||
return ExpressionTypingUtils.checkIsExtensionCallable(receiverValue, callableDescriptor, context, dataFlowInfo);
|
||||
}
|
||||
}));
|
||||
|
||||
return descriptorsSet;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
||||
import org.jetbrains.jet.lang.types.expressions.ExpressionTypingComponents;
|
||||
import org.jetbrains.jet.plugin.caches.resolve.ResolvePackage;
|
||||
@@ -46,6 +47,8 @@ import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage.getDataFlowInfo;
|
||||
|
||||
public abstract class BaseJetVariableMacro extends Macro {
|
||||
@Nullable
|
||||
private JetNamedDeclaration[] getVariables(Expression[] params, ExpressionContext context) {
|
||||
@@ -62,8 +65,8 @@ public abstract class BaseJetVariableMacro extends Macro {
|
||||
|
||||
ResolveSessionForBodies resolveSession = ResolvePackage.getLazyResolveSession((JetFile) psiFile);
|
||||
|
||||
BindingContext bc = resolveSession.resolveToElement(contextExpression);
|
||||
JetScope scope = bc.get(BindingContext.RESOLUTION_SCOPE, contextExpression);
|
||||
BindingContext bindingContext = resolveSession.resolveToElement(contextExpression);
|
||||
JetScope scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, contextExpression);
|
||||
if (scope == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -81,8 +84,10 @@ public abstract class BaseJetVariableMacro extends Macro {
|
||||
}
|
||||
}
|
||||
|
||||
DataFlowInfo dataFlowInfo = getDataFlowInfo(bindingContext, contextExpression);
|
||||
|
||||
List<JetNamedDeclaration> declarations = new ArrayList<JetNamedDeclaration>();
|
||||
for (DeclarationDescriptor declarationDescriptor : TipsManager.excludeNotCallableExtensions(filteredDescriptors, scope)) {
|
||||
for (DeclarationDescriptor declarationDescriptor : TipsManager.excludeNotCallableExtensions(filteredDescriptors, scope, bindingContext, dataFlowInfo)) {
|
||||
PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(declarationDescriptor);
|
||||
assert declaration == null || declaration instanceof PsiNamedElement;
|
||||
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package other
|
||||
|
||||
public val String.extensionPropNotImported: Int
|
||||
get() = 1
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
val String.extensionProp1: Int get() = 1
|
||||
val String.extensionProp2: Int get() = 1
|
||||
|
||||
fun foo(o: Any) {
|
||||
if (o !is String) return
|
||||
o.ext<caret>
|
||||
}
|
||||
|
||||
// EXIST: extensionProp1
|
||||
// EXIST: extensionProp2
|
||||
// EXIST: extensionPropNotImported
|
||||
@@ -0,0 +1,14 @@
|
||||
// "Import" "true"
|
||||
// ERROR: Unresolved reference: foo
|
||||
|
||||
package c
|
||||
|
||||
import g.foo
|
||||
|
||||
class B
|
||||
|
||||
fun test(a: Any) {
|
||||
if (a is B) {
|
||||
a.<caret>foo()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// "Import" "true"
|
||||
// ERROR: Unresolved reference: foo
|
||||
|
||||
package c
|
||||
|
||||
class B
|
||||
|
||||
fun test(a: Any) {
|
||||
if (a is B) {
|
||||
a.<caret>foo()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package g
|
||||
|
||||
import c.B
|
||||
|
||||
fun B.foo() {}
|
||||
@@ -73,6 +73,11 @@ public class MultiFileJvmBasicCompletionTestGenerated extends AbstractMultiFileJ
|
||||
doTest("idea/testData/completion/basic/multifile/ExtensionOnNullable/");
|
||||
}
|
||||
|
||||
@TestMetadata("ExtensionsForAutoCasted")
|
||||
public void testExtensionsForAutoCasted() throws Exception {
|
||||
doTest("idea/testData/completion/basic/multifile/ExtensionsForAutoCasted/");
|
||||
}
|
||||
|
||||
@TestMetadata("GroovyClassNameCompletionFromDefaultPackage")
|
||||
public void testGroovyClassNameCompletionFromDefaultPackage() throws Exception {
|
||||
doTest("idea/testData/completion/basic/multifile/GroovyClassNameCompletionFromDefaultPackage/");
|
||||
|
||||
@@ -174,6 +174,11 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
|
||||
doTestWithExtraFile("idea/testData/quickfix/autoImports/unaryPlusOperator.before.Main.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("withAutoCastedQualifier.before.Main.kt")
|
||||
public void testWithAutoCastedQualifier() throws Exception {
|
||||
doTestWithExtraFile("idea/testData/quickfix/autoImports/withAutoCastedQualifier.before.Main.kt");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/createFromUsage")
|
||||
|
||||
Reference in New Issue
Block a user