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 }