001    /*
002     * Copyright 2010-2013 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.jet.lang.resolve;
018    
019    import com.google.common.collect.Lists;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.jet.lang.descriptors.*;
023    import org.jetbrains.jet.lang.diagnostics.rendering.Renderers;
024    import org.jetbrains.jet.lang.psi.*;
025    import org.jetbrains.jet.lang.resolve.calls.CallResolver;
026    import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
027    import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintPosition;
028    import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem;
029    import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemCompleter;
030    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
031    import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
032    import org.jetbrains.jet.lang.resolve.calls.util.CallMaker;
033    import org.jetbrains.jet.lang.resolve.name.Name;
034    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
035    import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
036    import org.jetbrains.jet.lang.types.DeferredType;
037    import org.jetbrains.jet.lang.types.JetType;
038    import org.jetbrains.jet.lang.types.TypeUtils;
039    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
040    import org.jetbrains.jet.lang.types.expressions.ExpressionTypingContext;
041    import org.jetbrains.jet.lang.types.expressions.ExpressionTypingServices;
042    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
043    import org.jetbrains.jet.util.slicedmap.WritableSlice;
044    
045    import javax.inject.Inject;
046    import java.util.List;
047    
048    import static org.jetbrains.jet.lang.diagnostics.Errors.*;
049    import static org.jetbrains.jet.lang.psi.PsiPackage.JetPsiFactory;
050    import static org.jetbrains.jet.lang.resolve.BindingContext.*;
051    import static org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage.getCalleeExpressionIfAny;
052    import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
053    import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType;
054    import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.createFakeExpressionOfType;
055    
056    public class DelegatedPropertyResolver {
057       
058        @NotNull
059        private ExpressionTypingServices expressionTypingServices;
060    
061        @NotNull
062        private CallResolver callResolver;
063    
064        @Inject
065        public void setExpressionTypingServices(@NotNull ExpressionTypingServices expressionTypingServices) {
066            this.expressionTypingServices = expressionTypingServices;
067        }
068    
069        @Inject
070        public void setCallResolver(@NotNull CallResolver callResolver) {
071            this.callResolver = callResolver;
072        }
073    
074        @Nullable
075        public JetType getDelegatedPropertyGetMethodReturnType(
076                @NotNull PropertyDescriptor propertyDescriptor,
077                @NotNull JetExpression delegateExpression,
078                @NotNull JetType delegateType,
079                @NotNull BindingTrace trace,
080                @NotNull JetScope scope
081        ) {
082            resolveDelegatedPropertyConventionMethod(propertyDescriptor, delegateExpression, delegateType, trace, scope, true);
083            ResolvedCall<FunctionDescriptor> resolvedCall =
084                    trace.getBindingContext().get(DELEGATED_PROPERTY_RESOLVED_CALL, propertyDescriptor.getGetter());
085            return resolvedCall != null ? resolvedCall.getResultingDescriptor().getReturnType() : null;
086        }
087    
088        public void resolveDelegatedPropertyGetMethod(
089                @NotNull PropertyDescriptor propertyDescriptor,
090                @NotNull JetExpression delegateExpression,
091                @NotNull JetType delegateType,
092                @NotNull BindingTrace trace,
093                @NotNull JetScope scope
094        ) {
095            JetType returnType = getDelegatedPropertyGetMethodReturnType(
096                    propertyDescriptor, delegateExpression, delegateType, trace, scope);
097            JetType propertyType = propertyDescriptor.getType();
098    
099            /* Do not check return type of get() method of delegate for properties with DeferredType because property type is taken from it */
100            if (!(propertyType instanceof DeferredType) && returnType != null && !JetTypeChecker.DEFAULT.isSubtypeOf(returnType, propertyType)) {
101                Call call = trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, propertyDescriptor.getGetter());
102                assert call != null : "Call should exists for " + propertyDescriptor.getGetter();
103                trace.report(DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH
104                                     .on(delegateExpression, renderCall(call, trace.getBindingContext()), propertyDescriptor.getType(), returnType));
105            }
106        }
107    
108        public void resolveDelegatedPropertySetMethod(
109                @NotNull PropertyDescriptor propertyDescriptor,
110                @NotNull JetExpression delegateExpression,
111                @NotNull JetType delegateType,
112                @NotNull BindingTrace trace,
113                @NotNull JetScope scope
114        ) {
115            resolveDelegatedPropertyConventionMethod(propertyDescriptor, delegateExpression, delegateType, trace, scope, false);
116        }
117    
118        /* Resolve get() or set() methods from delegate */
119        private void resolveDelegatedPropertyConventionMethod(
120                @NotNull PropertyDescriptor propertyDescriptor,
121                @NotNull JetExpression delegateExpression,
122                @NotNull JetType delegateType,
123                @NotNull BindingTrace trace,
124                @NotNull JetScope scope,
125                boolean isGet
126        ) {
127            PropertyAccessorDescriptor accessor = isGet ? propertyDescriptor.getGetter() : propertyDescriptor.getSetter();
128            assert accessor != null : "Delegated property should have getter/setter " + propertyDescriptor + " " + delegateExpression.getText();
129    
130            if (trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, accessor) != null) return;
131    
132            OverloadResolutionResults<FunctionDescriptor> functionResults = getDelegatedPropertyConventionMethod(
133                    propertyDescriptor, delegateExpression, delegateType, trace, scope, isGet);
134            Call call = trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, accessor);
135            assert call != null : "'getDelegatedPropertyConventionMethod' didn't record a call";
136    
137            if (!functionResults.isSuccess()) {
138                String expectedFunction = renderCall(call, trace.getBindingContext());
139                if (functionResults.isIncomplete()) {
140                    trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType));
141                }
142                else if (functionResults.isSingleResult() ||
143                         functionResults.getResultCode() == OverloadResolutionResults.Code.MANY_FAILED_CANDIDATES) {
144                    trace.report(DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE
145                                                 .on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
146                }
147                else if (functionResults.isAmbiguity()) {
148                    trace.report(DELEGATE_SPECIAL_FUNCTION_AMBIGUITY
149                                                 .on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
150                }
151                else {
152                    trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType));
153                }
154                return;
155            }
156    
157            trace.record(DELEGATED_PROPERTY_RESOLVED_CALL, accessor, functionResults.getResultingCall());
158        }
159    
160        /* Resolve get() or set() methods from delegate */
161        public OverloadResolutionResults<FunctionDescriptor> getDelegatedPropertyConventionMethod(
162                @NotNull PropertyDescriptor propertyDescriptor,
163                @NotNull JetExpression delegateExpression,
164                @NotNull JetType delegateType,
165                @NotNull BindingTrace trace,
166                @NotNull JetScope scope,
167                boolean isGet
168        ) {
169            PropertyAccessorDescriptor accessor = isGet ? propertyDescriptor.getGetter() : propertyDescriptor.getSetter();
170            assert accessor != null : "Delegated property should have getter/setter " + propertyDescriptor + " " + delegateExpression.getText();
171    
172            ExpressionTypingContext context = ExpressionTypingContext.newContext(
173                    expressionTypingServices, trace, scope,
174                    DataFlowInfo.EMPTY, TypeUtils.NO_EXPECTED_TYPE);
175    
176            boolean hasThis = propertyDescriptor.getReceiverParameter() != null || propertyDescriptor.getExpectedThisObject() != null;
177    
178            List<JetExpression> arguments = Lists.newArrayList();
179            JetPsiFactory psiFactory = JetPsiFactory(delegateExpression);
180            arguments.add(psiFactory.createExpression(hasThis ? "this" : "null"));
181    
182            arguments.add(psiFactory.createExpression(KotlinBuiltIns.getInstance().getPropertyMetadataImpl().getName().asString() +
183                                                   "(\"" +
184                                                   propertyDescriptor.getName().asString() +
185                                                   "\")"));
186    
187            if (!isGet) {
188                JetReferenceExpression fakeArgument = (JetReferenceExpression) createFakeExpressionOfType(expressionTypingServices.getProject(), trace,
189                                                                                 "fakeArgument" + arguments.size(),
190                                                                                 propertyDescriptor.getType());
191                arguments.add(fakeArgument);
192                List<ValueParameterDescriptor> valueParameters = accessor.getValueParameters();
193                trace.record(REFERENCE_TARGET, fakeArgument, valueParameters.get(0));
194            }
195    
196            Name functionName = Name.identifier(isGet ? "get" : "set");
197            JetReferenceExpression fakeCalleeExpression = psiFactory.createSimpleName(functionName.asString());
198    
199            ExpressionReceiver receiver = new ExpressionReceiver(delegateExpression, delegateType);
200            Call call = CallMaker.makeCallWithExpressions(fakeCalleeExpression, receiver, null, fakeCalleeExpression, arguments, Call.CallType.DEFAULT);
201            trace.record(BindingContext.DELEGATED_PROPERTY_CALL, accessor, call);
202    
203            return callResolver.resolveCallWithGivenName(context, call, fakeCalleeExpression, functionName);
204        }
205    
206        private String renderCall(@NotNull Call call, @NotNull BindingContext context) {
207            JetExpression calleeExpression = call.getCalleeExpression();
208            assert calleeExpression != null : "CalleeExpression should exists for fake call of convention method";
209            StringBuilder builder = new StringBuilder(calleeExpression.getText());
210            builder.append("(");
211            List<JetType> argumentTypes = Lists.newArrayList();
212            for (ValueArgument argument : call.getValueArguments()) {
213                argumentTypes.add(context.get(EXPRESSION_TYPE, argument.getArgumentExpression()));
214    
215            }
216            builder.append(Renderers.RENDER_COLLECTION_OF_TYPES.render(argumentTypes));
217            builder.append(")");
218            return builder.toString();
219        }
220    
221        @Nullable
222        public JetType resolveDelegateExpression(
223                @NotNull JetExpression delegateExpression,
224                @NotNull JetProperty jetProperty,
225                @NotNull PropertyDescriptor propertyDescriptor,
226                @NotNull JetScope propertyDeclarationInnerScope,
227                @NotNull JetScope accessorScope,
228                @NotNull BindingTrace trace,
229                @NotNull DataFlowInfo dataFlowInfo
230        ) {
231            TemporaryBindingTrace traceToResolveDelegatedProperty = TemporaryBindingTrace.create(trace, "Trace to resolve delegated property");
232            JetExpression calleeExpression = getCalleeExpressionIfAny(delegateExpression);
233            ConstraintSystemCompleter completer = createConstraintSystemCompleter(
234                    jetProperty, propertyDescriptor, delegateExpression, accessorScope, trace);
235            if (calleeExpression != null) {
236                traceToResolveDelegatedProperty.record(CONSTRAINT_SYSTEM_COMPLETER, calleeExpression, completer);
237            }
238            JetType delegateType = expressionTypingServices.safeGetType(propertyDeclarationInnerScope, delegateExpression, NO_EXPECTED_TYPE,
239                                                                        dataFlowInfo, traceToResolveDelegatedProperty);
240            traceToResolveDelegatedProperty.commit(new TraceEntryFilter() {
241                @Override
242                public boolean accept(@Nullable WritableSlice<?, ?> slice, Object key) {
243                    return slice != CONSTRAINT_SYSTEM_COMPLETER;
244                }
245            }, true);
246            return delegateType;
247        }
248    
249        @NotNull
250        private ConstraintSystemCompleter createConstraintSystemCompleter(
251                @NotNull JetProperty property,
252                @NotNull final PropertyDescriptor propertyDescriptor,
253                @NotNull final JetExpression delegateExpression,
254                @NotNull final JetScope accessorScope,
255                @NotNull final BindingTrace trace
256        ) {
257            final JetType expectedType = property.getTypeRef() != null ? propertyDescriptor.getType() : NO_EXPECTED_TYPE;
258            return new ConstraintSystemCompleter() {
259                @Override
260                public void completeConstraintSystem(
261                        @NotNull ConstraintSystem constraintSystem, @NotNull ResolvedCall<?> resolvedCall
262                ) {
263                    JetType returnType = resolvedCall.getCandidateDescriptor().getReturnType();
264                    if (returnType == null) return;
265    
266                    TemporaryBindingTrace traceToResolveConventionMethods =
267                            TemporaryBindingTrace.create(trace, "Trace to resolve delegated property convention methods");
268                    OverloadResolutionResults<FunctionDescriptor>
269                            getMethodResults = getDelegatedPropertyConventionMethod(
270                            propertyDescriptor, delegateExpression, returnType, traceToResolveConventionMethods, accessorScope, true);
271    
272                    if (conventionMethodFound(getMethodResults)) {
273                        FunctionDescriptor descriptor = getMethodResults.getResultingDescriptor();
274                        JetType returnTypeOfGetMethod = descriptor.getReturnType();
275                        if (returnTypeOfGetMethod != null) {
276                            constraintSystem.addSupertypeConstraint(expectedType, returnTypeOfGetMethod, ConstraintPosition.FROM_COMPLETER);
277                        }
278                        addConstraintForThisValue(constraintSystem, descriptor);
279                    }
280                    if (!propertyDescriptor.isVar()) return;
281    
282                    // For the case: 'val v by d' (no declared type).
283                    // When we add a constraint for 'set' method for delegated expression 'd' we use a type of the declared variable 'v'.
284                    // 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).
285                    if (propertyDescriptor.getReturnType() instanceof DeferredType) return;
286    
287                    OverloadResolutionResults<FunctionDescriptor> setMethodResults =
288                            getDelegatedPropertyConventionMethod(
289                                    propertyDescriptor, delegateExpression, returnType, traceToResolveConventionMethods, accessorScope, false);
290    
291                    if (conventionMethodFound(setMethodResults)) {
292                        FunctionDescriptor descriptor = setMethodResults.getResultingDescriptor();
293                        List<ValueParameterDescriptor> valueParameters = descriptor.getValueParameters();
294                        if (valueParameters.size() == 3) {
295                            ValueParameterDescriptor valueParameterForThis = valueParameters.get(2);
296    
297                            if (!noExpectedType(expectedType)) {
298                                constraintSystem.addSubtypeConstraint(
299                                        expectedType, valueParameterForThis.getType(), ConstraintPosition.FROM_COMPLETER);
300                            }
301                            addConstraintForThisValue(constraintSystem, descriptor);
302                        }
303                    }
304                }
305    
306                private boolean conventionMethodFound(@NotNull OverloadResolutionResults<FunctionDescriptor> results) {
307                    return results.isSuccess() ||
308                           (results.isSingleResult() &&
309                            results.getResultCode() == OverloadResolutionResults.Code.SINGLE_CANDIDATE_ARGUMENT_MISMATCH);
310                }
311    
312                private void addConstraintForThisValue(ConstraintSystem constraintSystem, FunctionDescriptor resultingDescriptor) {
313                    ReceiverParameterDescriptor receiverParameter = propertyDescriptor.getReceiverParameter();
314                    ReceiverParameterDescriptor thisObject = propertyDescriptor.getExpectedThisObject();
315                    JetType typeOfThis =
316                            receiverParameter != null ? receiverParameter.getType() :
317                            thisObject != null ? thisObject.getType() :
318                            KotlinBuiltIns.getInstance().getNullableNothingType();
319    
320                    List<ValueParameterDescriptor> valueParameters = resultingDescriptor.getValueParameters();
321                    if (valueParameters.isEmpty()) return;
322                    ValueParameterDescriptor valueParameterForThis = valueParameters.get(0);
323    
324                    constraintSystem.addSubtypeConstraint(typeOfThis, valueParameterForThis.getType(), ConstraintPosition.FROM_COMPLETER);
325                }
326            };
327        }
328    }