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