001 /*
002 * Copyright 2010-2013 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.jet.lang.resolve.calls.smartcasts;
018
019 import com.google.common.collect.Lists;
020 import com.google.common.collect.Sets;
021 import org.jetbrains.annotations.NotNull;
022 import org.jetbrains.annotations.Nullable;
023 import org.jetbrains.jet.lang.psi.JetExpression;
024 import org.jetbrains.jet.lang.resolve.BindingContext;
025 import org.jetbrains.jet.lang.resolve.BindingTrace;
026 import org.jetbrains.jet.lang.resolve.calls.ArgumentTypeResolver;
027 import org.jetbrains.jet.lang.resolve.calls.context.ResolutionContext;
028 import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
029 import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
030 import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiver;
031 import org.jetbrains.jet.lang.types.JetType;
032 import org.jetbrains.jet.lang.types.TypeUtils;
033 import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
034
035 import java.util.Collections;
036 import java.util.List;
037 import java.util.Set;
038
039 import static org.jetbrains.jet.lang.diagnostics.Errors.SMARTCAST_IMPOSSIBLE;
040 import static org.jetbrains.jet.lang.resolve.BindingContext.EXPRESSION_TYPE;
041 import static org.jetbrains.jet.lang.resolve.BindingContext.SMARTCAST;
042
043 public class SmartCastUtils {
044
045 private SmartCastUtils() {}
046
047 @NotNull
048 public static List<JetType> getSmartCastVariants(
049 @NotNull ReceiverValue receiverToCast,
050 @NotNull ResolutionContext context
051 ) {
052 return getSmartCastVariants(receiverToCast, context.trace.getBindingContext(), context.dataFlowInfo);
053 }
054
055 @NotNull
056 public static List<JetType> getSmartCastVariants(
057 @NotNull ReceiverValue receiverToCast,
058 @NotNull BindingContext bindingContext,
059 @NotNull DataFlowInfo dataFlowInfo
060 ) {
061 List<JetType> variants = Lists.newArrayList();
062 variants.add(receiverToCast.getType());
063 variants.addAll(getSmartCastVariantsExcludingReceiver(bindingContext, dataFlowInfo, receiverToCast));
064 return variants;
065 }
066
067 /**
068 * @return variants @param receiverToCast may be cast to according to @param dataFlowInfo, @param receiverToCast itself is NOT included
069 */
070 @NotNull
071 public static List<JetType> getSmartCastVariantsExcludingReceiver(
072 @NotNull BindingContext bindingContext,
073 @NotNull DataFlowInfo dataFlowInfo,
074 @NotNull ReceiverValue receiverToCast
075 ) {
076 if (receiverToCast instanceof ThisReceiver) {
077 ThisReceiver receiver = (ThisReceiver) receiverToCast;
078 assert receiver.exists();
079 DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver);
080 return collectSmartCastReceiverValues(dataFlowInfo, dataFlowValue);
081 }
082 else if (receiverToCast instanceof ExpressionReceiver) {
083 ExpressionReceiver receiver = (ExpressionReceiver) receiverToCast;
084 DataFlowValue dataFlowValue =
085 DataFlowValueFactory.createDataFlowValue(receiver.getExpression(), receiver.getType(), bindingContext);
086 return collectSmartCastReceiverValues(dataFlowInfo, dataFlowValue);
087 }
088 return Collections.emptyList();
089 }
090
091 @NotNull
092 private static List<JetType> collectSmartCastReceiverValues(
093 @NotNull DataFlowInfo dataFlowInfo,
094 @NotNull DataFlowValue dataFlowValue
095 ) {
096 return Lists.newArrayList(dataFlowInfo.getPossibleTypes(dataFlowValue));
097 }
098
099 public static boolean isSubTypeBySmartCastIgnoringNullability(
100 @NotNull ReceiverValue receiverArgument,
101 @NotNull JetType receiverParameterType,
102 @NotNull ResolutionContext context
103 ) {
104 List<JetType> smartCastTypes = getSmartCastVariants(receiverArgument, context);
105 return getSmartCastSubType(TypeUtils.makeNullable(receiverParameterType), smartCastTypes) != null;
106 }
107
108 @Nullable
109 private static JetType getSmartCastSubType(
110 @NotNull JetType receiverParameterType,
111 @NotNull List<JetType> smartCastTypes
112 ) {
113 Set<JetType> subTypes = Sets.newHashSet();
114 for (JetType smartCastType : smartCastTypes) {
115 if (ArgumentTypeResolver.isSubtypeOfForArgumentType(smartCastType, receiverParameterType)) {
116 subTypes.add(smartCastType);
117 }
118 }
119 if (subTypes.isEmpty()) return null;
120
121 JetType intersection = TypeUtils.intersect(JetTypeChecker.DEFAULT, subTypes);
122 if (intersection == null || !intersection.getConstructor().isDenotable()) {
123 return receiverParameterType;
124 }
125 return intersection;
126 }
127
128 public static boolean recordSmartCastIfNecessary(
129 @NotNull ReceiverValue receiver,
130 @NotNull JetType receiverParameterType,
131 @NotNull ResolutionContext context,
132 boolean safeAccess
133 ) {
134 if (!(receiver instanceof ExpressionReceiver)) return false;
135
136 receiverParameterType = safeAccess ? TypeUtils.makeNullable(receiverParameterType) : receiverParameterType;
137 if (ArgumentTypeResolver.isSubtypeOfForArgumentType(receiver.getType(), receiverParameterType)) {
138 return false;
139 }
140
141 List<JetType> smartCastTypesExcludingReceiver = getSmartCastVariantsExcludingReceiver(
142 context.trace.getBindingContext(), context.dataFlowInfo, receiver);
143 JetType smartCastSubType = getSmartCastSubType(receiverParameterType, smartCastTypesExcludingReceiver);
144 if (smartCastSubType == null) return false;
145
146 JetExpression expression = ((ExpressionReceiver) receiver).getExpression();
147 DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver, context.trace.getBindingContext());
148
149 recordCastOrError(expression, smartCastSubType, context.trace, dataFlowValue.isStableIdentifier(), true);
150 return true;
151 }
152
153 public static void recordCastOrError(
154 @NotNull JetExpression expression,
155 @NotNull JetType type,
156 @NotNull BindingTrace trace,
157 boolean canBeCasted,
158 boolean recordExpressionType
159 ) {
160 if (canBeCasted) {
161 trace.record(SMARTCAST, expression, type);
162 if (recordExpressionType) {
163 //TODO
164 //Why the expression type is rewritten for receivers and is not rewritten for arguments? Is it necessary?
165 trace.record(EXPRESSION_TYPE, expression, type);
166 }
167 }
168 else {
169 trace.report(SMARTCAST_IMPOSSIBLE.on(expression, type, expression.getText()));
170 }
171 }
172
173 public static boolean isNotNull(
174 @NotNull ReceiverValue receiver,
175 @NotNull BindingContext bindingContext,
176 @NotNull DataFlowInfo dataFlowInfo
177 ) {
178 if (!receiver.getType().isNullable()) return true;
179
180 List<JetType> smartCastVariants = getSmartCastVariants(receiver, bindingContext, dataFlowInfo);
181 for (JetType smartCastVariant : smartCastVariants) {
182 if (!smartCastVariant.isNullable()) return true;
183 }
184 return false;
185 }
186 }