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.descriptors.DeclarationDescriptor;
026    import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor;
027    import org.jetbrains.kotlin.psi.JetExpression;
028    import org.jetbrains.kotlin.resolve.BindingContext;
029    import org.jetbrains.kotlin.resolve.BindingTrace;
030    import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver;
031    import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
032    import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
033    import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
034    import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver;
035    import org.jetbrains.kotlin.types.JetType;
036    import org.jetbrains.kotlin.types.TypeUtils;
037    import org.jetbrains.kotlin.types.checker.JetTypeChecker;
038    
039    import java.util.Collection;
040    import java.util.Collections;
041    import java.util.List;
042    import java.util.Set;
043    
044    import static org.jetbrains.kotlin.diagnostics.Errors.SMARTCAST_IMPOSSIBLE;
045    import static org.jetbrains.kotlin.resolve.BindingContext.SMARTCAST;
046    
047    public class SmartCastUtils {
048    
049        private SmartCastUtils() {}
050    
051        @NotNull
052        public static List<JetType> getSmartCastVariants(
053                @NotNull ReceiverValue receiverToCast,
054                @NotNull ResolutionContext context
055        ) {
056            return getSmartCastVariants(receiverToCast, context.trace.getBindingContext(), context.scope.getContainingDeclaration(), context.dataFlowInfo);
057        }
058    
059        @NotNull
060        public static List<JetType> getSmartCastVariants(
061                @NotNull ReceiverValue receiverToCast,
062                @NotNull BindingContext bindingContext,
063                @NotNull DeclarationDescriptor containingDeclarationOrModule,
064                @NotNull DataFlowInfo dataFlowInfo
065        ) {
066            List<JetType> variants = Lists.newArrayList();
067            variants.add(receiverToCast.getType());
068            variants.addAll(getSmartCastVariantsExcludingReceiver(bindingContext, containingDeclarationOrModule, dataFlowInfo, receiverToCast));
069            return variants;
070        }
071    
072        @NotNull
073        public static List<JetType> getSmartCastVariantsWithLessSpecificExcluded(
074                @NotNull ReceiverValue receiverToCast,
075                @NotNull BindingContext bindingContext,
076                @NotNull DeclarationDescriptor containingDeclarationOrModule,
077                @NotNull DataFlowInfo dataFlowInfo
078        ) {
079            final List<JetType> variants = getSmartCastVariants(receiverToCast, bindingContext,
080                                                                containingDeclarationOrModule, dataFlowInfo);
081            return KotlinPackage.filter(variants, new Function1<JetType, Boolean>() {
082                @Override
083                public Boolean invoke(final JetType type) {
084                    return !KotlinPackage.any(variants, new Function1<JetType, Boolean>() {
085                        @Override
086                        public Boolean invoke(JetType another) {
087                            return another != type && JetTypeChecker.DEFAULT.isSubtypeOf(another, type);
088                        }
089                    });
090                }
091            });
092        }
093    
094        /**
095         * @return variants @param receiverToCast may be cast to according to context dataFlowInfo, receiverToCast itself is NOT included
096         */
097        @NotNull
098        public static Collection<JetType> getSmartCastVariantsExcludingReceiver(
099                @NotNull ResolutionContext context,
100                @NotNull ReceiverValue receiverToCast
101        ) {
102            return getSmartCastVariantsExcludingReceiver(context.trace.getBindingContext(),
103                                                         context.scope.getContainingDeclaration(),
104                                                         context.dataFlowInfo,
105                                                         receiverToCast);
106        }
107    
108        /**
109         * @return variants @param receiverToCast may be cast to according to @param dataFlowInfo, @param receiverToCast itself is NOT included
110         */
111        @NotNull
112        public static Collection<JetType> getSmartCastVariantsExcludingReceiver(
113                @NotNull BindingContext bindingContext,
114                @NotNull DeclarationDescriptor containingDeclarationOrModule,
115                @NotNull DataFlowInfo dataFlowInfo,
116                @NotNull ReceiverValue receiverToCast
117        ) {
118            if (receiverToCast instanceof ThisReceiver) {
119                ThisReceiver receiver = (ThisReceiver) receiverToCast;
120                assert receiver.exists();
121                DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver);
122                return dataFlowInfo.getPossibleTypes(dataFlowValue);
123            }
124            else if (receiverToCast instanceof ExpressionReceiver) {
125                DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(
126                        receiverToCast, bindingContext, containingDeclarationOrModule);
127                return dataFlowInfo.getPossibleTypes(dataFlowValue);
128            }
129            return Collections.emptyList();
130        }
131    
132        public static boolean isSubTypeBySmartCastIgnoringNullability(
133                @NotNull ReceiverValue receiverArgument,
134                @NotNull JetType receiverParameterType,
135                @NotNull ResolutionContext context
136        ) {
137            List<JetType> smartCastTypes = getSmartCastVariants(receiverArgument, context);
138            return getSmartCastSubType(TypeUtils.makeNullable(receiverParameterType), smartCastTypes) != null;
139        }
140    
141        @Nullable
142        private static JetType getSmartCastSubType(
143                @NotNull JetType receiverParameterType,
144                @NotNull Collection<JetType> smartCastTypes
145        ) {
146            Set<JetType> subTypes = Sets.newHashSet();
147            for (JetType smartCastType : smartCastTypes) {
148                if (ArgumentTypeResolver.isSubtypeOfForArgumentType(smartCastType, receiverParameterType)) {
149                    subTypes.add(smartCastType);
150                }
151            }
152            if (subTypes.isEmpty()) return null;
153    
154            JetType intersection = TypeUtils.intersect(JetTypeChecker.DEFAULT, subTypes);
155            if (intersection == null || !intersection.getConstructor().isDenotable()) {
156                return receiverParameterType;
157            }
158            return intersection;
159        }
160    
161        // Returns false when we need smart cast but cannot do it, otherwise true
162        public static boolean recordSmartCastIfNecessary(
163                @NotNull ReceiverValue receiver,
164                @NotNull JetType receiverParameterType,
165                @NotNull ResolutionContext context,
166                boolean safeAccess
167        ) {
168            if (!(receiver instanceof ExpressionReceiver)) return true;
169    
170            receiverParameterType = safeAccess ? TypeUtils.makeNullable(receiverParameterType) : receiverParameterType;
171            if (ArgumentTypeResolver.isSubtypeOfForArgumentType(receiver.getType(), receiverParameterType)) {
172                return true;
173            }
174    
175            Collection<JetType> smartCastTypesExcludingReceiver = getSmartCastVariantsExcludingReceiver(context, receiver);
176            JetType smartCastSubType = getSmartCastSubType(receiverParameterType, smartCastTypesExcludingReceiver);
177            if (smartCastSubType == null) return true;
178    
179            JetExpression expression = ((ExpressionReceiver) receiver).getExpression();
180            DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver, context);
181    
182            return recordCastOrError(expression, smartCastSubType, context.trace, dataFlowValue.isPredictable(), true);
183        }
184    
185        public static boolean recordCastOrError(
186                @NotNull JetExpression expression,
187                @NotNull JetType type,
188                @NotNull BindingTrace trace,
189                boolean canBeCast,
190                boolean recordExpressionType
191        ) {
192            if (canBeCast) {
193                trace.record(SMARTCAST, expression, type);
194                if (recordExpressionType) {
195                    //TODO
196                    //Why the expression type is rewritten for receivers and is not rewritten for arguments? Is it necessary?
197                    trace.recordType(expression, type);
198                }
199            }
200            else {
201                trace.report(SMARTCAST_IMPOSSIBLE.on(expression, type, expression.getText()));
202            }
203            return canBeCast;
204        }
205    
206        public static boolean canBeSmartCast(
207                @NotNull ReceiverParameterDescriptor receiverParameter,
208                @NotNull ReceiverValue receiver,
209                @NotNull ResolutionContext context) {
210            if (!receiver.getType().isMarkedNullable()) return true;
211    
212            List<JetType> smartCastVariants = getSmartCastVariants(receiver, context);
213            for (JetType smartCastVariant : smartCastVariants) {
214                if (JetTypeChecker.DEFAULT.isSubtypeOf(smartCastVariant, receiverParameter.getType())) return true;
215            }
216            return false;
217        }
218    }