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