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