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 }