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 }