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                                         /* stableIdentifier  = */false,
085                                         /* uncapturedLocalVariable = */false,
086                                         Nullability.NOT_NULL);
087            }
088    
089            IdentifierInfo result = getIdForStableIdentifier(expression, bindingContext, containingDeclarationOrModule);
090            return new DataFlowValue(result == NO_IDENTIFIER_INFO ? expression : result.id,
091                                     type,
092                                     result.isStable,
093                                     result.isLocal,
094                                     getImmanentNullability(type));
095        }
096    
097        @NotNull
098        public static DataFlowValue createDataFlowValue(@NotNull ThisReceiver receiver) {
099            JetType type = receiver.getType();
100            return new DataFlowValue(receiver, type, true, false, getImmanentNullability(type));
101        }
102    
103        @NotNull
104        public static DataFlowValue createDataFlowValue(
105                @NotNull ReceiverValue receiverValue,
106                @NotNull ResolutionContext resolutionContext
107        ) {
108            return createDataFlowValue(receiverValue, resolutionContext.trace.getBindingContext(),
109                                       resolutionContext.scope.getOwnerDescriptor());
110        }
111    
112        @NotNull
113        public static DataFlowValue createDataFlowValue(
114                @NotNull ReceiverValue receiverValue,
115                @NotNull BindingContext bindingContext,
116                @NotNull DeclarationDescriptor containingDeclarationOrModule
117        ) {
118            if (receiverValue instanceof TransientReceiver || receiverValue instanceof ScriptReceiver) {
119                // SCRIPT: smartcasts data flow
120                JetType type = receiverValue.getType();
121                return new DataFlowValue(receiverValue, type, true, false, getImmanentNullability(type));
122            }
123            else if (receiverValue instanceof ClassReceiver || receiverValue instanceof ExtensionReceiver) {
124                return createDataFlowValue((ThisReceiver) receiverValue);
125            }
126            else if (receiverValue instanceof ExpressionReceiver) {
127                return createDataFlowValue(((ExpressionReceiver) receiverValue).getExpression(),
128                                           receiverValue.getType(),
129                                           bindingContext,
130                                           containingDeclarationOrModule);
131            }
132            else if (receiverValue == ReceiverValue.NO_RECEIVER) {
133                throw new IllegalArgumentException("No DataFlowValue exists for ReceiverValue.NO_RECEIVER");
134            }
135            else {
136                throw new UnsupportedOperationException("Unsupported receiver value: " + receiverValue.getClass().getName());
137            }
138        }
139    
140        @NotNull
141        public static DataFlowValue createDataFlowValue(
142                @NotNull VariableDescriptor variableDescriptor,
143                @NotNull BindingContext bindingContext,
144                @Nullable ModuleDescriptor usageContainingModule
145        ) {
146            JetType type = variableDescriptor.getType();
147            return new DataFlowValue(variableDescriptor, type,
148                                     isStableVariable(variableDescriptor, usageContainingModule),
149                                     isUncapturedLocalVariable(variableDescriptor, bindingContext),
150                                     getImmanentNullability(type));
151        }
152    
153        @NotNull
154        private static Nullability getImmanentNullability(@NotNull JetType type) {
155            return TypeUtils.isNullableType(type) ? Nullability.UNKNOWN : Nullability.NOT_NULL;
156        }
157    
158        private static class IdentifierInfo {
159            public final Object id;
160            public final boolean isStable;
161            public final boolean isLocal;
162            public final boolean isPackage;
163    
164            private IdentifierInfo(Object id, boolean isStable, boolean isLocal, boolean isPackage) {
165                assert !isStable || !isLocal : "Identifier info for object " + id + " cannot be stable and local at one time";
166                this.id = id;
167                this.isStable = isStable;
168                this.isLocal = isLocal;
169                this.isPackage = isPackage;
170            }
171        }
172    
173        private static final IdentifierInfo NO_IDENTIFIER_INFO = new IdentifierInfo(null, false, false, false) {
174            @Override
175            public String toString() {
176                return "NO_IDENTIFIER_INFO";
177            }
178        };
179    
180        @NotNull
181        private static IdentifierInfo createInfo(Object id, boolean isStable, boolean isLocal) {
182            return new IdentifierInfo(id, isStable, isLocal, false);
183        }
184    
185        @NotNull
186        private static IdentifierInfo createStableInfo(Object id) {
187            return createInfo(id, true, false);
188        }
189    
190        @NotNull
191        private static IdentifierInfo createPackageOrClassInfo(Object id) {
192            return new IdentifierInfo(id, true, false, true);
193        }
194    
195        @NotNull
196        private static IdentifierInfo combineInfo(@Nullable IdentifierInfo receiverInfo, @NotNull IdentifierInfo selectorInfo) {
197            if (selectorInfo.id == null || receiverInfo == NO_IDENTIFIER_INFO) {
198                return NO_IDENTIFIER_INFO;
199            }
200            if (receiverInfo == null || receiverInfo.isPackage) {
201                return selectorInfo;
202            }
203            return createInfo(Pair.create(receiverInfo.id, selectorInfo.id),
204                              receiverInfo.isStable && selectorInfo.isStable,
205                              // x.y can never be a local variable
206                              false);
207        }
208    
209        @NotNull
210        private static IdentifierInfo createPostfixInfo(@NotNull JetPostfixExpression expression, @NotNull IdentifierInfo argumentInfo) {
211            if (argumentInfo == NO_IDENTIFIER_INFO) {
212                return NO_IDENTIFIER_INFO;
213            }
214            return createInfo(Pair.create(expression, argumentInfo.id), argumentInfo.isStable, argumentInfo.isLocal);
215        }
216    
217        @NotNull
218        private static IdentifierInfo getIdForStableIdentifier(
219                @Nullable JetExpression expression,
220                @NotNull BindingContext bindingContext,
221                @NotNull DeclarationDescriptor containingDeclarationOrModule
222        ) {
223            if (expression != null) {
224                JetExpression deparenthesized = JetPsiUtil.deparenthesize(expression);
225                if (expression != deparenthesized) {
226                    return getIdForStableIdentifier(deparenthesized, bindingContext, containingDeclarationOrModule);
227                }
228            }
229            if (expression instanceof JetQualifiedExpression) {
230                JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) expression;
231                JetExpression receiverExpression = qualifiedExpression.getReceiverExpression();
232                JetExpression selectorExpression = qualifiedExpression.getSelectorExpression();
233                IdentifierInfo receiverId = getIdForStableIdentifier(receiverExpression, bindingContext, containingDeclarationOrModule);
234                IdentifierInfo selectorId = getIdForStableIdentifier(selectorExpression, bindingContext, containingDeclarationOrModule);
235    
236                return combineInfo(receiverId, selectorId);
237            }
238            if (expression instanceof JetSimpleNameExpression) {
239                return getIdForSimpleNameExpression((JetSimpleNameExpression) expression, bindingContext, containingDeclarationOrModule);
240            }
241            else if (expression instanceof JetThisExpression) {
242                JetThisExpression thisExpression = (JetThisExpression) expression;
243                DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, thisExpression.getInstanceReference());
244    
245                return getIdForThisReceiver(declarationDescriptor);
246            }
247            else if (expression instanceof JetPostfixExpression) {
248                JetPostfixExpression postfixExpression = (JetPostfixExpression) expression;
249                IElementType operationType = postfixExpression.getOperationReference().getReferencedNameElementType();
250                if (operationType == JetTokens.PLUSPLUS || operationType == JetTokens.MINUSMINUS) {
251                    return createPostfixInfo(postfixExpression,
252                            getIdForStableIdentifier(postfixExpression.getBaseExpression(), bindingContext, containingDeclarationOrModule));
253                }
254            }
255            else if (expression instanceof JetRootPackageExpression) {
256                //todo return createPackageInfo());
257            }
258            return NO_IDENTIFIER_INFO;
259        }
260    
261        @NotNull
262        private static IdentifierInfo getIdForSimpleNameExpression(
263                @NotNull JetSimpleNameExpression simpleNameExpression,
264                @NotNull BindingContext bindingContext,
265                @NotNull DeclarationDescriptor containingDeclarationOrModule
266        ) {
267            DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, simpleNameExpression);
268            if (declarationDescriptor instanceof VariableDescriptor) {
269                ResolvedCall<?> resolvedCall = CallUtilPackage.getResolvedCall(simpleNameExpression, bindingContext);
270    
271                // todo uncomment assert
272                // KT-4113
273                // for now it fails for resolving 'invoke' convention, return it after 'invoke' algorithm changes
274                // assert resolvedCall != null : "Cannot create right identifier info if the resolved call is not known yet for
275                ModuleDescriptor usageModuleDescriptor = DescriptorUtils.getContainingModuleOrNull(containingDeclarationOrModule);
276                IdentifierInfo receiverInfo =
277                        resolvedCall != null ? getIdForImplicitReceiver(resolvedCall.getDispatchReceiver(), simpleNameExpression) : null;
278    
279                VariableDescriptor variableDescriptor = (VariableDescriptor) declarationDescriptor;
280                return combineInfo(receiverInfo, createInfo(variableDescriptor,
281                                                            isStableVariable(variableDescriptor, usageModuleDescriptor),
282                                                            isUncapturedLocalVariable(variableDescriptor, bindingContext)));
283            }
284            if (declarationDescriptor instanceof PackageViewDescriptor || declarationDescriptor instanceof ClassDescriptor) {
285                return createPackageOrClassInfo(declarationDescriptor);
286            }
287            return NO_IDENTIFIER_INFO;
288        }
289    
290        @Nullable
291        private static IdentifierInfo getIdForImplicitReceiver(@NotNull ReceiverValue receiverValue, @Nullable JetExpression expression) {
292            if (receiverValue instanceof ThisReceiver) {
293                return getIdForThisReceiver(((ThisReceiver) receiverValue).getDeclarationDescriptor());
294            }
295            else {
296                assert !(receiverValue instanceof TransientReceiver)
297                        : "Transient receiver is implicit for an explicit expression: " + expression + ". Receiver: " + receiverValue;
298                // For ExpressionReceiver there is an explicit "this" expression and it was analyzed earlier
299                return null;
300            }
301        }
302    
303        @NotNull
304        private static IdentifierInfo getIdForThisReceiver(@Nullable DeclarationDescriptor descriptorOfThisReceiver) {
305            if (descriptorOfThisReceiver instanceof CallableDescriptor) {
306                ReceiverParameterDescriptor receiverParameter = ((CallableDescriptor) descriptorOfThisReceiver).getExtensionReceiverParameter();
307                assert receiverParameter != null : "'This' refers to the callable member without a receiver parameter: " +
308                                                   descriptorOfThisReceiver;
309                return createStableInfo(receiverParameter.getValue());
310            }
311            if (descriptorOfThisReceiver instanceof ClassDescriptor) {
312                return createStableInfo(((ClassDescriptor) descriptorOfThisReceiver).getThisAsReceiverParameter().getValue());
313            }
314            return NO_IDENTIFIER_INFO;
315        }
316    
317        public static boolean isUncapturedLocalVariable(@NotNull VariableDescriptor variableDescriptor, @NotNull BindingContext bindingContext) {
318            return variableDescriptor.isVar()
319                   && variableDescriptor instanceof LocalVariableDescriptor
320                   && !BindingContextUtils.isVarCapturedInClosure(bindingContext, variableDescriptor);
321        }
322    
323        /**
324         * Determines whether a variable with a given descriptor is stable or not at the given usage place.
325         * <p/>
326         * Stable means that the variable value cannot change. The simple (non-property) variable is considered stable if it's immutable (val).
327         * <p/>
328         * If the variable is a property, it's considered stable if it's immutable (val) AND it's final (not open) AND
329         * the default getter is in use (otherwise nobody can guarantee that a getter is consistent) AND
330         * (it's private OR internal OR used at the same module where it's defined).
331         * The last check corresponds to a risk of changing property definition in another module, e.g. from "val" to "var".
332         *
333         * @param variableDescriptor    descriptor of a considered variable
334         * @param usageModule a module with a considered usage place, or null if it's not known (not recommended)
335         * @return true if variable is stable, false otherwise
336         */
337        public static boolean isStableVariable(
338                @NotNull VariableDescriptor variableDescriptor,
339                @Nullable ModuleDescriptor usageModule
340        ) {
341            if (variableDescriptor.isVar()) return false;
342            if (variableDescriptor instanceof PropertyDescriptor) {
343                PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor;
344                if (!isFinal(propertyDescriptor)) return false;
345                if (!hasDefaultGetter(propertyDescriptor)) return false;
346                if (!invisibleFromOtherModules(propertyDescriptor)) {
347                    ModuleDescriptor declarationModule = DescriptorUtils.getContainingModule(propertyDescriptor);
348                    if (usageModule == null || !usageModule.equals(declarationModule)) {
349                        return false;
350                    }
351                }
352            }
353            return true;
354        }
355    
356        private static boolean isFinal(PropertyDescriptor propertyDescriptor) {
357            DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration();
358            if (containingDeclaration instanceof ClassDescriptor) {
359                ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration;
360                if (classDescriptor.getModality().isOverridable() && propertyDescriptor.getModality().isOverridable()) return false;
361            }
362            else {
363                if (propertyDescriptor.getModality().isOverridable()) {
364                    throw new IllegalStateException("Property outside a class must not be overridable: " + propertyDescriptor.getName());
365                }
366            }
367            return true;
368        }
369    
370        private static boolean invisibleFromOtherModules(@NotNull DeclarationDescriptorWithVisibility descriptor) {
371            if (Visibilities.INVISIBLE_FROM_OTHER_MODULES.contains(descriptor.getVisibility())) return true;
372    
373            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
374            if (!(containingDeclaration instanceof DeclarationDescriptorWithVisibility)) {
375                return false;
376            }
377    
378            return invisibleFromOtherModules((DeclarationDescriptorWithVisibility) containingDeclaration);
379        }
380    
381        private static boolean hasDefaultGetter(PropertyDescriptor propertyDescriptor) {
382            PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
383            return getter == null || getter.isDefault();
384        }
385    }