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;
018
019 import com.google.common.collect.Lists;
020 import com.intellij.psi.PsiElement;
021 import kotlin.Pair;
022 import org.jetbrains.annotations.NotNull;
023 import org.jetbrains.annotations.Nullable;
024 import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
025 import org.jetbrains.kotlin.descriptors.*;
026 import org.jetbrains.kotlin.diagnostics.rendering.Renderers;
027 import org.jetbrains.kotlin.name.Name;
028 import org.jetbrains.kotlin.psi.*;
029 import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
030 import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem;
031 import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemCompleter;
032 import org.jetbrains.kotlin.resolve.calls.inference.TypeVariableKt;
033 import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
034 import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults;
035 import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
036 import org.jetbrains.kotlin.resolve.scopes.JetScopeUtils;
037 import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
038 import org.jetbrains.kotlin.resolve.scopes.LexicalScopeImpl;
039 import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind;
040 import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
041 import org.jetbrains.kotlin.resolve.validation.OperatorValidator;
042 import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator;
043 import org.jetbrains.kotlin.types.*;
044 import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
045 import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext;
046 import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices;
047 import org.jetbrains.kotlin.types.expressions.FakeCallResolver;
048 import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
049
050 import java.util.Collections;
051 import java.util.List;
052
053 import static org.jetbrains.kotlin.diagnostics.Errors.*;
054 import static org.jetbrains.kotlin.psi.KtPsiFactoryKt.KtPsiFactory;
055 import static org.jetbrains.kotlin.resolve.BindingContext.*;
056 import static org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.FROM_COMPLETER;
057 import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE;
058 import static org.jetbrains.kotlin.types.TypeUtils.noExpectedType;
059 import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.createFakeExpressionOfType;
060
061 public class DelegatedPropertyResolver {
062
063 public static final Name PROPERTY_DELEGATED_FUNCTION_NAME = Name.identifier("propertyDelegated");
064 public static final Name GETTER_NAME = Name.identifier("getValue");
065 public static final Name SETTER_NAME = Name.identifier("setValue");
066
067 public static final Name OLD_GETTER_NAME = Name.identifier("get");
068 public static final Name OLD_SETTER_NAME = Name.identifier("set");
069
070 @NotNull private final ExpressionTypingServices expressionTypingServices;
071 @NotNull private final FakeCallResolver fakeCallResolver;
072 @NotNull private final KotlinBuiltIns builtIns;
073 @NotNull private final SymbolUsageValidator symbolUsageValidator;
074
075 public DelegatedPropertyResolver(
076 @NotNull SymbolUsageValidator symbolUsageValidator,
077 @NotNull KotlinBuiltIns builtIns,
078 @NotNull FakeCallResolver fakeCallResolver,
079 @NotNull ExpressionTypingServices expressionTypingServices
080 ) {
081 this.symbolUsageValidator = symbolUsageValidator;
082 this.builtIns = builtIns;
083 this.fakeCallResolver = fakeCallResolver;
084 this.expressionTypingServices = expressionTypingServices;
085 }
086
087 @Nullable
088 public KotlinType getDelegatedPropertyGetMethodReturnType(
089 @NotNull PropertyDescriptor propertyDescriptor,
090 @NotNull KtExpression delegateExpression,
091 @NotNull KotlinType delegateType,
092 @NotNull BindingTrace trace,
093 @NotNull LexicalScope delegateFunctionsScope
094 ) {
095 resolveDelegatedPropertyConventionMethod(propertyDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope, true);
096 ResolvedCall<FunctionDescriptor> resolvedCall =
097 trace.getBindingContext().get(DELEGATED_PROPERTY_RESOLVED_CALL, propertyDescriptor.getGetter());
098 return resolvedCall != null ? resolvedCall.getResultingDescriptor().getReturnType() : null;
099 }
100
101 public void resolveDelegatedPropertyGetMethod(
102 @NotNull PropertyDescriptor propertyDescriptor,
103 @NotNull KtExpression delegateExpression,
104 @NotNull KotlinType delegateType,
105 @NotNull BindingTrace trace,
106 @NotNull LexicalScope delegateFunctionsScope
107 ) {
108 KotlinType returnType = getDelegatedPropertyGetMethodReturnType(
109 propertyDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope);
110 KotlinType propertyType = propertyDescriptor.getType();
111
112 /* Do not check return type of get() method of delegate for properties with DeferredType because property type is taken from it */
113 if (!(propertyType instanceof DeferredType) && returnType != null && !KotlinTypeChecker.DEFAULT.isSubtypeOf(returnType, propertyType)) {
114 Call call = trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, propertyDescriptor.getGetter());
115 assert call != null : "Call should exists for " + propertyDescriptor.getGetter();
116 trace.report(DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH
117 .on(delegateExpression, renderCall(call, trace.getBindingContext()), propertyDescriptor.getType(), returnType));
118 }
119 }
120
121 public void resolveDelegatedPropertySetMethod(
122 @NotNull PropertyDescriptor propertyDescriptor,
123 @NotNull KtExpression delegateExpression,
124 @NotNull KotlinType delegateType,
125 @NotNull BindingTrace trace,
126 @NotNull LexicalScope delegateFunctionsScope
127 ) {
128 resolveDelegatedPropertyConventionMethod(propertyDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope, false);
129 }
130
131 @NotNull
132 private static KtExpression createExpressionForProperty(@NotNull KtPsiFactory psiFactory) {
133 return psiFactory.createExpression("null as " + KotlinBuiltIns.FQ_NAMES.kProperty.asSingleFqName().asString() + "<*>");
134 }
135
136 public void resolveDelegatedPropertyPDMethod(
137 @NotNull PropertyDescriptor propertyDescriptor,
138 @NotNull KtExpression delegateExpression,
139 @NotNull KotlinType delegateType,
140 @NotNull BindingTrace trace,
141 @NotNull LexicalScope delegateFunctionsScope
142 ) {
143 TemporaryBindingTrace traceToResolvePDMethod = TemporaryBindingTrace.create(trace, "Trace to resolve propertyDelegated method in delegated property");
144 ExpressionTypingContext context = ExpressionTypingContext.newContext(
145 traceToResolvePDMethod, delegateFunctionsScope,
146 DataFlowInfo.EMPTY, TypeUtils.NO_EXPECTED_TYPE);
147
148 KtPsiFactory psiFactory = KtPsiFactory(delegateExpression);
149 List<KtExpression> arguments = Collections.singletonList(createExpressionForProperty(psiFactory));
150 ExpressionReceiver receiver = ExpressionReceiver.Companion.create(delegateExpression, delegateType, trace.getBindingContext());
151
152 Pair<Call, OverloadResolutionResults<FunctionDescriptor>> resolutionResult =
153 fakeCallResolver.makeAndResolveFakeCallInContext(receiver, context, arguments, PROPERTY_DELEGATED_FUNCTION_NAME, delegateExpression);
154
155 Call call = resolutionResult.getFirst();
156 OverloadResolutionResults<FunctionDescriptor> functionResults = resolutionResult.getSecond();
157
158 if (!functionResults.isSuccess()) {
159 String expectedFunction = renderCall(call, traceToResolvePDMethod.getBindingContext());
160 if (functionResults.isIncomplete() || functionResults.isSingleResult() ||
161 functionResults.getResultCode() == OverloadResolutionResults.Code.MANY_FAILED_CANDIDATES) {
162 trace.report(DELEGATE_PD_METHOD_NONE_APPLICABLE.on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
163 } else if (functionResults.isAmbiguity()) {
164 trace.report(DELEGATE_SPECIAL_FUNCTION_AMBIGUITY
165 .on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
166 }
167 return;
168 }
169
170 trace.record(DELEGATED_PROPERTY_PD_RESOLVED_CALL, propertyDescriptor, functionResults.getResultingCall());
171 }
172
173 /* Resolve getValue() or setValue() methods from delegate */
174 private void resolveDelegatedPropertyConventionMethod(
175 @NotNull PropertyDescriptor propertyDescriptor,
176 @NotNull KtExpression delegateExpression,
177 @NotNull KotlinType delegateType,
178 @NotNull BindingTrace trace,
179 @NotNull LexicalScope delegateFunctionsScope,
180 boolean isGet
181 ) {
182 PropertyAccessorDescriptor accessor = isGet ? propertyDescriptor.getGetter() : propertyDescriptor.getSetter();
183 assert accessor != null : "Delegated property should have getter/setter " + propertyDescriptor + " " + delegateExpression.getText();
184
185 if (trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, accessor) != null) return;
186
187 OverloadResolutionResults<FunctionDescriptor> functionResults = getDelegatedPropertyConventionMethod(
188 propertyDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope, isGet, true);
189 Call call = trace.getBindingContext().get(DELEGATED_PROPERTY_CALL, accessor);
190 assert call != null : "'getDelegatedPropertyConventionMethod' didn't record a call";
191
192 if (!functionResults.isSuccess()) {
193 String expectedFunction = renderCall(call, trace.getBindingContext());
194 if (functionResults.isSingleResult() || functionResults.isIncomplete() ||
195 functionResults.getResultCode() == OverloadResolutionResults.Code.MANY_FAILED_CANDIDATES) {
196 trace.report(DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE
197 .on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
198 }
199 else if (functionResults.isAmbiguity()) {
200 trace.report(DELEGATE_SPECIAL_FUNCTION_AMBIGUITY
201 .on(delegateExpression, expectedFunction, functionResults.getResultingCalls()));
202 }
203 else {
204 trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType));
205 }
206 return;
207 }
208
209 FunctionDescriptor resultingDescriptor = functionResults.getResultingDescriptor();
210
211 ResolvedCall<FunctionDescriptor> resultingCall = functionResults.getResultingCall();
212 PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(propertyDescriptor);
213 if (declaration instanceof KtProperty) {
214 KtProperty property = (KtProperty) declaration;
215 KtPropertyDelegate delegate = property.getDelegate();
216 if (delegate != null) {
217 PsiElement byKeyword = delegate.getByKeywordNode().getPsi();
218
219 if (!resultingDescriptor.isOperator()) {
220 OperatorValidator.Companion.report(byKeyword, resultingDescriptor, trace);
221 }
222
223 symbolUsageValidator.validateCall(resultingCall, resultingCall.getResultingDescriptor(), trace, byKeyword);
224 }
225 }
226 trace.record(DELEGATED_PROPERTY_RESOLVED_CALL, accessor, resultingCall);
227 }
228
229 /* Resolve getValue() or setValue() methods from delegate */
230 public OverloadResolutionResults<FunctionDescriptor> getDelegatedPropertyConventionMethod(
231 @NotNull PropertyDescriptor propertyDescriptor,
232 @NotNull KtExpression delegateExpression,
233 @NotNull KotlinType delegateType,
234 @NotNull BindingTrace trace,
235 @NotNull LexicalScope delegateFunctionsScope,
236 boolean isGet,
237 boolean isComplete
238 ) {
239 PropertyAccessorDescriptor accessor = isGet ? propertyDescriptor.getGetter() : propertyDescriptor.getSetter();
240 assert accessor != null : "Delegated property should have getter/setter " + propertyDescriptor + " " + delegateExpression.getText();
241
242 KotlinType expectedType = isComplete && isGet && !(propertyDescriptor.getType() instanceof DeferredType)
243 ? propertyDescriptor.getType() : TypeUtils.NO_EXPECTED_TYPE;
244
245 ExpressionTypingContext context = ExpressionTypingContext.newContext(
246 trace, delegateFunctionsScope,
247 DataFlowInfo.EMPTY, expectedType);
248
249 boolean hasThis = propertyDescriptor.getExtensionReceiverParameter() != null || propertyDescriptor.getDispatchReceiverParameter() != null;
250
251 List<KtExpression> arguments = Lists.newArrayList();
252 KtPsiFactory psiFactory = KtPsiFactory(delegateExpression);
253 arguments.add(psiFactory.createExpression(hasThis ? "this" : "null"));
254 arguments.add(createExpressionForProperty(psiFactory));
255
256 if (!isGet) {
257 KtReferenceExpression fakeArgument = (KtReferenceExpression) createFakeExpressionOfType(delegateExpression.getProject(), trace,
258 "fakeArgument" + arguments.size(),
259 propertyDescriptor.getType());
260 arguments.add(fakeArgument);
261 List<ValueParameterDescriptor> valueParameters = accessor.getValueParameters();
262 trace.record(REFERENCE_TARGET, fakeArgument, valueParameters.get(0));
263 }
264
265 Name functionName = isGet ? GETTER_NAME : SETTER_NAME;
266 ExpressionReceiver receiver = ExpressionReceiver.Companion.create(delegateExpression, delegateType, trace.getBindingContext());
267
268 Pair<Call, OverloadResolutionResults<FunctionDescriptor>> resolutionResult =
269 fakeCallResolver.makeAndResolveFakeCallInContext(receiver, context, arguments, functionName, delegateExpression);
270
271 OverloadResolutionResults<FunctionDescriptor> resolutionResults = resolutionResult.getSecond();
272
273 // Resolve get/set is getValue/setValue was not found. Temporary, for code migration
274 if (!resolutionResults.isSuccess() && !resolutionResults.isAmbiguity()) {
275 Name oldFunctionName = isGet ? OLD_GETTER_NAME : OLD_SETTER_NAME;
276 Pair<Call, OverloadResolutionResults<FunctionDescriptor>> additionalResolutionResult =
277 fakeCallResolver.makeAndResolveFakeCallInContext(receiver, context, arguments, oldFunctionName, delegateExpression);
278 if (additionalResolutionResult.getSecond().isSuccess()) {
279 FunctionDescriptor resultingDescriptor = additionalResolutionResult.getSecond().getResultingDescriptor();
280
281 PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(propertyDescriptor);
282 if (declaration instanceof KtProperty) {
283 KtProperty property = (KtProperty) declaration;
284 KtPropertyDelegate delegate = property.getDelegate();
285 if (delegate != null) {
286 PsiElement byKeyword = delegate.getByKeywordNode().getPsi();
287
288 trace.report(DELEGATE_RESOLVED_TO_DEPRECATED_CONVENTION.on(
289 byKeyword, resultingDescriptor, delegateType, functionName.asString()));
290 }
291 }
292
293 trace.record(BindingContext.DELEGATED_PROPERTY_CALL, accessor, additionalResolutionResult.getFirst());
294 return additionalResolutionResult.getSecond();
295 }
296 }
297
298 trace.record(BindingContext.DELEGATED_PROPERTY_CALL, accessor, resolutionResult.getFirst());
299 return resolutionResults;
300 }
301
302 private static String renderCall(@NotNull Call call, @NotNull BindingContext context) {
303 KtExpression calleeExpression = call.getCalleeExpression();
304 assert calleeExpression != null : "CalleeExpression should exists for fake call of convention method";
305 StringBuilder builder = new StringBuilder(calleeExpression.getText());
306 builder.append("(");
307 List<KotlinType> argumentTypes = Lists.newArrayList();
308 for (ValueArgument argument : call.getValueArguments()) {
309 argumentTypes.add(context.getType(argument.getArgumentExpression()));
310
311 }
312 builder.append(Renderers.RENDER_COLLECTION_OF_TYPES.render(argumentTypes));
313 builder.append(")");
314 return builder.toString();
315 }
316
317 @NotNull
318 public KotlinType resolveDelegateExpression(
319 @NotNull KtExpression delegateExpression,
320 @NotNull KtProperty property,
321 @NotNull PropertyDescriptor propertyDescriptor,
322 @NotNull LexicalScope scopeForDelegate,
323 @NotNull BindingTrace trace,
324 @NotNull DataFlowInfo dataFlowInfo
325 ) {
326 TemporaryBindingTrace traceToResolveDelegatedProperty = TemporaryBindingTrace.create(trace, "Trace to resolve delegated property");
327 KtExpression calleeExpression = CallUtilKt.getCalleeExpressionIfAny(delegateExpression);
328 ConstraintSystemCompleter completer = createConstraintSystemCompleter(
329 property, propertyDescriptor, delegateExpression, scopeForDelegate, trace);
330 if (calleeExpression != null) {
331 traceToResolveDelegatedProperty.record(CONSTRAINT_SYSTEM_COMPLETER, calleeExpression, completer);
332 }
333 KotlinType delegateType = expressionTypingServices.safeGetType(scopeForDelegate, delegateExpression, NO_EXPECTED_TYPE,
334 dataFlowInfo, traceToResolveDelegatedProperty);
335 traceToResolveDelegatedProperty.commit(new TraceEntryFilter() {
336 @Override
337 public boolean accept(@Nullable WritableSlice<?, ?> slice, Object key) {
338 return slice != CONSTRAINT_SYSTEM_COMPLETER;
339 }
340 }, true);
341 return delegateType;
342 }
343
344 @NotNull
345 private ConstraintSystemCompleter createConstraintSystemCompleter(
346 @NotNull KtProperty property,
347 @NotNull final PropertyDescriptor propertyDescriptor,
348 @NotNull final KtExpression delegateExpression,
349 @NotNull LexicalScope scopeForDelegate,
350 @NotNull final BindingTrace trace
351 ) {
352 final LexicalScope delegateFunctionsScope = JetScopeUtils.makeScopeForDelegateConventionFunctions(scopeForDelegate, propertyDescriptor);
353 final KotlinType expectedType = property.getTypeReference() != null ? propertyDescriptor.getType() : NO_EXPECTED_TYPE;
354 return new ConstraintSystemCompleter() {
355 @Override
356 public void completeConstraintSystem(
357 @NotNull ConstraintSystem.Builder constraintSystem, @NotNull ResolvedCall<?> resolvedCall
358 ) {
359 KotlinType returnType = resolvedCall.getCandidateDescriptor().getReturnType();
360 if (returnType == null) return;
361
362 TypeSubstitutor typeVariableSubstitutor =
363 constraintSystem.getTypeVariableSubstitutors().get(TypeVariableKt.toHandle(resolvedCall.getCall()));
364 assert typeVariableSubstitutor != null : "No substitutor in the system for call: " + resolvedCall.getCall();
365
366 TemporaryBindingTrace traceToResolveConventionMethods =
367 TemporaryBindingTrace.create(trace, "Trace to resolve delegated property convention methods");
368 OverloadResolutionResults<FunctionDescriptor>
369 getMethodResults = getDelegatedPropertyConventionMethod(
370 propertyDescriptor, delegateExpression, returnType, traceToResolveConventionMethods, delegateFunctionsScope,
371 true, false
372 );
373
374 if (conventionMethodFound(getMethodResults)) {
375 FunctionDescriptor descriptor = getMethodResults.getResultingDescriptor();
376 KotlinType returnTypeOfGetMethod = descriptor.getReturnType();
377 if (returnTypeOfGetMethod != null && !TypeUtils.noExpectedType(expectedType)) {
378 KotlinType returnTypeInSystem = typeVariableSubstitutor.substitute(returnTypeOfGetMethod, Variance.INVARIANT);
379 if (returnTypeInSystem != null) {
380 constraintSystem.addSubtypeConstraint(returnTypeInSystem, expectedType, FROM_COMPLETER.position());
381 }
382 }
383 addConstraintForThisValue(constraintSystem, typeVariableSubstitutor, descriptor);
384 }
385 if (!propertyDescriptor.isVar()) return;
386
387 // For the case: 'val v by d' (no declared type).
388 // When we add a constraint for 'set' method for delegated expression 'd' we use a type of the declared variable 'v'.
389 // But if the type isn't known yet, the constraint shouldn't be added (we try to infer the type of 'v' here as well).
390 if (propertyDescriptor.getReturnType() instanceof DeferredType) return;
391
392 OverloadResolutionResults<FunctionDescriptor>
393 setMethodResults = getDelegatedPropertyConventionMethod(
394 propertyDescriptor, delegateExpression, returnType, traceToResolveConventionMethods, delegateFunctionsScope,
395 false, false
396 );
397
398 if (conventionMethodFound(setMethodResults)) {
399 FunctionDescriptor descriptor = setMethodResults.getResultingDescriptor();
400 List<ValueParameterDescriptor> valueParameters = descriptor.getValueParameters();
401 if (valueParameters.size() == 3) {
402 ValueParameterDescriptor valueParameterForThis = valueParameters.get(2);
403
404 if (!noExpectedType(expectedType)) {
405 constraintSystem.addSubtypeConstraint(
406 expectedType,
407 typeVariableSubstitutor.substitute(valueParameterForThis.getType(), Variance.INVARIANT),
408 FROM_COMPLETER.position()
409 );
410 }
411 addConstraintForThisValue(constraintSystem, typeVariableSubstitutor, descriptor);
412 }
413 }
414 }
415
416 private boolean conventionMethodFound(@NotNull OverloadResolutionResults<FunctionDescriptor> results) {
417 return results.isSuccess() ||
418 (results.isSingleResult() &&
419 results.getResultCode() == OverloadResolutionResults.Code.SINGLE_CANDIDATE_ARGUMENT_MISMATCH);
420 }
421
422 private void addConstraintForThisValue(
423 ConstraintSystem.Builder constraintSystem,
424 TypeSubstitutor typeVariableSubstitutor,
425 FunctionDescriptor resultingDescriptor
426 ) {
427 ReceiverParameterDescriptor extensionReceiver = propertyDescriptor.getExtensionReceiverParameter();
428 ReceiverParameterDescriptor dispatchReceiver = propertyDescriptor.getDispatchReceiverParameter();
429 KotlinType typeOfThis =
430 extensionReceiver != null ? extensionReceiver.getType() :
431 dispatchReceiver != null ? dispatchReceiver.getType() :
432 builtIns.getNullableNothingType();
433
434 List<ValueParameterDescriptor> valueParameters = resultingDescriptor.getValueParameters();
435 if (valueParameters.isEmpty()) return;
436 ValueParameterDescriptor valueParameterForThis = valueParameters.get(0);
437
438 constraintSystem.addSubtypeConstraint(
439 typeOfThis,
440 typeVariableSubstitutor.substitute(valueParameterForThis.getType(), Variance.INVARIANT),
441 FROM_COMPLETER.position()
442 );
443 }
444 };
445 }
446 }