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.SMARTCAST;
044    
045    public class SmartCastManager {
046    
047        @NotNull
048        public List<KotlinType> getSmartCastVariants(
049                @NotNull ReceiverValue receiverToCast,
050                @NotNull ResolutionContext context
051        ) {
052            return getSmartCastVariants(receiverToCast, context.trace.getBindingContext(), context.scope.getOwnerDescriptor(), context.dataFlowInfo);
053        }
054    
055        @NotNull
056        public List<KotlinType> getSmartCastVariants(
057                @NotNull ReceiverValue receiverToCast,
058                @NotNull BindingContext bindingContext,
059                @NotNull DeclarationDescriptor containingDeclarationOrModule,
060                @NotNull DataFlowInfo dataFlowInfo
061        ) {
062            List<KotlinType> variants = Lists.newArrayList();
063            variants.add(receiverToCast.getType());
064            variants.addAll(getSmartCastVariantsExcludingReceiver(bindingContext, containingDeclarationOrModule, dataFlowInfo, receiverToCast));
065            return variants;
066        }
067    
068        @NotNull
069        public List<KotlinType> getSmartCastVariantsWithLessSpecificExcluded(
070                @NotNull ReceiverValue receiverToCast,
071                @NotNull BindingContext bindingContext,
072                @NotNull DeclarationDescriptor containingDeclarationOrModule,
073                @NotNull DataFlowInfo dataFlowInfo
074        ) {
075            final List<KotlinType> variants = getSmartCastVariants(receiverToCast, bindingContext,
076                                                                   containingDeclarationOrModule, dataFlowInfo);
077            return CollectionsKt.filter(variants, new Function1<KotlinType, Boolean>() {
078                @Override
079                public Boolean invoke(final KotlinType type) {
080                    return !CollectionsKt.any(variants, new Function1<KotlinType, Boolean>() {
081                        @Override
082                        public Boolean invoke(KotlinType another) {
083                            return another != type && KotlinTypeChecker.DEFAULT.isSubtypeOf(another, type);
084                        }
085                    });
086                }
087            });
088        }
089    
090        /**
091         * @return variants @param receiverToCast may be cast to according to context dataFlowInfo, receiverToCast itself is NOT included
092         */
093        @NotNull
094        public Collection<KotlinType> getSmartCastVariantsExcludingReceiver(
095                @NotNull ResolutionContext context,
096                @NotNull ReceiverValue receiverToCast
097        ) {
098            return getSmartCastVariantsExcludingReceiver(context.trace.getBindingContext(),
099                                                         context.scope.getOwnerDescriptor(),
100                                                         context.dataFlowInfo,
101                                                         receiverToCast);
102        }
103    
104        /**
105         * @return variants @param receiverToCast may be cast to according to @param dataFlowInfo, @param receiverToCast itself is NOT included
106         */
107        @NotNull
108        public Collection<KotlinType> getSmartCastVariantsExcludingReceiver(
109                @NotNull BindingContext bindingContext,
110                @NotNull DeclarationDescriptor containingDeclarationOrModule,
111                @NotNull DataFlowInfo dataFlowInfo,
112                @NotNull ReceiverValue receiverToCast
113        ) {
114            DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(
115                    receiverToCast, bindingContext, containingDeclarationOrModule
116            );
117    
118            return dataFlowInfo.getPossibleTypes(dataFlowValue);
119        }
120    
121        public boolean isSubTypeBySmartCastIgnoringNullability(
122                @NotNull ReceiverValue receiverArgument,
123                @NotNull KotlinType receiverParameterType,
124                @NotNull ResolutionContext context
125        ) {
126            List<KotlinType> smartCastTypes = getSmartCastVariants(receiverArgument, context);
127            return getSmartCastSubType(TypeUtils.makeNullable(receiverParameterType), smartCastTypes) != null;
128        }
129    
130        @Nullable
131        private KotlinType getSmartCastSubType(
132                @NotNull KotlinType receiverParameterType,
133                @NotNull Collection<KotlinType> smartCastTypes
134        ) {
135            Set<KotlinType> subTypes = Sets.newHashSet();
136            for (KotlinType smartCastType : smartCastTypes) {
137                if (ArgumentTypeResolver.isSubtypeOfForArgumentType(smartCastType, receiverParameterType)) {
138                    subTypes.add(smartCastType);
139                }
140            }
141            if (subTypes.isEmpty()) return null;
142    
143            KotlinType intersection = TypeIntersector.intersectTypes(KotlinTypeChecker.DEFAULT, subTypes);
144            if (intersection == null || !intersection.getConstructor().isDenotable()) {
145                return receiverParameterType;
146            }
147            return intersection;
148        }
149    
150        private static void recordCastOrError(
151                @NotNull KtExpression expression,
152                @NotNull KotlinType type,
153                @NotNull BindingTrace trace,
154                @NotNull DataFlowValue dataFlowValue,
155                boolean recordExpressionType
156        ) {
157            if (KotlinBuiltIns.isNullableNothing(type)) return;
158            if (dataFlowValue.isPredictable()) {
159                trace.record(SMARTCAST, 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.recordType(expression, type);
164                }
165            }
166            else {
167                trace.report(SMARTCAST_IMPOSSIBLE.on(expression, type, expression.getText(), dataFlowValue.getKind().getDescription()));
168            }
169        }
170    
171        @Nullable
172        public static SmartCastResult checkAndRecordPossibleCast(
173                @NotNull DataFlowValue dataFlowValue,
174                @NotNull KotlinType expectedType,
175                @Nullable KtExpression expression,
176                @NotNull ResolutionContext c,
177                boolean recordExpressionType
178        ) {
179            for (KotlinType possibleType : c.dataFlowInfo.getPossibleTypes(dataFlowValue)) {
180                if (ArgumentTypeResolver.isSubtypeOfForArgumentType(possibleType, expectedType)) {
181                    if (expression != null) {
182                        recordCastOrError(expression, possibleType, c.trace, dataFlowValue, recordExpressionType);
183                    }
184                    return new SmartCastResult(possibleType, dataFlowValue.isPredictable());
185                }
186            }
187    
188            if (!c.dataFlowInfo.getNullability(dataFlowValue).canBeNull() && !expectedType.isMarkedNullable()) {
189                // Handling cases like:
190                // fun bar(x: Any) {}
191                // fun <T : Any?> foo(x: T) {
192                //      if (x != null) {
193                //          bar(x) // Should be allowed with smart cast
194                //      }
195                // }
196                //
197                // It doesn't handled by lower code with getPossibleTypes because smart cast of T after `x != null` is still has same type T.
198                // But at the same time we're sure that `x` can't be null and just check for such cases manually
199    
200                // E.g. in case x!! when x has type of T where T is type parameter with nullable upper bounds
201                // x!! is immanently not null (see DataFlowValueFactory.createDataFlowValue for expression)
202                boolean immanentlyNotNull = !dataFlowValue.getImmanentNullability().canBeNull();
203                KotlinType nullableExpectedType = TypeUtils.makeNullable(expectedType);
204    
205                if (ArgumentTypeResolver.isSubtypeOfForArgumentType(dataFlowValue.getType(), nullableExpectedType)) {
206                    if (!immanentlyNotNull) {
207                        if (expression != null) {
208                            recordCastOrError(expression, dataFlowValue.getType(), c.trace, dataFlowValue, recordExpressionType);
209                        }
210                    }
211    
212                    return new SmartCastResult(dataFlowValue.getType(), immanentlyNotNull || dataFlowValue.isPredictable());
213                }
214                return checkAndRecordPossibleCast(dataFlowValue, nullableExpectedType, expression, c, recordExpressionType);
215            }
216    
217            return null;
218        }
219    }