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.INSTANCE.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 }