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.calls;
018
019 import com.google.common.collect.Maps;
020 import com.google.common.collect.Sets;
021 import com.intellij.psi.impl.source.tree.LeafPsiElement;
022 import org.jetbrains.annotations.NotNull;
023 import org.jetbrains.annotations.Nullable;
024 import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor;
025 import org.jetbrains.kotlin.descriptors.CallableDescriptor;
026 import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor;
027 import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
028 import org.jetbrains.kotlin.diagnostics.Diagnostic;
029 import org.jetbrains.kotlin.name.Name;
030 import org.jetbrains.kotlin.psi.*;
031 import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage;
032 import org.jetbrains.kotlin.resolve.calls.model.*;
033 import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy;
034 import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
035
036 import java.util.List;
037 import java.util.Map;
038 import java.util.Set;
039
040 import static org.jetbrains.kotlin.diagnostics.Errors.*;
041 import static org.jetbrains.kotlin.diagnostics.Errors.BadNamedArgumentsTarget.INVOKE_ON_FUNCTION_TYPE;
042 import static org.jetbrains.kotlin.diagnostics.Errors.BadNamedArgumentsTarget.NON_KOTLIN_FUNCTION;
043 import static org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET;
044 import static org.jetbrains.kotlin.resolve.calls.ValueArgumentsToParametersMapper.Status.*;
045
046 public class ValueArgumentsToParametersMapper {
047
048 public enum Status {
049 STRONG_ERROR(false),
050 ERROR(false),
051 WEAK_ERROR(false),
052 OK(true);
053
054 private final boolean success;
055
056 private Status(boolean success) {
057 this.success = success;
058 }
059
060 public boolean isSuccess() {
061 return success;
062 }
063
064 public Status compose(Status other) {
065 if (this == STRONG_ERROR || other == STRONG_ERROR) return STRONG_ERROR;
066 if (this == ERROR || other == ERROR) return ERROR;
067 if (this == WEAK_ERROR || other == WEAK_ERROR) return WEAK_ERROR;
068 return this;
069 }
070 }
071 public static <D extends CallableDescriptor> Status mapValueArgumentsToParameters(
072 @NotNull Call call,
073 @NotNull TracingStrategy tracing,
074 @NotNull MutableResolvedCall<D> candidateCall,
075 @NotNull Set<ValueArgument> unmappedArguments
076 ) {
077 //return new ValueArgumentsToParametersMapper().process(call, tracing, candidateCall, unmappedArguments);
078 Processor<D> processor = new Processor<D>(call, candidateCall, tracing);
079 processor.process();
080 unmappedArguments.addAll(processor.unmappedArguments);
081 return processor.status;
082 }
083
084 private static class Processor<D extends CallableDescriptor> {
085 private final Call call;
086 private final TracingStrategy tracing;
087 private final MutableResolvedCall<D> candidateCall;
088
089 private final Map<Name,ValueParameterDescriptor> parameterByName;
090
091 private final Set<ValueArgument> unmappedArguments = Sets.newHashSet();
092 private final Map<ValueParameterDescriptor, VarargValueArgument> varargs = Maps.newHashMap();
093 private final Set<ValueParameterDescriptor> usedParameters = Sets.newHashSet();
094 private Status status = OK;
095
096 private Processor(@NotNull Call call, @NotNull MutableResolvedCall<D> candidateCall, @NotNull TracingStrategy tracing) {
097 this.call = call;
098 this.tracing = tracing;
099 this.candidateCall = candidateCall;
100
101 this.parameterByName = Maps.newHashMap();
102 for (ValueParameterDescriptor valueParameter : candidateCall.getCandidateDescriptor().getValueParameters()) {
103 parameterByName.put(valueParameter.getName(), valueParameter);
104 }
105 }
106
107 // We saw only positioned arguments so far
108 private final ProcessorState positionedOnly = new ProcessorState() {
109
110 private int currentParameter = 0;
111
112 @Nullable
113 public ValueParameterDescriptor nextValueParameter() {
114 List<ValueParameterDescriptor> parameters = candidateCall.getCandidateDescriptor().getValueParameters();
115 if (currentParameter >= parameters.size()) return null;
116
117 ValueParameterDescriptor head = parameters.get(currentParameter);
118
119 // If we found a vararg parameter, we are stuck with it forever
120 if (head.getVarargElementType() == null) {
121 currentParameter++;
122 }
123
124 return head;
125 }
126
127 @Override
128 public ProcessorState processNamedArgument(@NotNull ValueArgument argument) {
129 return positionedThenNamed.processNamedArgument(argument);
130 }
131
132 @Override
133 public ProcessorState processPositionedArgument(@NotNull ValueArgument argument, int index) {
134 ValueParameterDescriptor valueParameterDescriptor = nextValueParameter();
135
136 if (valueParameterDescriptor != null) {
137 usedParameters.add(valueParameterDescriptor);
138 putVararg(valueParameterDescriptor, argument);
139 }
140 else {
141 report(TOO_MANY_ARGUMENTS.on(argument.asElement(), candidateCall.getCandidateDescriptor()));
142 unmappedArguments.add(argument);
143 setStatus(WEAK_ERROR);
144 }
145
146 return positionedOnly;
147 }
148 };
149
150 // We saw zero or more positioned arguments and then a named one
151 private final ProcessorState positionedThenNamed = new ProcessorState() {
152 @Override
153 public ProcessorState processNamedArgument(@NotNull ValueArgument argument) {
154 assert argument.isNamed();
155
156 D candidate = candidateCall.getCandidateDescriptor();
157
158 JetSimpleNameExpression nameReference = argument.getArgumentName().getReferenceExpression();
159 ValueParameterDescriptor valueParameterDescriptor = parameterByName.get(nameReference.getReferencedNameAsName());
160 if (!candidate.hasStableParameterNames()) {
161 report(NAMED_ARGUMENTS_NOT_ALLOWED.on(
162 nameReference,
163 candidate instanceof FunctionInvokeDescriptor ? INVOKE_ON_FUNCTION_TYPE : NON_KOTLIN_FUNCTION
164 ));
165 }
166
167 if (valueParameterDescriptor == null) {
168 report(NAMED_PARAMETER_NOT_FOUND.on(nameReference, nameReference));
169 unmappedArguments.add(argument);
170 setStatus(WEAK_ERROR);
171 }
172 else {
173 candidateCall.getTrace().record(REFERENCE_TARGET, nameReference, valueParameterDescriptor);
174 if (!usedParameters.add(valueParameterDescriptor)) {
175 report(ARGUMENT_PASSED_TWICE.on(nameReference));
176 unmappedArguments.add(argument);
177 setStatus(WEAK_ERROR);
178 }
179 else {
180 putVararg(valueParameterDescriptor, argument);
181 }
182 }
183
184 return positionedThenNamed;
185 }
186
187 @Override
188 public ProcessorState processPositionedArgument(
189 @NotNull ValueArgument argument, int index
190 ) {
191 report(MIXING_NAMED_AND_POSITIONED_ARGUMENTS.on(argument.asElement()));
192 setStatus(WEAK_ERROR);
193 unmappedArguments.add(argument);
194
195 return positionedThenNamed;
196 }
197 };
198
199 public void process() {
200 ProcessorState state = positionedOnly;
201 List<? extends ValueArgument> argumentsInParentheses = CallUtilPackage.getValueArgumentsInParentheses(call);
202 for (int i = 0; i < argumentsInParentheses.size(); i++) {
203 ValueArgument valueArgument = argumentsInParentheses.get(i);
204 if (valueArgument.isNamed()) {
205 state = state.processNamedArgument(valueArgument);
206 }
207 else {
208 state = state.processPositionedArgument(valueArgument, i);
209 }
210 }
211
212 for (Map.Entry<ValueParameterDescriptor, VarargValueArgument> entry : varargs.entrySet()) {
213 candidateCall.recordValueArgument(entry.getKey(), entry.getValue());
214 }
215
216 processFunctionLiteralArguments();
217 reportUnmappedParameters();
218 checkReceiverArgument();
219 }
220
221 private void processFunctionLiteralArguments() {
222 D candidate = candidateCall.getCandidateDescriptor();
223 List<ValueParameterDescriptor> valueParameters = candidate.getValueParameters();
224
225 List<? extends FunctionLiteralArgument> functionLiteralArguments = call.getFunctionLiteralArguments();
226 if (!functionLiteralArguments.isEmpty()) {
227 FunctionLiteralArgument functionLiteralArgument = functionLiteralArguments.get(0);
228 JetExpression possiblyLabeledFunctionLiteral = functionLiteralArgument.getArgumentExpression();
229
230 if (valueParameters.isEmpty()) {
231 report(TOO_MANY_ARGUMENTS.on(possiblyLabeledFunctionLiteral, candidate));
232 setStatus(ERROR);
233 }
234 else {
235 ValueParameterDescriptor valueParameterDescriptor = valueParameters.get(valueParameters.size() - 1);
236 if (valueParameterDescriptor.getVarargElementType() != null) {
237 report(VARARG_OUTSIDE_PARENTHESES.on(possiblyLabeledFunctionLiteral));
238 setStatus(ERROR);
239 }
240 else {
241 if (!usedParameters.add(valueParameterDescriptor)) {
242 report(TOO_MANY_ARGUMENTS.on(possiblyLabeledFunctionLiteral, candidate));
243 setStatus(WEAK_ERROR);
244 }
245 else {
246 putVararg(valueParameterDescriptor, functionLiteralArgument);
247 }
248 }
249 }
250
251 for (int i = 1; i < functionLiteralArguments.size(); i++) {
252 JetExpression argument = functionLiteralArguments.get(i).getArgumentExpression();
253 report(MANY_FUNCTION_LITERAL_ARGUMENTS.on(argument));
254 setStatus(WEAK_ERROR);
255 }
256 }
257 }
258
259 private void reportUnmappedParameters() {
260
261 List<ValueParameterDescriptor> valueParameters = candidateCall.getCandidateDescriptor().getValueParameters();
262 for (ValueParameterDescriptor valueParameter : valueParameters) {
263 if (!usedParameters.contains(valueParameter)) {
264 if (valueParameter.hasDefaultValue()) {
265 candidateCall.recordValueArgument(valueParameter, DefaultValueArgument.DEFAULT);
266 }
267 else if (valueParameter.getVarargElementType() != null) {
268 candidateCall.recordValueArgument(valueParameter, new VarargValueArgument());
269 }
270 else {
271 tracing.noValueForParameter(candidateCall.getTrace(), valueParameter);
272 setStatus(ERROR);
273 }
274 }
275 }
276 }
277
278 private void checkReceiverArgument() {
279 D candidate = candidateCall.getCandidateDescriptor();
280
281 ReceiverParameterDescriptor receiverParameter = candidate.getExtensionReceiverParameter();
282 ReceiverValue receiverArgument = candidateCall.getExtensionReceiver();
283 if (receiverParameter != null &&!receiverArgument.exists()) {
284 tracing.missingReceiver(candidateCall.getTrace(), receiverParameter);
285 setStatus(ERROR);
286 }
287 if (receiverParameter == null && receiverArgument.exists()) {
288 tracing.noReceiverAllowed(candidateCall.getTrace());
289 if (call.getCalleeExpression() instanceof JetSimpleNameExpression) {
290 setStatus(STRONG_ERROR);
291 }
292 else {
293 setStatus(ERROR);
294 }
295 }
296 }
297
298 private void putVararg(
299 ValueParameterDescriptor valueParameterDescriptor,
300 ValueArgument valueArgument
301 ) {
302 if (valueParameterDescriptor.getVarargElementType() != null) {
303 VarargValueArgument vararg = varargs.get(valueParameterDescriptor);
304 if (vararg == null) {
305 vararg = new VarargValueArgument();
306 varargs.put(valueParameterDescriptor, vararg);
307 }
308 vararg.addArgument(valueArgument);
309 }
310 else {
311 LeafPsiElement spread = valueArgument.getSpreadElement();
312 if (spread != null) {
313 candidateCall.getTrace().report(NON_VARARG_SPREAD.on(spread));
314 setStatus(WEAK_ERROR);
315 }
316 ResolvedValueArgument argument = new ExpressionValueArgument(valueArgument);
317 candidateCall.recordValueArgument(valueParameterDescriptor, argument);
318 }
319 }
320
321 private void setStatus(@NotNull Status newStatus) {
322 status = status.compose(newStatus);
323 }
324
325 private void report(Diagnostic diagnostic) {
326 candidateCall.getTrace().report(diagnostic);
327 }
328
329 private interface ProcessorState {
330 ProcessorState processNamedArgument(@NotNull ValueArgument argument);
331 ProcessorState processPositionedArgument(@NotNull ValueArgument argument, int index);
332 }
333
334 }
335
336 private ValueArgumentsToParametersMapper() {}
337 }