KT-1645 Propose members according to smart casts
#KT-1645 fixed
This commit is contained in:
@@ -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<DeclarationDescriptor> descriptors = new HashSet<DeclarationDescriptor>();
|
||||
|
||||
DataFlowInfo info = context.get(BindingContext.NON_DEFAULT_EXPRESSION_DATA_FLOW, expression);
|
||||
if (info == null) {
|
||||
info = DataFlowInfo.EMPTY;
|
||||
}
|
||||
|
||||
AutoCastServiceImpl autoCastService = new AutoCastServiceImpl(info, context);
|
||||
List<ReceiverDescriptor> 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<DeclarationDescriptor> includeExternalCallableExtensions(
|
||||
private static Set<DeclarationDescriptor> includeExternalCallableExtensions(
|
||||
@NotNull Collection<DeclarationDescriptor> 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<DeclarationDescriptor>(descriptors);
|
||||
}
|
||||
|
||||
Set<DeclarationDescriptor> descriptorsSet = Sets.newHashSet(descriptors);
|
||||
|
||||
@@ -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<JetExpression, JetScope> RESOLUTION_SCOPE = Slices.createSimpleSlice();
|
||||
|
||||
/** Collected during analyze, used in IDE in auto-cast completion */
|
||||
WritableSlice<JetExpression, DataFlowInfo> NON_DEFAULT_EXPRESSION_DATA_FLOW = Slices.createSimpleSlice();
|
||||
|
||||
WritableSlice<JetExpression, Boolean> VARIABLE_REASSIGNMENT = Slices.createSimpleSetSlice();
|
||||
WritableSlice<ValueParameterDescriptor, Boolean> AUTO_CREATED_IT = Slices.createSimpleSetSlice();
|
||||
WritableSlice<JetExpression, DeclarationDescriptor> VARIABLE_ASSIGNMENT = Slices.createSimpleSlice();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
+6
-2
@@ -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<ReceiverDescriptor> getAutoCastVariants(@NotNull final BindingContext bindingContext, @NotNull final DataFlowInfo dataFlowInfo, @NotNull ReceiverDescriptor receiverToCast) {
|
||||
public static List<ReceiverDescriptor> getAutoCastVariants(
|
||||
@NotNull final BindingContext bindingContext,
|
||||
@NotNull final DataFlowInfo dataFlowInfo, @NotNull ReceiverDescriptor receiverToCast
|
||||
) {
|
||||
return receiverToCast.accept(new ReceiverDescriptorVisitor<List<ReceiverDescriptor>, Object>() {
|
||||
@Override
|
||||
public List<ReceiverDescriptor> 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<ReceiverDescriptor> result = Lists.newArrayList();
|
||||
for (JetType possibleType : dataFlowInfo.getPossibleTypes(dataFlowValue)) {
|
||||
result.add(new AutoCastReceiver(receiver, possibleType, dataFlowValue.isStableIdentifier()));
|
||||
|
||||
+7
-1
@@ -50,7 +50,9 @@ public class DataFlowInfo {
|
||||
}
|
||||
};
|
||||
|
||||
public static DataFlowInfo EMPTY = new DataFlowInfo(ImmutableMap.<DataFlowValue, Nullability>of(), Multimaps.newListMultimap(Collections.<DataFlowValue, Collection<JetType>>emptyMap(), CommonSuppliers.<JetType>getArrayListSupplier()));
|
||||
public static DataFlowInfo EMPTY = new DataFlowInfo(
|
||||
ImmutableMap.<DataFlowValue, Nullability>of(),
|
||||
Multimaps.newListMultimap(Collections.<DataFlowValue, Collection<JetType>>emptyMap(), CommonSuppliers.<JetType>getArrayListSupplier()));
|
||||
|
||||
private final ImmutableMap<DataFlowValue, Nullability> 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 {
|
||||
|
||||
+8
@@ -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) {
|
||||
|
||||
+5
-2
@@ -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<DataFlowInfo> result = new Ref<DataFlowInfo>(context.dataFlowInfo);
|
||||
pattern.accept(new JetVisitorVoid() {
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
trait Expr
|
||||
class Num(val value : Int) : Expr
|
||||
|
||||
fun eval(e : Expr) {
|
||||
if (e is Num) {
|
||||
return e.<caret>
|
||||
}
|
||||
}
|
||||
|
||||
// EXIST: value
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
class Expr {}
|
||||
class Num : Expr() {
|
||||
fun testing() {}
|
||||
}
|
||||
|
||||
fun eval(e : Expr) {
|
||||
if (e is Num) {
|
||||
return e.<caret>()
|
||||
}
|
||||
}
|
||||
|
||||
// EXIST: testing
|
||||
@@ -0,0 +1,11 @@
|
||||
trait Expr {
|
||||
public fun testThis() {
|
||||
if (this is Num) {
|
||||
this.<caret>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Num(val toCheck : Int) : Expr
|
||||
|
||||
// EXIST: toCheck
|
||||
@@ -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.<caret>
|
||||
}
|
||||
|
||||
// EXIST: left, right
|
||||
@@ -0,0 +1,11 @@
|
||||
class TestClass {
|
||||
public fun testMethod() {
|
||||
}
|
||||
}
|
||||
|
||||
fun testFun() {
|
||||
val lambda = {() -> TestClass() }
|
||||
lambda().<caret>
|
||||
}
|
||||
|
||||
// EXIST: testMethod
|
||||
@@ -1,13 +1,13 @@
|
||||
package something
|
||||
|
||||
class SomeTempClass {
|
||||
fun testSome() {
|
||||
fun helloWorld() {
|
||||
|
||||
}
|
||||
|
||||
fun test() {
|
||||
test<caret>()
|
||||
hello<caret>()
|
||||
}
|
||||
}
|
||||
|
||||
// EXIST: test, testSome
|
||||
// EXIST: helloWorld
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user