KT-4976 Completion ignores smart casts

#KT-4976 Fixed
 #KT-5718 Fixed
This commit is contained in:
Valentin Kipyatkov
2014-09-01 22:05:02 +04:00
parent c79204a41d
commit 855cc2443f
11 changed files with 109 additions and 32 deletions
@@ -33,6 +33,8 @@ import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.*; import org.jetbrains.jet.lang.resolve.*;
import org.jetbrains.jet.lang.resolve.calls.CallResolver; 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.callUtil.CallUtilPackage;
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintPosition; import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintPosition;
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem; 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.psi.PsiPackage.JetPsiFactory;
import static org.jetbrains.jet.lang.resolve.BindingContext.*; import static org.jetbrains.jet.lang.resolve.BindingContext.*;
import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType; import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType;
import static org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage.getDataFlowInfo;
public class ExpressionTypingUtils { public class ExpressionTypingUtils {
@@ -199,7 +202,8 @@ public class ExpressionTypingUtils {
@NotNull JetExpression receiverExpression, @NotNull JetExpression receiverExpression,
@NotNull JetType receiverType, @NotNull JetType receiverType,
@NotNull JetScope scope, @NotNull JetScope scope,
@NotNull ModuleDescriptor module @NotNull ModuleDescriptor module,
@NotNull BindingContext bindingContext
) { ) {
JetImportDirective importDirective = JetPsiFactory(receiverExpression).createImportDirective(callableFQN.asString()); JetImportDirective importDirective = JetPsiFactory(receiverExpression).createImportDirective(callableFQN.asString());
@@ -209,11 +213,13 @@ public class ExpressionTypingUtils {
List<CallableDescriptor> callableExtensionDescriptors = new ArrayList<CallableDescriptor>(); List<CallableDescriptor> callableExtensionDescriptors = new ArrayList<CallableDescriptor>();
ReceiverValue receiverValue = new ExpressionReceiver(receiverExpression, receiverType); ReceiverValue receiverValue = new ExpressionReceiver(receiverExpression, receiverType);
DataFlowInfo dataFlowInfo = getDataFlowInfo(bindingContext, receiverExpression);
for (DeclarationDescriptor declarationDescriptor : declarationDescriptors) { for (DeclarationDescriptor declarationDescriptor : declarationDescriptors) {
if (declarationDescriptor instanceof CallableDescriptor) { if (declarationDescriptor instanceof CallableDescriptor) {
CallableDescriptor callableDescriptor = (CallableDescriptor) declarationDescriptor; CallableDescriptor callableDescriptor = (CallableDescriptor) declarationDescriptor;
if (checkIsExtensionCallable(receiverValue, callableDescriptor)) { if (checkIsExtensionCallable(receiverValue, callableDescriptor, bindingContext, dataFlowInfo)) {
callableExtensionDescriptors.add(callableDescriptor); callableExtensionDescriptors.add(callableDescriptor);
} }
} }
@@ -227,15 +233,20 @@ public class ExpressionTypingUtils {
*/ */
public static boolean checkIsExtensionCallable ( public static boolean checkIsExtensionCallable (
@NotNull ReceiverValue receiverArgument, @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; for (JetType type : types) {
if (type.isNullable()) { if (checkReceiverResolution(receiverArgument, type, callableDescriptor)) return true;
JetType notNullableType = TypeUtils.makeNotNullable(type); if (type.isNullable()) {
if (checkReceiverResolution(receiverArgument, notNullableType, callableDescriptor)) return true; JetType notNullableType = TypeUtils.makeNotNullable(type);
if (checkReceiverResolution(receiverArgument, notNullableType, callableDescriptor)) return true;
}
} }
return false; return false;
} }
@@ -158,7 +158,7 @@ public class KotlinIndicesHelper(private val project: Project) {
return allFqNames return allFqNames
.filter { nameFilter(it.shortName().asString()) } .filter { nameFilter(it.shortName().asString()) }
.toSet() .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> { 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>(); Set<DeclarationDescriptor> descriptors = new HashSet<DeclarationDescriptor>();
// Process as call expression // Process as call expression
JetScope resolutionScope = context.get(BindingContext.RESOLUTION_SCOPE, expression); JetScope resolutionScope = context.get(BindingContext.RESOLUTION_SCOPE, expression);
if (resolutionScope == null) return descriptors;
Qualifier qualifier = context.get(BindingContext.QUALIFIER, receiverExpression); 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) // 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); 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); ExpressionReceiver receiverValue = new ExpressionReceiver(receiverExpression, expressionType);
DataFlowInfo info = getDataFlowInfo(context, expression); DataFlowInfo info = getDataFlowInfo(context, expression);
List<JetType> variantsForExplicitReceiver = AutoCastUtils.getAutoCastVariants(receiverValue, context, info); List<JetType> variantsForExplicitReceiver = AutoCastUtils.getAutoCastVariants(receiverValue, context, info);
for (JetType variant : variantsForExplicitReceiver) { for (JetType variant : variantsForExplicitReceiver) {
descriptors.addAll(includeExternalCallableExtensions( descriptors.addAll(excludePrivateDescriptors(variant.getMemberScope().getAllDescriptors()));
excludePrivateDescriptors(variant.getMemberScope().getAllDescriptors()),
resolutionScope, receiverValue));
} }
descriptors.addAll(externalCallableExtensions(resolutionScope, receiverValue, context, info));
} }
return descriptors; return descriptors;
} }
else { else {
@@ -102,7 +105,10 @@ public final class TipsManager {
} }
descriptorsSet.addAll(resolutionScope.getAllDescriptors()); descriptorsSet.addAll(resolutionScope.getAllDescriptors());
return excludeNotCallableExtensions(excludePrivateDescriptors(descriptorsSet), resolutionScope);
DataFlowInfo info = getDataFlowInfo(context, expression);
return excludeNotCallableExtensions(excludePrivateDescriptors(descriptorsSet), resolutionScope, context, info);
} }
} }
return Collections.emptyList(); return Collections.emptyList();
@@ -138,7 +144,10 @@ public final class TipsManager {
} }
public static Collection<DeclarationDescriptor> excludeNotCallableExtensions( 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); Set<DeclarationDescriptor> descriptorsSet = Sets.newHashSet(descriptors);
@@ -152,7 +161,7 @@ public final class TipsManager {
return false; return false;
} }
for (ReceiverParameterDescriptor receiverDescriptor : result) { for (ReceiverParameterDescriptor receiverDescriptor : result) {
if (ExpressionTypingUtils.checkIsExtensionCallable(receiverDescriptor.getValue(), callableDescriptor)) { if (ExpressionTypingUtils.checkIsExtensionCallable(receiverDescriptor.getValue(), callableDescriptor, context, dataFlowInfo)) {
return false; return false;
} }
} }
@@ -184,22 +193,18 @@ public final class TipsManager {
}); });
} }
private static Set<DeclarationDescriptor> includeExternalCallableExtensions( private static Collection<CallableDescriptor> externalCallableExtensions(
@NotNull Collection<DeclarationDescriptor> descriptors,
@NotNull JetScope externalScope, @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); return Collections2.filter(JetScopeUtils.getAllExtensions(externalScope),
descriptorsSet.addAll(
Collections2.filter(JetScopeUtils.getAllExtensions(externalScope),
new Predicate<CallableDescriptor>() { new Predicate<CallableDescriptor>() {
@Override @Override
public boolean apply(CallableDescriptor callableDescriptor) { 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.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils; 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.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.types.expressions.ExpressionTypingComponents; import org.jetbrains.jet.lang.types.expressions.ExpressionTypingComponents;
import org.jetbrains.jet.plugin.caches.resolve.ResolvePackage; import org.jetbrains.jet.plugin.caches.resolve.ResolvePackage;
@@ -46,6 +47,8 @@ import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import static org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage.getDataFlowInfo;
public abstract class BaseJetVariableMacro extends Macro { public abstract class BaseJetVariableMacro extends Macro {
@Nullable @Nullable
private JetNamedDeclaration[] getVariables(Expression[] params, ExpressionContext context) { private JetNamedDeclaration[] getVariables(Expression[] params, ExpressionContext context) {
@@ -62,8 +65,8 @@ public abstract class BaseJetVariableMacro extends Macro {
ResolveSessionForBodies resolveSession = ResolvePackage.getLazyResolveSession((JetFile) psiFile); ResolveSessionForBodies resolveSession = ResolvePackage.getLazyResolveSession((JetFile) psiFile);
BindingContext bc = resolveSession.resolveToElement(contextExpression); BindingContext bindingContext = resolveSession.resolveToElement(contextExpression);
JetScope scope = bc.get(BindingContext.RESOLUTION_SCOPE, contextExpression); JetScope scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, contextExpression);
if (scope == null) { if (scope == null) {
return null; return null;
} }
@@ -81,8 +84,10 @@ public abstract class BaseJetVariableMacro extends Macro {
} }
} }
DataFlowInfo dataFlowInfo = getDataFlowInfo(bindingContext, contextExpression);
List<JetNamedDeclaration> declarations = new ArrayList<JetNamedDeclaration>(); 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); PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(declarationDescriptor);
assert declaration == null || declaration instanceof PsiNamedElement; assert declaration == null || declaration instanceof PsiNamedElement;
@@ -0,0 +1,4 @@
package other
public val String.extensionPropNotImported: Int
get() = 1
@@ -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/"); doTest("idea/testData/completion/basic/multifile/ExtensionOnNullable/");
} }
@TestMetadata("ExtensionsForAutoCasted")
public void testExtensionsForAutoCasted() throws Exception {
doTest("idea/testData/completion/basic/multifile/ExtensionsForAutoCasted/");
}
@TestMetadata("GroovyClassNameCompletionFromDefaultPackage") @TestMetadata("GroovyClassNameCompletionFromDefaultPackage")
public void testGroovyClassNameCompletionFromDefaultPackage() throws Exception { public void testGroovyClassNameCompletionFromDefaultPackage() throws Exception {
doTest("idea/testData/completion/basic/multifile/GroovyClassNameCompletionFromDefaultPackage/"); 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"); 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") @TestMetadata("idea/testData/quickfix/createFromUsage")