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;
018
019import com.google.common.collect.Lists;
020import com.intellij.openapi.util.text.StringUtil;
021import com.intellij.psi.PsiElement;
022import com.intellij.psi.PsiFile;
023import com.intellij.psi.util.PsiTreeUtil;
024import org.jetbrains.annotations.NotNull;
025import org.jetbrains.annotations.Nullable;
026import org.jetbrains.jet.lang.descriptors.*;
027import org.jetbrains.jet.lang.psi.*;
028import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
029import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
030import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall;
031import org.jetbrains.jet.lang.resolve.scopes.JetScope;
032import org.jetbrains.jet.lang.types.JetType;
033import org.jetbrains.jet.lang.types.JetTypeInfo;
034import org.jetbrains.jet.util.slicedmap.ReadOnlySlice;
035import org.jetbrains.jet.util.slicedmap.Slices;
036
037import java.util.*;
038
039import static org.jetbrains.jet.lang.diagnostics.Errors.AMBIGUOUS_LABEL;
040import static org.jetbrains.jet.lang.resolve.BindingContext.AMBIGUOUS_LABEL_TARGET;
041import static org.jetbrains.jet.lang.resolve.BindingContext.DECLARATION_TO_DESCRIPTOR;
042
043public 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}