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