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    }