001 /*
002 * Copyright 2010-2015 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.kotlin.resolve.calls.smartcasts;
018
019 import com.google.common.collect.Lists;
020 import com.google.common.collect.Sets;
021 import kotlin.KotlinPackage;
022 import kotlin.jvm.functions.Function1;
023 import org.jetbrains.annotations.NotNull;
024 import org.jetbrains.annotations.Nullable;
025 import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
026 import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor;
027 import org.jetbrains.kotlin.psi.JetExpression;
028 import org.jetbrains.kotlin.resolve.BindingContext;
029 import org.jetbrains.kotlin.resolve.BindingTrace;
030 import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver;
031 import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
032 import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
033 import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
034 import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver;
035 import org.jetbrains.kotlin.types.JetType;
036 import org.jetbrains.kotlin.types.TypeUtils;
037 import org.jetbrains.kotlin.types.checker.JetTypeChecker;
038
039 import java.util.Collection;
040 import java.util.Collections;
041 import java.util.List;
042 import java.util.Set;
043
044 import static org.jetbrains.kotlin.diagnostics.Errors.SMARTCAST_IMPOSSIBLE;
045 import static org.jetbrains.kotlin.resolve.BindingContext.SMARTCAST;
046
047 public class SmartCastUtils {
048
049 private SmartCastUtils() {}
050
051 @NotNull
052 public static List<JetType> getSmartCastVariants(
053 @NotNull ReceiverValue receiverToCast,
054 @NotNull ResolutionContext context
055 ) {
056 return getSmartCastVariants(receiverToCast, context.trace.getBindingContext(), context.scope.getContainingDeclaration(), context.dataFlowInfo);
057 }
058
059 @NotNull
060 public static List<JetType> getSmartCastVariants(
061 @NotNull ReceiverValue receiverToCast,
062 @NotNull BindingContext bindingContext,
063 @NotNull DeclarationDescriptor containingDeclarationOrModule,
064 @NotNull DataFlowInfo dataFlowInfo
065 ) {
066 List<JetType> variants = Lists.newArrayList();
067 variants.add(receiverToCast.getType());
068 variants.addAll(getSmartCastVariantsExcludingReceiver(bindingContext, containingDeclarationOrModule, dataFlowInfo, receiverToCast));
069 return variants;
070 }
071
072 @NotNull
073 public static List<JetType> getSmartCastVariantsWithLessSpecificExcluded(
074 @NotNull ReceiverValue receiverToCast,
075 @NotNull BindingContext bindingContext,
076 @NotNull DeclarationDescriptor containingDeclarationOrModule,
077 @NotNull DataFlowInfo dataFlowInfo
078 ) {
079 final List<JetType> variants = getSmartCastVariants(receiverToCast, bindingContext,
080 containingDeclarationOrModule, dataFlowInfo);
081 return KotlinPackage.filter(variants, new Function1<JetType, Boolean>() {
082 @Override
083 public Boolean invoke(final JetType type) {
084 return !KotlinPackage.any(variants, new Function1<JetType, Boolean>() {
085 @Override
086 public Boolean invoke(JetType another) {
087 return another != type && JetTypeChecker.DEFAULT.isSubtypeOf(another, type);
088 }
089 });
090 }
091 });
092 }
093
094 /**
095 * @return variants @param receiverToCast may be cast to according to context dataFlowInfo, receiverToCast itself is NOT included
096 */
097 @NotNull
098 public static Collection<JetType> getSmartCastVariantsExcludingReceiver(
099 @NotNull ResolutionContext context,
100 @NotNull ReceiverValue receiverToCast
101 ) {
102 return getSmartCastVariantsExcludingReceiver(context.trace.getBindingContext(),
103 context.scope.getContainingDeclaration(),
104 context.dataFlowInfo,
105 receiverToCast);
106 }
107
108 /**
109 * @return variants @param receiverToCast may be cast to according to @param dataFlowInfo, @param receiverToCast itself is NOT included
110 */
111 @NotNull
112 public static Collection<JetType> getSmartCastVariantsExcludingReceiver(
113 @NotNull BindingContext bindingContext,
114 @NotNull DeclarationDescriptor containingDeclarationOrModule,
115 @NotNull DataFlowInfo dataFlowInfo,
116 @NotNull ReceiverValue receiverToCast
117 ) {
118 if (receiverToCast instanceof ThisReceiver) {
119 ThisReceiver receiver = (ThisReceiver) receiverToCast;
120 assert receiver.exists();
121 DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver);
122 return dataFlowInfo.getPossibleTypes(dataFlowValue);
123 }
124 else if (receiverToCast instanceof ExpressionReceiver) {
125 DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(
126 receiverToCast, bindingContext, containingDeclarationOrModule);
127 return dataFlowInfo.getPossibleTypes(dataFlowValue);
128 }
129 return Collections.emptyList();
130 }
131
132 public static boolean isSubTypeBySmartCastIgnoringNullability(
133 @NotNull ReceiverValue receiverArgument,
134 @NotNull JetType receiverParameterType,
135 @NotNull ResolutionContext context
136 ) {
137 List<JetType> smartCastTypes = getSmartCastVariants(receiverArgument, context);
138 return getSmartCastSubType(TypeUtils.makeNullable(receiverParameterType), smartCastTypes) != null;
139 }
140
141 @Nullable
142 private static JetType getSmartCastSubType(
143 @NotNull JetType receiverParameterType,
144 @NotNull Collection<JetType> smartCastTypes
145 ) {
146 Set<JetType> subTypes = Sets.newHashSet();
147 for (JetType smartCastType : smartCastTypes) {
148 if (ArgumentTypeResolver.isSubtypeOfForArgumentType(smartCastType, receiverParameterType)) {
149 subTypes.add(smartCastType);
150 }
151 }
152 if (subTypes.isEmpty()) return null;
153
154 JetType intersection = TypeUtils.intersect(JetTypeChecker.DEFAULT, subTypes);
155 if (intersection == null || !intersection.getConstructor().isDenotable()) {
156 return receiverParameterType;
157 }
158 return intersection;
159 }
160
161 // Returns false when we need smart cast but cannot do it, otherwise true
162 public static boolean recordSmartCastIfNecessary(
163 @NotNull ReceiverValue receiver,
164 @NotNull JetType receiverParameterType,
165 @NotNull ResolutionContext context,
166 boolean safeAccess
167 ) {
168 if (!(receiver instanceof ExpressionReceiver)) return true;
169
170 receiverParameterType = safeAccess ? TypeUtils.makeNullable(receiverParameterType) : receiverParameterType;
171 if (ArgumentTypeResolver.isSubtypeOfForArgumentType(receiver.getType(), receiverParameterType)) {
172 return true;
173 }
174
175 Collection<JetType> smartCastTypesExcludingReceiver = getSmartCastVariantsExcludingReceiver(context, receiver);
176 JetType smartCastSubType = getSmartCastSubType(receiverParameterType, smartCastTypesExcludingReceiver);
177 if (smartCastSubType == null) return true;
178
179 JetExpression expression = ((ExpressionReceiver) receiver).getExpression();
180 DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver, context);
181
182 return recordCastOrError(expression, smartCastSubType, context.trace, dataFlowValue.isPredictable(), true);
183 }
184
185 public static boolean recordCastOrError(
186 @NotNull JetExpression expression,
187 @NotNull JetType type,
188 @NotNull BindingTrace trace,
189 boolean canBeCast,
190 boolean recordExpressionType
191 ) {
192 if (canBeCast) {
193 trace.record(SMARTCAST, expression, type);
194 if (recordExpressionType) {
195 //TODO
196 //Why the expression type is rewritten for receivers and is not rewritten for arguments? Is it necessary?
197 trace.recordType(expression, type);
198 }
199 }
200 else {
201 trace.report(SMARTCAST_IMPOSSIBLE.on(expression, type, expression.getText()));
202 }
203 return canBeCast;
204 }
205
206 public static boolean canBeSmartCast(
207 @NotNull ReceiverParameterDescriptor receiverParameter,
208 @NotNull ReceiverValue receiver,
209 @NotNull ResolutionContext context) {
210 if (!receiver.getType().isMarkedNullable()) return true;
211
212 List<JetType> smartCastVariants = getSmartCastVariants(receiver, context);
213 for (JetType smartCastVariant : smartCastVariants) {
214 if (JetTypeChecker.DEFAULT.isSubtypeOf(smartCastVariant, receiverParameter.getType())) return true;
215 }
216 return false;
217 }
218 }