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