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