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