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