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