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    }