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    }