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