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.autocasts;
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.JetModuleUtil;
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.lang.KotlinBuiltIns;
032    
033    import java.util.List;
034    
035    import static org.jetbrains.jet.lang.resolve.BindingContext.REFERENCE_TARGET;
036    import static org.jetbrains.jet.lang.resolve.BindingContext.RESOLVED_CALL;
037    
038    public class DataFlowValueFactory {
039        public static final DataFlowValueFactory INSTANCE = new DataFlowValueFactory();
040    
041        private DataFlowValueFactory() {}
042    
043        @NotNull
044        public DataFlowValue createDataFlowValue(@NotNull JetExpression expression, @NotNull JetType type, @NotNull BindingContext bindingContext) {
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.id == null ? expression : result.id, type, result.isStable, getImmanentNullability(type));
052        }
053    
054        @NotNull
055        public 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 DataFlowValue createDataFlowValue(@NotNull VariableDescriptor variableDescriptor) {
062            JetType type = variableDescriptor.getType();
063            return new DataFlowValue(variableDescriptor, type, isStableVariable(variableDescriptor), getImmanentNullability(type));
064        }
065    
066        @NotNull
067        public DataFlowValue createDataFlowValue(@NotNull ReceiverValue receiverValue, @NotNull BindingContext bindingContext) {
068            return receiverValue.accept(new ReceiverValueVisitor<DataFlowValue, BindingContext>() {
069                @Override
070                public DataFlowValue visitNoReceiver(ReceiverValue noReceiver, BindingContext data) {
071                    throw new IllegalArgumentException("No DataFlowValue exists for ReceiverValue.NO_RECEIVER");
072                }
073    
074                @Override
075                public DataFlowValue visitExtensionReceiver(ExtensionReceiver receiver, BindingContext data) {
076                    return createDataFlowValue(receiver);
077                }
078    
079                @Override
080                public DataFlowValue visitExpressionReceiver(ExpressionReceiver receiver, BindingContext bindingContext) {
081                    return createDataFlowValue(receiver.getExpression(), receiver.getType(), bindingContext);
082                }
083    
084                @Override
085                public DataFlowValue visitClassReceiver(ClassReceiver receiver, BindingContext data) {
086                    return createDataFlowValue(receiver);
087                }
088    
089                @Override
090                public DataFlowValue visitTransientReceiver(TransientReceiver receiver, BindingContext data) {
091                    return createTransientDataFlowValue(receiver);
092                }
093    
094                @Override
095                public DataFlowValue visitScriptReceiver(ScriptReceiver receiver, BindingContext data) {
096                    return createTransientDataFlowValue(receiver);
097                }
098    
099                @NotNull
100                private DataFlowValue createTransientDataFlowValue(ReceiverValue receiver) {
101                    JetType type = receiver.getType();
102                    boolean nullable = type.isNullable() || TypeUtils.hasNullableSuperType(type);
103                    return new DataFlowValue(receiver, type, nullable, Nullability.NOT_NULL);
104                }
105            }, bindingContext);
106        }
107    
108        private Nullability getImmanentNullability(JetType type) {
109            return type.isNullable() || TypeUtils.hasNullableSuperType(type) ? Nullability.UNKNOWN : Nullability.NOT_NULL;
110        }
111    
112        private static class IdentifierInfo {
113            public final Object id;
114            public final boolean isStable;
115            public final boolean isNamespace;
116    
117            private IdentifierInfo(Object id, boolean isStable, boolean isNamespace) {
118                this.id = id;
119                this.isStable = isStable;
120                this.isNamespace = isNamespace;
121            }
122        }
123    
124        private static final IdentifierInfo NO_IDENTIFIER_INFO = new IdentifierInfo(null, false, false) {
125            @Override
126            public String toString() {
127                return "NO_IDENTIFIER_INFO";
128            }
129        };
130    
131        @NotNull
132        private static IdentifierInfo createInfo(Object id, boolean isStable) {
133            return new IdentifierInfo(id, isStable, false);
134        }
135    
136        @NotNull
137        private static IdentifierInfo createNamespaceInfo(Object id) {
138            return new IdentifierInfo(id, true, true);
139        }
140    
141        @NotNull
142        private static IdentifierInfo combineInfo(@Nullable IdentifierInfo receiverInfo, @NotNull IdentifierInfo selectorInfo) {
143            if (selectorInfo.id == null) {
144                return NO_IDENTIFIER_INFO;
145            }
146            if (receiverInfo == null || receiverInfo == NO_IDENTIFIER_INFO || receiverInfo.isNamespace) {
147                return selectorInfo;
148            }
149            return createInfo(Pair.create(receiverInfo.id, selectorInfo.id), receiverInfo.isStable && selectorInfo.isStable);
150        }
151    
152        @NotNull
153        private static IdentifierInfo getIdForStableIdentifier(
154                @Nullable JetExpression expression,
155                @NotNull BindingContext bindingContext
156        ) {
157            if (expression instanceof JetParenthesizedExpression) {
158                JetParenthesizedExpression parenthesizedExpression = (JetParenthesizedExpression) expression;
159                JetExpression innerExpression = parenthesizedExpression.getExpression();
160    
161                return getIdForStableIdentifier(innerExpression, bindingContext);
162            }
163            else if (expression instanceof JetQualifiedExpression) {
164                JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) expression;
165                JetExpression receiverExpression = qualifiedExpression.getReceiverExpression();
166                JetExpression selectorExpression = qualifiedExpression.getSelectorExpression();
167                IdentifierInfo receiverId = getIdForStableIdentifier(receiverExpression, bindingContext);
168                IdentifierInfo selectorId = getIdForStableIdentifier(selectorExpression, bindingContext);
169    
170                return combineInfo(receiverId, selectorId);
171            }
172            if (expression instanceof JetSimpleNameExpression) {
173                return getIdForSimpleNameExpression((JetSimpleNameExpression) expression, bindingContext);
174            }
175            else if (expression instanceof JetThisExpression) {
176                JetThisExpression thisExpression = (JetThisExpression) expression;
177                DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, thisExpression.getInstanceReference());
178    
179                return getIdForThisReceiver(declarationDescriptor);
180            }
181            else if (expression instanceof JetRootNamespaceExpression) {
182                return createNamespaceInfo(JetModuleUtil.getRootNamespaceType(expression));
183            }
184            return NO_IDENTIFIER_INFO;
185        }
186    
187        @NotNull
188        private static IdentifierInfo getIdForSimpleNameExpression(
189                @NotNull JetSimpleNameExpression simpleNameExpression,
190                @NotNull BindingContext bindingContext
191        ) {
192            DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, simpleNameExpression);
193            if (declarationDescriptor instanceof VariableDescriptor) {
194                ResolvedCall<?> resolvedCall = bindingContext.get(RESOLVED_CALL, simpleNameExpression);
195                // todo uncomment assert
196                // for now it fails for resolving 'invoke' convention, return it after 'invoke' algorithm changes
197                // assert resolvedCall != null : "Cannot create right identifier info if the resolved call is not known yet for " + declarationDescriptor;
198    
199                IdentifierInfo receiverInfo = resolvedCall != null ? getIdForImplicitReceiver(resolvedCall.getThisObject(), simpleNameExpression) : null;
200    
201                VariableDescriptor variableDescriptor = (VariableDescriptor) declarationDescriptor;
202                return combineInfo(receiverInfo, createInfo(variableDescriptor, isStableVariable(variableDescriptor)));
203            }
204            if (declarationDescriptor instanceof NamespaceDescriptor) {
205                return createNamespaceInfo(declarationDescriptor);
206            }
207            return NO_IDENTIFIER_INFO;
208        }
209    
210        @Nullable
211        private static IdentifierInfo getIdForImplicitReceiver(@NotNull ReceiverValue receiverValue, @Nullable final JetExpression expression) {
212            return receiverValue.accept(new ReceiverValueVisitor<IdentifierInfo, Void>() {
213    
214                @Override
215                public IdentifierInfo visitNoReceiver(ReceiverValue noReceiver, Void data) {
216                    return null;
217                }
218    
219                @Override
220                public IdentifierInfo visitTransientReceiver(TransientReceiver receiver, Void data) {
221                    assert false: "Transient receiver is implicit for an explicit expression: " + expression + ". Receiver: " + receiver;
222                    return null;
223                }
224    
225                @Override
226                public IdentifierInfo visitExtensionReceiver(ExtensionReceiver receiver, Void data) {
227                    return getIdForThisReceiver(receiver);
228                }
229    
230                @Override
231                public IdentifierInfo visitExpressionReceiver(ExpressionReceiver receiver, Void data) {
232                    // there is an explicit "this" expression and it was analyzed earlier
233                    return null;
234                }
235    
236                @Override
237                public IdentifierInfo visitClassReceiver(ClassReceiver receiver, Void data) {
238                    return getIdForThisReceiver(receiver);
239                }
240    
241                @Override
242                public IdentifierInfo visitScriptReceiver(ScriptReceiver receiver, Void data) {
243                    return getIdForThisReceiver(receiver);
244                }
245            }, null);
246        }
247    
248        @NotNull
249        private static IdentifierInfo getIdForThisReceiver(@NotNull ThisReceiver thisReceiver) {
250            return getIdForThisReceiver(thisReceiver.getDeclarationDescriptor());
251        }
252    
253        @NotNull
254        private static IdentifierInfo getIdForThisReceiver(@Nullable DeclarationDescriptor descriptorOfThisReceiver) {
255            if (descriptorOfThisReceiver instanceof CallableDescriptor) {
256                ReceiverParameterDescriptor receiverParameter = ((CallableDescriptor) descriptorOfThisReceiver).getReceiverParameter();
257                assert receiverParameter != null : "'This' refers to the callable member without a receiver parameter: " + descriptorOfThisReceiver;
258                return createInfo(receiverParameter.getValue(), true);
259            }
260            if (descriptorOfThisReceiver instanceof ClassDescriptor) {
261                return createInfo(((ClassDescriptor) descriptorOfThisReceiver).getThisAsReceiverParameter().getValue(), true);
262            }
263            return NO_IDENTIFIER_INFO;
264        }
265    
266        public static boolean isStableVariable(@NotNull VariableDescriptor variableDescriptor) {
267            if (variableDescriptor.isVar()) return false;
268            if (variableDescriptor instanceof PropertyDescriptor) {
269                PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor;
270                if (!invisibleFromOtherModules(propertyDescriptor)) return false;
271                if (!isFinal(propertyDescriptor)) return false;
272                if (!hasDefaultGetter(propertyDescriptor)) return false;
273            }
274            return true;
275        }
276    
277        private static boolean isFinal(PropertyDescriptor propertyDescriptor) {
278            DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration();
279            if (containingDeclaration instanceof ClassDescriptor) {
280                ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration;
281                if (classDescriptor.getModality().isOverridable() && propertyDescriptor.getModality().isOverridable()) return false;
282            }
283            else {
284                if (propertyDescriptor.getModality().isOverridable()) {
285                    throw new IllegalStateException("Property outside a class must not be overridable: " + propertyDescriptor.getName());
286                }
287            }
288            return true;
289        }
290    
291        private static boolean invisibleFromOtherModules(@NotNull DeclarationDescriptorWithVisibility descriptor) {
292            if (Visibilities.INVISIBLE_FROM_OTHER_MODULES.contains(descriptor.getVisibility())) return true;
293    
294            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
295            if (!(containingDeclaration instanceof DeclarationDescriptorWithVisibility)) {
296                return false;
297            }
298    
299            return invisibleFromOtherModules((DeclarationDescriptorWithVisibility) containingDeclaration);
300        }
301    
302        private static boolean hasDefaultGetter(PropertyDescriptor propertyDescriptor) {
303            PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
304            return getter == null || getter.isDefault();
305        }
306    }