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