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    }