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.intellij.psi.PsiElement;
020 import com.intellij.util.containers.Stack;
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.DescriptorResolver;
028 import org.jetbrains.jet.lang.resolve.name.LabelName;
029
030 import java.util.Collection;
031 import java.util.HashMap;
032 import java.util.Iterator;
033 import java.util.Map;
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 @NotNull
043 public static LabelResolver create() {
044 return new LabelResolver();
045 }
046
047 private final Map<LabelName, Stack<JetElement>> labeledElements = new HashMap<LabelName, Stack<JetElement>>();
048
049 private LabelResolver() {}
050
051 public void enterLabeledElement(@NotNull LabelName labelName, @NotNull JetExpression labeledExpression) {
052 JetExpression cacheExpression = getCachingExpression(labeledExpression);
053 if (cacheExpression != null) {
054 Stack<JetElement> stack = labeledElements.get(labelName);
055 if (stack == null) {
056 stack = new Stack<JetElement>();
057 labeledElements.put(labelName, stack);
058 }
059 stack.push(cacheExpression);
060 }
061 }
062
063 public void exitLabeledElement(@NotNull JetExpression expression) {
064 JetExpression cacheExpression = getCachingExpression(expression);
065
066 // TODO : really suboptimal
067 for (Iterator<Map.Entry<LabelName,Stack<JetElement>>> mapIter = labeledElements.entrySet().iterator(); mapIter.hasNext(); ) {
068 Map.Entry<LabelName, Stack<JetElement>> entry = mapIter.next();
069 Stack<JetElement> stack = entry.getValue();
070 for (Iterator<JetElement> stackIter = stack.iterator(); stackIter.hasNext(); ) {
071 JetElement recorded = stackIter.next();
072 if (recorded == cacheExpression) {
073 stackIter.remove();
074 }
075 }
076 if (stack.isEmpty()) {
077 mapIter.remove();
078 }
079 }
080 }
081
082 @NotNull
083 private JetExpression getCachingExpression(@NotNull JetExpression labeledExpression) {
084 JetExpression expression = JetPsiUtil.deparenthesize(labeledExpression);
085 if (expression instanceof JetFunctionLiteralExpression) {
086 expression = ((JetFunctionLiteralExpression) expression).getFunctionLiteral();
087 }
088 return expression;
089 }
090
091 @Nullable
092 private JetElement resolveControlLabel(@NotNull LabelName labelName, @NotNull JetSimpleNameExpression labelExpression, boolean reportUnresolved, ExpressionTypingContext context) {
093 Collection<DeclarationDescriptor> declarationsByLabel = context.scope.getDeclarationsByLabel(labelName);
094 int size = declarationsByLabel.size();
095
096 if (size == 1) {
097 DeclarationDescriptor declarationDescriptor = declarationsByLabel.iterator().next();
098 JetElement element;
099 if (declarationDescriptor instanceof FunctionDescriptor || declarationDescriptor instanceof ClassDescriptor) {
100 element = (JetElement) BindingContextUtils.descriptorToDeclaration(context.trace.getBindingContext(), declarationDescriptor);
101 }
102 else {
103 throw new UnsupportedOperationException(declarationDescriptor.getClass().toString()); // TODO
104 }
105 context.trace.record(LABEL_TARGET, labelExpression, element);
106 return element;
107 }
108 else if (size == 0) {
109 return resolveNamedLabel(labelName, labelExpression, reportUnresolved, context);
110 }
111 BindingContextUtils.reportAmbiguousLabel(context.trace, labelExpression, declarationsByLabel);
112 return null;
113 }
114
115 @Nullable
116 public JetElement resolveLabel(JetLabelQualifiedExpression expression, ExpressionTypingContext context) {
117 JetSimpleNameExpression labelElement = expression.getTargetLabel();
118 if (labelElement != null) {
119 LabelName labelName = new LabelName(expression.getLabelName());
120 return resolveControlLabel(labelName, labelElement, true, context);
121 }
122 return null;
123 }
124
125 private JetElement resolveNamedLabel(@NotNull LabelName labelName, @NotNull JetSimpleNameExpression labelExpression, boolean reportUnresolved, ExpressionTypingContext context) {
126 Stack<JetElement> stack = labeledElements.get(labelName);
127 if (stack == null || stack.isEmpty()) {
128 if (reportUnresolved) {
129 context.trace.report(UNRESOLVED_REFERENCE.on(labelExpression, labelExpression));
130 }
131 return null;
132 }
133 else if (stack.size() > 1) {
134 context.trace.report(LABEL_NAME_CLASH.on(labelExpression));
135 }
136
137 JetElement result = stack.peek();
138 context.trace.record(LABEL_TARGET, labelExpression, result);
139 return result;
140 }
141
142 public LabeledReceiverResolutionResult resolveThisLabel(JetReferenceExpression thisReference, JetSimpleNameExpression targetLabel,
143 ExpressionTypingContext context, LabelName labelName) {
144 Collection<DeclarationDescriptor> declarationsByLabel = context.scope.getDeclarationsByLabel(labelName);
145 int size = declarationsByLabel.size();
146 assert targetLabel != null;
147 if (size == 1) {
148 DeclarationDescriptor declarationDescriptor = declarationsByLabel.iterator().next();
149 ReceiverParameterDescriptor thisReceiver;
150 if (declarationDescriptor instanceof ClassDescriptor) {
151 ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
152 thisReceiver = classDescriptor.getThisAsReceiverParameter();
153 }
154 else if (declarationDescriptor instanceof FunctionDescriptor) {
155 FunctionDescriptor functionDescriptor = (FunctionDescriptor) declarationDescriptor;
156 thisReceiver = functionDescriptor.getReceiverParameter();
157 }
158 else if (declarationDescriptor instanceof PropertyDescriptor) {
159 PropertyDescriptor propertyDescriptor = (PropertyDescriptor) declarationDescriptor;
160 thisReceiver = propertyDescriptor.getReceiverParameter();
161 }
162 else {
163 throw new UnsupportedOperationException("Unsupported descriptor: " + declarationDescriptor); // TODO
164 }
165 PsiElement element = BindingContextUtils.descriptorToDeclaration(context.trace.getBindingContext(), declarationDescriptor);
166 assert element != null : "No PSI element for descriptor: " + declarationDescriptor;
167 context.trace.record(LABEL_TARGET, targetLabel, element);
168 context.trace.record(REFERENCE_TARGET, thisReference, declarationDescriptor);
169
170 if (declarationDescriptor instanceof ClassDescriptor) {
171 ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
172 if (!DescriptorResolver.checkHasOuterClassInstance(context.scope, context.trace, targetLabel, classDescriptor)) {
173 return LabeledReceiverResolutionResult.labelResolutionFailed();
174 }
175 }
176
177 return LabeledReceiverResolutionResult.labelResolutionSuccess(thisReceiver);
178 }
179 else if (size == 0) {
180 JetElement element = resolveNamedLabel(labelName, targetLabel, false, context);
181 if (element instanceof JetFunctionLiteral) {
182 DeclarationDescriptor declarationDescriptor =
183 context.trace.getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, element);
184 if (declarationDescriptor instanceof FunctionDescriptor) {
185 ReceiverParameterDescriptor thisReceiver = ((FunctionDescriptor) declarationDescriptor).getReceiverParameter();
186 if (thisReceiver != null) {
187 context.trace.record(LABEL_TARGET, targetLabel, element);
188 context.trace.record(REFERENCE_TARGET, thisReference, declarationDescriptor);
189 }
190 return LabeledReceiverResolutionResult.labelResolutionSuccess(thisReceiver);
191 }
192 else {
193 context.trace.report(UNRESOLVED_REFERENCE.on(targetLabel, targetLabel));
194 }
195 }
196 else {
197 context.trace.report(UNRESOLVED_REFERENCE.on(targetLabel, targetLabel));
198 }
199 }
200 else {
201 BindingContextUtils.reportAmbiguousLabel(context.trace, targetLabel, declarationsByLabel);
202 }
203 return LabeledReceiverResolutionResult.labelResolutionFailed();
204 }
205
206 public static final class LabeledReceiverResolutionResult {
207 public static LabeledReceiverResolutionResult labelResolutionSuccess(@Nullable ReceiverParameterDescriptor receiverParameterDescriptor) {
208 if (receiverParameterDescriptor == null) {
209 return new LabeledReceiverResolutionResult(Code.NO_THIS, null);
210 }
211 return new LabeledReceiverResolutionResult(Code.SUCCESS, receiverParameterDescriptor);
212 }
213
214 public static LabeledReceiverResolutionResult labelResolutionFailed() {
215 return new LabeledReceiverResolutionResult(Code.LABEL_RESOLUTION_ERROR, null);
216 }
217
218 public enum Code {
219 LABEL_RESOLUTION_ERROR,
220 NO_THIS,
221 SUCCESS
222 }
223
224 private final Code code;
225 private final ReceiverParameterDescriptor receiverParameterDescriptor;
226
227 private LabeledReceiverResolutionResult(
228 Code code,
229 ReceiverParameterDescriptor receiverParameterDescriptor
230 ) {
231 this.code = code;
232 this.receiverParameterDescriptor = receiverParameterDescriptor;
233 }
234
235 public Code getCode() {
236 return code;
237 }
238
239 public boolean success() {
240 return code == Code.SUCCESS;
241 }
242
243 public ReceiverParameterDescriptor getReceiverParameterDescriptor() {
244 assert success() : "Don't try to obtain the receiver when resolution failed with " + code;
245 return receiverParameterDescriptor;
246 }
247 }
248 }