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