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.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;
@@ -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/");
}
@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")