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