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