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