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 kotlin.Pair;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
025    import org.jetbrains.kotlin.descriptors.*;
026    import org.jetbrains.kotlin.diagnostics.rendering.Renderers;
027    import org.jetbrains.kotlin.name.Name;
028    import org.jetbrains.kotlin.psi.*;
029    import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
030    import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem;
031    import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemCompleter;
032    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
033    import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults;
034    import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
035    import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
036    import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
037    import org.jetbrains.kotlin.resolve.validation.OperatorValidator;
038    import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator;
039    import org.jetbrains.kotlin.types.DeferredType;
040    import org.jetbrains.kotlin.types.KotlinType;
041    import org.jetbrains.kotlin.types.TypeUtils;
042    import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
043    import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext;
044    import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices;
045    import org.jetbrains.kotlin.types.expressions.FakeCallResolver;
046    import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
047    
048    import java.util.Collections;
049    import java.util.List;
050    
051    import static org.jetbrains.kotlin.diagnostics.Errors.*;
052    import static org.jetbrains.kotlin.psi.KtPsiFactoryKt.KtPsiFactory;
053    import static org.jetbrains.kotlin.resolve.BindingContext.*;
054    import static org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.FROM_COMPLETER;
055    import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE;
056    import static org.jetbrains.kotlin.types.TypeUtils.noExpectedType;
057    import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.createFakeExpressionOfType;
058    
059    public class DelegatedPropertyResolver {
060    
061        public static final Name PROPERTY_DELEGATED_FUNCTION_NAME = Name.identifier("propertyDelegated");
062        public static final Name GETTER_NAME = Name.identifier("getValue");
063        public static final Name SETTER_NAME = Name.identifier("setValue");
064    
065        public static final Name OLD_GETTER_NAME = Name.identifier("get");
066        public static final Name OLD_SETTER_NAME = Name.identifier("set");
067    
068        @NotNull private final ExpressionTypingServices expressionTypingServices;
069        @NotNull private final FakeCallResolver fakeCallResolver;
070        @NotNull private final KotlinBuiltIns builtIns;
071        @NotNull private final SymbolUsageValidator symbolUsageValidator;
072    
073        public DelegatedPropertyResolver(
074                @NotNull SymbolUsageValidator symbolUsageValidator,
075                @NotNull KotlinBuiltIns builtIns,
076                @NotNull FakeCallResolver fakeCallResolver,
077                @NotNull ExpressionTypingServices expressionTypingServices
078        ) {
079            this.symbolUsageValidator = symbolUsageValidator;
080            this.builtIns = builtIns;
081            this.fakeCallResolver = fakeCallResolver;
082            this.expressionTypingServices = expressionTypingServices;
083        }
084    
085        @Nullable
086        public KotlinType getDelegatedPropertyGetMethodReturnType(
087                @NotNull PropertyDescriptor propertyDescriptor,
088                @NotNull KtExpression delegateExpression,
089                @NotNull KotlinType delegateType,
090                @NotNull BindingTrace trace,
091                @NotNull LexicalScope scope
092        ) {
093            resolveDelegatedPropertyConventionMethod(propertyDescriptor, delegateExpression, delegateType, trace, scope, true);
094            ResolvedCall<FunctionDescriptor> resolvedCall =
095                    trace.getBindingContext().get(DELEGATED_PROPERTY_RESOLVED_CALL, propertyDescriptor.getGetter());
096            return resolvedCall != null ? resolvedCall.getResultingDescriptor().getReturnType() : null;
097        }
098    
099        public void resolveDelegatedPropertyGetMethod(
100                @NotNull PropertyDescriptor propertyDescriptor,
101                @NotNull KtExpression delegateExpression,
102                @NotNull KotlinType delegateType,
103                @NotNull BindingTrace trace,
104                @NotNull LexicalScope scope
105        ) {
106            KotlinType returnType = getDelegatedPropertyGetMethodReturnType(
107                    propertyDescriptor, delegateExpression, delegateType, trace, scope);
108            KotlinType propertyType = propertyDescriptor.getType();
109    
110            /* Do not check return type of get() method of delegate for properties with DeferredType because property type is taken from it */
111            if (!(propertyType instanceof DeferredType) && returnType != null && !KotlinTypeChecker.DEFAULT.isSubtypeOf(returnType, propertyType)) {
112                Call call = trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, propertyDescriptor.getGetter());
113                assert call != null : "Call should exists for " + propertyDescriptor.getGetter();
114                trace.report(DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH
115                                     .on(delegateExpression, renderCall(call, trace.getBindingContext()), propertyDescriptor.getType(), returnType));
116            }
117        }
118    
119        public void resolveDelegatedPropertySetMethod(
120                @NotNull PropertyDescriptor propertyDescriptor,
121                @NotNull KtExpression delegateExpression,
122                @NotNull KotlinType delegateType,
123                @NotNull BindingTrace trace,
124                @NotNull LexicalScope scope
125        ) {
126            resolveDelegatedPropertyConventionMethod(propertyDescriptor, delegateExpression, delegateType, trace, scope, false);
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 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            KtPsiFactory psiFactory = KtPsiFactory(delegateExpression);
147            List<KtExpression> arguments = Collections.singletonList(createExpressionForProperty(psiFactory));
148            ExpressionReceiver receiver = new ExpressionReceiver(delegateExpression, delegateType);
149    
150            Pair<Call, OverloadResolutionResults<FunctionDescriptor>> resolutionResult =
151                    fakeCallResolver.makeAndResolveFakeCallInContext(receiver, context, arguments, PROPERTY_DELEGATED_FUNCTION_NAME, delegateExpression);
152    
153            Call call = resolutionResult.getFirst();
154            OverloadResolutionResults<FunctionDescriptor> functionResults = resolutionResult.getSecond();
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 getValue() or setValue() methods from delegate */
172        private void resolveDelegatedPropertyConventionMethod(
173                @NotNull PropertyDescriptor propertyDescriptor,
174                @NotNull KtExpression delegateExpression,
175                @NotNull KotlinType 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            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                        OperatorValidator.Companion.report(byKeyword, resultingDescriptor, trace);
222                    }
223    
224                    symbolUsageValidator.validateCall(resultingCall, resultingCall.getResultingDescriptor(), trace, byKeyword);
225                }
226            }
227            trace.record(DELEGATED_PROPERTY_RESOLVED_CALL, accessor, resultingCall);
228        }
229    
230        /* Resolve getValue() or setValue() methods from delegate */
231        public OverloadResolutionResults<FunctionDescriptor> getDelegatedPropertyConventionMethod(
232                @NotNull PropertyDescriptor propertyDescriptor,
233                @NotNull KtExpression delegateExpression,
234                @NotNull KotlinType delegateType,
235                @NotNull BindingTrace trace,
236                @NotNull LexicalScope scope,
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(
247                    trace, scope,
248                    DataFlowInfo.EMPTY, expectedType);
249    
250            boolean hasThis = propertyDescriptor.getExtensionReceiverParameter() != null || propertyDescriptor.getDispatchReceiverParameter() != null;
251    
252            List<KtExpression> arguments = Lists.newArrayList();
253            KtPsiFactory psiFactory = KtPsiFactory(delegateExpression);
254            arguments.add(psiFactory.createExpression(hasThis ? "this" : "null"));
255            arguments.add(createExpressionForProperty(psiFactory));
256    
257            if (!isGet) {
258                KtReferenceExpression fakeArgument = (KtReferenceExpression) createFakeExpressionOfType(delegateExpression.getProject(), trace,
259                                                                                                          "fakeArgument" + arguments.size(),
260                                                                                                        propertyDescriptor.getType());
261                arguments.add(fakeArgument);
262                List<ValueParameterDescriptor> valueParameters = accessor.getValueParameters();
263                trace.record(REFERENCE_TARGET, fakeArgument, valueParameters.get(0));
264            }
265    
266            Name functionName = isGet ? GETTER_NAME : SETTER_NAME;
267            ExpressionReceiver receiver = new ExpressionReceiver(delegateExpression, delegateType);
268    
269            Pair<Call, OverloadResolutionResults<FunctionDescriptor>> resolutionResult =
270                    fakeCallResolver.makeAndResolveFakeCallInContext(receiver, context, arguments, functionName, delegateExpression);
271    
272            OverloadResolutionResults<FunctionDescriptor> resolutionResults = resolutionResult.getSecond();
273    
274            // Resolve get/set is getValue/setValue was not found. Temporary, for code migration
275            if (!resolutionResults.isSuccess() && !resolutionResults.isAmbiguity()) {
276                Name oldFunctionName = isGet ? OLD_GETTER_NAME : OLD_SETTER_NAME;
277                Pair<Call, OverloadResolutionResults<FunctionDescriptor>> additionalResolutionResult =
278                        fakeCallResolver.makeAndResolveFakeCallInContext(receiver, context, arguments, oldFunctionName, delegateExpression);
279                if (additionalResolutionResult.getSecond().isSuccess()) {
280                    FunctionDescriptor resultingDescriptor = additionalResolutionResult.getSecond().getResultingDescriptor();
281    
282                    PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(propertyDescriptor);
283                    if (declaration instanceof KtProperty) {
284                        KtProperty property = (KtProperty) declaration;
285                        KtPropertyDelegate delegate = property.getDelegate();
286                        if (delegate != null) {
287                            PsiElement byKeyword = delegate.getByKeywordNode().getPsi();
288    
289                            trace.report(DELEGATE_RESOLVED_TO_DEPRECATED_CONVENTION.on(
290                                    byKeyword, resultingDescriptor, delegateType, functionName.asString()));
291                        }
292                    }
293    
294                    trace.record(BindingContext.DELEGATED_PROPERTY_CALL, accessor, additionalResolutionResult.getFirst());
295                    return additionalResolutionResult.getSecond();
296                }
297            }
298    
299            trace.record(BindingContext.DELEGATED_PROPERTY_CALL, accessor, resolutionResult.getFirst());
300            return resolutionResults;
301        }
302    
303        private static String renderCall(@NotNull Call call, @NotNull BindingContext context) {
304            KtExpression calleeExpression = call.getCalleeExpression();
305            assert calleeExpression != null : "CalleeExpression should exists for fake call of convention method";
306            StringBuilder builder = new StringBuilder(calleeExpression.getText());
307            builder.append("(");
308            List<KotlinType> argumentTypes = Lists.newArrayList();
309            for (ValueArgument argument : call.getValueArguments()) {
310                argumentTypes.add(context.getType(argument.getArgumentExpression()));
311    
312            }
313            builder.append(Renderers.RENDER_COLLECTION_OF_TYPES.render(argumentTypes));
314            builder.append(")");
315            return builder.toString();
316        }
317    
318        @Nullable
319        public KotlinType resolveDelegateExpression(
320                @NotNull KtExpression delegateExpression,
321                @NotNull KtProperty jetProperty,
322                @NotNull PropertyDescriptor propertyDescriptor,
323                @NotNull LexicalScope propertyDeclarationInnerScope,
324                @NotNull LexicalScope accessorScope,
325                @NotNull BindingTrace trace,
326                @NotNull DataFlowInfo dataFlowInfo
327        ) {
328            TemporaryBindingTrace traceToResolveDelegatedProperty = TemporaryBindingTrace.create(trace, "Trace to resolve delegated property");
329            KtExpression calleeExpression = CallUtilKt.getCalleeExpressionIfAny(delegateExpression);
330            ConstraintSystemCompleter completer = createConstraintSystemCompleter(
331                    jetProperty, propertyDescriptor, delegateExpression, accessorScope, trace);
332            if (calleeExpression != null) {
333                traceToResolveDelegatedProperty.record(CONSTRAINT_SYSTEM_COMPLETER, calleeExpression, completer);
334            }
335            KotlinType delegateType = expressionTypingServices.safeGetType(propertyDeclarationInnerScope, delegateExpression, NO_EXPECTED_TYPE,
336                                                                           dataFlowInfo, traceToResolveDelegatedProperty);
337            traceToResolveDelegatedProperty.commit(new TraceEntryFilter() {
338                @Override
339                public boolean accept(@Nullable WritableSlice<?, ?> slice, Object key) {
340                    return slice != CONSTRAINT_SYSTEM_COMPLETER;
341                }
342            }, true);
343            return delegateType;
344        }
345    
346        @NotNull
347        private ConstraintSystemCompleter createConstraintSystemCompleter(
348                @NotNull KtProperty property,
349                @NotNull final PropertyDescriptor propertyDescriptor,
350                @NotNull final KtExpression delegateExpression,
351                @NotNull final LexicalScope accessorScope,
352                @NotNull final BindingTrace trace
353        ) {
354            final KotlinType expectedType = property.getTypeReference() != null ? propertyDescriptor.getType() : NO_EXPECTED_TYPE;
355            return new ConstraintSystemCompleter() {
356                @Override
357                public void completeConstraintSystem(
358                        @NotNull ConstraintSystem constraintSystem, @NotNull ResolvedCall<?> resolvedCall
359                ) {
360                    KotlinType returnType = resolvedCall.getCandidateDescriptor().getReturnType();
361                    if (returnType == null) return;
362    
363                    TemporaryBindingTrace traceToResolveConventionMethods =
364                            TemporaryBindingTrace.create(trace, "Trace to resolve delegated property convention methods");
365                    OverloadResolutionResults<FunctionDescriptor>
366                            getMethodResults = getDelegatedPropertyConventionMethod(
367                                    propertyDescriptor, delegateExpression, returnType, traceToResolveConventionMethods, accessorScope,
368                                    true, false
369                            );
370    
371                    if (conventionMethodFound(getMethodResults)) {
372                        FunctionDescriptor descriptor = getMethodResults.getResultingDescriptor();
373                        KotlinType returnTypeOfGetMethod = descriptor.getReturnType();
374                        if (returnTypeOfGetMethod != null) {
375                            constraintSystem.addSupertypeConstraint(expectedType, returnTypeOfGetMethod, FROM_COMPLETER.position());
376                        }
377                        addConstraintForThisValue(constraintSystem, descriptor);
378                    }
379                    if (!propertyDescriptor.isVar()) return;
380    
381                    // For the case: 'val v by d' (no declared type).
382                    // When we add a constraint for 'set' method for delegated expression 'd' we use a type of the declared variable 'v'.
383                    // 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).
384                    if (propertyDescriptor.getReturnType() instanceof DeferredType) return;
385    
386                    OverloadResolutionResults<FunctionDescriptor>
387                            setMethodResults = getDelegatedPropertyConventionMethod(
388                                    propertyDescriptor, delegateExpression, returnType, traceToResolveConventionMethods, accessorScope,
389                                    false, false
390                            );
391    
392                    if (conventionMethodFound(setMethodResults)) {
393                        FunctionDescriptor descriptor = setMethodResults.getResultingDescriptor();
394                        List<ValueParameterDescriptor> valueParameters = descriptor.getValueParameters();
395                        if (valueParameters.size() == 3) {
396                            ValueParameterDescriptor valueParameterForThis = valueParameters.get(2);
397    
398                            if (!noExpectedType(expectedType)) {
399                                constraintSystem.addSubtypeConstraint(
400                                        expectedType, valueParameterForThis.getType(), FROM_COMPLETER.position());
401                            }
402                            addConstraintForThisValue(constraintSystem, descriptor);
403                        }
404                    }
405                }
406    
407                private boolean conventionMethodFound(@NotNull OverloadResolutionResults<FunctionDescriptor> results) {
408                    return results.isSuccess() ||
409                           (results.isSingleResult() &&
410                            results.getResultCode() == OverloadResolutionResults.Code.SINGLE_CANDIDATE_ARGUMENT_MISMATCH);
411                }
412    
413                private void addConstraintForThisValue(ConstraintSystem constraintSystem, FunctionDescriptor resultingDescriptor) {
414                    ReceiverParameterDescriptor extensionReceiver = propertyDescriptor.getExtensionReceiverParameter();
415                    ReceiverParameterDescriptor dispatchReceiver = propertyDescriptor.getDispatchReceiverParameter();
416                    KotlinType typeOfThis =
417                            extensionReceiver != null ? extensionReceiver.getType() :
418                            dispatchReceiver != null ? dispatchReceiver.getType() :
419                            builtIns.getNullableNothingType();
420    
421                    List<ValueParameterDescriptor> valueParameters = resultingDescriptor.getValueParameters();
422                    if (valueParameters.isEmpty()) return;
423                    ValueParameterDescriptor valueParameterForThis = valueParameters.get(0);
424    
425                    constraintSystem.addSubtypeConstraint(typeOfThis, valueParameterForThis.getType(), FROM_COMPLETER.position());
426                }
427            };
428        }
429    }