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