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