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