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    }