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