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