001    /*
002     * Copyright 2010-2014 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;
018    
019    import com.google.common.collect.Lists;
020    import com.intellij.openapi.util.text.StringUtil;
021    import com.intellij.psi.PsiElement;
022    import com.intellij.psi.PsiFile;
023    import com.intellij.psi.util.PsiTreeUtil;
024    import org.jetbrains.annotations.NotNull;
025    import org.jetbrains.annotations.Nullable;
026    import org.jetbrains.jet.lang.descriptors.*;
027    import org.jetbrains.jet.lang.psi.*;
028    import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
029    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
030    import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall;
031    import org.jetbrains.jet.lang.types.JetType;
032    import org.jetbrains.jet.lang.types.JetTypeInfo;
033    import org.jetbrains.jet.lang.types.TypeUtils;
034    import org.jetbrains.jet.util.slicedmap.ReadOnlySlice;
035    import org.jetbrains.jet.util.slicedmap.Slices;
036    
037    import java.util.*;
038    
039    import static org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor.Kind.DECLARATION;
040    import static org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor.Kind.SYNTHESIZED;
041    import static org.jetbrains.jet.lang.diagnostics.Errors.AMBIGUOUS_LABEL;
042    import static org.jetbrains.jet.lang.resolve.BindingContext.*;
043    
044    public class BindingContextUtils {
045        private BindingContextUtils() {
046        }
047    
048        private static final Slices.KeyNormalizer<DeclarationDescriptor> DECLARATION_DESCRIPTOR_NORMALIZER = new Slices.KeyNormalizer<DeclarationDescriptor>() {
049            @Override
050            public DeclarationDescriptor normalize(DeclarationDescriptor declarationDescriptor) {
051                if (declarationDescriptor instanceof CallableMemberDescriptor) {
052                    CallableMemberDescriptor callable = (CallableMemberDescriptor) declarationDescriptor;
053                    if (callable.getKind() != DECLARATION) {
054                        throw new IllegalStateException("non-declaration descriptors should be filtered out earlier: " + callable);
055                    }
056                }
057                //if (declarationDescriptor instanceof VariableAsFunctionDescriptor) {
058                //    VariableAsFunctionDescriptor descriptor = (VariableAsFunctionDescriptor) declarationDescriptor;
059                //    if (descriptor.getOriginal() != descriptor) {
060                //        throw new IllegalStateException("original should be resolved earlier: " + descriptor);
061                //    }
062                //}
063                return declarationDescriptor.getOriginal();
064            }
065        };
066    
067        /*package*/ static final ReadOnlySlice<DeclarationDescriptor, PsiElement> DESCRIPTOR_TO_DECLARATION =
068                Slices.<DeclarationDescriptor, PsiElement>sliceBuilder().setKeyNormalizer(DECLARATION_DESCRIPTOR_NORMALIZER).setDebugName("DESCRIPTOR_TO_DECLARATION").build();
069    
070        @Nullable
071        public static VariableDescriptor extractVariableDescriptorIfAny(@NotNull BindingContext bindingContext, @Nullable JetElement element, boolean onlyReference) {
072            DeclarationDescriptor descriptor = null;
073            if (!onlyReference && (element instanceof JetVariableDeclaration || element instanceof JetParameter)) {
074                descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element);
075            }
076            else if (element instanceof JetSimpleNameExpression) {
077                descriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) element);
078            }
079            else if (element instanceof JetQualifiedExpression) {
080                descriptor = extractVariableDescriptorIfAny(bindingContext, ((JetQualifiedExpression) element).getSelectorExpression(), onlyReference);
081            }
082            if (descriptor instanceof VariableDescriptor) {
083                return (VariableDescriptor) descriptor;
084            }
085            return null;
086        }
087    
088        @Nullable
089        public static JetFile getContainingFile(@NotNull BindingContext context, @NotNull DeclarationDescriptor declarationDescriptor) {
090            // declarationDescriptor may describe a synthesized element which doesn't have PSI
091            // To workaround that, we find a top-level parent (which is inside a PackageFragmentDescriptor), which is guaranteed to have PSI
092            DeclarationDescriptor descriptor = findTopLevelParent(declarationDescriptor);
093            if (descriptor == null) return null;
094    
095            PsiElement declaration = descriptorToDeclaration(context, descriptor);
096            if (declaration == null) return null;
097    
098            PsiFile containingFile = declaration.getContainingFile();
099            if (!(containingFile instanceof JetFile)) return null;
100            return (JetFile) containingFile;
101        }
102    
103        @Nullable
104        private static DeclarationDescriptor findTopLevelParent(@NotNull DeclarationDescriptor declarationDescriptor) {
105            DeclarationDescriptor descriptor = declarationDescriptor;
106            if (declarationDescriptor instanceof PropertyAccessorDescriptor) {
107                descriptor = ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty();
108            }
109            while (!(descriptor == null || DescriptorUtils.isTopLevelDeclaration(descriptor))) {
110                descriptor = descriptor.getContainingDeclaration();
111            }
112            return descriptor;
113        }
114    
115        @Nullable
116        private static PsiElement doGetDescriptorToDeclaration(@NotNull BindingContext context, @NotNull DeclarationDescriptor descriptor) {
117            return context.get(DESCRIPTOR_TO_DECLARATION, descriptor);
118        }
119    
120        // NOTE this is also used by KDoc
121        @Nullable
122        public static PsiElement descriptorToDeclaration(@NotNull BindingContext context, @NotNull DeclarationDescriptor descriptor) {
123            if (descriptor instanceof CallableMemberDescriptor) {
124                return callableDescriptorToDeclaration(context, (CallableMemberDescriptor) descriptor);
125            }
126            else if (descriptor instanceof ClassDescriptor) {
127                return classDescriptorToDeclaration(context, (ClassDescriptor) descriptor);
128            }
129            else {
130                return doGetDescriptorToDeclaration(context, descriptor);
131            }
132        }
133    
134        @NotNull
135        public static List<PsiElement> descriptorToDeclarations(@NotNull BindingContext context, @NotNull DeclarationDescriptor descriptor) {
136            if (descriptor instanceof CallableMemberDescriptor) {
137                return callableDescriptorToDeclarations(context, (CallableMemberDescriptor) descriptor);
138            }
139            else {
140                PsiElement psiElement = descriptorToDeclaration(context, descriptor);
141                if (psiElement != null) {
142                    return Lists.newArrayList(psiElement);
143                } else {
144                    return Lists.newArrayList();
145                }
146            }
147        }
148    
149        @Nullable
150        public static PsiElement callableDescriptorToDeclaration(@NotNull BindingContext context, @NotNull CallableMemberDescriptor callable) {
151            if (callable.getKind() == SYNTHESIZED) {
152                CallableMemberDescriptor original = callable.getOriginal();
153                if (original instanceof SynthesizedCallableMemberDescriptor<?>) {
154                    DeclarationDescriptor base = ((SynthesizedCallableMemberDescriptor<?>) original).getBaseForSynthesized();
155                    return descriptorToDeclaration(context, base);
156                }
157                return null;
158            }
159    
160            if (callable.getKind() == DECLARATION) {
161                return doGetDescriptorToDeclaration(context, callable.getOriginal());
162            }
163    
164            Set<? extends CallableMemberDescriptor> overriddenDescriptors = callable.getOverriddenDescriptors();
165            if (overriddenDescriptors.size() != 1) {
166                throw new IllegalStateException(
167                        "Cannot find declaration: fake descriptor " + callable + " has more than one overridden descriptor:\n" +
168                        StringUtil.join(overriddenDescriptors, ",\n"));
169            }
170    
171            return callableDescriptorToDeclaration(context, overriddenDescriptors.iterator().next());
172        }
173    
174        @NotNull
175        public static List<PsiElement> callableDescriptorToDeclarations(
176                @NotNull BindingContext context,
177                @NotNull CallableMemberDescriptor callable
178        ) {
179            if (callable.getKind() == SYNTHESIZED) {
180                CallableMemberDescriptor original = callable.getOriginal();
181                if (original instanceof SynthesizedCallableMemberDescriptor<?>) {
182                    DeclarationDescriptor base = ((SynthesizedCallableMemberDescriptor<?>) original).getBaseForSynthesized();
183                    return descriptorToDeclarations(context, base);
184                }
185                return Collections.emptyList();
186            }
187    
188            if (callable.getKind() == DECLARATION) {
189                PsiElement psiElement = doGetDescriptorToDeclaration(context, callable);
190                return psiElement != null ? Lists.newArrayList(psiElement) : Lists.<PsiElement>newArrayList();
191            }
192    
193            List<PsiElement> r = new ArrayList<PsiElement>();
194            Set<? extends CallableMemberDescriptor> overriddenDescriptors = callable.getOverriddenDescriptors();
195            for (CallableMemberDescriptor overridden : overriddenDescriptors) {
196                r.addAll(callableDescriptorToDeclarations(context, overridden));
197            }
198            return r;
199        }
200    
201        @Nullable
202        public static PsiElement classDescriptorToDeclaration(@NotNull BindingContext context, @NotNull ClassDescriptor clazz) {
203            return doGetDescriptorToDeclaration(context, clazz);
204        }
205    
206        public static void recordFunctionDeclarationToDescriptor(@NotNull BindingTrace trace,
207                @NotNull PsiElement psiElement, @NotNull SimpleFunctionDescriptor function) {
208    
209            if (function.getKind() != DECLARATION) {
210                throw new IllegalArgumentException("function of kind " + function.getKind() + " cannot have declaration");
211            }
212    
213            trace.record(BindingContext.FUNCTION, psiElement, function);
214        }
215    
216        @NotNull
217        public static <K, V> V getNotNull(
218            @NotNull BindingContext bindingContext,
219            @NotNull ReadOnlySlice<K, V> slice,
220            @NotNull K key
221        ) {
222            return getNotNull(bindingContext, slice, key, "Value at " + slice + " must not be null for " + key);
223        }
224    
225        @NotNull
226        public static <K, V> V getNotNull(
227                @NotNull BindingContext bindingContext,
228                @NotNull ReadOnlySlice<K, V> slice,
229                @NotNull K key,
230                @NotNull String messageIfNull
231        ) {
232            V value = bindingContext.get(slice, key);
233            if (value == null) {
234                throw new IllegalStateException(messageIfNull);
235            }
236            return value;
237        }
238    
239        @NotNull
240        public static DeclarationDescriptor getEnclosingDescriptor(@NotNull BindingContext context, @NotNull JetElement element) {
241            JetNamedDeclaration declaration = PsiTreeUtil.getParentOfType(element, JetNamedDeclaration.class);
242            if (declaration instanceof JetFunctionLiteral) {
243                return getEnclosingDescriptor(context, declaration);
244            }
245            DeclarationDescriptor descriptor = context.get(DECLARATION_TO_DESCRIPTOR, declaration);
246            assert descriptor != null : "No descriptor for named declaration: " + declaration.getText() + "\n(of type " + declaration.getClass() + ")";
247            return descriptor;
248        }
249    
250        public static FunctionDescriptor getEnclosingFunctionDescriptor(@NotNull BindingContext context, @NotNull JetElement element) {
251            JetFunction function = PsiTreeUtil.getParentOfType(element, JetFunction.class);
252            return (FunctionDescriptor)context.get(DECLARATION_TO_DESCRIPTOR, function);
253        }
254    
255        public static void reportAmbiguousLabel(
256                @NotNull BindingTrace trace,
257                @NotNull JetSimpleNameExpression targetLabel,
258                @NotNull Collection<DeclarationDescriptor> declarationsByLabel
259        ) {
260            Collection<PsiElement> targets = Lists.newArrayList();
261            for (DeclarationDescriptor descriptor : declarationsByLabel) {
262                PsiElement element = descriptorToDeclaration(trace.getBindingContext(), descriptor);
263                assert element != null : "Label can only point to something in the same lexical scope";
264                targets.add(element);
265            }
266            if (!targets.isEmpty()) {
267                trace.record(AMBIGUOUS_LABEL_TARGET, targetLabel, targets);
268            }
269            trace.report(AMBIGUOUS_LABEL.on(targetLabel));
270        }
271    
272        @Nullable
273        public static JetType updateRecordedType(
274                @Nullable JetType type,
275                @NotNull JetExpression expression,
276                @NotNull BindingTrace trace,
277                boolean shouldBeMadeNullable
278        ) {
279            if (type == null) return null;
280            if (shouldBeMadeNullable) {
281                type = TypeUtils.makeNullable(type);
282            }
283            trace.record(BindingContext.EXPRESSION_TYPE, expression, type);
284            return type;
285        }
286    
287        @Nullable
288        public static JetTypeInfo getRecordedTypeInfo(@NotNull JetExpression expression, @NotNull BindingContext context) {
289            if (!context.get(BindingContext.PROCESSED, expression)) return null;
290            DataFlowInfo dataFlowInfo = context.get(BindingContext.EXPRESSION_DATA_FLOW_INFO, expression);
291            if (dataFlowInfo == null) {
292                dataFlowInfo = DataFlowInfo.EMPTY;
293            }
294            JetType type = context.get(BindingContext.EXPRESSION_TYPE, expression);
295            return JetTypeInfo.create(type, dataFlowInfo);
296        }
297    
298        public static boolean isExpressionWithValidReference(
299                @NotNull JetExpression expression,
300                @NotNull BindingContext context
301        ) {
302            if (expression instanceof JetCallExpression) {
303                return isCallExpressionWithValidReference(expression, context);
304            }
305    
306            return expression instanceof JetReferenceExpression;
307        }
308    
309        public static boolean isCallExpressionWithValidReference(
310                @NotNull JetExpression expression,
311                @NotNull BindingContext context
312        ) {
313            if (expression instanceof JetCallExpression) {
314                JetExpression calleeExpression = ((JetCallExpression) expression).getCalleeExpression();
315                ResolvedCall<?> resolvedCall = context.get(BindingContext.RESOLVED_CALL, calleeExpression);
316                if (resolvedCall instanceof VariableAsFunctionResolvedCall) {
317                    return true;
318                }
319            }
320            return false;
321        }
322    
323        public static boolean isVarCapturedInClosure(BindingContext bindingContext, DeclarationDescriptor descriptor) {
324            if (!(descriptor instanceof VariableDescriptor) || descriptor instanceof PropertyDescriptor) return false;
325            VariableDescriptor variableDescriptor = (VariableDescriptor) descriptor;
326            return bindingContext.get(CAPTURED_IN_CLOSURE, variableDescriptor) != null && variableDescriptor.isVar();
327        }
328    }