001 /*
002 * Copyright 2010-2015 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.kotlin.resolve;
018
019 import com.google.common.collect.Lists;
020 import com.intellij.psi.PsiElement;
021 import kotlin.Pair;
022 import org.jetbrains.annotations.NotNull;
023 import org.jetbrains.annotations.Nullable;
024 import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
025 import org.jetbrains.kotlin.descriptors.*;
026 import org.jetbrains.kotlin.diagnostics.rendering.Renderers;
027 import org.jetbrains.kotlin.name.Name;
028 import org.jetbrains.kotlin.psi.*;
029 import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem;
030 import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemCompleter;
031 import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
032 import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults;
033 import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
034 import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
035 import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
036 import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator;
037 import org.jetbrains.kotlin.types.DeferredType;
038 import org.jetbrains.kotlin.types.JetType;
039 import org.jetbrains.kotlin.types.TypeUtils;
040 import org.jetbrains.kotlin.types.checker.JetTypeChecker;
041 import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext;
042 import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices;
043 import org.jetbrains.kotlin.types.expressions.FakeCallResolver;
044 import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
045
046 import java.util.Collections;
047 import java.util.List;
048
049 import static org.jetbrains.kotlin.diagnostics.Errors.*;
050 import static org.jetbrains.kotlin.psi.PsiPackage.JetPsiFactory;
051 import static org.jetbrains.kotlin.resolve.BindingContext.*;
052 import static org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage.getCalleeExpressionIfAny;
053 import static org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.FROM_COMPLETER;
054 import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE;
055 import static org.jetbrains.kotlin.types.TypeUtils.noExpectedType;
056 import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.createFakeExpressionOfType;
057
058 public class DelegatedPropertyResolver {
059
060 public static final Name PROPERTY_DELEGATED_FUNCTION_NAME = Name.identifier("propertyDelegated");
061 public static final Name GETTER_NAME = Name.identifier("get");
062 public static final Name SETTER_NAME = Name.identifier("set");
063
064 @NotNull private final ExpressionTypingServices expressionTypingServices;
065 @NotNull private final FakeCallResolver fakeCallResolver;
066 @NotNull private final KotlinBuiltIns builtIns;
067 @NotNull private final SymbolUsageValidator symbolUsageValidator;
068
069 public DelegatedPropertyResolver(
070 @NotNull SymbolUsageValidator symbolUsageValidator,
071 @NotNull KotlinBuiltIns builtIns,
072 @NotNull FakeCallResolver fakeCallResolver,
073 @NotNull ExpressionTypingServices expressionTypingServices
074 ) {
075 this.symbolUsageValidator = symbolUsageValidator;
076 this.builtIns = builtIns;
077 this.fakeCallResolver = fakeCallResolver;
078 this.expressionTypingServices = expressionTypingServices;
079 }
080
081 @Nullable
082 public JetType getDelegatedPropertyGetMethodReturnType(
083 @NotNull PropertyDescriptor propertyDescriptor,
084 @NotNull JetExpression delegateExpression,
085 @NotNull JetType delegateType,
086 @NotNull BindingTrace trace,
087 @NotNull LexicalScope scope
088 ) {
089 resolveDelegatedPropertyConventionMethod(propertyDescriptor, delegateExpression, delegateType, trace, scope, true);
090 ResolvedCall<FunctionDescriptor> resolvedCall =
091 trace.getBindingContext().get(DELEGATED_PROPERTY_RESOLVED_CALL, propertyDescriptor.getGetter());
092 return resolvedCall != null ? resolvedCall.getResultingDescriptor().getReturnType() : null;
093 }
094
095 public void resolveDelegatedPropertyGetMethod(
096 @NotNull PropertyDescriptor propertyDescriptor,
097 @NotNull JetExpression delegateExpression,
098 @NotNull JetType delegateType,
099 @NotNull BindingTrace trace,
100 @NotNull LexicalScope scope
101 ) {
102 JetType returnType = getDelegatedPropertyGetMethodReturnType(
103 propertyDescriptor, delegateExpression, delegateType, trace, scope);
104 JetType propertyType = propertyDescriptor.getType();
105
106 /* Do not check return type of get() method of delegate for properties with DeferredType because property type is taken from it */
107 if (!(propertyType instanceof DeferredType) && returnType != null && !JetTypeChecker.DEFAULT.isSubtypeOf(returnType, propertyType)) {
108 Call call = trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, propertyDescriptor.getGetter());
109 assert call != null : "Call should exists for " + propertyDescriptor.getGetter();
110 trace.report(DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH
111 .on(delegateExpression, renderCall(call, trace.getBindingContext()), propertyDescriptor.getType(), returnType));
112 }
113 }
114
115 public void resolveDelegatedPropertySetMethod(
116 @NotNull PropertyDescriptor propertyDescriptor,
117 @NotNull JetExpression delegateExpression,
118 @NotNull JetType delegateType,
119 @NotNull BindingTrace trace,
120 @NotNull LexicalScope scope
121 ) {
122 resolveDelegatedPropertyConventionMethod(propertyDescriptor, delegateExpression, delegateType, trace, scope, false);
123 }
124
125 @NotNull
126 private JetExpression createExpressionForPropertyMetadata(
127 @NotNull JetPsiFactory psiFactory,
128 @NotNull PropertyDescriptor propertyDescriptor
129 ) {
130 return psiFactory.createExpression(builtIns.getPropertyMetadataImpl().getName().asString() +
131 "(\"" +
132 propertyDescriptor.getName().asString() +
133 "\"): " +
134 builtIns.getPropertyMetadata().getName().asString());
135 }
136
137 public void resolveDelegatedPropertyPDMethod(
138 @NotNull PropertyDescriptor propertyDescriptor,
139 @NotNull JetExpression delegateExpression,
140 @NotNull JetType delegateType,
141 @NotNull BindingTrace trace,
142 @NotNull LexicalScope scope
143 ) {
144 TemporaryBindingTrace traceToResolvePDMethod = TemporaryBindingTrace.create(trace, "Trace to resolve propertyDelegated method in delegated property");
145 ExpressionTypingContext context = ExpressionTypingContext.newContext(
146 traceToResolvePDMethod, scope,
147 DataFlowInfo.EMPTY, TypeUtils.NO_EXPECTED_TYPE);
148
149 JetPsiFactory psiFactory = JetPsiFactory(delegateExpression);
150 List<JetExpression> arguments = Collections.singletonList(createExpressionForPropertyMetadata(psiFactory, propertyDescriptor));
151 ExpressionReceiver receiver = new ExpressionReceiver(delegateExpression, delegateType);
152
153 Pair<Call, OverloadResolutionResults<FunctionDescriptor>> resolutionResult =
154 fakeCallResolver.makeAndResolveFakeCallInContext(receiver, context, arguments, PROPERTY_DELEGATED_FUNCTION_NAME, delegateExpression);
155
156 Call call = resolutionResult.getFirst();
157 OverloadResolutionResults<FunctionDescriptor> functionResults = resolutionResult.getSecond();
158
159 if (!functionResults.isSuccess()) {
160 String expectedFunction = renderCall(call, traceToResolvePDMethod.getBindingContext());
161 if (functionResults.isIncomplete() || functionResults.isSingleResult() ||
162 functionResults.getResultCode() == OverloadResolutionResults.Code.MANY_FAILED_CANDIDATES) {
163 trace.report(DELEGATE_PD_METHOD_NONE_APPLICABLE.on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
164 } else if (functionResults.isAmbiguity()) {
165 trace.report(DELEGATE_SPECIAL_FUNCTION_AMBIGUITY
166 .on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
167 }
168 return;
169 }
170
171 trace.record(DELEGATED_PROPERTY_PD_RESOLVED_CALL, propertyDescriptor, functionResults.getResultingCall());
172 }
173
174 /* Resolve get() or set() methods from delegate */
175 private void resolveDelegatedPropertyConventionMethod(
176 @NotNull PropertyDescriptor propertyDescriptor,
177 @NotNull JetExpression delegateExpression,
178 @NotNull JetType delegateType,
179 @NotNull BindingTrace trace,
180 @NotNull LexicalScope scope,
181 boolean isGet
182 ) {
183 PropertyAccessorDescriptor accessor = isGet ? propertyDescriptor.getGetter() : propertyDescriptor.getSetter();
184 assert accessor != null : "Delegated property should have getter/setter " + propertyDescriptor + " " + delegateExpression.getText();
185
186 if (trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, accessor) != null) return;
187
188 OverloadResolutionResults<FunctionDescriptor> functionResults = getDelegatedPropertyConventionMethod(
189 propertyDescriptor, delegateExpression, delegateType, trace, scope, isGet, true);
190 Call call = trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, accessor);
191 assert call != null : "'getDelegatedPropertyConventionMethod' didn't record a call";
192
193 if (!functionResults.isSuccess()) {
194 String expectedFunction = renderCall(call, trace.getBindingContext());
195 if (functionResults.isIncomplete()) {
196 trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType));
197 }
198 else if (functionResults.isSingleResult() ||
199 functionResults.getResultCode() == OverloadResolutionResults.Code.MANY_FAILED_CANDIDATES) {
200 trace.report(DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE
201 .on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
202 }
203 else if (functionResults.isAmbiguity()) {
204 trace.report(DELEGATE_SPECIAL_FUNCTION_AMBIGUITY
205 .on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
206 }
207 else {
208 trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType));
209 }
210 return;
211 }
212
213 ResolvedCall<FunctionDescriptor> resultingCall = functionResults.getResultingCall();
214 PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(propertyDescriptor);
215 if (declaration instanceof JetProperty) {
216 JetProperty property = (JetProperty) declaration;
217 JetPropertyDelegate delegate = property.getDelegate();
218 if (delegate != null) {
219 PsiElement byKeyword = delegate.getByKeywordNode().getPsi();
220 symbolUsageValidator.validateCall(resultingCall.getResultingDescriptor(), trace, byKeyword);
221 }
222 }
223 trace.record(DELEGATED_PROPERTY_RESOLVED_CALL, accessor, resultingCall);
224 }
225
226 /* Resolve get() or set() methods from delegate */
227 public OverloadResolutionResults<FunctionDescriptor> getDelegatedPropertyConventionMethod(
228 @NotNull PropertyDescriptor propertyDescriptor,
229 @NotNull JetExpression delegateExpression,
230 @NotNull JetType delegateType,
231 @NotNull BindingTrace trace,
232 @NotNull LexicalScope scope,
233 boolean isGet,
234 boolean isComplete
235 ) {
236 PropertyAccessorDescriptor accessor = isGet ? propertyDescriptor.getGetter() : propertyDescriptor.getSetter();
237 assert accessor != null : "Delegated property should have getter/setter " + propertyDescriptor + " " + delegateExpression.getText();
238
239 JetType expectedType = isComplete && isGet && !(propertyDescriptor.getType() instanceof DeferredType)
240 ? propertyDescriptor.getType() : TypeUtils.NO_EXPECTED_TYPE;
241
242 ExpressionTypingContext context = ExpressionTypingContext.newContext(
243 trace, scope,
244 DataFlowInfo.EMPTY, expectedType);
245
246 boolean hasThis = propertyDescriptor.getExtensionReceiverParameter() != null || propertyDescriptor.getDispatchReceiverParameter() != null;
247
248 List<JetExpression> arguments = Lists.newArrayList();
249 JetPsiFactory psiFactory = JetPsiFactory(delegateExpression);
250 arguments.add(psiFactory.createExpression(hasThis ? "this" : "null"));
251 arguments.add(createExpressionForPropertyMetadata(psiFactory, propertyDescriptor));
252
253 if (!isGet) {
254 JetReferenceExpression fakeArgument = (JetReferenceExpression) createFakeExpressionOfType(delegateExpression.getProject(), trace,
255 "fakeArgument" + arguments.size(),
256 propertyDescriptor.getType());
257 arguments.add(fakeArgument);
258 List<ValueParameterDescriptor> valueParameters = accessor.getValueParameters();
259 trace.record(REFERENCE_TARGET, fakeArgument, valueParameters.get(0));
260 }
261
262 Name functionName = isGet ? GETTER_NAME : SETTER_NAME;
263 ExpressionReceiver receiver = new ExpressionReceiver(delegateExpression, delegateType);
264
265 Pair<Call, OverloadResolutionResults<FunctionDescriptor>> resolutionResult =
266 fakeCallResolver.makeAndResolveFakeCallInContext(receiver, context, arguments, functionName, delegateExpression);
267
268 trace.record(BindingContext.DELEGATED_PROPERTY_CALL, accessor, resolutionResult.getFirst());
269 return resolutionResult.getSecond();
270 }
271
272 private static String renderCall(@NotNull Call call, @NotNull BindingContext context) {
273 JetExpression calleeExpression = call.getCalleeExpression();
274 assert calleeExpression != null : "CalleeExpression should exists for fake call of convention method";
275 StringBuilder builder = new StringBuilder(calleeExpression.getText());
276 builder.append("(");
277 List<JetType> argumentTypes = Lists.newArrayList();
278 for (ValueArgument argument : call.getValueArguments()) {
279 argumentTypes.add(context.getType(argument.getArgumentExpression()));
280
281 }
282 builder.append(Renderers.RENDER_COLLECTION_OF_TYPES.render(argumentTypes));
283 builder.append(")");
284 return builder.toString();
285 }
286
287 @Nullable
288 public JetType resolveDelegateExpression(
289 @NotNull JetExpression delegateExpression,
290 @NotNull JetProperty jetProperty,
291 @NotNull PropertyDescriptor propertyDescriptor,
292 @NotNull LexicalScope propertyDeclarationInnerScope,
293 @NotNull LexicalScope accessorScope,
294 @NotNull BindingTrace trace,
295 @NotNull DataFlowInfo dataFlowInfo
296 ) {
297 TemporaryBindingTrace traceToResolveDelegatedProperty = TemporaryBindingTrace.create(trace, "Trace to resolve delegated property");
298 JetExpression calleeExpression = getCalleeExpressionIfAny(delegateExpression);
299 ConstraintSystemCompleter completer = createConstraintSystemCompleter(
300 jetProperty, propertyDescriptor, delegateExpression, accessorScope, trace);
301 if (calleeExpression != null) {
302 traceToResolveDelegatedProperty.record(CONSTRAINT_SYSTEM_COMPLETER, calleeExpression, completer);
303 }
304 JetType delegateType = expressionTypingServices.safeGetType(propertyDeclarationInnerScope, delegateExpression, NO_EXPECTED_TYPE,
305 dataFlowInfo, traceToResolveDelegatedProperty);
306 traceToResolveDelegatedProperty.commit(new TraceEntryFilter() {
307 @Override
308 public boolean accept(@Nullable WritableSlice<?, ?> slice, Object key) {
309 return slice != CONSTRAINT_SYSTEM_COMPLETER;
310 }
311 }, true);
312 return delegateType;
313 }
314
315 @NotNull
316 private ConstraintSystemCompleter createConstraintSystemCompleter(
317 @NotNull JetProperty property,
318 @NotNull final PropertyDescriptor propertyDescriptor,
319 @NotNull final JetExpression delegateExpression,
320 @NotNull final LexicalScope accessorScope,
321 @NotNull final BindingTrace trace
322 ) {
323 final JetType expectedType = property.getTypeReference() != null ? propertyDescriptor.getType() : NO_EXPECTED_TYPE;
324 return new ConstraintSystemCompleter() {
325 @Override
326 public void completeConstraintSystem(
327 @NotNull ConstraintSystem constraintSystem, @NotNull ResolvedCall<?> resolvedCall
328 ) {
329 JetType returnType = resolvedCall.getCandidateDescriptor().getReturnType();
330 if (returnType == null) return;
331
332 TemporaryBindingTrace traceToResolveConventionMethods =
333 TemporaryBindingTrace.create(trace, "Trace to resolve delegated property convention methods");
334 OverloadResolutionResults<FunctionDescriptor>
335 getMethodResults = getDelegatedPropertyConventionMethod(
336 propertyDescriptor, delegateExpression, returnType, traceToResolveConventionMethods, accessorScope,
337 true, false
338 );
339
340 if (conventionMethodFound(getMethodResults)) {
341 FunctionDescriptor descriptor = getMethodResults.getResultingDescriptor();
342 JetType returnTypeOfGetMethod = descriptor.getReturnType();
343 if (returnTypeOfGetMethod != null) {
344 constraintSystem.addSupertypeConstraint(expectedType, returnTypeOfGetMethod, FROM_COMPLETER.position());
345 }
346 addConstraintForThisValue(constraintSystem, descriptor);
347 }
348 if (!propertyDescriptor.isVar()) return;
349
350 // For the case: 'val v by d' (no declared type).
351 // When we add a constraint for 'set' method for delegated expression 'd' we use a type of the declared variable 'v'.
352 // 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).
353 if (propertyDescriptor.getReturnType() instanceof DeferredType) return;
354
355 OverloadResolutionResults<FunctionDescriptor>
356 setMethodResults = getDelegatedPropertyConventionMethod(
357 propertyDescriptor, delegateExpression, returnType, traceToResolveConventionMethods, accessorScope,
358 false, false
359 );
360
361 if (conventionMethodFound(setMethodResults)) {
362 FunctionDescriptor descriptor = setMethodResults.getResultingDescriptor();
363 List<ValueParameterDescriptor> valueParameters = descriptor.getValueParameters();
364 if (valueParameters.size() == 3) {
365 ValueParameterDescriptor valueParameterForThis = valueParameters.get(2);
366
367 if (!noExpectedType(expectedType)) {
368 constraintSystem.addSubtypeConstraint(
369 expectedType, valueParameterForThis.getType(), FROM_COMPLETER.position());
370 }
371 addConstraintForThisValue(constraintSystem, descriptor);
372 }
373 }
374 }
375
376 private boolean conventionMethodFound(@NotNull OverloadResolutionResults<FunctionDescriptor> results) {
377 return results.isSuccess() ||
378 (results.isSingleResult() &&
379 results.getResultCode() == OverloadResolutionResults.Code.SINGLE_CANDIDATE_ARGUMENT_MISMATCH);
380 }
381
382 private void addConstraintForThisValue(ConstraintSystem constraintSystem, FunctionDescriptor resultingDescriptor) {
383 ReceiverParameterDescriptor extensionReceiver = propertyDescriptor.getExtensionReceiverParameter();
384 ReceiverParameterDescriptor dispatchReceiver = propertyDescriptor.getDispatchReceiverParameter();
385 JetType typeOfThis =
386 extensionReceiver != null ? extensionReceiver.getType() :
387 dispatchReceiver != null ? dispatchReceiver.getType() :
388 builtIns.getNullableNothingType();
389
390 List<ValueParameterDescriptor> valueParameters = resultingDescriptor.getValueParameters();
391 if (valueParameters.isEmpty()) return;
392 ValueParameterDescriptor valueParameterForThis = valueParameters.get(0);
393
394 constraintSystem.addSubtypeConstraint(typeOfThis, valueParameterForThis.getType(), FROM_COMPLETER.position());
395 }
396 };
397 }
398 }