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    }