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