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.resolve;
018    
019    import com.google.common.collect.Lists;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.jet.lang.descriptors.*;
023    import org.jetbrains.jet.lang.diagnostics.rendering.Renderers;
024    import org.jetbrains.jet.lang.psi.*;
025    import org.jetbrains.jet.lang.resolve.calls.CallResolver;
026    import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintPosition;
027    import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem;
028    import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemCompleter;
029    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
030    import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
031    import org.jetbrains.jet.lang.resolve.calls.smartcasts.DataFlowInfo;
032    import org.jetbrains.jet.lang.resolve.calls.util.CallMaker;
033    import org.jetbrains.jet.lang.resolve.name.Name;
034    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
035    import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
036    import org.jetbrains.jet.lang.types.DeferredType;
037    import org.jetbrains.jet.lang.types.JetType;
038    import org.jetbrains.jet.lang.types.TypeUtils;
039    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
040    import org.jetbrains.jet.lang.types.expressions.ExpressionTypingContext;
041    import org.jetbrains.jet.lang.types.expressions.ExpressionTypingServices;
042    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
043    import org.jetbrains.jet.util.slicedmap.WritableSlice;
044    
045    import javax.inject.Inject;
046    import java.util.List;
047    
048    import static org.jetbrains.jet.lang.diagnostics.Errors.*;
049    import static org.jetbrains.jet.lang.psi.PsiPackage.JetPsiFactory;
050    import static org.jetbrains.jet.lang.resolve.BindingContext.*;
051    import static org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage.getCalleeExpressionIfAny;
052    import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
053    import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType;
054    import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.createFakeExpressionOfType;
055    
056    public class DelegatedPropertyResolver {
057       
058        @NotNull
059        private ExpressionTypingServices expressionTypingServices;
060    
061        @NotNull
062        private CallResolver callResolver;
063    
064        private static final String PD_METHOD_NAME = "propertyDelegated";
065    
066        @Inject
067        public void setExpressionTypingServices(@NotNull ExpressionTypingServices expressionTypingServices) {
068            this.expressionTypingServices = expressionTypingServices;
069        }
070    
071        @Inject
072        public void setCallResolver(@NotNull CallResolver callResolver) {
073            this.callResolver = callResolver;
074        }
075    
076        @Nullable
077        public JetType getDelegatedPropertyGetMethodReturnType(
078                @NotNull PropertyDescriptor propertyDescriptor,
079                @NotNull JetExpression delegateExpression,
080                @NotNull JetType delegateType,
081                @NotNull BindingTrace trace,
082                @NotNull JetScope scope
083        ) {
084            resolveDelegatedPropertyConventionMethod(propertyDescriptor, delegateExpression, delegateType, trace, scope, true);
085            ResolvedCall<FunctionDescriptor> resolvedCall =
086                    trace.getBindingContext().get(DELEGATED_PROPERTY_RESOLVED_CALL, propertyDescriptor.getGetter());
087            return resolvedCall != null ? resolvedCall.getResultingDescriptor().getReturnType() : null;
088        }
089    
090        public void resolveDelegatedPropertyGetMethod(
091                @NotNull PropertyDescriptor propertyDescriptor,
092                @NotNull JetExpression delegateExpression,
093                @NotNull JetType delegateType,
094                @NotNull BindingTrace trace,
095                @NotNull JetScope scope
096        ) {
097            JetType returnType = getDelegatedPropertyGetMethodReturnType(
098                    propertyDescriptor, delegateExpression, delegateType, trace, scope);
099            JetType propertyType = propertyDescriptor.getType();
100    
101            /* Do not check return type of get() method of delegate for properties with DeferredType because property type is taken from it */
102            if (!(propertyType instanceof DeferredType) && returnType != null && !JetTypeChecker.DEFAULT.isSubtypeOf(returnType, propertyType)) {
103                Call call = trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, propertyDescriptor.getGetter());
104                assert call != null : "Call should exists for " + propertyDescriptor.getGetter();
105                trace.report(DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH
106                                     .on(delegateExpression, renderCall(call, trace.getBindingContext()), propertyDescriptor.getType(), returnType));
107            }
108        }
109    
110        public void resolveDelegatedPropertySetMethod(
111                @NotNull PropertyDescriptor propertyDescriptor,
112                @NotNull JetExpression delegateExpression,
113                @NotNull JetType delegateType,
114                @NotNull BindingTrace trace,
115                @NotNull JetScope scope
116        ) {
117            resolveDelegatedPropertyConventionMethod(propertyDescriptor, delegateExpression, delegateType, trace, scope, false);
118        }
119    
120        @NotNull
121        private static JetExpression createExpressionForPropertyMetadata(
122                @NotNull JetPsiFactory psiFactory,
123                @NotNull PropertyDescriptor propertyDescriptor
124        ) {
125            return psiFactory.createExpression(KotlinBuiltIns.getInstance().getPropertyMetadataImpl().getName().asString() +
126                                               "(\"" +
127                                               propertyDescriptor.getName().asString() +
128                                               "\")");
129        }
130    
131        public void resolveDelegatedPropertyPDMethod(
132                @NotNull PropertyDescriptor propertyDescriptor,
133                @NotNull JetExpression delegateExpression,
134                @NotNull JetType delegateType,
135                @NotNull BindingTrace trace,
136                @NotNull JetScope scope
137        ) {
138            TemporaryBindingTrace traceToResolvePDMethod = TemporaryBindingTrace.create(trace, "Trace to resolve propertyDelegated method in delegated property");
139            ExpressionTypingContext context = ExpressionTypingContext.newContext(
140                    expressionTypingServices, traceToResolvePDMethod, scope,
141                    DataFlowInfo.EMPTY, TypeUtils.NO_EXPECTED_TYPE);
142    
143            List<JetExpression> arguments = Lists.newArrayList();
144            JetPsiFactory psiFactory = JetPsiFactory(delegateExpression);
145            arguments.add(createExpressionForPropertyMetadata(psiFactory, propertyDescriptor));
146            Name functionName = Name.identifier(PD_METHOD_NAME);
147            JetReferenceExpression fakeCalleeExpression = psiFactory.createSimpleName(functionName.asString());
148            ExpressionReceiver receiver = new ExpressionReceiver(delegateExpression, delegateType);
149            Call call = CallMaker.makeCallWithExpressions(fakeCalleeExpression, receiver, null, fakeCalleeExpression, arguments, Call.CallType.DEFAULT);
150    
151            OverloadResolutionResults<FunctionDescriptor> functionResults =
152                    callResolver.resolveCallWithGivenName(context, call, fakeCalleeExpression, functionName);
153    
154            if (!functionResults.isSuccess()) {
155                String expectedFunction = renderCall(call, traceToResolvePDMethod.getBindingContext());
156                if (functionResults.isIncomplete() || functionResults.isSingleResult() ||
157                    functionResults.getResultCode() == OverloadResolutionResults.Code.MANY_FAILED_CANDIDATES) {
158                    trace.report(DELEGATE_PD_METHOD_NONE_APPLICABLE.on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
159                } else if (functionResults.isAmbiguity()) {
160                    trace.report(DELEGATE_SPECIAL_FUNCTION_AMBIGUITY
161                                         .on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
162                }
163                return;
164            }
165    
166            trace.record(DELEGATED_PROPERTY_PD_RESOLVED_CALL, propertyDescriptor, functionResults.getResultingCall());
167        }
168    
169        /* Resolve get() or set() methods from delegate */
170        private void resolveDelegatedPropertyConventionMethod(
171                @NotNull PropertyDescriptor propertyDescriptor,
172                @NotNull JetExpression delegateExpression,
173                @NotNull JetType delegateType,
174                @NotNull BindingTrace trace,
175                @NotNull JetScope scope,
176                boolean isGet
177        ) {
178            PropertyAccessorDescriptor accessor = isGet ? propertyDescriptor.getGetter() : propertyDescriptor.getSetter();
179            assert accessor != null : "Delegated property should have getter/setter " + propertyDescriptor + " " + delegateExpression.getText();
180    
181            if (trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, accessor) != null) return;
182    
183            OverloadResolutionResults<FunctionDescriptor> functionResults = getDelegatedPropertyConventionMethod(
184                    propertyDescriptor, delegateExpression, delegateType, trace, scope, isGet);
185            Call call = trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, accessor);
186            assert call != null : "'getDelegatedPropertyConventionMethod' didn't record a call";
187    
188            if (!functionResults.isSuccess()) {
189                String expectedFunction = renderCall(call, trace.getBindingContext());
190                if (functionResults.isIncomplete()) {
191                    trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType));
192                }
193                else if (functionResults.isSingleResult() ||
194                         functionResults.getResultCode() == OverloadResolutionResults.Code.MANY_FAILED_CANDIDATES) {
195                    trace.report(DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE
196                                                 .on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
197                }
198                else if (functionResults.isAmbiguity()) {
199                    trace.report(DELEGATE_SPECIAL_FUNCTION_AMBIGUITY
200                                                 .on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
201                }
202                else {
203                    trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType));
204                }
205                return;
206            }
207    
208            trace.record(DELEGATED_PROPERTY_RESOLVED_CALL, accessor, functionResults.getResultingCall());
209        }
210    
211        /* Resolve get() or set() methods from delegate */
212        public OverloadResolutionResults<FunctionDescriptor> getDelegatedPropertyConventionMethod(
213                @NotNull PropertyDescriptor propertyDescriptor,
214                @NotNull JetExpression delegateExpression,
215                @NotNull JetType delegateType,
216                @NotNull BindingTrace trace,
217                @NotNull JetScope scope,
218                boolean isGet
219        ) {
220            PropertyAccessorDescriptor accessor = isGet ? propertyDescriptor.getGetter() : propertyDescriptor.getSetter();
221            assert accessor != null : "Delegated property should have getter/setter " + propertyDescriptor + " " + delegateExpression.getText();
222    
223            ExpressionTypingContext context = ExpressionTypingContext.newContext(
224                    expressionTypingServices, trace, scope,
225                    DataFlowInfo.EMPTY, TypeUtils.NO_EXPECTED_TYPE);
226    
227            boolean hasThis = propertyDescriptor.getExtensionReceiverParameter() != null || propertyDescriptor.getDispatchReceiverParameter() != null;
228    
229            List<JetExpression> arguments = Lists.newArrayList();
230            JetPsiFactory psiFactory = JetPsiFactory(delegateExpression);
231            arguments.add(psiFactory.createExpression(hasThis ? "this" : "null"));
232    
233            arguments.add(createExpressionForPropertyMetadata(psiFactory, propertyDescriptor));
234    
235            if (!isGet) {
236                JetReferenceExpression fakeArgument = (JetReferenceExpression) createFakeExpressionOfType(expressionTypingServices.getProject(), trace,
237                                                                                 "fakeArgument" + arguments.size(),
238                                                                                 propertyDescriptor.getType());
239                arguments.add(fakeArgument);
240                List<ValueParameterDescriptor> valueParameters = accessor.getValueParameters();
241                trace.record(REFERENCE_TARGET, fakeArgument, valueParameters.get(0));
242            }
243    
244            Name functionName = Name.identifier(isGet ? "get" : "set");
245            JetReferenceExpression fakeCalleeExpression = psiFactory.createSimpleName(functionName.asString());
246    
247            ExpressionReceiver receiver = new ExpressionReceiver(delegateExpression, delegateType);
248            Call call = CallMaker.makeCallWithExpressions(fakeCalleeExpression, receiver, null, fakeCalleeExpression, arguments, Call.CallType.DEFAULT);
249            trace.record(BindingContext.DELEGATED_PROPERTY_CALL, accessor, call);
250    
251            return callResolver.resolveCallWithGivenName(context, call, fakeCalleeExpression, functionName);
252        }
253    
254        private String renderCall(@NotNull Call call, @NotNull BindingContext context) {
255            JetExpression calleeExpression = call.getCalleeExpression();
256            assert calleeExpression != null : "CalleeExpression should exists for fake call of convention method";
257            StringBuilder builder = new StringBuilder(calleeExpression.getText());
258            builder.append("(");
259            List<JetType> argumentTypes = Lists.newArrayList();
260            for (ValueArgument argument : call.getValueArguments()) {
261                argumentTypes.add(context.get(EXPRESSION_TYPE, argument.getArgumentExpression()));
262    
263            }
264            builder.append(Renderers.RENDER_COLLECTION_OF_TYPES.render(argumentTypes));
265            builder.append(")");
266            return builder.toString();
267        }
268    
269        @Nullable
270        public JetType resolveDelegateExpression(
271                @NotNull JetExpression delegateExpression,
272                @NotNull JetProperty jetProperty,
273                @NotNull PropertyDescriptor propertyDescriptor,
274                @NotNull JetScope propertyDeclarationInnerScope,
275                @NotNull JetScope accessorScope,
276                @NotNull BindingTrace trace,
277                @NotNull DataFlowInfo dataFlowInfo
278        ) {
279            TemporaryBindingTrace traceToResolveDelegatedProperty = TemporaryBindingTrace.create(trace, "Trace to resolve delegated property");
280            JetExpression calleeExpression = getCalleeExpressionIfAny(delegateExpression);
281            ConstraintSystemCompleter completer = createConstraintSystemCompleter(
282                    jetProperty, propertyDescriptor, delegateExpression, accessorScope, trace);
283            if (calleeExpression != null) {
284                traceToResolveDelegatedProperty.record(CONSTRAINT_SYSTEM_COMPLETER, calleeExpression, completer);
285            }
286            JetType delegateType = expressionTypingServices.safeGetType(propertyDeclarationInnerScope, delegateExpression, NO_EXPECTED_TYPE,
287                                                                        dataFlowInfo, traceToResolveDelegatedProperty);
288            traceToResolveDelegatedProperty.commit(new TraceEntryFilter() {
289                @Override
290                public boolean accept(@Nullable WritableSlice<?, ?> slice, Object key) {
291                    return slice != CONSTRAINT_SYSTEM_COMPLETER;
292                }
293            }, true);
294            return delegateType;
295        }
296    
297        @NotNull
298        private ConstraintSystemCompleter createConstraintSystemCompleter(
299                @NotNull JetProperty property,
300                @NotNull final PropertyDescriptor propertyDescriptor,
301                @NotNull final JetExpression delegateExpression,
302                @NotNull final JetScope accessorScope,
303                @NotNull final BindingTrace trace
304        ) {
305            final JetType expectedType = property.getTypeReference() != null ? propertyDescriptor.getType() : NO_EXPECTED_TYPE;
306            return new ConstraintSystemCompleter() {
307                @Override
308                public void completeConstraintSystem(
309                        @NotNull ConstraintSystem constraintSystem, @NotNull ResolvedCall<?> resolvedCall
310                ) {
311                    JetType returnType = resolvedCall.getCandidateDescriptor().getReturnType();
312                    if (returnType == null) return;
313    
314                    TemporaryBindingTrace traceToResolveConventionMethods =
315                            TemporaryBindingTrace.create(trace, "Trace to resolve delegated property convention methods");
316                    OverloadResolutionResults<FunctionDescriptor>
317                            getMethodResults = getDelegatedPropertyConventionMethod(
318                            propertyDescriptor, delegateExpression, returnType, traceToResolveConventionMethods, accessorScope, true);
319    
320                    if (conventionMethodFound(getMethodResults)) {
321                        FunctionDescriptor descriptor = getMethodResults.getResultingDescriptor();
322                        JetType returnTypeOfGetMethod = descriptor.getReturnType();
323                        if (returnTypeOfGetMethod != null) {
324                            constraintSystem.addSupertypeConstraint(expectedType, returnTypeOfGetMethod, ConstraintPosition.FROM_COMPLETER);
325                        }
326                        addConstraintForThisValue(constraintSystem, descriptor);
327                    }
328                    if (!propertyDescriptor.isVar()) return;
329    
330                    // For the case: 'val v by d' (no declared type).
331                    // When we add a constraint for 'set' method for delegated expression 'd' we use a type of the declared variable 'v'.
332                    // But if the type isn't known yet, the constraint shouldn't be added (we try to infer the type of 'v' here as well).
333                    if (propertyDescriptor.getReturnType() instanceof DeferredType) return;
334    
335                    OverloadResolutionResults<FunctionDescriptor> setMethodResults =
336                            getDelegatedPropertyConventionMethod(
337                                    propertyDescriptor, delegateExpression, returnType, traceToResolveConventionMethods, accessorScope, false);
338    
339                    if (conventionMethodFound(setMethodResults)) {
340                        FunctionDescriptor descriptor = setMethodResults.getResultingDescriptor();
341                        List<ValueParameterDescriptor> valueParameters = descriptor.getValueParameters();
342                        if (valueParameters.size() == 3) {
343                            ValueParameterDescriptor valueParameterForThis = valueParameters.get(2);
344    
345                            if (!noExpectedType(expectedType)) {
346                                constraintSystem.addSubtypeConstraint(
347                                        expectedType, valueParameterForThis.getType(), ConstraintPosition.FROM_COMPLETER);
348                            }
349                            addConstraintForThisValue(constraintSystem, descriptor);
350                        }
351                    }
352                }
353    
354                private boolean conventionMethodFound(@NotNull OverloadResolutionResults<FunctionDescriptor> results) {
355                    return results.isSuccess() ||
356                           (results.isSingleResult() &&
357                            results.getResultCode() == OverloadResolutionResults.Code.SINGLE_CANDIDATE_ARGUMENT_MISMATCH);
358                }
359    
360                private void addConstraintForThisValue(ConstraintSystem constraintSystem, FunctionDescriptor resultingDescriptor) {
361                    ReceiverParameterDescriptor extensionReceiver = propertyDescriptor.getExtensionReceiverParameter();
362                    ReceiverParameterDescriptor dispatchReceiver = propertyDescriptor.getDispatchReceiverParameter();
363                    JetType typeOfThis =
364                            extensionReceiver != null ? extensionReceiver.getType() :
365                            dispatchReceiver != null ? dispatchReceiver.getType() :
366                            KotlinBuiltIns.getInstance().getNullableNothingType();
367    
368                    List<ValueParameterDescriptor> valueParameters = resultingDescriptor.getValueParameters();
369                    if (valueParameters.isEmpty()) return;
370                    ValueParameterDescriptor valueParameterForThis = valueParameters.get(0);
371    
372                    constraintSystem.addSubtypeConstraint(typeOfThis, valueParameterForThis.getType(), ConstraintPosition.FROM_COMPLETER);
373                }
374            };
375        }
376    }