001    /*
002     * Copyright 2010-2015 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.kotlin.types.expressions;
018    
019    import com.google.common.collect.Sets;
020    import com.intellij.psi.PsiElement;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.kotlin.descriptors.*;
024    import org.jetbrains.kotlin.name.Name;
025    import org.jetbrains.kotlin.psi.*;
026    import org.jetbrains.kotlin.resolve.*;
027    import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
028    import org.jetbrains.kotlin.resolve.scopes.utils.UtilsPackage;
029    
030    import java.util.Collection;
031    import java.util.Set;
032    
033    import static org.jetbrains.kotlin.diagnostics.Errors.LABEL_NAME_CLASH;
034    import static org.jetbrains.kotlin.diagnostics.Errors.UNRESOLVED_REFERENCE;
035    import static org.jetbrains.kotlin.resolve.BindingContext.LABEL_TARGET;
036    import static org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET;
037    
038    public class LabelResolver {
039        
040        public static LabelResolver INSTANCE = new LabelResolver();
041    
042        private LabelResolver() {}
043    
044        @NotNull
045        private Set<JetElement> getElementsByLabelName(@NotNull Name labelName, @NotNull JetSimpleNameExpression labelExpression) {
046            Set<JetElement> elements = Sets.newLinkedHashSet();
047            PsiElement parent = labelExpression.getParent();
048            while (parent != null) {
049                Name name = getLabelNameIfAny(parent);
050                if (name != null && name.equals(labelName)) {
051                    elements.add(getExpressionUnderLabel((JetExpression) parent));
052                }
053                parent = parent.getParent();
054            }
055            return elements;
056        }
057    
058        @Nullable
059        public Name getLabelNameIfAny(@NotNull PsiElement element) {
060            if (element instanceof JetLabeledExpression) {
061                return ((JetLabeledExpression) element).getLabelNameAsName();
062            }
063    
064            if (element instanceof JetFunctionLiteral) {
065                return getLabelNameIfAny(element.getParent());
066            }
067    
068            if (element instanceof JetFunctionLiteralExpression) {
069                return getLabelForFunctionalExpression((JetExpression) element);
070            }
071    
072            if (element instanceof JetNamedFunction) {
073                Name name = ((JetNamedFunction) element).getNameAsName();
074                if (name != null) return name;
075                return getLabelForFunctionalExpression((JetExpression) element);
076            }
077    
078            return null;
079        }
080    
081        private Name getLabelForFunctionalExpression(@NotNull JetExpression element) {
082            if (element.getParent() instanceof JetLabeledExpression) {
083                return getLabelNameIfAny(element.getParent());
084            }
085            return getCallerName(element);
086        }
087    
088        @NotNull
089        private JetExpression getExpressionUnderLabel(@NotNull JetExpression labeledExpression) {
090            JetExpression expression = JetPsiUtil.safeDeparenthesize(labeledExpression, true);
091            if (expression instanceof JetFunctionLiteralExpression) {
092                return ((JetFunctionLiteralExpression) expression).getFunctionLiteral();
093            }
094            return expression;
095        }
096    
097        @Nullable
098        private Name getCallerName(@NotNull JetExpression expression) {
099            JetCallExpression callExpression = getContainingCallExpression(expression);
100            if (callExpression == null) return null;
101    
102            JetExpression calleeExpression = callExpression.getCalleeExpression();
103            if (calleeExpression instanceof JetSimpleNameExpression) {
104                JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) calleeExpression;
105                return nameExpression.getReferencedNameAsName();
106            }
107    
108            return null;
109        }
110    
111        @Nullable
112        private JetCallExpression getContainingCallExpression(@NotNull JetExpression expression) {
113            PsiElement parent = expression.getParent();
114            if (parent instanceof JetFunctionLiteralArgument) {
115                // f {}
116                PsiElement call = parent.getParent();
117                if (call instanceof JetCallExpression) {
118                    return (JetCallExpression) call;
119                }
120            }
121    
122            if (parent instanceof JetValueArgument) {
123                // f ({}) or f(p = {}) or f (fun () {})
124                JetValueArgument argument = (JetValueArgument) parent;
125                PsiElement argList = argument.getParent();
126                if (argList == null) return null;
127                PsiElement call = argList.getParent();
128                if (call instanceof JetCallExpression) {
129                    return (JetCallExpression) call;
130                }
131            }
132            return null;
133        }
134    
135        @Nullable
136        public JetElement resolveControlLabel(
137                @NotNull JetExpressionWithLabel expression,
138                @NotNull ResolutionContext context
139        ) {
140            JetSimpleNameExpression labelElement = expression.getTargetLabel();
141            Name labelName = expression.getLabelNameAsName();
142            if (labelElement == null || labelName == null) return null;
143    
144            Collection<DeclarationDescriptor> declarationsByLabel = UtilsPackage.getDeclarationsByLabel(context.scope, labelName);
145            int size = declarationsByLabel.size();
146    
147            if (size > 1) {
148                BindingContextUtils.reportAmbiguousLabel(context.trace, labelElement, declarationsByLabel);
149                return null;
150            }
151            if (size == 0) {
152                JetElement element = resolveNamedLabel(labelName, labelElement, context.trace);
153                if (element == null) {
154                    context.trace.report(UNRESOLVED_REFERENCE.on(labelElement, labelElement));
155                }
156                return element;
157            }
158            DeclarationDescriptor declarationDescriptor = declarationsByLabel.iterator().next();
159            JetElement element;
160            if (declarationDescriptor instanceof FunctionDescriptor || declarationDescriptor instanceof ClassDescriptor) {
161                element = (JetElement) DescriptorToSourceUtils.descriptorToDeclaration(declarationDescriptor);
162            }
163            else {
164                throw new UnsupportedOperationException(declarationDescriptor.getClass().toString()); // TODO
165            }
166            context.trace.record(LABEL_TARGET, labelElement, element);
167            return element;
168        }
169    
170        private JetElement resolveNamedLabel(
171                @NotNull Name labelName, 
172                @NotNull JetSimpleNameExpression labelExpression, 
173                @NotNull BindingTrace trace
174        ) {
175            Set<JetElement> list = getElementsByLabelName(labelName, labelExpression);
176            if (list.isEmpty()) return null;
177    
178            if (list.size() > 1) {
179                trace.report(LABEL_NAME_CLASH.on(labelExpression));
180            }
181    
182            JetElement result = list.iterator().next();
183            trace.record(LABEL_TARGET, labelExpression, result);
184            return result;
185        }
186        
187        @NotNull
188        public LabeledReceiverResolutionResult resolveThisOrSuperLabel(
189                @NotNull JetInstanceExpressionWithLabel expression,
190                @NotNull ResolutionContext context,
191                @NotNull Name labelName
192        ) {
193            JetReferenceExpression referenceExpression = expression.getInstanceReference();
194            JetSimpleNameExpression targetLabel = expression.getTargetLabel();
195            assert targetLabel != null : expression;
196    
197            Collection<DeclarationDescriptor> declarationsByLabel = UtilsPackage.getDeclarationsByLabel(context.scope, labelName);
198            int size = declarationsByLabel.size();
199            if (size == 1) {
200                DeclarationDescriptor declarationDescriptor = declarationsByLabel.iterator().next();
201                ReceiverParameterDescriptor thisReceiver;
202                if (declarationDescriptor instanceof ClassDescriptor) {
203                    ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
204                    thisReceiver = classDescriptor.getThisAsReceiverParameter();
205                }
206                else if (declarationDescriptor instanceof FunctionDescriptor) {
207                    FunctionDescriptor functionDescriptor = (FunctionDescriptor) declarationDescriptor;
208                    thisReceiver = functionDescriptor.getExtensionReceiverParameter();
209                }
210                else if (declarationDescriptor instanceof PropertyDescriptor) {
211                    PropertyDescriptor propertyDescriptor = (PropertyDescriptor) declarationDescriptor;
212                    thisReceiver = propertyDescriptor.getExtensionReceiverParameter();
213                }
214                else {
215                    throw new UnsupportedOperationException("Unsupported descriptor: " + declarationDescriptor); // TODO
216                }
217                PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(declarationDescriptor);
218                assert element != null : "No PSI element for descriptor: " + declarationDescriptor;
219                context.trace.record(LABEL_TARGET, targetLabel, element);
220                context.trace.record(REFERENCE_TARGET, referenceExpression, declarationDescriptor);
221    
222                if (declarationDescriptor instanceof ClassDescriptor) {
223                    ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
224                    if (!DescriptorResolver.checkHasOuterClassInstance(context.scope, context.trace, targetLabel, classDescriptor)) {
225                        return LabeledReceiverResolutionResult.labelResolutionFailed();
226                    }
227                }
228    
229                return LabeledReceiverResolutionResult.labelResolutionSuccess(thisReceiver);
230            }
231            else if (size == 0) {
232                JetElement element = resolveNamedLabel(labelName, targetLabel, context.trace);
233                if (element instanceof JetFunctionLiteral) {
234                    DeclarationDescriptor declarationDescriptor =
235                            context.trace.getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, element);
236                    if (declarationDescriptor instanceof FunctionDescriptor) {
237                        ReceiverParameterDescriptor thisReceiver = ((FunctionDescriptor) declarationDescriptor).getExtensionReceiverParameter();
238                        if (thisReceiver != null) {
239                            context.trace.record(LABEL_TARGET, targetLabel, element);
240                            context.trace.record(REFERENCE_TARGET, referenceExpression, declarationDescriptor);
241                        }
242                        return LabeledReceiverResolutionResult.labelResolutionSuccess(thisReceiver);
243                    }
244                    else {
245                        context.trace.report(UNRESOLVED_REFERENCE.on(targetLabel, targetLabel));
246                    }
247                }
248                else {
249                    context.trace.report(UNRESOLVED_REFERENCE.on(targetLabel, targetLabel));
250                }
251            }
252            else {
253                BindingContextUtils.reportAmbiguousLabel(context.trace, targetLabel, declarationsByLabel);
254            }
255            return LabeledReceiverResolutionResult.labelResolutionFailed();
256        }
257    
258        public static final class LabeledReceiverResolutionResult {
259            public static LabeledReceiverResolutionResult labelResolutionSuccess(@Nullable ReceiverParameterDescriptor receiverParameterDescriptor) {
260                if (receiverParameterDescriptor == null) {
261                    return new LabeledReceiverResolutionResult(Code.NO_THIS, null);
262                }
263                return new LabeledReceiverResolutionResult(Code.SUCCESS, receiverParameterDescriptor);
264            }
265    
266            public static LabeledReceiverResolutionResult labelResolutionFailed() {
267                return new LabeledReceiverResolutionResult(Code.LABEL_RESOLUTION_ERROR, null);
268            }
269    
270            public enum Code {
271                LABEL_RESOLUTION_ERROR,
272                NO_THIS,
273                SUCCESS
274            }
275    
276            private final Code code;
277            private final ReceiverParameterDescriptor receiverParameterDescriptor;
278    
279            private LabeledReceiverResolutionResult(
280                    Code code,
281                    ReceiverParameterDescriptor receiverParameterDescriptor
282            ) {
283                this.code = code;
284                this.receiverParameterDescriptor = receiverParameterDescriptor;
285            }
286    
287            public Code getCode() {
288                return code;
289            }
290    
291            public boolean success() {
292                return code == Code.SUCCESS;
293            }
294    
295            public ReceiverParameterDescriptor getReceiverParameterDescriptor() {
296                assert success() : "Don't try to obtain the receiver when resolution failed with " + code;
297                return receiverParameterDescriptor;
298            }
299        }
300    }