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.CollectionsKt;
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.psi.KtExpression;
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.ReceiverValue;
033    import org.jetbrains.kotlin.types.KotlinType;
034    import org.jetbrains.kotlin.types.TypeIntersector;
035    import org.jetbrains.kotlin.types.TypeUtils;
036    import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
037    
038    import java.util.Collection;
039    import java.util.List;
040    import java.util.Set;
041    
042    import static org.jetbrains.kotlin.diagnostics.Errors.SMARTCAST_IMPOSSIBLE;
043    import static org.jetbrains.kotlin.resolve.BindingContext.IMPLICIT_RECEIVER_SMARTCAST;
044    import static org.jetbrains.kotlin.resolve.BindingContext.SMARTCAST;
045    
046    public class SmartCastManager {
047    
048        @NotNull
049        public List<KotlinType> getSmartCastVariants(
050                @NotNull ReceiverValue receiverToCast,
051                @NotNull ResolutionContext context
052        ) {
053            return getSmartCastVariants(receiverToCast, context.trace.getBindingContext(), context.scope.getOwnerDescriptor(), context.dataFlowInfo);
054        }
055    
056        @NotNull
057        public List<KotlinType> getSmartCastVariants(
058                @NotNull ReceiverValue receiverToCast,
059                @NotNull BindingContext bindingContext,
060                @NotNull DeclarationDescriptor containingDeclarationOrModule,
061                @NotNull DataFlowInfo dataFlowInfo
062        ) {
063            List<KotlinType> variants = Lists.newArrayList();
064            variants.add(receiverToCast.getType());
065            variants.addAll(getSmartCastVariantsExcludingReceiver(bindingContext, containingDeclarationOrModule, dataFlowInfo, receiverToCast));
066            return variants;
067        }
068    
069        @NotNull
070        public List<KotlinType> getSmartCastVariantsWithLessSpecificExcluded(
071                @NotNull ReceiverValue receiverToCast,
072                @NotNull BindingContext bindingContext,
073                @NotNull DeclarationDescriptor containingDeclarationOrModule,
074                @NotNull DataFlowInfo dataFlowInfo
075        ) {
076            final List<KotlinType> variants = getSmartCastVariants(receiverToCast, bindingContext,
077                                                                   containingDeclarationOrModule, dataFlowInfo);
078            return CollectionsKt.filter(variants, new Function1<KotlinType, Boolean>() {
079                @Override
080                public Boolean invoke(final KotlinType type) {
081                    return !CollectionsKt.any(variants, new Function1<KotlinType, Boolean>() {
082                        @Override
083                        public Boolean invoke(KotlinType another) {
084                            return another != type && KotlinTypeChecker.DEFAULT.isSubtypeOf(another, type);
085                        }
086                    });
087                }
088            });
089        }
090    
091        /**
092         * @return variants @param receiverToCast may be cast to according to context dataFlowInfo, receiverToCast itself is NOT included
093         */
094        @NotNull
095        public Collection<KotlinType> getSmartCastVariantsExcludingReceiver(
096                @NotNull ResolutionContext context,
097                @NotNull ReceiverValue receiverToCast
098        ) {
099            return getSmartCastVariantsExcludingReceiver(context.trace.getBindingContext(),
100                                                         context.scope.getOwnerDescriptor(),
101                                                         context.dataFlowInfo,
102                                                         receiverToCast);
103        }
104    
105        /**
106         * @return variants @param receiverToCast may be cast to according to @param dataFlowInfo, @param receiverToCast itself is NOT included
107         */
108        @NotNull
109        public Collection<KotlinType> getSmartCastVariantsExcludingReceiver(
110                @NotNull BindingContext bindingContext,
111                @NotNull DeclarationDescriptor containingDeclarationOrModule,
112                @NotNull DataFlowInfo dataFlowInfo,
113                @NotNull ReceiverValue receiverToCast
114        ) {
115            DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(
116                    receiverToCast, bindingContext, containingDeclarationOrModule
117            );
118    
119            return dataFlowInfo.getPossibleTypes(dataFlowValue);
120        }
121    
122        public boolean isSubTypeBySmartCastIgnoringNullability(
123                @NotNull ReceiverValue receiverArgument,
124                @NotNull KotlinType receiverParameterType,
125                @NotNull ResolutionContext context
126        ) {
127            List<KotlinType> smartCastTypes = getSmartCastVariants(receiverArgument, context);
128            return getSmartCastSubType(TypeUtils.makeNullable(receiverParameterType), smartCastTypes) != null;
129        }
130    
131        @Nullable
132        private KotlinType getSmartCastSubType(
133                @NotNull KotlinType receiverParameterType,
134                @NotNull Collection<KotlinType> smartCastTypes
135        ) {
136            Set<KotlinType> subTypes = Sets.newHashSet();
137            for (KotlinType smartCastType : smartCastTypes) {
138                if (ArgumentTypeResolver.isSubtypeOfForArgumentType(smartCastType, receiverParameterType)) {
139                    subTypes.add(smartCastType);
140                }
141            }
142            if (subTypes.isEmpty()) return null;
143    
144            KotlinType intersection = TypeIntersector.intersectTypes(KotlinTypeChecker.DEFAULT, subTypes);
145            if (intersection == null || !intersection.getConstructor().isDenotable()) {
146                return receiverParameterType;
147            }
148            return intersection;
149        }
150    
151        private static void recordCastOrError(
152                @NotNull KtExpression expression,
153                @NotNull KotlinType type,
154                @NotNull BindingTrace trace,
155                @NotNull DataFlowValue dataFlowValue,
156                boolean recordExpressionType
157        ) {
158            if (KotlinBuiltIns.isNullableNothing(type)) return;
159            if (dataFlowValue.isPredictable()) {
160                trace.record(SMARTCAST, expression, type);
161                if (recordExpressionType) {
162                    //TODO
163                    //Why the expression type is rewritten for receivers and is not rewritten for arguments? Is it necessary?
164                    trace.recordType(expression, type);
165                }
166            }
167            else {
168                trace.report(SMARTCAST_IMPOSSIBLE.on(expression, type, expression.getText(), dataFlowValue.getKind().getDescription()));
169            }
170        }
171    
172        @Nullable
173        public static SmartCastResult checkAndRecordPossibleCast(
174                @NotNull DataFlowValue dataFlowValue,
175                @NotNull KotlinType expectedType,
176                @Nullable KtExpression expression,
177                @NotNull ResolutionContext c,
178                @Nullable KtExpression calleeExpression,
179                boolean recordExpressionType
180        ) {
181            for (KotlinType possibleType : c.dataFlowInfo.getPossibleTypes(dataFlowValue)) {
182                if (ArgumentTypeResolver.isSubtypeOfForArgumentType(possibleType, expectedType)) {
183                    if (expression != null) {
184                        recordCastOrError(expression, possibleType, c.trace, dataFlowValue, recordExpressionType);
185                    }
186                    else if (calleeExpression != null && dataFlowValue.isPredictable()) {
187                        c.trace.record(IMPLICIT_RECEIVER_SMARTCAST, calleeExpression, possibleType);
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                KotlinType 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, recordExpressionType);
214                        }
215                    }
216    
217                    return new SmartCastResult(dataFlowValue.getType(), immanentlyNotNull || dataFlowValue.isPredictable());
218                }
219                return checkAndRecordPossibleCast(dataFlowValue, nullableExpectedType, expression, c, calleeExpression, recordExpressionType);
220            }
221    
222            return null;
223        }
224    }