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.BindingContext;
026 import org.jetbrains.jet.lang.resolve.BindingContextUtils;
027 import org.jetbrains.jet.lang.resolve.BindingTrace;
028 import org.jetbrains.jet.lang.resolve.DescriptorResolver;
029 import org.jetbrains.jet.lang.resolve.calls.context.ResolutionContext;
030 import org.jetbrains.jet.lang.resolve.name.Name;
031
032 import java.util.Collection;
033 import java.util.List;
034
035 import static org.jetbrains.jet.lang.diagnostics.Errors.LABEL_NAME_CLASH;
036 import static org.jetbrains.jet.lang.diagnostics.Errors.UNRESOLVED_REFERENCE;
037 import static org.jetbrains.jet.lang.resolve.BindingContext.LABEL_TARGET;
038 import static org.jetbrains.jet.lang.resolve.BindingContext.REFERENCE_TARGET;
039
040 public class LabelResolver {
041
042 public static LabelResolver INSTANCE = new LabelResolver();
043
044 private LabelResolver() {}
045
046 @NotNull
047 private List<JetElement> getElementsByLabelName(@NotNull Name labelName, @NotNull JetSimpleNameExpression labelExpression) {
048 List<JetElement> elements = Lists.newArrayList();
049 PsiElement parent = labelExpression.getParent();
050 while (parent != null) {
051 Name name = getLabelNameIfAny(parent);
052 if (name != null && name.equals(labelName)) {
053 elements.add(getExpressionUnderLabel((JetExpression) parent));
054 }
055 parent = parent.getParent();
056 }
057 return elements;
058 }
059
060 @Nullable
061 private Name getLabelNameIfAny(@NotNull PsiElement element) {
062 if (element instanceof JetLabeledExpression) {
063 String labelName = ((JetLabeledExpression) element).getLabelName();
064 if (labelName != null) {
065 return Name.identifier(labelName);
066 }
067 }
068 if (element instanceof JetFunctionLiteralExpression) {
069 return getCallerName((JetFunctionLiteralExpression) element);
070 }
071 return null;
072 }
073
074 @NotNull
075 private JetExpression getExpressionUnderLabel(@NotNull JetExpression labeledExpression) {
076 JetExpression expression = JetPsiUtil.safeDeparenthesize(labeledExpression, true);
077 if (expression instanceof JetFunctionLiteralExpression) {
078 return ((JetFunctionLiteralExpression) expression).getFunctionLiteral();
079 }
080 return expression;
081 }
082
083 @Nullable
084 private Name getCallerName(@NotNull JetFunctionLiteralExpression expression) {
085 JetCallExpression callExpression = getContainingCallExpression(expression);
086 if (callExpression == null) return null;
087
088 JetExpression calleeExpression = callExpression.getCalleeExpression();
089 if (calleeExpression instanceof JetSimpleNameExpression) {
090 JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) calleeExpression;
091 return nameExpression.getReferencedNameAsName();
092 }
093
094 return null;
095 }
096
097 @Nullable
098 private JetCallExpression getContainingCallExpression(@NotNull JetFunctionLiteralExpression expression) {
099 PsiElement parent = expression.getParent();
100 if (parent instanceof JetCallExpression) {
101 // f {}
102 return (JetCallExpression) parent;
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) BindingContextUtils.descriptorToDeclaration(context.trace.getBindingContext(), 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.getReceiverParameter();
193 }
194 else if (declarationDescriptor instanceof PropertyDescriptor) {
195 PropertyDescriptor propertyDescriptor = (PropertyDescriptor) declarationDescriptor;
196 thisReceiver = propertyDescriptor.getReceiverParameter();
197 }
198 else {
199 throw new UnsupportedOperationException("Unsupported descriptor: " + declarationDescriptor); // TODO
200 }
201 PsiElement element = BindingContextUtils.descriptorToDeclaration(context.trace.getBindingContext(), 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).getReceiverParameter();
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 }