001    /*
002     * Copyright 2010-2013 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.jet.lang.resolve.calls.smartcasts;
018    
019    import com.intellij.openapi.util.Pair;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.jet.JetNodeTypes;
023    import org.jetbrains.jet.lang.descriptors.*;
024    import org.jetbrains.jet.lang.psi.*;
025    import org.jetbrains.jet.lang.resolve.BindingContext;
026    import org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage;
027    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
028    import org.jetbrains.jet.lang.resolve.scopes.receivers.*;
029    import org.jetbrains.jet.lang.types.JetType;
030    import org.jetbrains.jet.lang.types.TypeUtils;
031    import org.jetbrains.jet.lang.types.TypesPackage;
032    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
033    
034    import static org.jetbrains.jet.lang.resolve.BindingContext.REFERENCE_TARGET;
035    
036    public class DataFlowValueFactory {
037        private DataFlowValueFactory() {}
038    
039        @NotNull
040        public static DataFlowValue createDataFlowValue(
041                @NotNull JetExpression expression,
042                @NotNull JetType type,
043                @NotNull BindingContext bindingContext
044        ) {
045            if (expression instanceof JetConstantExpression) {
046                JetConstantExpression constantExpression = (JetConstantExpression) expression;
047                if (constantExpression.getNode().getElementType() == JetNodeTypes.NULL) return DataFlowValue.NULL;
048            }
049            if (TypeUtils.equalTypes(type, KotlinBuiltIns.getInstance().getNullableNothingType())) return DataFlowValue.NULL; // 'null' is the only inhabitant of 'Nothing?'
050            IdentifierInfo result = getIdForStableIdentifier(expression, bindingContext);
051            return new DataFlowValue(result == NO_IDENTIFIER_INFO ? expression : result.id, type, result.isStable, getImmanentNullability(type));
052        }
053    
054        @NotNull
055        public static DataFlowValue createDataFlowValue(@NotNull ThisReceiver receiver) {
056            JetType type = receiver.getType();
057            return new DataFlowValue(receiver, type, true, getImmanentNullability(type));
058        }
059    
060        @NotNull
061        public static DataFlowValue createDataFlowValue(@NotNull ReceiverValue receiverValue, @NotNull BindingContext bindingContext) {
062            if (receiverValue instanceof TransientReceiver || receiverValue instanceof ScriptReceiver) {
063                // SCRIPT: smartcasts data flow
064                JetType type = receiverValue.getType();
065                boolean nullable = type.isNullable() || TypeUtils.hasNullableSuperType(type);
066                return new DataFlowValue(receiverValue, type, nullable, Nullability.NOT_NULL);
067            }
068            else if (receiverValue instanceof ClassReceiver || receiverValue instanceof ExtensionReceiver) {
069                return createDataFlowValue((ThisReceiver) receiverValue);
070            }
071            else if (receiverValue instanceof ExpressionReceiver) {
072                return createDataFlowValue(((ExpressionReceiver) receiverValue).getExpression(), receiverValue.getType(), bindingContext);
073            }
074            else if (receiverValue == ReceiverValue.NO_RECEIVER) {
075                throw new IllegalArgumentException("No DataFlowValue exists for ReceiverValue.NO_RECEIVER");
076            }
077            else {
078                throw new UnsupportedOperationException("Unsupported receiver value: " + receiverValue.getClass().getName());
079            }
080        }
081    
082        @NotNull
083        private static Nullability getImmanentNullability(@NotNull JetType type) {
084            return TypeUtils.isNullableType(type) ? Nullability.UNKNOWN : Nullability.NOT_NULL;
085        }
086    
087        private static class IdentifierInfo {
088            public final Object id;
089            public final boolean isStable;
090            public final boolean isPackage;
091    
092            private IdentifierInfo(Object id, boolean isStable, boolean isPackage) {
093                this.id = id;
094                this.isStable = isStable;
095                this.isPackage = isPackage;
096            }
097        }
098    
099        private static final IdentifierInfo NO_IDENTIFIER_INFO = new IdentifierInfo(null, false, false) {
100            @Override
101            public String toString() {
102                return "NO_IDENTIFIER_INFO";
103            }
104        };
105    
106        @NotNull
107        private static IdentifierInfo createInfo(Object id, boolean isStable) {
108            return new IdentifierInfo(id, isStable, false);
109        }
110    
111        @NotNull
112        private static IdentifierInfo createPackageInfo(Object id) {
113            return new IdentifierInfo(id, true, true);
114        }
115    
116        @NotNull
117        private static IdentifierInfo combineInfo(@Nullable IdentifierInfo receiverInfo, @NotNull IdentifierInfo selectorInfo) {
118            if (selectorInfo.id == null) {
119                return NO_IDENTIFIER_INFO;
120            }
121            if (receiverInfo == null || receiverInfo == NO_IDENTIFIER_INFO || receiverInfo.isPackage) {
122                return selectorInfo;
123            }
124            return createInfo(Pair.create(receiverInfo.id, selectorInfo.id), receiverInfo.isStable && selectorInfo.isStable);
125        }
126    
127        @NotNull
128        private static IdentifierInfo getIdForStableIdentifier(
129                @Nullable JetExpression expression,
130                @NotNull BindingContext bindingContext
131        ) {
132            if (expression != null) {
133                JetExpression deparenthesized = JetPsiUtil.deparenthesize(expression);
134                if (expression != deparenthesized) {
135                    return getIdForStableIdentifier(deparenthesized, bindingContext);
136                }
137            }
138            if (expression instanceof JetQualifiedExpression) {
139                JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) expression;
140                JetExpression receiverExpression = qualifiedExpression.getReceiverExpression();
141                JetExpression selectorExpression = qualifiedExpression.getSelectorExpression();
142                IdentifierInfo receiverId = getIdForStableIdentifier(receiverExpression, bindingContext);
143                IdentifierInfo selectorId = getIdForStableIdentifier(selectorExpression, bindingContext);
144    
145                return combineInfo(receiverId, selectorId);
146            }
147            if (expression instanceof JetSimpleNameExpression) {
148                return getIdForSimpleNameExpression((JetSimpleNameExpression) expression, bindingContext);
149            }
150            else if (expression instanceof JetThisExpression) {
151                JetThisExpression thisExpression = (JetThisExpression) expression;
152                DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, thisExpression.getInstanceReference());
153    
154                return getIdForThisReceiver(declarationDescriptor);
155            }
156            else if (expression instanceof JetRootPackageExpression) {
157                //todo return createPackageInfo());
158            }
159            return NO_IDENTIFIER_INFO;
160        }
161    
162        @NotNull
163        private static IdentifierInfo getIdForSimpleNameExpression(
164                @NotNull JetSimpleNameExpression simpleNameExpression,
165                @NotNull BindingContext bindingContext
166        ) {
167            DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, simpleNameExpression);
168            if (declarationDescriptor instanceof VariableDescriptor) {
169                ResolvedCall<?> resolvedCall = CallUtilPackage.getResolvedCall(simpleNameExpression, bindingContext);
170                // todo uncomment assert
171                // KT-4113
172                // for now it fails for resolving 'invoke' convention, return it after 'invoke' algorithm changes
173                // assert resolvedCall != null : "Cannot create right identifier info if the resolved call is not known yet for " + declarationDescriptor;
174    
175                IdentifierInfo receiverInfo =
176                        resolvedCall != null ? getIdForImplicitReceiver(resolvedCall.getDispatchReceiver(), simpleNameExpression) : null;
177    
178                VariableDescriptor variableDescriptor = (VariableDescriptor) declarationDescriptor;
179                return combineInfo(receiverInfo, createInfo(variableDescriptor, isStableVariable(variableDescriptor)));
180            }
181            if (declarationDescriptor instanceof PackageViewDescriptor) {
182                return createPackageInfo(declarationDescriptor);
183            }
184            return NO_IDENTIFIER_INFO;
185        }
186    
187        @Nullable
188        private static IdentifierInfo getIdForImplicitReceiver(@NotNull ReceiverValue receiverValue, @Nullable JetExpression expression) {
189            if (receiverValue instanceof ThisReceiver) {
190                return getIdForThisReceiver(((ThisReceiver) receiverValue).getDeclarationDescriptor());
191            }
192            else {
193                assert !(receiverValue instanceof TransientReceiver)
194                        : "Transient receiver is implicit for an explicit expression: " + expression + ". Receiver: " + receiverValue;
195                // For ExpressionReceiver there is an explicit "this" expression and it was analyzed earlier
196                return null;
197            }
198        }
199    
200        @NotNull
201        private static IdentifierInfo getIdForThisReceiver(@Nullable DeclarationDescriptor descriptorOfThisReceiver) {
202            if (descriptorOfThisReceiver instanceof CallableDescriptor) {
203                ReceiverParameterDescriptor receiverParameter = ((CallableDescriptor) descriptorOfThisReceiver).getExtensionReceiverParameter();
204                assert receiverParameter != null : "'This' refers to the callable member without a receiver parameter: " + descriptorOfThisReceiver;
205                return createInfo(receiverParameter.getValue(), true);
206            }
207            if (descriptorOfThisReceiver instanceof ClassDescriptor) {
208                return createInfo(((ClassDescriptor) descriptorOfThisReceiver).getThisAsReceiverParameter().getValue(), true);
209            }
210            return NO_IDENTIFIER_INFO;
211        }
212    
213        public static boolean isStableVariable(@NotNull VariableDescriptor variableDescriptor) {
214            if (variableDescriptor.isVar()) return false;
215            if (variableDescriptor instanceof PropertyDescriptor) {
216                PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor;
217                if (!invisibleFromOtherModules(propertyDescriptor)) return false;
218                if (!isFinal(propertyDescriptor)) return false;
219                if (!hasDefaultGetter(propertyDescriptor)) return false;
220            }
221            return true;
222        }
223    
224        private static boolean isFinal(PropertyDescriptor propertyDescriptor) {
225            DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration();
226            if (containingDeclaration instanceof ClassDescriptor) {
227                ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration;
228                if (classDescriptor.getModality().isOverridable() && propertyDescriptor.getModality().isOverridable()) return false;
229            }
230            else {
231                if (propertyDescriptor.getModality().isOverridable()) {
232                    throw new IllegalStateException("Property outside a class must not be overridable: " + propertyDescriptor.getName());
233                }
234            }
235            return true;
236        }
237    
238        private static boolean invisibleFromOtherModules(@NotNull DeclarationDescriptorWithVisibility descriptor) {
239            if (Visibilities.INVISIBLE_FROM_OTHER_MODULES.contains(descriptor.getVisibility())) return true;
240    
241            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
242            if (!(containingDeclaration instanceof DeclarationDescriptorWithVisibility)) {
243                return false;
244            }
245    
246            return invisibleFromOtherModules((DeclarationDescriptorWithVisibility) containingDeclaration);
247        }
248    
249        private static boolean hasDefaultGetter(PropertyDescriptor propertyDescriptor) {
250            PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
251            return getter == null || getter.isDefault();
252        }
253    }