diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java index e1df0aefd4c..7aa35d4f809 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java @@ -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 callableExtensionDescriptors = new ArrayList(); 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 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; } diff --git a/idea/src/org/jetbrains/jet/plugin/caches/KotlinIndicesHelper.kt b/idea/src/org/jetbrains/jet/plugin/caches/KotlinIndicesHelper.kt index 8653f68a7da..ed0ed2b421a 100644 --- a/idea/src/org/jetbrains/jet/plugin/caches/KotlinIndicesHelper.kt +++ b/idea/src/org/jetbrains/jet/plugin/caches/KotlinIndicesHelper.kt @@ -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 { diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/TipsManager.java b/idea/src/org/jetbrains/jet/plugin/codeInsight/TipsManager.java index d72b2395649..322788d35e5 100644 --- a/idea/src/org/jetbrains/jet/plugin/codeInsight/TipsManager.java +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/TipsManager.java @@ -58,26 +58,29 @@ public final class TipsManager { Set descriptors = new HashSet(); // 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(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 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 excludeNotCallableExtensions( - @NotNull Collection descriptors, @NotNull JetScope scope + @NotNull Collection descriptors, + @NotNull JetScope scope, + @NotNull final BindingContext context, + @NotNull final DataFlowInfo dataFlowInfo ) { Set 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 includeExternalCallableExtensions( - @NotNull Collection descriptors, + private static Collection externalCallableExtensions( @NotNull JetScope externalScope, - @NotNull final ReceiverValue receiverValue + @NotNull final ReceiverValue receiverValue, + @NotNull final BindingContext context, + @NotNull final DataFlowInfo dataFlowInfo ) { - Set descriptorsSet = Sets.newHashSet(descriptors); - - descriptorsSet.addAll( - Collections2.filter(JetScopeUtils.getAllExtensions(externalScope), + return Collections2.filter(JetScopeUtils.getAllExtensions(externalScope), new Predicate() { @Override public boolean apply(CallableDescriptor callableDescriptor) { - return ExpressionTypingUtils.checkIsExtensionCallable(receiverValue, callableDescriptor); + return ExpressionTypingUtils.checkIsExtensionCallable(receiverValue, callableDescriptor, context, dataFlowInfo); } - })); - - return descriptorsSet; + }); } } diff --git a/idea/src/org/jetbrains/jet/plugin/liveTemplates/macro/BaseJetVariableMacro.java b/idea/src/org/jetbrains/jet/plugin/liveTemplates/macro/BaseJetVariableMacro.java index bd9e3c4988b..0a4394e480a 100644 --- a/idea/src/org/jetbrains/jet/plugin/liveTemplates/macro/BaseJetVariableMacro.java +++ b/idea/src/org/jetbrains/jet/plugin/liveTemplates/macro/BaseJetVariableMacro.java @@ -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 declarations = new ArrayList(); - 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; diff --git a/idea/testData/completion/basic/multifile/ExtensionsForAutoCasted/ExtensionsForAutoCasted.dependency.kt b/idea/testData/completion/basic/multifile/ExtensionsForAutoCasted/ExtensionsForAutoCasted.dependency.kt new file mode 100644 index 00000000000..e385da1cc33 --- /dev/null +++ b/idea/testData/completion/basic/multifile/ExtensionsForAutoCasted/ExtensionsForAutoCasted.dependency.kt @@ -0,0 +1,4 @@ +package other + +public val String.extensionPropNotImported: Int + get() = 1 diff --git a/idea/testData/completion/basic/multifile/ExtensionsForAutoCasted/ExtensionsForAutoCasted.kt b/idea/testData/completion/basic/multifile/ExtensionsForAutoCasted/ExtensionsForAutoCasted.kt new file mode 100644 index 00000000000..6eed5a2ba0d --- /dev/null +++ b/idea/testData/completion/basic/multifile/ExtensionsForAutoCasted/ExtensionsForAutoCasted.kt @@ -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 +} + +// EXIST: extensionProp1 +// EXIST: extensionProp2 +// EXIST: extensionPropNotImported diff --git a/idea/testData/quickfix/autoImports/withAutoCastedQualifier.after.kt b/idea/testData/quickfix/autoImports/withAutoCastedQualifier.after.kt new file mode 100644 index 00000000000..387a829bdf1 --- /dev/null +++ b/idea/testData/quickfix/autoImports/withAutoCastedQualifier.after.kt @@ -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.foo() + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/autoImports/withAutoCastedQualifier.before.Main.kt b/idea/testData/quickfix/autoImports/withAutoCastedQualifier.before.Main.kt new file mode 100644 index 00000000000..d95c4b6d14e --- /dev/null +++ b/idea/testData/quickfix/autoImports/withAutoCastedQualifier.before.Main.kt @@ -0,0 +1,12 @@ +// "Import" "true" +// ERROR: Unresolved reference: foo + +package c + +class B + +fun test(a: Any) { + if (a is B) { + a.foo() + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/autoImports/withAutoCastedQualifier.before.data.Sample.kt b/idea/testData/quickfix/autoImports/withAutoCastedQualifier.before.data.Sample.kt new file mode 100644 index 00000000000..4e1e3d654c4 --- /dev/null +++ b/idea/testData/quickfix/autoImports/withAutoCastedQualifier.before.data.Sample.kt @@ -0,0 +1,5 @@ +package g + +import c.B + +fun B.foo() {} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/completion/MultiFileJvmBasicCompletionTestGenerated.java b/idea/tests/org/jetbrains/jet/completion/MultiFileJvmBasicCompletionTestGenerated.java index c423fac8a15..f950a32c2ff 100644 --- a/idea/tests/org/jetbrains/jet/completion/MultiFileJvmBasicCompletionTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/completion/MultiFileJvmBasicCompletionTestGenerated.java @@ -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/"); diff --git a/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixMultiFileTestGenerated.java index 3be6fa55225..ec12e41c8bb 100644 --- a/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixMultiFileTestGenerated.java @@ -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")