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
017package org.jetbrains.jet.lang.resolve.calls.autocasts;
018
019import com.intellij.openapi.util.Pair;
020import org.jetbrains.annotations.NotNull;
021import org.jetbrains.annotations.Nullable;
022import org.jetbrains.jet.JetNodeTypes;
023import org.jetbrains.jet.lang.descriptors.*;
024import org.jetbrains.jet.lang.psi.*;
025import org.jetbrains.jet.lang.resolve.BindingContext;
026import org.jetbrains.jet.lang.resolve.JetModuleUtil;
027import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
028import org.jetbrains.jet.lang.resolve.scopes.receivers.*;
029import org.jetbrains.jet.lang.types.JetType;
030import org.jetbrains.jet.lang.types.TypeUtils;
031import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
032
033import static org.jetbrains.jet.lang.resolve.BindingContext.REFERENCE_TARGET;
034import static org.jetbrains.jet.lang.resolve.BindingContext.RESOLVED_CALL;
035
036public 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}