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