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.autocasts;
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.AUTOCAST_IMPOSSIBLE;
040    import static org.jetbrains.jet.lang.resolve.BindingContext.AUTOCAST;
041    import static org.jetbrains.jet.lang.resolve.BindingContext.EXPRESSION_TYPE;
042    
043    public class AutoCastUtils {
044    
045        private AutoCastUtils() {}
046    
047        public static List<JetType> getAutoCastVariants(
048                @NotNull ReceiverValue receiverToCast,
049                @NotNull ResolutionContext context
050        ) {
051            return getAutoCastVariants(receiverToCast, context.trace.getBindingContext(), context.dataFlowInfo);
052        }
053    
054        public static List<JetType> getAutoCastVariants(
055                @NotNull ReceiverValue receiverToCast,
056                @NotNull BindingContext bindingContext,
057                @NotNull DataFlowInfo dataFlowInfo
058        ) {
059            List<JetType> variants = Lists.newArrayList();
060            variants.add(receiverToCast.getType());
061            variants.addAll(getAutoCastVariantsExcludingReceiver(bindingContext, dataFlowInfo, receiverToCast));
062            return variants;
063        }
064    
065        /**
066         * @return variants @param receiverToCast may be cast to according to @param dataFlowInfo, @param receiverToCast itself is NOT included
067         */
068        @NotNull
069        public static List<JetType> getAutoCastVariantsExcludingReceiver(
070                @NotNull BindingContext bindingContext,
071                @NotNull DataFlowInfo dataFlowInfo,
072                @NotNull ReceiverValue receiverToCast
073        ) {
074            if (receiverToCast instanceof ThisReceiver) {
075                ThisReceiver receiver = (ThisReceiver) receiverToCast;
076                assert receiver.exists();
077                DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver);
078                return collectAutoCastReceiverValues(dataFlowInfo, dataFlowValue);
079            }
080            else if (receiverToCast instanceof ExpressionReceiver) {
081                ExpressionReceiver receiver = (ExpressionReceiver) receiverToCast;
082                DataFlowValue dataFlowValue =
083                        DataFlowValueFactory.createDataFlowValue(receiver.getExpression(), receiver.getType(), bindingContext);
084                return collectAutoCastReceiverValues(dataFlowInfo, dataFlowValue);
085            }
086            return Collections.emptyList();
087        }
088    
089        @NotNull
090        private static List<JetType> collectAutoCastReceiverValues(
091                @NotNull DataFlowInfo dataFlowInfo,
092                @NotNull DataFlowValue dataFlowValue
093        ) {
094            return Lists.newArrayList(dataFlowInfo.getPossibleTypes(dataFlowValue));
095        }
096    
097        public static boolean isSubTypeByAutoCastIgnoringNullability(
098                @NotNull ReceiverValue receiverArgument,
099                @NotNull JetType receiverParameterType,
100                @NotNull ResolutionContext context
101        ) {
102            List<JetType> autoCastTypes = getAutoCastVariants(receiverArgument, context);
103            return getAutoCastSubType(TypeUtils.makeNullable(receiverParameterType), autoCastTypes) != null;
104        }
105    
106        @Nullable
107        private static JetType getAutoCastSubType(
108                @NotNull JetType receiverParameterType,
109                @NotNull List<JetType> autoCastTypes
110        ) {
111            Set<JetType> subTypes = Sets.newHashSet();
112            for (JetType autoCastType : autoCastTypes) {
113                if (ArgumentTypeResolver.isSubtypeOfForArgumentType(autoCastType, receiverParameterType)) {
114                    subTypes.add(autoCastType);
115                }
116            }
117            if (subTypes.isEmpty()) return null;
118    
119            JetType intersection = TypeUtils.intersect(JetTypeChecker.INSTANCE, subTypes);
120            if (intersection == null || !intersection.getConstructor().isDenotable()) {
121                return receiverParameterType;
122            }
123            return intersection;
124        }
125    
126        public static boolean recordAutoCastIfNecessary(
127                @NotNull ReceiverValue receiver,
128                @NotNull JetType receiverParameterType,
129                @NotNull ResolutionContext context,
130                boolean safeAccess
131        ) {
132            if (!(receiver instanceof ExpressionReceiver)) return false;
133    
134            receiverParameterType = safeAccess ? TypeUtils.makeNullable(receiverParameterType) : receiverParameterType;
135            if (ArgumentTypeResolver.isSubtypeOfForArgumentType(receiver.getType(), receiverParameterType)) {
136                return false;
137            }
138    
139            List<JetType> autoCastTypesExcludingReceiver = getAutoCastVariantsExcludingReceiver(
140                    context.trace.getBindingContext(), context.dataFlowInfo, receiver);
141            JetType autoCastSubType = getAutoCastSubType(receiverParameterType, autoCastTypesExcludingReceiver);
142            if (autoCastSubType == null) return false;
143    
144            JetExpression expression = ((ExpressionReceiver) receiver).getExpression();
145            DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver, context.trace.getBindingContext());
146    
147            recordCastOrError(expression, autoCastSubType, context.trace, dataFlowValue.isStableIdentifier(), true);
148            return true;
149        }
150    
151        public static void recordCastOrError(
152                @NotNull JetExpression expression,
153                @NotNull JetType type,
154                @NotNull BindingTrace trace,
155                boolean canBeCasted,
156                boolean recordExpressionType
157        ) {
158            if (canBeCasted) {
159                trace.record(AUTOCAST, 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.record(EXPRESSION_TYPE, expression, type);
164                }
165            }
166            else {
167                trace.report(AUTOCAST_IMPOSSIBLE.on(expression, type, expression.getText()));
168            }
169        }
170    
171        public static boolean isNotNull(
172                @NotNull ReceiverValue receiver,
173                @NotNull BindingContext bindingContext,
174                @NotNull DataFlowInfo dataFlowInfo
175        ) {
176            if (!receiver.getType().isNullable()) return true;
177    
178            List<JetType> autoCastVariants = getAutoCastVariants(receiver, bindingContext, dataFlowInfo);
179            for (JetType autoCastVariant : autoCastVariants) {
180                if (!autoCastVariant.isNullable()) return true;
181            }
182            return false;
183        }
184    }