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