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.types.expressions;
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.FunctionDescriptor;
024 import org.jetbrains.jet.lang.descriptors.PropertyAccessorDescriptor;
025 import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
026 import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
027 import org.jetbrains.jet.lang.diagnostics.rendering.Renderers;
028 import org.jetbrains.jet.lang.psi.Call;
029 import org.jetbrains.jet.lang.psi.JetExpression;
030 import org.jetbrains.jet.lang.psi.JetReferenceExpression;
031 import org.jetbrains.jet.lang.psi.ValueArgument;
032 import org.jetbrains.jet.lang.resolve.BindingContext;
033 import org.jetbrains.jet.lang.resolve.BindingTrace;
034 import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
035 import org.jetbrains.jet.lang.resolve.calls.context.ExpressionPosition;
036 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
037 import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
038 import org.jetbrains.jet.lang.resolve.calls.util.CallMaker;
039 import org.jetbrains.jet.lang.resolve.name.Name;
040 import org.jetbrains.jet.lang.resolve.scopes.JetScope;
041 import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
042 import org.jetbrains.jet.lang.types.DeferredType;
043 import org.jetbrains.jet.lang.types.JetType;
044 import org.jetbrains.jet.lang.types.TypeUtils;
045 import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
046 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
047
048 import java.util.List;
049
050 import static org.jetbrains.jet.lang.diagnostics.Errors.*;
051 import static org.jetbrains.jet.lang.psi.JetPsiFactory.createExpression;
052 import static org.jetbrains.jet.lang.psi.JetPsiFactory.createSimpleName;
053 import static org.jetbrains.jet.lang.resolve.BindingContext.*;
054 import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.createFakeExpressionOfType;
055
056 public class DelegatedPropertyUtils {
057
058 @Nullable
059 public static JetType getDelegatedPropertyGetMethodReturnType(
060 @NotNull PropertyDescriptor propertyDescriptor,
061 @NotNull JetExpression delegateExpression,
062 @NotNull JetType delegateType,
063 @NotNull ExpressionTypingServices expressionTypingServices,
064 @NotNull BindingTrace trace,
065 @NotNull JetScope scope
066 ) {
067 resolveDelegatedPropertyConventionMethod(propertyDescriptor, delegateExpression, delegateType, expressionTypingServices, trace,
068 scope, true);
069 return getDelegateGetMethodReturnType(trace.getBindingContext(), propertyDescriptor);
070 }
071
072 @Nullable
073 private static JetType getDelegateGetMethodReturnType(@NotNull BindingContext context, @NotNull PropertyDescriptor descriptor) {
074 ResolvedCall<FunctionDescriptor> resolvedCall =
075 context.get(DELEGATED_PROPERTY_RESOLVED_CALL, descriptor.getGetter());
076 return resolvedCall != null ? resolvedCall.getResultingDescriptor().getReturnType() : null;
077 }
078
079 public static void resolveDelegatedPropertyGetMethod(
080 @NotNull PropertyDescriptor propertyDescriptor,
081 @NotNull JetExpression delegateExpression,
082 @NotNull JetType delegateType,
083 @NotNull ExpressionTypingServices expressionTypingServices,
084 @NotNull BindingTrace trace,
085 @NotNull JetScope scope
086 ) {
087 resolveDelegatedPropertyConventionMethod(propertyDescriptor, delegateExpression, delegateType, expressionTypingServices, trace,
088 scope, true);
089 JetType returnType = getDelegateGetMethodReturnType(trace.getBindingContext(), propertyDescriptor);
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 static void resolveDelegatedPropertySetMethod(
102 @NotNull PropertyDescriptor propertyDescriptor,
103 @NotNull JetExpression delegateExpression,
104 @NotNull JetType delegateType,
105 @NotNull ExpressionTypingServices expressionTypingServices,
106 @NotNull BindingTrace trace,
107 @NotNull JetScope scope
108 ) {
109 resolveDelegatedPropertyConventionMethod(propertyDescriptor, delegateExpression, delegateType, expressionTypingServices, trace,
110 scope, false);
111 }
112
113 /* Resolve get() or set() methods from delegate */
114 private static void resolveDelegatedPropertyConventionMethod(
115 @NotNull PropertyDescriptor propertyDescriptor,
116 @NotNull JetExpression delegateExpression,
117 @NotNull JetType delegateType,
118 @NotNull ExpressionTypingServices expressionTypingServices,
119 @NotNull BindingTrace trace,
120 @NotNull JetScope scope,
121 boolean isGet
122 ) {
123 PropertyAccessorDescriptor accessor = isGet ? propertyDescriptor.getGetter() : propertyDescriptor.getSetter();
124 assert accessor != null : "Delegated property should have getter/setter " + propertyDescriptor + " " + delegateExpression.getText();
125
126 if (trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, accessor) != null) return;
127
128 OverloadResolutionResults<FunctionDescriptor> functionResults = getDelegatedPropertyConventionMethod(
129 propertyDescriptor, delegateExpression, delegateType, expressionTypingServices, trace, scope, isGet);
130 Call call = trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, accessor);
131 assert call != null : "'getDelegatedPropertyConventionMethod' didn't record a call";
132
133 if (!functionResults.isSuccess()) {
134 String expectedFunction = renderCall(call, trace.getBindingContext());
135 if (functionResults.isIncomplete()) {
136 trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType));
137 }
138 else if (functionResults.isSingleResult() ||
139 functionResults.getResultCode() == OverloadResolutionResults.Code.MANY_FAILED_CANDIDATES) {
140 trace.report(DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE
141 .on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
142 }
143 else if (functionResults.isAmbiguity()) {
144 trace.report(DELEGATE_SPECIAL_FUNCTION_AMBIGUITY
145 .on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
146 }
147 else {
148 trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType));
149 }
150 return;
151 }
152
153 trace.record(DELEGATED_PROPERTY_RESOLVED_CALL, accessor, functionResults.getResultingCall());
154 }
155
156 /* Resolve get() or set() methods from delegate */
157 public static OverloadResolutionResults<FunctionDescriptor> getDelegatedPropertyConventionMethod(
158 @NotNull PropertyDescriptor propertyDescriptor,
159 @NotNull JetExpression delegateExpression,
160 @NotNull JetType delegateType,
161 @NotNull ExpressionTypingServices expressionTypingServices,
162 @NotNull BindingTrace trace,
163 @NotNull JetScope scope,
164 boolean isGet
165 ) {
166 PropertyAccessorDescriptor accessor = isGet ? propertyDescriptor.getGetter() : propertyDescriptor.getSetter();
167 assert accessor != null : "Delegated property should have getter/setter " + propertyDescriptor + " " + delegateExpression.getText();
168
169 ExpressionTypingContext context = ExpressionTypingContext.newContext(
170 expressionTypingServices, trace, scope,
171 DataFlowInfo.EMPTY, TypeUtils.NO_EXPECTED_TYPE, ExpressionPosition.FREE);
172 Project project = context.expressionTypingServices.getProject();
173
174 boolean hasThis = propertyDescriptor.getReceiverParameter() != null || propertyDescriptor.getExpectedThisObject() != null;
175
176 List<JetExpression> arguments = Lists.newArrayList();
177 arguments.add(createExpression(project, hasThis ? "this" : "null"));
178
179 arguments.add(createExpression(project, KotlinBuiltIns.getInstance().getPropertyMetadataImpl().getName().asString() + "(\"" + propertyDescriptor.getName().asString() + "\")"));
180
181 if (!isGet) {
182 JetReferenceExpression fakeArgument = (JetReferenceExpression) createFakeExpressionOfType(context.expressionTypingServices.getProject(), trace,
183 "fakeArgument" + arguments.size(),
184 propertyDescriptor.getType());
185 arguments.add(fakeArgument);
186 List<ValueParameterDescriptor> valueParameters = accessor.getValueParameters();
187 trace.record(REFERENCE_TARGET, fakeArgument, valueParameters.get(0));
188 }
189
190 Name functionName = Name.identifier(isGet ? "get" : "set");
191 JetReferenceExpression fakeCalleeExpression = createSimpleName(project, functionName.asString());
192
193 ExpressionReceiver receiver = new ExpressionReceiver(delegateExpression, delegateType);
194 Call call = CallMaker.makeCallWithExpressions(fakeCalleeExpression, receiver, null, fakeCalleeExpression, arguments, Call.CallType.DEFAULT);
195 trace.record(BindingContext.DELEGATED_PROPERTY_CALL, accessor, call);
196
197 return context.resolveCallWithGivenName(call, fakeCalleeExpression, functionName);
198 }
199
200 private static String renderCall(@NotNull Call call, @NotNull BindingContext context) {
201 JetExpression calleeExpression = call.getCalleeExpression();
202 assert calleeExpression != null : "CalleeExpression should exists for fake call of convention method";
203 StringBuilder builder = new StringBuilder(calleeExpression.getText());
204 builder.append("(");
205 List<JetType> argumentTypes = Lists.newArrayList();
206 for (ValueArgument argument : call.getValueArguments()) {
207 argumentTypes.add(context.get(EXPRESSION_TYPE, argument.getArgumentExpression()));
208
209 }
210 builder.append(Renderers.RENDER_COLLECTION_OF_TYPES.render(argumentTypes));
211 builder.append(")");
212 return builder.toString();
213 }
214
215 private DelegatedPropertyUtils() {
216 }
217 }