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