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