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