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 }