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