001 /*
002 * Copyright 2010-2015 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.kotlin.types.expressions;
018
019 import com.google.common.collect.Sets;
020 import com.intellij.psi.PsiElement;
021 import org.jetbrains.annotations.NotNull;
022 import org.jetbrains.annotations.Nullable;
023 import org.jetbrains.kotlin.descriptors.*;
024 import org.jetbrains.kotlin.name.Name;
025 import org.jetbrains.kotlin.psi.*;
026 import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt;
027 import org.jetbrains.kotlin.resolve.*;
028 import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
029 import org.jetbrains.kotlin.resolve.scopes.utils.ScopeUtilsKt;
030
031 import java.util.Collection;
032 import java.util.Set;
033
034 import static org.jetbrains.kotlin.diagnostics.Errors.LABEL_NAME_CLASH;
035 import static org.jetbrains.kotlin.diagnostics.Errors.UNRESOLVED_REFERENCE;
036 import static org.jetbrains.kotlin.resolve.BindingContext.LABEL_TARGET;
037 import static org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET;
038
039 public class LabelResolver {
040
041 public static LabelResolver INSTANCE = new LabelResolver();
042
043 private LabelResolver() {}
044
045 @NotNull
046 private Set<KtElement> getElementsByLabelName(@NotNull Name labelName, @NotNull KtSimpleNameExpression labelExpression) {
047 Set<KtElement> elements = Sets.newLinkedHashSet();
048 PsiElement parent = labelExpression.getParent();
049 while (parent != null) {
050 Name name = getLabelNameIfAny(parent);
051 if (name != null && name.equals(labelName)) {
052 elements.add(getExpressionUnderLabel((KtExpression) parent));
053 }
054 parent = parent.getParent();
055 }
056 return elements;
057 }
058
059 @Nullable
060 public Name getLabelNameIfAny(@NotNull PsiElement element) {
061 if (element instanceof KtLabeledExpression) {
062 return ((KtLabeledExpression) element).getLabelNameAsName();
063 }
064
065 if (element instanceof KtFunctionLiteral) {
066 return getLabelNameIfAny(element.getParent());
067 }
068
069 if (element instanceof KtLambdaExpression) {
070 return getLabelForFunctionalExpression((KtExpression) element);
071 }
072
073 if (element instanceof KtNamedFunction) {
074 Name name = ((KtNamedFunction) element).getNameAsName();
075 if (name != null) return name;
076 return getLabelForFunctionalExpression((KtExpression) element);
077 }
078
079 return null;
080 }
081
082 private Name getLabelForFunctionalExpression(@NotNull KtExpression element) {
083 if (element.getParent() instanceof KtLabeledExpression) {
084 return getLabelNameIfAny(element.getParent());
085 }
086 return getCallerName(element);
087 }
088
089 @NotNull
090 private KtExpression getExpressionUnderLabel(@NotNull KtExpression labeledExpression) {
091 KtExpression expression = KtPsiUtil.safeDeparenthesize(labeledExpression);
092 if (expression instanceof KtLambdaExpression) {
093 return ((KtLambdaExpression) expression).getFunctionLiteral();
094 }
095 return expression;
096 }
097
098 @Nullable
099 private Name getCallerName(@NotNull KtExpression expression) {
100 KtCallExpression callExpression = getContainingCallExpression(expression);
101 if (callExpression == null) return null;
102
103 KtExpression calleeExpression = callExpression.getCalleeExpression();
104 if (calleeExpression instanceof KtSimpleNameExpression) {
105 KtSimpleNameExpression nameExpression = (KtSimpleNameExpression) calleeExpression;
106 return nameExpression.getReferencedNameAsName();
107 }
108
109 return null;
110 }
111
112 @Nullable
113 private KtCallExpression getContainingCallExpression(@NotNull KtExpression expression) {
114 PsiElement parent = expression.getParent();
115 if (parent instanceof KtLambdaArgument) {
116 // f {}
117 PsiElement call = parent.getParent();
118 if (call instanceof KtCallExpression) {
119 return (KtCallExpression) call;
120 }
121 }
122
123 if (parent instanceof KtValueArgument) {
124 // f ({}) or f(p = {}) or f (fun () {})
125 KtValueArgument argument = (KtValueArgument) parent;
126 PsiElement argList = argument.getParent();
127 if (argList == null) return null;
128 PsiElement call = argList.getParent();
129 if (call instanceof KtCallExpression) {
130 return (KtCallExpression) call;
131 }
132 }
133 return null;
134 }
135
136 @Nullable
137 public KtElement resolveControlLabel(
138 @NotNull KtExpressionWithLabel expression,
139 @NotNull ResolutionContext context
140 ) {
141 KtSimpleNameExpression labelElement = expression.getTargetLabel();
142 KtPsiUtilKt.checkReservedYield(labelElement, context.trace);
143
144 Name labelName = expression.getLabelNameAsName();
145 if (labelElement == null || labelName == null) return null;
146
147 Collection<DeclarationDescriptor> declarationsByLabel = ScopeUtilsKt.getDeclarationsByLabel(context.scope, labelName);
148 int size = declarationsByLabel.size();
149
150 if (size > 1) {
151 BindingContextUtils.reportAmbiguousLabel(context.trace, labelElement, declarationsByLabel);
152 return null;
153 }
154 if (size == 0) {
155 KtElement element = resolveNamedLabel(labelName, labelElement, context.trace);
156 if (element == null) {
157 context.trace.report(UNRESOLVED_REFERENCE.on(labelElement, labelElement));
158 }
159 return element;
160 }
161 DeclarationDescriptor declarationDescriptor = declarationsByLabel.iterator().next();
162 KtElement element;
163 if (declarationDescriptor instanceof FunctionDescriptor || declarationDescriptor instanceof ClassDescriptor) {
164 element = (KtElement) DescriptorToSourceUtils.descriptorToDeclaration(declarationDescriptor);
165 }
166 else {
167 throw new UnsupportedOperationException(declarationDescriptor.getClass().toString()); // TODO
168 }
169 context.trace.record(LABEL_TARGET, labelElement, element);
170 return element;
171 }
172
173 private KtElement resolveNamedLabel(
174 @NotNull Name labelName,
175 @NotNull KtSimpleNameExpression labelExpression,
176 @NotNull BindingTrace trace
177 ) {
178 Set<KtElement> list = getElementsByLabelName(labelName, labelExpression);
179 if (list.isEmpty()) return null;
180
181 if (list.size() > 1) {
182 trace.report(LABEL_NAME_CLASH.on(labelExpression));
183 }
184
185 KtElement result = list.iterator().next();
186 trace.record(LABEL_TARGET, labelExpression, result);
187 return result;
188 }
189
190 @NotNull
191 public LabeledReceiverResolutionResult resolveThisOrSuperLabel(
192 @NotNull KtInstanceExpressionWithLabel expression,
193 @NotNull ResolutionContext context,
194 @NotNull Name labelName
195 ) {
196 KtReferenceExpression referenceExpression = expression.getInstanceReference();
197 KtSimpleNameExpression targetLabel = expression.getTargetLabel();
198 assert targetLabel != null : expression;
199
200 Collection<DeclarationDescriptor> declarationsByLabel = ScopeUtilsKt.getDeclarationsByLabel(context.scope, labelName);
201 int size = declarationsByLabel.size();
202 if (size == 1) {
203 DeclarationDescriptor declarationDescriptor = declarationsByLabel.iterator().next();
204 ReceiverParameterDescriptor thisReceiver;
205 if (declarationDescriptor instanceof ClassDescriptor) {
206 ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
207 thisReceiver = classDescriptor.getThisAsReceiverParameter();
208 }
209 else if (declarationDescriptor instanceof FunctionDescriptor) {
210 FunctionDescriptor functionDescriptor = (FunctionDescriptor) declarationDescriptor;
211 thisReceiver = functionDescriptor.getExtensionReceiverParameter();
212 }
213 else if (declarationDescriptor instanceof PropertyDescriptor) {
214 PropertyDescriptor propertyDescriptor = (PropertyDescriptor) declarationDescriptor;
215 thisReceiver = propertyDescriptor.getExtensionReceiverParameter();
216 }
217 else {
218 throw new UnsupportedOperationException("Unsupported descriptor: " + declarationDescriptor); // TODO
219 }
220 PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(declarationDescriptor);
221 assert element != null : "No PSI element for descriptor: " + declarationDescriptor;
222 context.trace.record(LABEL_TARGET, targetLabel, element);
223 context.trace.record(REFERENCE_TARGET, referenceExpression, declarationDescriptor);
224
225 if (declarationDescriptor instanceof ClassDescriptor) {
226 ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
227 if (!DescriptorResolver.checkHasOuterClassInstance(context.scope, context.trace, targetLabel, classDescriptor)) {
228 return LabeledReceiverResolutionResult.labelResolutionFailed();
229 }
230 }
231
232 return LabeledReceiverResolutionResult.labelResolutionSuccess(thisReceiver);
233 }
234 else if (size == 0) {
235 KtElement element = resolveNamedLabel(labelName, targetLabel, context.trace);
236 if (element instanceof KtFunctionLiteral) {
237 DeclarationDescriptor declarationDescriptor =
238 context.trace.getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, element);
239 if (declarationDescriptor instanceof FunctionDescriptor) {
240 ReceiverParameterDescriptor thisReceiver = ((FunctionDescriptor) declarationDescriptor).getExtensionReceiverParameter();
241 if (thisReceiver != null) {
242 context.trace.record(LABEL_TARGET, targetLabel, element);
243 context.trace.record(REFERENCE_TARGET, referenceExpression, declarationDescriptor);
244 }
245 return LabeledReceiverResolutionResult.labelResolutionSuccess(thisReceiver);
246 }
247 else {
248 context.trace.report(UNRESOLVED_REFERENCE.on(targetLabel, targetLabel));
249 }
250 }
251 else {
252 context.trace.report(UNRESOLVED_REFERENCE.on(targetLabel, targetLabel));
253 }
254 }
255 else {
256 BindingContextUtils.reportAmbiguousLabel(context.trace, targetLabel, declarationsByLabel);
257 }
258 return LabeledReceiverResolutionResult.labelResolutionFailed();
259 }
260
261 public static final class LabeledReceiverResolutionResult {
262 public static LabeledReceiverResolutionResult labelResolutionSuccess(@Nullable ReceiverParameterDescriptor receiverParameterDescriptor) {
263 if (receiverParameterDescriptor == null) {
264 return new LabeledReceiverResolutionResult(Code.NO_THIS, null);
265 }
266 return new LabeledReceiverResolutionResult(Code.SUCCESS, receiverParameterDescriptor);
267 }
268
269 public static LabeledReceiverResolutionResult labelResolutionFailed() {
270 return new LabeledReceiverResolutionResult(Code.LABEL_RESOLUTION_ERROR, null);
271 }
272
273 public enum Code {
274 LABEL_RESOLUTION_ERROR,
275 NO_THIS,
276 SUCCESS
277 }
278
279 private final Code code;
280 private final ReceiverParameterDescriptor receiverParameterDescriptor;
281
282 private LabeledReceiverResolutionResult(
283 Code code,
284 ReceiverParameterDescriptor receiverParameterDescriptor
285 ) {
286 this.code = code;
287 this.receiverParameterDescriptor = receiverParameterDescriptor;
288 }
289
290 public Code getCode() {
291 return code;
292 }
293
294 public boolean success() {
295 return code == Code.SUCCESS;
296 }
297
298 public ReceiverParameterDescriptor getReceiverParameterDescriptor() {
299 assert success() : "Don't try to obtain the receiver when resolution failed with " + code;
300 return receiverParameterDescriptor;
301 }
302 }
303 }