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 017package org.jetbrains.jet.lang.types.expressions; 018 019import com.google.common.collect.Lists; 020import com.intellij.openapi.project.Project; 021import org.jetbrains.annotations.NotNull; 022import org.jetbrains.annotations.Nullable; 023import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; 024import org.jetbrains.jet.lang.descriptors.PropertyAccessorDescriptor; 025import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; 026import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor; 027import org.jetbrains.jet.lang.diagnostics.rendering.Renderers; 028import org.jetbrains.jet.lang.psi.Call; 029import org.jetbrains.jet.lang.psi.JetExpression; 030import org.jetbrains.jet.lang.psi.JetReferenceExpression; 031import org.jetbrains.jet.lang.psi.ValueArgument; 032import org.jetbrains.jet.lang.resolve.BindingContext; 033import org.jetbrains.jet.lang.resolve.BindingTrace; 034import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; 035import org.jetbrains.jet.lang.resolve.calls.context.ExpressionPosition; 036import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; 037import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults; 038import org.jetbrains.jet.lang.resolve.calls.util.CallMaker; 039import org.jetbrains.jet.lang.resolve.name.Name; 040import org.jetbrains.jet.lang.resolve.scopes.JetScope; 041import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; 042import org.jetbrains.jet.lang.types.DeferredType; 043import org.jetbrains.jet.lang.types.JetType; 044import org.jetbrains.jet.lang.types.TypeUtils; 045import org.jetbrains.jet.lang.types.checker.JetTypeChecker; 046import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; 047 048import java.util.List; 049 050import static org.jetbrains.jet.lang.diagnostics.Errors.*; 051import static org.jetbrains.jet.lang.psi.JetPsiFactory.createExpression; 052import static org.jetbrains.jet.lang.psi.JetPsiFactory.createSimpleName; 053import static org.jetbrains.jet.lang.resolve.BindingContext.*; 054import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.createFakeExpressionOfType; 055 056public 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}