diff --git a/compiler/backend/src/org/jetbrains/jet/compiler/TipsManager.java b/compiler/backend/src/org/jetbrains/jet/compiler/TipsManager.java index 6e1466ae670..8b6cb8c4a16 100644 --- a/compiler/backend/src/org/jetbrains/jet/compiler/TipsManager.java +++ b/compiler/backend/src/org/jetbrains/jet/compiler/TipsManager.java @@ -30,6 +30,8 @@ import org.jetbrains.jet.lang.psi.JetImportDirective; import org.jetbrains.jet.lang.psi.JetNamespaceHeader; import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastServiceImpl; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.resolve.scopes.JetScopeUtils; import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; @@ -53,10 +55,31 @@ public final class TipsManager { JetExpression receiverExpression = expression.getReceiverExpression(); if (receiverExpression != null) { // Process as call expression - final JetScope resolutionScope = context.get(BindingContext.RESOLUTION_SCOPE, receiverExpression); + final JetScope resolutionScope = context.get(BindingContext.RESOLUTION_SCOPE, expression); final JetType expressionType = context.get(BindingContext.EXPRESSION_TYPE, receiverExpression); if (expressionType != null && resolutionScope != null) { + if (!(expressionType instanceof NamespaceType)) { + ExpressionReceiver receiverDescriptor = new ExpressionReceiver(receiverExpression, expressionType); + Set descriptors = new HashSet(); + + DataFlowInfo info = context.get(BindingContext.NON_DEFAULT_EXPRESSION_DATA_FLOW, expression); + if (info == null) { + info = DataFlowInfo.EMPTY; + } + + AutoCastServiceImpl autoCastService = new AutoCastServiceImpl(info, context); + List variantsForExplicitReceiver = autoCastService.getVariantsForReceiver(receiverDescriptor); + + for (ReceiverDescriptor descriptor : variantsForExplicitReceiver) { + descriptors.addAll(includeExternalCallableExtensions( + excludePrivateDescriptors(descriptor.getType().getMemberScope().getAllDescriptors()), + resolutionScope, descriptor)); + } + + return descriptors; + } + return includeExternalCallableExtensions( excludePrivateDescriptors(expressionType.getMemberScope().getAllDescriptors()), resolutionScope, new ExpressionReceiver(receiverExpression, expressionType)); @@ -161,7 +184,7 @@ public final class TipsManager { }); } - private static Collection includeExternalCallableExtensions( + private static Set includeExternalCallableExtensions( @NotNull Collection descriptors, @NotNull final JetScope externalScope, @NotNull final ReceiverDescriptor receiverDescriptor @@ -169,7 +192,7 @@ public final class TipsManager { // It's impossible to add extension function for namespace JetType receiverType = receiverDescriptor.getType(); if (receiverType instanceof NamespaceType) { - return descriptors; + return new HashSet(descriptors); } Set descriptorsSet = Sets.newHashSet(descriptors); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java index e733a887eda..5ebff0f781e 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java @@ -24,6 +24,7 @@ import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.jet.lang.diagnostics.Diagnostic; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.types.DeferredType; @@ -32,7 +33,6 @@ import org.jetbrains.jet.util.Box; import org.jetbrains.jet.util.slicedmap.*; import java.util.Collection; -import java.util.Set; import static org.jetbrains.jet.util.slicedmap.RewritePolicy.DO_NOTHING; @@ -63,6 +63,9 @@ public interface BindingContext { /** A scope where type of expression has been resolved */ WritableSlice RESOLUTION_SCOPE = Slices.createSimpleSlice(); + /** Collected during analyze, used in IDE in auto-cast completion */ + WritableSlice NON_DEFAULT_EXPRESSION_DATA_FLOW = Slices.createSimpleSlice(); + WritableSlice VARIABLE_REASSIGNMENT = Slices.createSimpleSetSlice(); WritableSlice AUTO_CREATED_IT = Slices.createSimpleSetSlice(); WritableSlice VARIABLE_ASSIGNMENT = Slices.createSimpleSlice(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java index e77c9c43acb..adb1d676230 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java @@ -358,7 +358,9 @@ public class BodyResolver { public void visitDelegationToSuperCallSpecifier(JetDelegatorToSuperCall call) { JetTypeReference typeReference = call.getTypeReference(); if (typeReference != null) { - callResolver.resolveFunctionCall(trace, scopeForSupertypeInitializers, CallMaker.makeCall(ReceiverDescriptor.NO_RECEIVER, null, call), NO_EXPECTED_TYPE, dataFlowInfo); + callResolver.resolveFunctionCall(trace, scopeForSupertypeInitializers, + CallMaker.makeCall(ReceiverDescriptor.NO_RECEIVER, null, call), + NO_EXPECTED_TYPE, dataFlowInfo); } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java index f5cc258199c..bb344def4ee 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java @@ -28,7 +28,10 @@ import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.*; import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastServiceImpl; import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; -import org.jetbrains.jet.lang.resolve.calls.inference.*; +import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem; +import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemSolution; +import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemWithPriorities; +import org.jetbrains.jet.lang.resolve.calls.inference.DebugConstraintResolutionListener; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; @@ -42,8 +45,7 @@ import javax.inject.Inject; import java.util.*; import static org.jetbrains.jet.lang.diagnostics.Errors.*; -import static org.jetbrains.jet.lang.resolve.BindingContext.AUTOCAST; -import static org.jetbrains.jet.lang.resolve.BindingContext.RESOLUTION_SCOPE; +import static org.jetbrains.jet.lang.resolve.BindingContext.*; import static org.jetbrains.jet.lang.resolve.calls.ResolutionStatus.*; import static org.jetbrains.jet.lang.resolve.calls.ResolvedCallImpl.MAP_TO_CANDIDATE; import static org.jetbrains.jet.lang.resolve.calls.ResolvedCallImpl.MAP_TO_RESULT; @@ -255,6 +257,10 @@ public class CallResolver { context.trace.record(ResolutionDebugInfo.RESOLUTION_DEBUG_INFO, context.call.getCallElement(), debugInfo); context.trace.record(RESOLUTION_SCOPE, context.call.getCalleeExpression(), context.scope); + if (context.dataFlowInfo.hasTypeInfoConstraints()) { + context.trace.record(NON_DEFAULT_EXPRESSION_DATA_FLOW, context.call.getCalleeExpression(), context.dataFlowInfo); + } + debugInfo.set(ResolutionDebugInfo.TASKS, prioritizedTasks); TemporaryBindingTrace traceForFirstNonemptyCandidateSet = null; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastUtils.java index 688b1c8ad88..44d4277b840 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastUtils.java @@ -36,7 +36,10 @@ public class AutoCastUtils { /** * @return variants @param receiverToCast may be cast to according to @param dataFlowInfo, @param receiverToCast itself is NOT included */ - public static List getAutoCastVariants(@NotNull final BindingContext bindingContext, @NotNull final DataFlowInfo dataFlowInfo, @NotNull ReceiverDescriptor receiverToCast) { + public static List getAutoCastVariants( + @NotNull final BindingContext bindingContext, + @NotNull final DataFlowInfo dataFlowInfo, @NotNull ReceiverDescriptor receiverToCast + ) { return receiverToCast.accept(new ReceiverDescriptorVisitor, Object>() { @Override public List visitNoReceiver(ReceiverDescriptor noReceiver, Object data) { @@ -72,7 +75,8 @@ public class AutoCastUtils { // else if (expression instanceof JetThisExpression) { // return castThis(dataFlowInfo, receiver); // } - DataFlowValue dataFlowValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(receiver.getExpression(),receiver.getType(), bindingContext); + DataFlowValue dataFlowValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(receiver.getExpression(),receiver.getType(), + bindingContext); List result = Lists.newArrayList(); for (JetType possibleType : dataFlowInfo.getPossibleTypes(dataFlowValue)) { result.add(new AutoCastReceiver(receiver, possibleType, dataFlowValue.isStableIdentifier())); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowInfo.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowInfo.java index da2db981af3..ab56ec113ed 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowInfo.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowInfo.java @@ -50,7 +50,9 @@ public class DataFlowInfo { } }; - public static DataFlowInfo EMPTY = new DataFlowInfo(ImmutableMap.of(), Multimaps.newListMultimap(Collections.>emptyMap(), CommonSuppliers.getArrayListSupplier())); + public static DataFlowInfo EMPTY = new DataFlowInfo( + ImmutableMap.of(), + Multimaps.newListMultimap(Collections.>emptyMap(), CommonSuppliers.getArrayListSupplier())); private final ImmutableMap nullabilityInfo; /** Also immutable */ @@ -195,6 +197,10 @@ public class DataFlowInfo { return new DataFlowInfo(ImmutableMap.copyOf(builder), newTypeInfo); } + public boolean hasTypeInfoConstraints() { + return !typeInfo.isEmpty(); + } + } //public class DataFlowInfo { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java index cc14ac19b04..66059c490fe 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java @@ -617,6 +617,13 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { temporaryTrace.commit(); return null; } + + // Uncommitted changes in temp context + context.trace.record(RESOLUTION_SCOPE, nameExpression, context.scope); + if (context.dataFlowInfo.hasTypeInfoConstraints()) { + context.trace.record(NON_DEFAULT_EXPRESSION_DATA_FLOW, nameExpression, context.dataFlowInfo); + } + ExpressionTypingContext newContext = receiver.exists() ? context.replaceScope(receiver.getType().getMemberScope()) : context; @@ -624,6 +631,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { if (jetType == null) { context.trace.report(UNRESOLVED_REFERENCE.on(nameExpression)); } + return jetType; } else if (selectorExpression instanceof JetQualifiedExpression) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java index df89ce15acc..f68e296f64f 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java @@ -59,7 +59,8 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { JetPattern pattern = expression.getPattern(); if (pattern != null) { WritableScopeImpl scopeToExtend = newWritableScopeImpl(context).setDebugName("Scope extended in 'is'"); - DataFlowInfo newDataFlowInfo = checkPatternType(pattern, knownType, false, scopeToExtend, context, DataFlowValueFactory.INSTANCE.createDataFlowValue(leftHandSide, knownType, context.trace.getBindingContext())); + DataFlowValue dataFlowValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(leftHandSide, knownType, context.trace.getBindingContext()); + DataFlowInfo newDataFlowInfo = checkPatternType(pattern, knownType, false, scopeToExtend, context, dataFlowValue); context.patternsToDataFlowInfo.put(pattern, newDataFlowInfo); context.patternsToBoundVariableLists.put(pattern, scopeToExtend.getDeclaredVariables()); } @@ -175,7 +176,9 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { return newDataFlowInfo[0]; } - private DataFlowInfo checkPatternType(@NotNull JetPattern pattern, @NotNull final JetType subjectType, final boolean conditionExpected, @NotNull final WritableScope scopeToExtend, final ExpressionTypingContext context, @NotNull final DataFlowValue... subjectVariables) { + private DataFlowInfo checkPatternType(@NotNull JetPattern pattern, @NotNull final JetType subjectType, final boolean conditionExpected, + @NotNull final WritableScope scopeToExtend, final ExpressionTypingContext context, @NotNull final DataFlowValue... subjectVariables + ) { final Ref result = new Ref(context.dataFlowInfo); pattern.accept(new JetVisitorVoid() { @Override diff --git a/idea/testData/completion/basic/AutoCastAfterIf.kt b/idea/testData/completion/basic/AutoCastAfterIf.kt new file mode 100644 index 00000000000..053908d80ad --- /dev/null +++ b/idea/testData/completion/basic/AutoCastAfterIf.kt @@ -0,0 +1,14 @@ +trait Expr +class Num(val value : Int) : Expr + +fun eval(e : Expr) { + if (e is Num) { + return e. + } +} + +// EXIST: value + + + + diff --git a/idea/testData/completion/basic/AutoCastAfterIfMethod.kt b/idea/testData/completion/basic/AutoCastAfterIfMethod.kt new file mode 100644 index 00000000000..d3ce17519fb --- /dev/null +++ b/idea/testData/completion/basic/AutoCastAfterIfMethod.kt @@ -0,0 +1,12 @@ +class Expr {} +class Num : Expr() { + fun testing() {} +} + +fun eval(e : Expr) { + if (e is Num) { + return e.() + } +} + +// EXIST: testing \ No newline at end of file diff --git a/idea/testData/completion/basic/AutoCastForThis.kt b/idea/testData/completion/basic/AutoCastForThis.kt new file mode 100644 index 00000000000..d77a1be3a7c --- /dev/null +++ b/idea/testData/completion/basic/AutoCastForThis.kt @@ -0,0 +1,11 @@ +trait Expr { + public fun testThis() { + if (this is Num) { + this. + } + } +} + +class Num(val toCheck : Int) : Expr + +// EXIST: toCheck \ No newline at end of file diff --git a/idea/testData/completion/basic/AutoCastInWhen.kt b/idea/testData/completion/basic/AutoCastInWhen.kt new file mode 100644 index 00000000000..4d5ce298e6b --- /dev/null +++ b/idea/testData/completion/basic/AutoCastInWhen.kt @@ -0,0 +1,8 @@ +trait Expr +class Sum(val left : Expr, val right : Expr) : Expr + +fun evalWhen(e : Expr) : Int = when (e) { + is Sum -> e. +} + +// EXIST: left, right \ No newline at end of file diff --git a/idea/testData/completion/basic/CallLocalLambda.kt b/idea/testData/completion/basic/CallLocalLambda.kt new file mode 100644 index 00000000000..2b80b2c859e --- /dev/null +++ b/idea/testData/completion/basic/CallLocalLambda.kt @@ -0,0 +1,11 @@ +class TestClass { + public fun testMethod() { + } +} + +fun testFun() { + val lambda = {() -> TestClass() } + lambda(). +} + +// EXIST: testMethod \ No newline at end of file diff --git a/idea/testData/completion/basic/InCallExpression.kt b/idea/testData/completion/basic/InCallExpression.kt index 3d8347cfd30..16fad2dbc1c 100644 --- a/idea/testData/completion/basic/InCallExpression.kt +++ b/idea/testData/completion/basic/InCallExpression.kt @@ -1,13 +1,13 @@ package something class SomeTempClass { - fun testSome() { + fun helloWorld() { } fun test() { - test() + hello() } } -// EXIST: test, testSome \ No newline at end of file +// EXIST: helloWorld \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/completion/JetBasicCompletionTest.java b/idea/tests/org/jetbrains/jet/completion/JetBasicCompletionTest.java index 811a6369d19..5cc1ece871a 100644 --- a/idea/tests/org/jetbrains/jet/completion/JetBasicCompletionTest.java +++ b/idea/tests/org/jetbrains/jet/completion/JetBasicCompletionTest.java @@ -25,6 +25,22 @@ import java.io.File; */ public class JetBasicCompletionTest extends JetCompletionTestBase { + public void testAutoCastAfterIf() { + doTest(); + } + + public void testAutoCastAfterIfMethod() { + doTest(); + } + + public void testAutoCastForThis() { + doTest(); + } + + public void testAutoCastInWhen() { + doTest(); + } + public void testBasicAny() { doTest(); } @@ -37,6 +53,10 @@ public class JetBasicCompletionTest extends JetCompletionTestBase { doTest(); } + public void testCallLocalLambda() { + doTest(); + } + public void testExtendClassName() { doTest(); }