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.collections.CollectionsKt;
022 import kotlin.jvm.functions.Function1;
023 import org.jetbrains.annotations.NotNull;
024 import org.jetbrains.annotations.Nullable;
025 import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
026 import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
027 import org.jetbrains.kotlin.psi.KtExpression;
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.ReceiverValue;
033 import org.jetbrains.kotlin.types.KotlinType;
034 import org.jetbrains.kotlin.types.TypeIntersector;
035 import org.jetbrains.kotlin.types.TypeUtils;
036 import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
037
038 import java.util.Collection;
039 import java.util.List;
040 import java.util.Set;
041
042 import static org.jetbrains.kotlin.diagnostics.Errors.SMARTCAST_IMPOSSIBLE;
043 import static org.jetbrains.kotlin.resolve.BindingContext.IMPLICIT_RECEIVER_SMARTCAST;
044 import static org.jetbrains.kotlin.resolve.BindingContext.SMARTCAST;
045
046 // We do not want to make methods static to keep SmartCastManager as a component
047 @SuppressWarnings("MethodMayBeStatic")
048 public class SmartCastManager {
049
050 @NotNull
051 public List<KotlinType> getSmartCastVariants(
052 @NotNull ReceiverValue receiverToCast,
053 @NotNull ResolutionContext context
054 ) {
055 return getSmartCastVariants(receiverToCast, context.trace.getBindingContext(), context.scope.getOwnerDescriptor(), context.dataFlowInfo);
056 }
057
058 @NotNull
059 public List<KotlinType> getSmartCastVariants(
060 @NotNull ReceiverValue receiverToCast,
061 @NotNull BindingContext bindingContext,
062 @NotNull DeclarationDescriptor containingDeclarationOrModule,
063 @NotNull DataFlowInfo dataFlowInfo
064 ) {
065 List<KotlinType> variants = Lists.newArrayList();
066 variants.add(receiverToCast.getType());
067 variants.addAll(getSmartCastVariantsExcludingReceiver(bindingContext, containingDeclarationOrModule, dataFlowInfo, receiverToCast));
068 return variants;
069 }
070
071 @NotNull
072 public List<KotlinType> getSmartCastVariantsWithLessSpecificExcluded(
073 @NotNull ReceiverValue receiverToCast,
074 @NotNull BindingContext bindingContext,
075 @NotNull DeclarationDescriptor containingDeclarationOrModule,
076 @NotNull DataFlowInfo dataFlowInfo
077 ) {
078 final List<KotlinType> variants = CollectionsKt.distinct(
079 getSmartCastVariants(receiverToCast, bindingContext, containingDeclarationOrModule, dataFlowInfo));
080 return CollectionsKt.filter(variants, new Function1<KotlinType, Boolean>() {
081 @Override
082 public Boolean invoke(final KotlinType type) {
083 return CollectionsKt.none(variants, new Function1<KotlinType, Boolean>() {
084 @Override
085 public Boolean invoke(KotlinType another) {
086 return another != type && KotlinTypeChecker.DEFAULT.isSubtypeOf(another, type);
087 }
088 });
089 }
090 });
091 }
092
093 /**
094 * @return variants @param receiverToCast may be cast to according to context dataFlowInfo, receiverToCast itself is NOT included
095 */
096 @NotNull
097 public Collection<KotlinType> getSmartCastVariantsExcludingReceiver(
098 @NotNull ResolutionContext context,
099 @NotNull ReceiverValue receiverToCast
100 ) {
101 return getSmartCastVariantsExcludingReceiver(context.trace.getBindingContext(),
102 context.scope.getOwnerDescriptor(),
103 context.dataFlowInfo,
104 receiverToCast);
105 }
106
107 /**
108 * @return variants @param receiverToCast may be cast to according to @param dataFlowInfo, @param receiverToCast itself is NOT included
109 */
110 @NotNull
111 public Collection<KotlinType> getSmartCastVariantsExcludingReceiver(
112 @NotNull BindingContext bindingContext,
113 @NotNull DeclarationDescriptor containingDeclarationOrModule,
114 @NotNull DataFlowInfo dataFlowInfo,
115 @NotNull ReceiverValue receiverToCast
116 ) {
117 DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(
118 receiverToCast, bindingContext, containingDeclarationOrModule
119 );
120
121 return dataFlowInfo.getCollectedTypes(dataFlowValue);
122 }
123
124 public boolean isSubTypeBySmartCastIgnoringNullability(
125 @NotNull ReceiverValue receiverArgument,
126 @NotNull KotlinType receiverParameterType,
127 @NotNull ResolutionContext context
128 ) {
129 List<KotlinType> smartCastTypes = getSmartCastVariants(receiverArgument, context);
130 return getSmartCastSubType(TypeUtils.makeNullable(receiverParameterType), smartCastTypes) != null;
131 }
132
133 @Nullable
134 private KotlinType getSmartCastSubType(
135 @NotNull KotlinType receiverParameterType,
136 @NotNull Collection<KotlinType> smartCastTypes
137 ) {
138 Set<KotlinType> subTypes = Sets.newHashSet();
139 for (KotlinType smartCastType : smartCastTypes) {
140 if (ArgumentTypeResolver.isSubtypeOfForArgumentType(smartCastType, receiverParameterType)) {
141 subTypes.add(smartCastType);
142 }
143 }
144 if (subTypes.isEmpty()) return null;
145
146 KotlinType intersection = TypeIntersector.intersectTypes(KotlinTypeChecker.DEFAULT, subTypes);
147 if (intersection == null || !intersection.getConstructor().isDenotable()) {
148 return receiverParameterType;
149 }
150 return intersection;
151 }
152
153 private static void recordCastOrError(
154 @NotNull KtExpression expression,
155 @NotNull KotlinType type,
156 @NotNull BindingTrace trace,
157 @NotNull DataFlowValue dataFlowValue,
158 boolean recordExpressionType
159 ) {
160 if (KotlinBuiltIns.isNullableNothing(type)) return;
161 if (dataFlowValue.isPredictable()) {
162 trace.record(SMARTCAST, expression, type);
163 if (recordExpressionType) {
164 //TODO
165 //Why the expression type is rewritten for receivers and is not rewritten for arguments? Is it necessary?
166 trace.recordType(expression, type);
167 }
168 }
169 else {
170 trace.report(SMARTCAST_IMPOSSIBLE.on(expression, type, expression.getText(), dataFlowValue.getKind().getDescription()));
171 }
172 }
173
174 @Nullable
175 public static SmartCastResult checkAndRecordPossibleCast(
176 @NotNull DataFlowValue dataFlowValue,
177 @NotNull KotlinType expectedType,
178 @Nullable KtExpression expression,
179 @NotNull ResolutionContext c,
180 @Nullable KtExpression calleeExpression,
181 boolean recordExpressionType
182 ) {
183 for (KotlinType possibleType : c.dataFlowInfo.getCollectedTypes(dataFlowValue)) {
184 if (ArgumentTypeResolver.isSubtypeOfForArgumentType(possibleType, expectedType)) {
185 if (expression != null) {
186 recordCastOrError(expression, possibleType, c.trace, dataFlowValue, recordExpressionType);
187 }
188 else if (calleeExpression != null && dataFlowValue.isPredictable()) {
189 c.trace.record(IMPLICIT_RECEIVER_SMARTCAST, calleeExpression, possibleType);
190 }
191 return new SmartCastResult(possibleType, dataFlowValue.isPredictable());
192 }
193 }
194
195 if (!c.dataFlowInfo.getCollectedNullability(dataFlowValue).canBeNull() && !expectedType.isMarkedNullable()) {
196 // Handling cases like:
197 // fun bar(x: Any) {}
198 // fun <T : Any?> foo(x: T) {
199 // if (x != null) {
200 // bar(x) // Should be allowed with smart cast
201 // }
202 // }
203 //
204 // It doesn't handled by lower code with getPossibleTypes because smart cast of T after `x != null` is still has same type T.
205 // But at the same time we're sure that `x` can't be null and just check for such cases manually
206
207 // E.g. in case x!! when x has type of T where T is type parameter with nullable upper bounds
208 // x!! is immanently not null (see DataFlowValueFactory.createDataFlowValue for expression)
209 boolean immanentlyNotNull = !dataFlowValue.getImmanentNullability().canBeNull();
210 KotlinType nullableExpectedType = TypeUtils.makeNullable(expectedType);
211
212 if (ArgumentTypeResolver.isSubtypeOfForArgumentType(dataFlowValue.getType(), nullableExpectedType)) {
213 if (!immanentlyNotNull) {
214 if (expression != null) {
215 recordCastOrError(expression, dataFlowValue.getType(), c.trace, dataFlowValue, recordExpressionType);
216 }
217 }
218
219 return new SmartCastResult(dataFlowValue.getType(), immanentlyNotNull || dataFlowValue.isPredictable());
220 }
221 return checkAndRecordPossibleCast(dataFlowValue, nullableExpectedType, expression, c, calleeExpression, recordExpressionType);
222 }
223
224 return null;
225 }
226 }