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