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