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