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.ValueParameterDescriptor;
027 import org.jetbrains.kotlin.diagnostics.Diagnostic;
028 import org.jetbrains.kotlin.name.Name;
029 import org.jetbrains.kotlin.psi.*;
030 import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage;
031 import org.jetbrains.kotlin.resolve.calls.model.*;
032 import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy;
033 import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage;
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
088 private final Set<ValueArgument> unmappedArguments = Sets.newHashSet();
089 private final Map<ValueParameterDescriptor, VarargValueArgument> varargs = Maps.newHashMap();
090 private final Set<ValueParameterDescriptor> usedParameters = Sets.newHashSet();
091 private Status status = OK;
092
093 private Processor(@NotNull Call call, @NotNull MutableResolvedCall<D> candidateCall, @NotNull TracingStrategy tracing) {
094 this.call = call;
095 this.tracing = tracing;
096 this.candidateCall = candidateCall;
097
098 this.parameterByName = Maps.newHashMap();
099 for (ValueParameterDescriptor valueParameter : candidateCall.getCandidateDescriptor().getValueParameters()) {
100 parameterByName.put(valueParameter.getName(), valueParameter);
101 }
102 }
103
104 // We saw only positioned arguments so far
105 private final ProcessorState positionedOnly = new ProcessorState() {
106
107 private int currentParameter = 0;
108
109 @Nullable
110 public ValueParameterDescriptor nextValueParameter() {
111 List<ValueParameterDescriptor> parameters = candidateCall.getCandidateDescriptor().getValueParameters();
112 if (currentParameter >= parameters.size()) return null;
113
114 ValueParameterDescriptor head = parameters.get(currentParameter);
115
116 // If we found a vararg parameter, we are stuck with it forever
117 if (head.getVarargElementType() == null) {
118 currentParameter++;
119 }
120
121 return head;
122 }
123
124 @Override
125 public ProcessorState processNamedArgument(@NotNull ValueArgument argument) {
126 return positionedThenNamed.processNamedArgument(argument);
127 }
128
129 @Override
130 public ProcessorState processPositionedArgument(@NotNull ValueArgument argument, int index) {
131 ValueParameterDescriptor valueParameterDescriptor = nextValueParameter();
132
133 if (valueParameterDescriptor != null) {
134 usedParameters.add(valueParameterDescriptor);
135 putVararg(valueParameterDescriptor, argument);
136 }
137 else {
138 report(TOO_MANY_ARGUMENTS.on(argument.asElement(), candidateCall.getCandidateDescriptor()));
139 unmappedArguments.add(argument);
140 setStatus(WEAK_ERROR);
141 }
142
143 return positionedOnly;
144 }
145 };
146
147 // We saw zero or more positioned arguments and then a named one
148 private final ProcessorState positionedThenNamed = new ProcessorState() {
149 @Override
150 public ProcessorState processNamedArgument(@NotNull ValueArgument argument) {
151 assert argument.isNamed();
152
153 D candidate = candidateCall.getCandidateDescriptor();
154
155 ValueArgumentName argumentName = argument.getArgumentName();
156 assert argumentName != null;
157 ValueParameterDescriptor valueParameterDescriptor = parameterByName.get(argumentName.getAsName());
158 JetReferenceExpression nameReference = argumentName.getReferenceExpression();
159 if (!candidate.hasStableParameterNames() && nameReference != null) {
160 report(NAMED_ARGUMENTS_NOT_ALLOWED.on(
161 nameReference,
162 candidate instanceof FunctionInvokeDescriptor ? INVOKE_ON_FUNCTION_TYPE : NON_KOTLIN_FUNCTION
163 ));
164 }
165
166 if (valueParameterDescriptor == null) {
167 if (nameReference != null) {
168 report(NAMED_PARAMETER_NOT_FOUND.on(nameReference, nameReference));
169 }
170 unmappedArguments.add(argument);
171 setStatus(WEAK_ERROR);
172 }
173 else {
174 if (nameReference != null) {
175 candidateCall.getTrace().record(REFERENCE_TARGET, nameReference, valueParameterDescriptor);
176 }
177 if (!usedParameters.add(valueParameterDescriptor)) {
178 if (nameReference != null) {
179 report(ARGUMENT_PASSED_TWICE.on(nameReference));
180 }
181 unmappedArguments.add(argument);
182 setStatus(WEAK_ERROR);
183 }
184 else {
185 putVararg(valueParameterDescriptor, argument);
186 }
187 }
188
189 return positionedThenNamed;
190 }
191
192 @Override
193 public ProcessorState processPositionedArgument(
194 @NotNull ValueArgument argument, int index
195 ) {
196 report(MIXING_NAMED_AND_POSITIONED_ARGUMENTS.on(argument.asElement()));
197 setStatus(WEAK_ERROR);
198 unmappedArguments.add(argument);
199
200 return positionedThenNamed;
201 }
202 };
203
204 public void process() {
205 ProcessorState state = positionedOnly;
206 List<? extends ValueArgument> argumentsInParentheses = CallUtilPackage.getValueArgumentsInParentheses(call);
207 for (int i = 0; i < argumentsInParentheses.size(); i++) {
208 ValueArgument valueArgument = argumentsInParentheses.get(i);
209 if (valueArgument.isNamed()) {
210 state = state.processNamedArgument(valueArgument);
211 }
212 else {
213 state = state.processPositionedArgument(valueArgument, i);
214 }
215 }
216
217 for (Map.Entry<ValueParameterDescriptor, VarargValueArgument> entry : varargs.entrySet()) {
218 candidateCall.recordValueArgument(entry.getKey(), entry.getValue());
219 }
220
221 processFunctionLiteralArguments();
222 reportUnmappedParameters();
223 }
224
225 private void processFunctionLiteralArguments() {
226 D candidate = candidateCall.getCandidateDescriptor();
227 List<ValueParameterDescriptor> valueParameters = candidate.getValueParameters();
228
229 List<? extends FunctionLiteralArgument> functionLiteralArguments = call.getFunctionLiteralArguments();
230 if (!functionLiteralArguments.isEmpty()) {
231 FunctionLiteralArgument functionLiteralArgument = functionLiteralArguments.get(0);
232 JetExpression possiblyLabeledFunctionLiteral = functionLiteralArgument.getArgumentExpression();
233
234 if (valueParameters.isEmpty()) {
235 report(TOO_MANY_ARGUMENTS.on(possiblyLabeledFunctionLiteral, candidate));
236 setStatus(ERROR);
237 }
238 else {
239 ValueParameterDescriptor valueParameterDescriptor = valueParameters.get(valueParameters.size() - 1);
240 if (valueParameterDescriptor.getVarargElementType() != null) {
241 report(VARARG_OUTSIDE_PARENTHESES.on(possiblyLabeledFunctionLiteral));
242 setStatus(ERROR);
243 }
244 else {
245 if (!usedParameters.add(valueParameterDescriptor)) {
246 report(TOO_MANY_ARGUMENTS.on(possiblyLabeledFunctionLiteral, candidate));
247 setStatus(WEAK_ERROR);
248 }
249 else {
250 putVararg(valueParameterDescriptor, functionLiteralArgument);
251 }
252 }
253 }
254
255 for (int i = 1; i < functionLiteralArguments.size(); i++) {
256 JetExpression argument = functionLiteralArguments.get(i).getArgumentExpression();
257 report(MANY_FUNCTION_LITERAL_ARGUMENTS.on(argument));
258 setStatus(WEAK_ERROR);
259 }
260 }
261 }
262
263 private void reportUnmappedParameters() {
264 List<ValueParameterDescriptor> valueParameters = candidateCall.getCandidateDescriptor().getValueParameters();
265 for (ValueParameterDescriptor valueParameter : valueParameters) {
266 if (!usedParameters.contains(valueParameter)) {
267 if (DescriptorUtilPackage.hasDefaultValue(valueParameter)) {
268 candidateCall.recordValueArgument(valueParameter, DefaultValueArgument.DEFAULT);
269 }
270 else if (valueParameter.getVarargElementType() != null) {
271 candidateCall.recordValueArgument(valueParameter, new VarargValueArgument());
272 }
273 else {
274 tracing.noValueForParameter(candidateCall.getTrace(), valueParameter);
275 setStatus(ERROR);
276 }
277 }
278 }
279 }
280
281 private void putVararg(
282 ValueParameterDescriptor valueParameterDescriptor,
283 ValueArgument valueArgument
284 ) {
285 if (valueParameterDescriptor.getVarargElementType() != null) {
286 VarargValueArgument vararg = varargs.get(valueParameterDescriptor);
287 if (vararg == null) {
288 vararg = new VarargValueArgument();
289 varargs.put(valueParameterDescriptor, vararg);
290 }
291 vararg.addArgument(valueArgument);
292 }
293 else {
294 LeafPsiElement spread = valueArgument.getSpreadElement();
295 if (spread != null) {
296 candidateCall.getTrace().report(NON_VARARG_SPREAD.on(spread));
297 setStatus(WEAK_ERROR);
298 }
299 ResolvedValueArgument argument = new ExpressionValueArgument(valueArgument);
300 candidateCall.recordValueArgument(valueParameterDescriptor, argument);
301 }
302 }
303
304 private void setStatus(@NotNull Status newStatus) {
305 status = status.compose(newStatus);
306 }
307
308 private void report(Diagnostic diagnostic) {
309 candidateCall.getTrace().report(diagnostic);
310 }
311
312 private interface ProcessorState {
313 ProcessorState processNamedArgument(@NotNull ValueArgument argument);
314 ProcessorState processPositionedArgument(@NotNull ValueArgument argument, int index);
315 }
316
317 }
318
319 private ValueArgumentsToParametersMapper() {}
320 }