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.intellij.openapi.util.Pair;
020    import com.intellij.psi.tree.IElementType;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.kotlin.JetNodeTypes;
024    import org.jetbrains.kotlin.descriptors.*;
025    import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
026    import org.jetbrains.kotlin.lexer.JetTokens;
027    import org.jetbrains.kotlin.psi.*;
028    import org.jetbrains.kotlin.resolve.BindingContext;
029    import org.jetbrains.kotlin.resolve.BindingContextUtils;
030    import org.jetbrains.kotlin.resolve.DescriptorUtils;
031    import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage;
032    import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
033    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
034    import org.jetbrains.kotlin.resolve.scopes.receivers.*;
035    import org.jetbrains.kotlin.types.JetType;
036    import org.jetbrains.kotlin.types.TypeUtils;
037    import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils;
038    
039    import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableNothing;
040    import static org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET;
041    
042    /**
043     * This class is intended to create data flow values for different kind of expressions.
044     * Then data flow values serve as keys to obtain data flow information for these expressions.
045     */
046    public class DataFlowValueFactory {
047        private DataFlowValueFactory() {
048        }
049    
050        @NotNull
051        public static DataFlowValue createDataFlowValue(
052                @NotNull JetExpression expression,
053                @NotNull JetType type,
054                @NotNull ResolutionContext resolutionContext
055        ) {
056            return createDataFlowValue(expression, type, resolutionContext.trace.getBindingContext(),
057                                       resolutionContext.scope.getOwnerDescriptor());
058        }
059    
060        @NotNull
061        public static DataFlowValue createDataFlowValue(
062                @NotNull JetExpression expression,
063                @NotNull JetType type,
064                @NotNull BindingContext bindingContext,
065                @NotNull DeclarationDescriptor containingDeclarationOrModule
066        ) {
067            if (expression instanceof JetConstantExpression) {
068                JetConstantExpression constantExpression = (JetConstantExpression) expression;
069                if (constantExpression.getNode().getElementType() == JetNodeTypes.NULL) return DataFlowValue.NULL;
070            }
071            if (type.isError()) return DataFlowValue.ERROR;
072            if (isNullableNothing(type)) {
073                return DataFlowValue.NULL; // 'null' is the only inhabitant of 'Nothing?'
074            }
075    
076            if (ExpressionTypingUtils.isExclExclExpression(JetPsiUtil.deparenthesize(expression))) {
077                // In most cases type of `E!!`-expression is strictly not nullable and we could get proper Nullability
078                // by calling `getImmanentNullability` (as it happens below).
079                //
080                // But there are some problem with types built on type parameters, e.g.
081                // fun <T : Any?> foo(x: T) = x!!.hashCode() // there no way in type system to denote that `x!!` is not nullable
082                return new DataFlowValue(expression,
083                                         type,
084                                         DataFlowValue.Kind.OTHER,
085                                         Nullability.NOT_NULL);
086            }
087    
088            IdentifierInfo result = getIdForStableIdentifier(expression, bindingContext, containingDeclarationOrModule);
089            return new DataFlowValue(result == NO_IDENTIFIER_INFO ? expression : result.id,
090                                     type,
091                                     result.kind,
092                                     getImmanentNullability(type));
093        }
094    
095        @NotNull
096        public static DataFlowValue createDataFlowValue(@NotNull ThisReceiver receiver) {
097            JetType type = receiver.getType();
098            return new DataFlowValue(receiver, type, DataFlowValue.Kind.STABLE_VALUE, getImmanentNullability(type));
099        }
100    
101        @NotNull
102        public static DataFlowValue createDataFlowValue(
103                @NotNull ReceiverValue receiverValue,
104                @NotNull ResolutionContext resolutionContext
105        ) {
106            return createDataFlowValue(receiverValue, resolutionContext.trace.getBindingContext(),
107                                       resolutionContext.scope.getOwnerDescriptor());
108        }
109    
110        @NotNull
111        public static DataFlowValue createDataFlowValue(
112                @NotNull ReceiverValue receiverValue,
113                @NotNull BindingContext bindingContext,
114                @NotNull DeclarationDescriptor containingDeclarationOrModule
115        ) {
116            if (receiverValue instanceof TransientReceiver || receiverValue instanceof ScriptReceiver) {
117                // SCRIPT: smartcasts data flow
118                JetType type = receiverValue.getType();
119                return new DataFlowValue(receiverValue, type, DataFlowValue.Kind.STABLE_VALUE, getImmanentNullability(type));
120            }
121            else if (receiverValue instanceof ClassReceiver || receiverValue instanceof ExtensionReceiver) {
122                return createDataFlowValue((ThisReceiver) receiverValue);
123            }
124            else if (receiverValue instanceof ExpressionReceiver) {
125                return createDataFlowValue(((ExpressionReceiver) receiverValue).getExpression(),
126                                           receiverValue.getType(),
127                                           bindingContext,
128                                           containingDeclarationOrModule);
129            }
130            else if (receiverValue == ReceiverValue.NO_RECEIVER) {
131                throw new IllegalArgumentException("No DataFlowValue exists for ReceiverValue.NO_RECEIVER");
132            }
133            else {
134                throw new UnsupportedOperationException("Unsupported receiver value: " + receiverValue.getClass().getName());
135            }
136        }
137    
138        @NotNull
139        public static DataFlowValue createDataFlowValue(
140                @NotNull VariableDescriptor variableDescriptor,
141                @NotNull BindingContext bindingContext,
142                @Nullable ModuleDescriptor usageContainingModule
143        ) {
144            JetType type = variableDescriptor.getType();
145            return new DataFlowValue(variableDescriptor, type,
146                                     variableKind(variableDescriptor, usageContainingModule, bindingContext),
147                                     getImmanentNullability(type));
148        }
149    
150        @NotNull
151        private static Nullability getImmanentNullability(@NotNull JetType type) {
152            return TypeUtils.isNullableType(type) ? Nullability.UNKNOWN : Nullability.NOT_NULL;
153        }
154    
155        private static class IdentifierInfo {
156            public final Object id;
157            public final DataFlowValue.Kind kind;
158            public final boolean isPackage;
159    
160            private IdentifierInfo(Object id, DataFlowValue.Kind kind, boolean isPackage) {
161                this.id = id;
162                this.kind = kind;
163                this.isPackage = isPackage;
164            }
165        }
166    
167        private static final IdentifierInfo NO_IDENTIFIER_INFO = new IdentifierInfo(null, DataFlowValue.Kind.OTHER, false) {
168            @Override
169            public String toString() {
170                return "NO_IDENTIFIER_INFO";
171            }
172        };
173    
174        @NotNull
175        private static IdentifierInfo createInfo(Object id, DataFlowValue.Kind kind) {
176            return new IdentifierInfo(id, kind, false);
177        }
178    
179        @NotNull
180        private static IdentifierInfo createStableInfo(Object id) {
181            return createInfo(id, DataFlowValue.Kind.STABLE_VALUE);
182        }
183    
184        @NotNull
185        private static IdentifierInfo createPackageOrClassInfo(Object id) {
186            return new IdentifierInfo(id, DataFlowValue.Kind.STABLE_VALUE, true);
187        }
188    
189        @NotNull
190        private static IdentifierInfo combineInfo(@Nullable IdentifierInfo receiverInfo, @NotNull IdentifierInfo selectorInfo) {
191            if (selectorInfo.id == null || receiverInfo == NO_IDENTIFIER_INFO) {
192                return NO_IDENTIFIER_INFO;
193            }
194            if (receiverInfo == null || receiverInfo.isPackage) {
195                return selectorInfo;
196            }
197            return createInfo(Pair.create(receiverInfo.id, selectorInfo.id),
198                              receiverInfo.kind.isStable() && selectorInfo.kind.isStable()
199                              ? DataFlowValue.Kind.STABLE_VALUE
200                              // x.y can never be a local variable
201                              : DataFlowValue.Kind.OTHER);
202        }
203    
204        @NotNull
205        private static IdentifierInfo createPostfixInfo(@NotNull JetPostfixExpression expression, @NotNull IdentifierInfo argumentInfo) {
206            if (argumentInfo == NO_IDENTIFIER_INFO) {
207                return NO_IDENTIFIER_INFO;
208            }
209            return createInfo(Pair.create(expression, argumentInfo.id), argumentInfo.kind);
210        }
211    
212        @NotNull
213        private static IdentifierInfo getIdForStableIdentifier(
214                @Nullable JetExpression expression,
215                @NotNull BindingContext bindingContext,
216                @NotNull DeclarationDescriptor containingDeclarationOrModule
217        ) {
218            if (expression != null) {
219                JetExpression deparenthesized = JetPsiUtil.deparenthesize(expression);
220                if (expression != deparenthesized) {
221                    return getIdForStableIdentifier(deparenthesized, bindingContext, containingDeclarationOrModule);
222                }
223            }
224            if (expression instanceof JetQualifiedExpression) {
225                JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) expression;
226                JetExpression receiverExpression = qualifiedExpression.getReceiverExpression();
227                JetExpression selectorExpression = qualifiedExpression.getSelectorExpression();
228                IdentifierInfo receiverId = getIdForStableIdentifier(receiverExpression, bindingContext, containingDeclarationOrModule);
229                IdentifierInfo selectorId = getIdForStableIdentifier(selectorExpression, bindingContext, containingDeclarationOrModule);
230    
231                return combineInfo(receiverId, selectorId);
232            }
233            if (expression instanceof JetSimpleNameExpression) {
234                return getIdForSimpleNameExpression((JetSimpleNameExpression) expression, bindingContext, containingDeclarationOrModule);
235            }
236            else if (expression instanceof JetThisExpression) {
237                JetThisExpression thisExpression = (JetThisExpression) expression;
238                DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, thisExpression.getInstanceReference());
239    
240                return getIdForThisReceiver(declarationDescriptor);
241            }
242            else if (expression instanceof JetPostfixExpression) {
243                JetPostfixExpression postfixExpression = (JetPostfixExpression) expression;
244                IElementType operationType = postfixExpression.getOperationReference().getReferencedNameElementType();
245                if (operationType == JetTokens.PLUSPLUS || operationType == JetTokens.MINUSMINUS) {
246                    return createPostfixInfo(postfixExpression,
247                            getIdForStableIdentifier(postfixExpression.getBaseExpression(), bindingContext, containingDeclarationOrModule));
248                }
249            }
250            else if (expression instanceof JetRootPackageExpression) {
251                //todo return createPackageInfo());
252            }
253            return NO_IDENTIFIER_INFO;
254        }
255    
256        @NotNull
257        private static IdentifierInfo getIdForSimpleNameExpression(
258                @NotNull JetSimpleNameExpression simpleNameExpression,
259                @NotNull BindingContext bindingContext,
260                @NotNull DeclarationDescriptor containingDeclarationOrModule
261        ) {
262            DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, simpleNameExpression);
263            if (declarationDescriptor instanceof VariableDescriptor) {
264                ResolvedCall<?> resolvedCall = CallUtilPackage.getResolvedCall(simpleNameExpression, bindingContext);
265    
266                // todo uncomment assert
267                // KT-4113
268                // for now it fails for resolving 'invoke' convention, return it after 'invoke' algorithm changes
269                // assert resolvedCall != null : "Cannot create right identifier info if the resolved call is not known yet for
270                ModuleDescriptor usageModuleDescriptor = DescriptorUtils.getContainingModuleOrNull(containingDeclarationOrModule);
271                IdentifierInfo receiverInfo =
272                        resolvedCall != null ? getIdForImplicitReceiver(resolvedCall.getDispatchReceiver(), simpleNameExpression) : null;
273    
274                VariableDescriptor variableDescriptor = (VariableDescriptor) declarationDescriptor;
275                return combineInfo(receiverInfo, createInfo(variableDescriptor,
276                                                            variableKind(variableDescriptor, usageModuleDescriptor, bindingContext)));
277            }
278            if (declarationDescriptor instanceof PackageViewDescriptor || declarationDescriptor instanceof ClassDescriptor) {
279                return createPackageOrClassInfo(declarationDescriptor);
280            }
281            return NO_IDENTIFIER_INFO;
282        }
283    
284        @Nullable
285        private static IdentifierInfo getIdForImplicitReceiver(@NotNull ReceiverValue receiverValue, @Nullable JetExpression expression) {
286            if (receiverValue instanceof ThisReceiver) {
287                return getIdForThisReceiver(((ThisReceiver) receiverValue).getDeclarationDescriptor());
288            }
289            else {
290                assert !(receiverValue instanceof TransientReceiver)
291                        : "Transient receiver is implicit for an explicit expression: " + expression + ". Receiver: " + receiverValue;
292                // For ExpressionReceiver there is an explicit "this" expression and it was analyzed earlier
293                return null;
294            }
295        }
296    
297        @NotNull
298        private static IdentifierInfo getIdForThisReceiver(@Nullable DeclarationDescriptor descriptorOfThisReceiver) {
299            if (descriptorOfThisReceiver instanceof CallableDescriptor) {
300                ReceiverParameterDescriptor receiverParameter = ((CallableDescriptor) descriptorOfThisReceiver).getExtensionReceiverParameter();
301                assert receiverParameter != null : "'This' refers to the callable member without a receiver parameter: " +
302                                                   descriptorOfThisReceiver;
303                return createStableInfo(receiverParameter.getValue());
304            }
305            if (descriptorOfThisReceiver instanceof ClassDescriptor) {
306                return createStableInfo(((ClassDescriptor) descriptorOfThisReceiver).getThisAsReceiverParameter().getValue());
307            }
308            return NO_IDENTIFIER_INFO;
309        }
310    
311        public static DataFlowValue.Kind variableKind(
312                @NotNull VariableDescriptor variableDescriptor,
313                @Nullable ModuleDescriptor usageModule,
314                @NotNull BindingContext bindingContext
315        ) {
316            if (isStableVariable(variableDescriptor, usageModule)) return DataFlowValue.Kind.STABLE_VALUE;
317            boolean isLocalVar = variableDescriptor.isVar() && variableDescriptor instanceof LocalVariableDescriptor;
318            if (!isLocalVar) return DataFlowValue.Kind.OTHER;
319            if (BindingContextUtils.isVarCapturedInClosure(bindingContext, variableDescriptor)) {
320                return DataFlowValue.Kind.UNPREDICTABLE_VARIABLE;
321            }
322            return DataFlowValue.Kind.PREDICTABLE_VARIABLE;
323        }
324    
325        /**
326         * Determines whether a variable with a given descriptor is stable or not at the given usage place.
327         * <p/>
328         * Stable means that the variable value cannot change. The simple (non-property) variable is considered stable if it's immutable (val).
329         * <p/>
330         * If the variable is a property, it's considered stable if it's immutable (val) AND it's final (not open) AND
331         * the default getter is in use (otherwise nobody can guarantee that a getter is consistent) AND
332         * (it's private OR internal OR used at the same module where it's defined).
333         * The last check corresponds to a risk of changing property definition in another module, e.g. from "val" to "var".
334         *
335         * @param variableDescriptor    descriptor of a considered variable
336         * @param usageModule a module with a considered usage place, or null if it's not known (not recommended)
337         * @return true if variable is stable, false otherwise
338         */
339        public static boolean isStableVariable(
340                @NotNull VariableDescriptor variableDescriptor,
341                @Nullable ModuleDescriptor usageModule
342        ) {
343            if (variableDescriptor.isVar()) return false;
344            if (variableDescriptor instanceof PropertyDescriptor) {
345                PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor;
346                if (!isFinal(propertyDescriptor)) return false;
347                if (!hasDefaultGetter(propertyDescriptor)) return false;
348                if (!invisibleFromOtherModules(propertyDescriptor)) {
349                    ModuleDescriptor declarationModule = DescriptorUtils.getContainingModule(propertyDescriptor);
350                    if (usageModule == null || !usageModule.equals(declarationModule)) {
351                        return false;
352                    }
353                }
354            }
355            return true;
356        }
357    
358        private static boolean isFinal(PropertyDescriptor propertyDescriptor) {
359            DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration();
360            if (containingDeclaration instanceof ClassDescriptor) {
361                ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration;
362                if (classDescriptor.getModality().isOverridable() && propertyDescriptor.getModality().isOverridable()) return false;
363            }
364            else {
365                if (propertyDescriptor.getModality().isOverridable()) {
366                    throw new IllegalStateException("Property outside a class must not be overridable: " + propertyDescriptor.getName());
367                }
368            }
369            return true;
370        }
371    
372        private static boolean invisibleFromOtherModules(@NotNull DeclarationDescriptorWithVisibility descriptor) {
373            if (Visibilities.INVISIBLE_FROM_OTHER_MODULES.contains(descriptor.getVisibility())) return true;
374    
375            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
376            if (!(containingDeclaration instanceof DeclarationDescriptorWithVisibility)) {
377                return false;
378            }
379    
380            return invisibleFromOtherModules((DeclarationDescriptorWithVisibility) containingDeclaration);
381        }
382    
383        private static boolean hasDefaultGetter(PropertyDescriptor propertyDescriptor) {
384            PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
385            return getter == null || getter.isDefault();
386        }
387    }