1 /*******************************************************************************
2 * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v0.5
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v05.html
9 * IBM Corporation - initial API and implementation
10 ******************************************************************************/
11 package net.sourceforge.phpdt.internal.codeassist.complete;
14 * Parser able to build specific completion parse nodes, given a cursorLocation.
16 * Cursor location denotes the position of the last character behind which completion
18 * -1 means completion at the very beginning of the source
19 * 0 means completion behind the first character
20 * n means completion behind the n-th character
22 import net.sourceforge.phpdt.internal.codeassist.impl.AssistParser;
23 import net.sourceforge.phpdt.internal.compiler.CompilationResult;
24 import net.sourceforge.phpdt.internal.compiler.ast.AbstractMethodDeclaration;
25 import net.sourceforge.phpdt.internal.compiler.ast.AbstractVariableDeclaration;
26 import net.sourceforge.phpdt.internal.compiler.ast.AllocationExpression;
27 import net.sourceforge.phpdt.internal.compiler.ast.Argument;
28 import net.sourceforge.phpdt.internal.compiler.ast.Assignment;
29 import net.sourceforge.phpdt.internal.compiler.ast.AstNode;
30 import net.sourceforge.phpdt.internal.compiler.ast.BinaryExpression;
31 import net.sourceforge.phpdt.internal.compiler.ast.CastExpression;
32 import net.sourceforge.phpdt.internal.compiler.ast.CompilationUnitDeclaration;
33 import net.sourceforge.phpdt.internal.compiler.ast.ConditionalExpression;
34 import net.sourceforge.phpdt.internal.compiler.ast.ExplicitConstructorCall;
35 import net.sourceforge.phpdt.internal.compiler.ast.Expression;
36 import net.sourceforge.phpdt.internal.compiler.ast.FieldDeclaration;
37 import net.sourceforge.phpdt.internal.compiler.ast.ImportReference;
38 import net.sourceforge.phpdt.internal.compiler.ast.Initializer;
39 import net.sourceforge.phpdt.internal.compiler.ast.InstanceOfExpression;
40 import net.sourceforge.phpdt.internal.compiler.ast.LabeledStatement;
41 import net.sourceforge.phpdt.internal.compiler.ast.Literal;
42 import net.sourceforge.phpdt.internal.compiler.ast.LocalDeclaration;
43 import net.sourceforge.phpdt.internal.compiler.ast.NameReference;
44 import net.sourceforge.phpdt.internal.compiler.ast.QualifiedAllocationExpression;
45 import net.sourceforge.phpdt.internal.compiler.ast.QualifiedNameReference;
46 import net.sourceforge.phpdt.internal.compiler.ast.Reference;
47 import net.sourceforge.phpdt.internal.compiler.ast.SingleNameReference;
48 import net.sourceforge.phpdt.internal.compiler.ast.SingleTypeReference;
49 import net.sourceforge.phpdt.internal.compiler.ast.Statement;
50 import net.sourceforge.phpdt.internal.compiler.ast.SuperReference;
51 import net.sourceforge.phpdt.internal.compiler.ast.ThisReference;
52 import net.sourceforge.phpdt.internal.compiler.ast.TypeDeclaration;
53 import net.sourceforge.phpdt.internal.compiler.ast.TypeReference;
54 import net.sourceforge.phpdt.internal.compiler.ast.UnaryExpression;
55 import net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit;
56 import net.sourceforge.phpdt.internal.compiler.parser.RecoveredMethod;
57 import net.sourceforge.phpdt.internal.compiler.parser.RecoveredType;
58 import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
60 public class CompletionParser extends AssistParser {
64 public int cursorLocation;
65 public char[][] labels; // the visible labels up to the cursor location
66 public AstNode assistNodeParent; // the parent node of assist node
67 /* the following fields are internal flags */
69 boolean betweenNewAndLeftBraket; // whether we are between the keyword 'new' and the following left braket, ie. '[', '(' or '{'
70 boolean betweenCatchAndRightParen; // whether we are between the keyword 'catch' and the following ')'
71 boolean completionBehindDot; // true when completion identifier immediately follows a dot
73 boolean nextTypeReferenceIsClass;
74 boolean nextTypeReferenceIsException;
75 boolean nextTypeReferenceIsInterface;
78 int throwBracketDepth;
80 // the stacks of types and qualifiers for invocations (ie. method invocations, allocation expressions and
81 // explicit constructor invocations). They use the same stack pointer as the selector stack (ie. invocationPtr)
82 // the invocation type stack contains one of the invocation type constants below
83 // the qualifier stack contains pointers to the expression stack or -1 if there is no qualifier
84 // (a qualifier is the expression that qualifies a 'new', a 'super' constructor or a 'this' constructor
85 // or it is the receiver of a message send)
86 int[] invocationTypeStack = new int[StackIncrement];
87 int[] qualifierStack = new int[StackIncrement];
89 // invocation type constants
90 static final int EXPLICIT_RECEIVER = 0;
91 static final int NO_RECEIVER = -1;
92 static final int SUPER_RECEIVER = -2;
93 static final int NAME_RECEIVER = -3;
94 static final int ALLOCATION = -4;
95 static final int QUALIFIED_ALLOCATION = -5;
97 // the type of the current invocation (one of the invocation type constants)
100 // a pointer in the expression stack to the qualifier of a invocation
103 // a stack of label counters
104 // a new counter is pushed on the stack each time when a method (or a constructor) is entered,
105 // it is poped when the method (or constructor) is exited,
106 // it is incremented when a new label is defined
108 int[] labelCounterStack = new int[StackIncrement];
110 // a stack of invocationPtr: contains the first invocationPtr of a block
111 // the current invocationPtr+1 is pushed when a block is entered
112 // it is poped when a block is exited
113 int blockInvocationPtr;
114 int[] blockInvocationStack = new int[StackIncrement];
116 // last modifiers info
117 int lastModifiers = AccDefault;
118 int lastModifiersStart = -1;
120 public CompletionParser(ProblemReporter problemReporter, boolean assertMode) {
121 super(problemReporter, assertMode);
123 public char[] assistIdentifier(){
124 return ((CompletionScanner)scanner).completionIdentifier;
126 protected void attachOrphanCompletionNode(){
127 if (this.isOrphanCompletionNode) {
128 AstNode orphan = this.assistNode;
129 this.isOrphanCompletionNode = false;
131 /* if in context of a type, then persists the identifier into a fake field return type */
132 if (currentElement instanceof RecoveredType){
133 RecoveredType recoveredType = (RecoveredType)currentElement;
134 /* filter out cases where scanner is still inside type header */
135 if (recoveredType.foundOpeningBrace) {
136 /* generate a pseudo field with a completion on type reference */
137 if (orphan instanceof TypeReference){
138 CompletionOnFieldType fieldDeclaration = new CompletionOnFieldType((TypeReference)orphan, false);
140 // retrieve available modifiers if any
141 if (intPtr >= 2 && intStack[intPtr-1] == this.lastModifiersStart && intStack[intPtr-2] == this.lastModifiers){
142 fieldDeclaration.modifiersSourceStart = intStack[intPtr-1];
143 fieldDeclaration.modifiers = intStack[intPtr-2];
146 currentElement = currentElement.add(fieldDeclaration, 0);
151 /* if in context of a method, persists if inside arguments as a type */
152 if (currentElement instanceof RecoveredMethod){
153 RecoveredMethod recoveredMethod = (RecoveredMethod)currentElement;
154 /* only consider if inside method header */
155 if (!recoveredMethod.foundOpeningBrace) {
156 //if (rParenPos < lParenPos){ // inside arguments
157 if (orphan instanceof TypeReference){
158 currentElement = currentElement.parent.add(
159 new CompletionOnFieldType((TypeReference)orphan, true), 0);
165 // add the completion node to the method declaration or constructor declaration
166 if (orphan instanceof Statement) {
167 /* check for completion at the beginning of method body
168 behind an invalid signature
170 RecoveredMethod method = currentElement.enclosingMethod();
172 AbstractMethodDeclaration methodDecl = method.methodDeclaration;
173 if ((methodDecl.bodyStart == methodDecl.sourceEnd+1) // was missing opening brace
174 && (scanner.getLineNumber(orphan.sourceStart) == scanner.getLineNumber(methodDecl.sourceEnd))){
178 // add the completion node as a statement to the list of block statements
179 currentElement = currentElement.add((Statement)orphan, 0);
184 // the following code applies only in methods, constructors or initializers
185 if ((!this.inMethodStack[this.inMethodPtr] && !this.inFieldInitializationStack[this.inFieldInitializationPtr])) {
189 // push top expression on ast stack if it contains the completion node
190 Expression expression;
191 if (this.expressionPtr > -1 && containsCompletionNode(expression = this.expressionStack[this.expressionPtr])) {
192 /* check for completion at the beginning of method body
193 behind an invalid signature
195 RecoveredMethod method = currentElement.enclosingMethod();
197 AbstractMethodDeclaration methodDecl = method.methodDeclaration;
198 if ((methodDecl.bodyStart == methodDecl.sourceEnd+1) // was missing opening brace
199 && (scanner.getLineNumber(expression.sourceStart) == scanner.getLineNumber(methodDecl.sourceEnd))){
203 if (expression instanceof AllocationExpression) {
204 // keep the context if it is an allocation expression
205 Statement statement = (Statement)wrapWithExplicitConstructorCallIfNeeded(expression);
206 currentElement = currentElement.add(statement, 0);
208 Statement statement = (Statement)wrapWithExplicitConstructorCallIfNeeded(this.assistNode);
209 currentElement = currentElement.add(statement, 0);
213 public int bodyEnd(AbstractMethodDeclaration method){
214 return cursorLocation;
216 public int bodyEnd(Initializer initializer){
217 return cursorLocation;
220 * Checks if the completion is on the exception type of a catch clause.
221 * Returns whether we found a completion node.
223 private boolean checkCatchClause() {
224 if (this.betweenCatchAndRightParen && this.identifierPtr > -1) {
225 // NB: if the cursor is on the variable, then it has been reduced (so identifierPtr is -1),
226 // thus this can only be a completion on the type of the catch clause
227 this.assistNode = getTypeReference(0);
228 this.lastCheckPoint = this.assistNode.sourceEnd + 1;
229 this.isOrphanCompletionNode = true;
235 * Checks if the completion is on the type following a 'new'.
236 * Returns whether we found a completion node.
238 private boolean checkClassInstanceCreation() {
239 if (this.betweenNewAndLeftBraket) {
240 // completion on type inside an allocation expression
242 if(this.throwBracketDepth != -1 && this.throwBracketDepth == this.bracketDepth) {
243 this.nextTypeReferenceIsException = true;
245 TypeReference type = getTypeReference(0);
246 this.nextTypeReferenceIsException = false;
247 this.assistNode = type;
248 this.lastCheckPoint = type.sourceEnd + 1;
249 if (this.invocationType == ALLOCATION) {
250 // non qualified allocation expression
251 AllocationExpression allocExpr = new AllocationExpression();
252 allocExpr.type = type;
253 allocExpr.sourceStart = type.sourceStart;
254 allocExpr.sourceEnd = type.sourceEnd;
255 pushOnExpressionStack(allocExpr);
256 this.isOrphanCompletionNode = false;
258 // qualified allocation expression
259 QualifiedAllocationExpression allocExpr = new QualifiedAllocationExpression();
260 allocExpr.type = type;
261 allocExpr.enclosingInstance = this.expressionStack[this.qualifier];
262 allocExpr.sourceStart = this.intStack[this.intPtr--];
263 allocExpr.sourceEnd = type.sourceEnd;
264 this.expressionStack[this.qualifier] = allocExpr; // attach it now (it replaces the qualifier expression)
265 this.isOrphanCompletionNode = false;
272 * Checks if the completion is on the dot following an array type,
273 * a primitive type or an primitive array type.
274 * Returns whether we found a completion node.
276 private boolean checkClassLiteralAccess() {
277 if (this.identifierLengthPtr >= 1 && this.previousToken == TokenNameDOT) { // (NB: the top id length is 1 and it is for the completion identifier)
279 // if the penultimate id length is negative,
280 // the completion is after a primitive type or a primitive array type
281 if ((length = this.identifierLengthStack[this.identifierLengthPtr-1]) < 0) {
282 // build the primitive type node
283 int dim = this.isAfterArrayType() ? this.intStack[this.intPtr--] : 0;
284 SingleTypeReference typeRef = (SingleTypeReference)TypeReference.baseTypeReference(-length, dim);
285 typeRef.sourceStart = this.intStack[this.intPtr--];
287 typeRef.sourceEnd = this.intStack[this.intPtr--];
290 typeRef.sourceEnd = this.endPosition;
292 //typeRef.sourceEnd = typeRef.sourceStart + typeRef.token.length; // NB: It's ok to use the length of the token since it doesn't contain any unicode
294 // find the completion identifier and its source positions
295 char[] source = identifierStack[identifierPtr];
296 long pos = this.identifierPositionStack[this.identifierPtr--];
297 this.identifierLengthPtr--; // it can only be a simple identifier (so its length is one)
299 // build the completion on class literal access node
300 CompletionOnClassLiteralAccess access = new CompletionOnClassLiteralAccess(pos, typeRef);
301 access.completionIdentifier = source;
302 this.identifierLengthPtr--; // pop the length that was used to say it is a primitive type
303 this.assistNode = access;
304 this.isOrphanCompletionNode = true;
308 // if the completion is after a regular array type
309 if (isAfterArrayType()) {
310 // find the completion identifier and its source positions
311 char[] source = identifierStack[identifierPtr];
312 long pos = this.identifierPositionStack[this.identifierPtr--];
313 this.identifierLengthPtr--; // it can only be a simple identifier (so its length is one)
315 // get the type reference
316 TypeReference typeRef = getTypeReference(this.intPtr--);
318 // build the completion on class literal access node
319 CompletionOnClassLiteralAccess access = new CompletionOnClassLiteralAccess(pos, typeRef);
320 access.completionIdentifier = source;
321 this.assistNode = access;
322 this.isOrphanCompletionNode = true;
330 * Checks if the completion is inside a method invocation or a constructor invocation.
331 * Returns whether we found a completion node.
333 private boolean checkInvocation() {
334 Expression topExpression = this.expressionPtr >= 0 ?
335 this.expressionStack[this.expressionPtr] :
337 boolean isEmptyNameCompletion = false;
338 boolean isEmptyAssistIdentifier = false;
339 int startInvocationPtr = this.blockInvocationPtr >= 0 ? this.blockInvocationStack[this.blockInvocationPtr] : 0;
340 if (this.invocationPtr >= startInvocationPtr
341 && ((isEmptyNameCompletion = topExpression == this.assistNode && this.isEmptyNameCompletion()) // eg. it is something like "this.fred([cursor]" but it is not something like "this.fred(1 + [cursor]"
342 || (isEmptyAssistIdentifier = this.indexOfAssistIdentifier() >= 0 && this.identifierStack[this.identifierPtr].length == 0))) { // eg. it is something like "this.fred(1 [cursor]"
344 // pop empty name completion
345 if (isEmptyNameCompletion) {
346 this.expressionPtr--;
347 this.expressionLengthStack[this.expressionLengthPtr]--;
348 } else if (isEmptyAssistIdentifier) {
349 this.identifierPtr--;
350 this.identifierLengthPtr--;
353 // find receiver and qualifier
354 int invocationType = this.invocationTypeStack[this.invocationPtr];
355 int qualifierExprPtr = this.qualifierStack[this.invocationPtr];
358 int numArgs = this.expressionPtr - qualifierExprPtr;
359 int argStart = qualifierExprPtr + 1;
360 Expression[] arguments = null;
362 // remember the arguments
363 arguments = new Expression[numArgs];
364 System.arraycopy(this.expressionStack, argStart, arguments, 0, numArgs);
366 // consume the expression arguments
367 this.expressionPtr -= numArgs;
370 count -= this.expressionLengthStack[this.expressionLengthPtr--];
375 if (invocationType != ALLOCATION && invocationType != QUALIFIED_ALLOCATION) {
376 // creates completion on message send
377 CompletionOnMessageSend messageSend = new CompletionOnMessageSend();
378 messageSend.arguments = arguments;
379 switch (invocationType) {
382 messageSend.receiver = ThisReference.ThisImplicit;
385 // remove special flags for primitive types
386 while (this.identifierLengthPtr >= 0 && this.identifierLengthStack[this.identifierLengthPtr] < 0) {
387 this.identifierLengthPtr--;
391 this.identifierPtr--;
392 this.identifierLengthStack[this.identifierLengthPtr]--;
393 // consume the receiver
394 messageSend.receiver = this.getUnspecifiedReference();
397 messageSend.receiver = SuperReference.Super;
399 case EXPLICIT_RECEIVER:
400 messageSend.receiver = this.expressionStack[qualifierExprPtr];
404 int selectorPtr = this.selectorStack[this.invocationPtr];
405 messageSend.selector = this.identifierStack[selectorPtr];
407 if (this.identifierLengthPtr >=0 && this.identifierLengthStack[this.identifierLengthPtr] == 1) {
408 this.identifierPtr--;
409 this.identifierLengthPtr--;
412 // the entire message may be replaced in case qualification is needed
413 messageSend.sourceStart = (int)(this.identifierPositionStack[selectorPtr] >> 32); //this.cursorLocation + 1;
414 messageSend.sourceEnd = this.cursorLocation;
416 // remember the message send as an orphan completion node
417 this.assistNode = messageSend;
418 this.lastCheckPoint = messageSend.sourceEnd + 1;
419 this.isOrphanCompletionNode = true;
422 int selectorPtr = this.selectorStack[this.invocationPtr];
423 if (selectorPtr == THIS_CONSTRUCTOR || selectorPtr == SUPER_CONSTRUCTOR) {
424 // creates an explicit constructor call
425 CompletionOnExplicitConstructorCall call = new CompletionOnExplicitConstructorCall(
426 (selectorPtr == THIS_CONSTRUCTOR) ? ExplicitConstructorCall.This : ExplicitConstructorCall.Super);
427 call.arguments = arguments;
428 if (invocationType == QUALIFIED_ALLOCATION) {
429 call.qualification = this.expressionStack[qualifierExprPtr];
432 // no source is going to be replaced
433 call.sourceStart = this.cursorLocation + 1;
434 call.sourceEnd = this.cursorLocation;
436 // remember the explicit constructor call as an orphan completion node
437 this.assistNode = call;
438 this.lastCheckPoint = call.sourceEnd + 1;
439 this.isOrphanCompletionNode = true;
442 // creates an allocation expression
443 CompletionOnQualifiedAllocationExpression allocExpr = new CompletionOnQualifiedAllocationExpression();
444 allocExpr.arguments = arguments;
445 allocExpr.type = super.getTypeReference(0); // we don't want a completion node here, so call super
446 if (invocationType == QUALIFIED_ALLOCATION) {
447 allocExpr.enclosingInstance = this.expressionStack[qualifierExprPtr];
449 // no source is going to be replaced
450 allocExpr.sourceStart = this.cursorLocation + 1;
451 allocExpr.sourceEnd = this.cursorLocation;
453 // remember the allocation expression as an orphan completion node
454 this.assistNode = allocExpr;
455 this.lastCheckPoint = allocExpr.sourceEnd + 1;
456 this.isOrphanCompletionNode = true;
464 * Checks if the completion is on a member access (ie. in an identifier following a dot).
465 * Returns whether we found a completion node.
467 private boolean checkMemberAccess() {
468 if (this.previousToken == TokenNameDOT && this.qualifier > -1 && this.expressionPtr == this.qualifier) {
469 // the receiver is an expression
470 pushCompletionOnMemberAccessOnExpressionStack(false);
476 * Checks if the completion is on a name reference.
477 * Returns whether we found a completion node.
479 private boolean checkNameCompletion() {
481 We didn't find any other completion, but the completion identifier is on the identifier stack,
482 so it can only be a completion on name.
483 Note that we allow the completion on a name even if nothing is expected (eg. foo() b[cursor] would
484 be a completion on 'b'). This policy gives more to the user than he/she would expect, but this
485 simplifies the problem. To fix this, the recovery must be changed to work at a 'statement' granularity
486 instead of at the 'expression' granularity as it does right now.
489 // NB: at this point the completion identifier is on the identifier stack
490 this.assistNode = getUnspecifiedReferenceOptimized();
491 this.lastCheckPoint = this.assistNode.sourceEnd + 1;
492 this.isOrphanCompletionNode = true;
496 * Checks if the completion is in the context of a method and on the type of one of its arguments
497 * Returns whether we found a completion node.
499 private boolean checkRecoveredMethod() {
500 if (currentElement instanceof RecoveredMethod){
501 /* check if current awaiting identifier is the completion identifier */
502 if (this.indexOfAssistIdentifier() < 0) return false;
504 /* check if on line with an error already - to avoid completing inside
505 illegal type names e.g. int[<cursor> */
506 if (lastErrorEndPosition <= cursorLocation+1
507 && scanner.getLineNumber(lastErrorEndPosition)
508 == scanner.getLineNumber(((CompletionScanner)scanner).completedIdentifierStart)){
511 RecoveredMethod recoveredMethod = (RecoveredMethod)currentElement;
512 /* only consider if inside method header */
513 if (!recoveredMethod.foundOpeningBrace
514 && lastIgnoredToken == -1) {
515 //if (rParenPos < lParenPos){ // inside arguments
516 this.assistNode = this.getTypeReference(0);
517 this.lastCheckPoint = this.assistNode.sourceEnd + 1;
518 this.isOrphanCompletionNode = true;
525 * Checks if the completion is in the context of a type and on a type reference in this type.
526 * Persists the identifier into a fake field return type
527 * Returns whether we found a completion node.
529 private boolean checkRecoveredType() {
530 if (currentElement instanceof RecoveredType){
531 /* check if current awaiting identifier is the completion identifier */
532 if (this.indexOfAssistIdentifier() < 0) return false;
534 /* check if on line with an error already - to avoid completing inside
535 illegal type names e.g. int[<cursor> */
536 if ((lastErrorEndPosition <= cursorLocation+1)
537 && scanner.getLineNumber(lastErrorEndPosition)
538 == scanner.getLineNumber(((CompletionScanner)scanner).completedIdentifierStart)){
541 RecoveredType recoveredType = (RecoveredType)currentElement;
542 /* filter out cases where scanner is still inside type header */
543 if (recoveredType.foundOpeningBrace) {
544 this.assistNode = this.getTypeReference(0);
545 this.lastCheckPoint = this.assistNode.sourceEnd + 1;
546 this.isOrphanCompletionNode = true;
553 * Check whether about to shift beyond the completion token.
554 * If so, depending on the context, a special node might need to be created
555 * and attached to the existing recovered structure so as to be remember in the
556 * resulting parsed structure.
558 public void completionIdentifierCheck(){
560 if (checkRecoveredType()) return;
561 if (checkRecoveredMethod()) return;
563 // if not in a method in non diet mode and if not inside a field initializer, only record references attached to types
564 if (!(this.inMethodStack[this.inMethodPtr] && !this.diet)
565 && !insideFieldInitialization()) return;
568 In some cases, the completion identifier may not have yet been consumed,
570 This is because the grammar does not allow any (empty) identifier to follow
571 a base type. We thus have to manually force the identifier to be consumed
574 if (assistIdentifier() == null && this.currentToken == TokenNameIdentifier) { // Test below copied from CompletionScanner.getCurrentIdentifierSource()
575 if (cursorLocation < this.scanner.startPosition && this.scanner.currentPosition == this.scanner.startPosition){ // fake empty identifier got issued
576 this.pushIdentifier();
577 } else if (cursorLocation+1 >= this.scanner.startPosition && cursorLocation < this.scanner.currentPosition){
578 this.pushIdentifier();
582 // check for different scenarii
584 // no need to go further if we found a non empty completion node
585 // (we still need to store labels though)
586 if (this.assistNode != null) {
587 // however inside an invocation, the completion identifier may already have been consumed into an empty name
588 // completion, so this check should be before we check that we are at the cursor location
589 if (!isEmptyNameCompletion() || checkInvocation()) return;
592 // no need to check further if we are not at the cursor location
593 if (this.indexOfAssistIdentifier() < 0) return;
595 if (checkClassInstanceCreation()) return;
596 if (checkCatchClause()) return;
597 if (checkMemberAccess()) return;
598 if (checkClassLiteralAccess()) return;
600 // if the completion was not on an empty name, it can still be inside an invocation (eg. this.fred("abc"[cursor])
601 // (NB: Put this check before checkNameCompletion() because the selector of the invocation can be on the identifier stack)
602 if (checkInvocation()) return;
604 if (checkNameCompletion()) return;
606 storeLabelsIfNeeded();
609 protected void consumeCaseLabel() {
610 Expression caseExpression = this.expressionStack[this.expressionPtr];
611 if (caseExpression instanceof SingleNameReference || caseExpression instanceof QualifiedNameReference) {
612 // label counter was wrongly incremented in consumeToken
613 if (this.labelCounterPtr >= 0) this.labelCounterStack[this.labelCounterPtr]--;
615 super.consumeCaseLabel();
617 protected void consumeClassHeaderExtends() {
618 this.nextTypeReferenceIsClass = true;
619 super.consumeClassHeaderExtends();
620 this.nextTypeReferenceIsClass = false;
622 protected void consumeClassTypeElt() {
623 this.nextTypeReferenceIsException = true;
624 super.consumeClassTypeElt();
625 this.nextTypeReferenceIsException = false;
627 protected void consumeConditionalExpression(int op) {
628 Expression valueIfTrue = this.expressionStack[this.expressionPtr - 1];
629 if (valueIfTrue instanceof SingleNameReference || valueIfTrue instanceof QualifiedNameReference) {
630 // label counter was wrongly incremented in consumeToken
631 if (this.labelCounterPtr >= 0) this.labelCounterStack[this.labelCounterPtr]--;
633 super.consumeConditionalExpression(op);
635 protected void consumeConstructorBody() {
636 super.consumeConstructorBody();
637 this.labelCounterPtr--;
638 if (this.blockInvocationPtr >= 0) this.blockInvocationPtr--;
640 protected void consumeConstructorHeader() {
641 super.consumeConstructorHeader();
642 pushBlockInvocationPtr();
644 protected void consumeConstructorHeaderName() {
646 /* no need to take action if not inside assist identifiers */
647 if (indexOfAssistIdentifier() < 0) {
648 super.consumeConstructorHeaderName();
652 /* force to start recovering in order to get fake field behavior */
653 if (currentElement == null){
654 this.hasReportedError = true; // do not report any error
656 this.restartRecovery = true;
658 protected void consumeEnterVariable() {
660 identifierLengthPtr--;
662 boolean isLocalDeclaration = nestedMethod[nestedType] != 0;
663 int variableIndex = variablesCounter[nestedType];
664 int extendedDimension = intStack[intPtr + 1];
666 if(isLocalDeclaration || indexOfAssistIdentifier() < 0 || variableIndex != 0 || extendedDimension != 0) {
668 identifierLengthPtr++;
669 super.consumeEnterVariable();
671 restartRecovery = true;
674 if (currentElement != null) {
675 int nameSourceStart = (int)(identifierPositionStack[identifierPtr] >>> 32);
678 TypeReference type = getTypeReference(intStack[intPtr--]);
681 if (!(currentElement instanceof RecoveredType)
682 && (currentToken == TokenNameDOT
683 || (scanner.getLineNumber(type.sourceStart)
684 != scanner.getLineNumber(nameSourceStart)))){
685 lastCheckPoint = nameSourceStart;
686 restartRecovery = true;
690 FieldDeclaration completionFieldDecl = new CompletionOnFieldType(type, false);
691 completionFieldDecl.modifiers = intStack[intPtr--];
692 assistNode = completionFieldDecl;
693 lastCheckPoint = type.sourceEnd + 1;
694 currentElement = currentElement.add(completionFieldDecl, 0);
695 lastIgnoredToken = -1;
699 protected void consumeExitVariableWithInitialization() {
700 super.consumeExitVariableWithInitialization();
702 // does not keep the initialization if completion is not inside
703 AbstractVariableDeclaration variable = (AbstractVariableDeclaration) astStack[astPtr];
704 if (cursorLocation + 1 < variable.initialization.sourceStart ||
705 cursorLocation > variable.initialization.sourceEnd) {
706 variable.initialization = null;
711 * Copy of code from superclass with the following change:
712 * If the cursor location is on the field access, then create a
713 * CompletionOnMemberAccess instead.
715 protected void consumeFieldAccess(boolean isSuperAccess) {
716 // FieldAccess ::= Primary '.' 'Identifier'
717 // FieldAccess ::= 'super' '.' 'Identifier'
719 // potential receiver is being poped, so reset potential receiver
720 this.invocationType = NO_RECEIVER;
722 if (this.indexOfAssistIdentifier() < 0) {
723 super.consumeFieldAccess(isSuperAccess);
725 this.pushCompletionOnMemberAccessOnExpressionStack(isSuperAccess);
729 protected void consumeFormalParameter() {
730 if (this.indexOfAssistIdentifier() < 0) {
731 super.consumeFormalParameter();
734 identifierLengthPtr--;
735 char[] name = identifierStack[identifierPtr];
736 long namePositions = identifierPositionStack[identifierPtr--];
737 TypeReference type = getTypeReference(intStack[intPtr--] + intStack[intPtr--]);
740 new CompletionOnArgumentName(
744 intStack[intPtr + 1] & ~AccDeprecated); // modifiers
748 this.lastCheckPoint = (int) namePositions;
749 isOrphanCompletionNode = true;
751 /* if incomplete method header, listLength counter will not have been reset,
752 indicating that some arguments are available on the stack */
756 protected void consumeInterfaceType() {
757 this.nextTypeReferenceIsInterface = true;
758 super.consumeInterfaceType();
759 this.nextTypeReferenceIsInterface = false;
761 protected void consumeMethodHeaderName() {
762 if(this.indexOfAssistIdentifier() < 0) {
764 identifierLengthPtr--;
765 if(this.indexOfAssistIdentifier() != 0) {
767 identifierLengthPtr++;
768 super.consumeMethodHeaderName();
770 restartRecovery = true;
773 if (currentElement != null) {
775 char[] selector = identifierStack[identifierPtr + 1];
776 long selectorSource = identifierPositionStack[identifierPtr + 1];
779 TypeReference type = getTypeReference(intStack[intPtr--]);
780 ((CompletionOnSingleTypeReference)type).isCompletionNode = false;
782 int declarationSourceStart = intStack[intPtr--];
783 int modifiers = intStack[intPtr--];
785 if(scanner.getLineNumber(type.sourceStart) != scanner.getLineNumber((int) (selectorSource >>> 32))) {
786 FieldDeclaration completionFieldDecl = new CompletionOnFieldType(type, false);
787 completionFieldDecl.modifiers = modifiers;
788 assistNode = completionFieldDecl;
789 lastCheckPoint = type.sourceEnd + 1;
790 currentElement = currentElement.add(completionFieldDecl, 0);
791 lastIgnoredToken = -1;
793 CompletionOnMethodReturnType md = new CompletionOnMethodReturnType(type, this.compilationUnit.compilationResult);
794 md.selector = selector;
795 md.declarationSourceStart = declarationSourceStart;
796 md.modifiers = modifiers;
797 md.bodyStart = lParenPos+1;
798 listLength = 0; // initialize listLength before reading parameters/throws
800 this.lastCheckPoint = md.bodyStart;
801 currentElement = currentElement.add(md, 0);
802 lastIgnoredToken = -1;
807 // MethodHeaderName ::= Modifiersopt Type 'Identifier' '('
808 CompletionOnMethodName md = new CompletionOnMethodName(this.compilationUnit.compilationResult);
811 md.selector = identifierStack[identifierPtr];
812 long selectorSource = identifierPositionStack[identifierPtr--];
814 md.returnType = getTypeReference(intStack[intPtr--]);
816 md.declarationSourceStart = intStack[intPtr--];
817 md.modifiers = intStack[intPtr--];
819 //highlight starts at selector start
820 md.sourceStart = (int) (selectorSource >>> 32);
821 md.selectorEnd = (int) selectorSource;
823 md.sourceEnd = lParenPos;
824 md.bodyStart = lParenPos+1;
825 listLength = 0; // initialize listLength before reading parameters/throws
827 this.assistNode = md;
828 this.lastCheckPoint = md.sourceEnd;
830 if (currentElement != null){
831 if (currentElement instanceof RecoveredType
832 //|| md.modifiers != 0
833 || (scanner.getLineNumber(md.returnType.sourceStart)
834 == scanner.getLineNumber(md.sourceStart))){
835 lastCheckPoint = md.bodyStart;
836 currentElement = currentElement.add(md, 0);
837 lastIgnoredToken = -1;
839 lastCheckPoint = md.sourceStart;
840 restartRecovery = true;
847 protected void consumeMethodBody() {
848 super.consumeMethodBody();
849 this.labelCounterPtr--;
850 if (this.blockInvocationPtr >= 0) this.blockInvocationPtr--;
853 protected void consumeMethodHeader() {
854 super.consumeMethodHeader();
855 pushBlockInvocationPtr();
857 protected void consumeModifiers() {
858 super.consumeModifiers();
859 // save from stack values
860 this.lastModifiersStart = intStack[intPtr];
861 this.lastModifiers = intStack[intPtr-1];
863 protected void consumeNestedMethod() {
864 super.consumeNestedMethod();
865 this.pushNewLabelCounter();
867 protected void consumeStatementLabel() {
868 super.consumeStatementLabel();
869 if (this.labelCounterPtr >= 0) this.labelCounterStack[this.labelCounterPtr]--;
871 protected void consumeToken(int token) {
872 int previous = this.previousToken;
873 int previousIdentifierPtr = this.previousIdentifierPtr;
874 super.consumeToken(token);
876 // if in field initializer (directly or not), on the completion identifier and not in recovery mode yet
877 // then position end of file at cursor location (so that we have the same behavior as
879 if (token == TokenNameIdentifier
880 && this.identifierStack[this.identifierPtr] == assistIdentifier()
881 && this.currentElement == null
882 && this.insideFieldInitialization()) {
883 this.scanner.eofPosition = cursorLocation < Integer.MAX_VALUE ? cursorLocation+1 : cursorLocation;
886 // if in a method or if in a field initializer
887 if (this.inMethodStack[this.inMethodPtr] || this.inFieldInitializationStack[this.inFieldInitializationPtr]) {
891 // case TokenNamethis: // eg. this[.]fred()
892 // this.invocationType = EXPLICIT_RECEIVER;
894 // case TokenNamesuper: // eg. super[.]fred()
895 // this.invocationType = SUPER_RECEIVER;
897 case TokenNameIdentifier: // eg. bar[.]fred()
898 if (!this.betweenNewAndLeftBraket) { // eg. not new z.y[.]X()
899 if (this.identifierPtr != previousIdentifierPtr) { // if identifier has been consumed, eg. this.x[.]fred()
900 this.invocationType = EXPLICIT_RECEIVER;
902 this.invocationType = NAME_RECEIVER;
908 case TokenNameIdentifier:
909 if (previous == TokenNameDOT) { // eg. foo().[fred]()
910 // if current identifier is the empty completion one
911 if (identifierStack[identifierPtr] == CompletionScanner.EmptyCompletionIdentifier){
912 this.completionBehindDot = true;
914 if (this.invocationType != SUPER_RECEIVER // eg. not super.[fred]()
915 && this.invocationType != NAME_RECEIVER // eg. not bar.[fred]()
916 && this.invocationType != ALLOCATION // eg. not new foo.[Bar]()
917 && this.invocationType != QUALIFIED_ALLOCATION) { // eg. not fred().new foo.[Bar]()
919 this.invocationType = EXPLICIT_RECEIVER;
920 this.qualifier = this.expressionPtr;
925 this.betweenNewAndLeftBraket = true;
926 this.qualifier = this.expressionPtr; // NB: even if there is no qualification, set it to the expression ptr so that the number of arguments are correctly computed
927 if (previous == TokenNameDOT) { // eg. fred().[new] X()
928 this.invocationType = QUALIFIED_ALLOCATION;
929 } else { // eg. [new] X()
930 this.invocationType = ALLOCATION;
933 // case TokenNamethis:
934 // if (previous == TokenNameDOT) { // eg. fred().[this]()
935 // this.invocationType = QUALIFIED_ALLOCATION;
936 // this.qualifier = this.expressionPtr;
939 // case TokenNamesuper:
940 // if (previous == TokenNameDOT) { // eg. fred().[super]()
941 // this.invocationType = QUALIFIED_ALLOCATION;
942 // this.qualifier = this.expressionPtr;
945 // case TokenNamecatch:
946 // this.betweenCatchAndRightParen = true;
948 case TokenNameLPAREN:
949 this.betweenNewAndLeftBraket = false;
951 if (this.invocationType == NO_RECEIVER || this.invocationType == NAME_RECEIVER) {
952 this.qualifier = this.expressionPtr; // remenber the last expression so that arguments are correctly computed
955 case TokenNameIdentifier: // eg. fred[(]) or foo.fred[(])
956 this.pushOnInvocationStacks(this.invocationType, this.qualifier);
957 this.invocationType = NO_RECEIVER;
959 // case TokenNamethis: // explicit constructor invocation, eg. this[(]1, 2)
960 // this.pushOnInvocationStacks(
961 // (this.invocationType == QUALIFIED_ALLOCATION) ? QUALIFIED_ALLOCATION : ALLOCATION,
963 // this.invocationType = NO_RECEIVER;
965 // case TokenNamesuper: // explicit constructor invocation, eg. super[(]1, 2)
966 // this.pushOnInvocationStacks(
967 // (this.invocationType == QUALIFIED_ALLOCATION) ? QUALIFIED_ALLOCATION : ALLOCATION,
969 // this.invocationType = NO_RECEIVER;
973 case TokenNameLBRACE:
974 this.betweenNewAndLeftBraket = false;
976 this.pushBlockInvocationPtr();
978 case TokenNameLBRACKET:
979 this.betweenNewAndLeftBraket = false;
982 case TokenNameRBRACE:
984 if (this.blockInvocationPtr >= 0) this.blockInvocationPtr--;
986 case TokenNameRBRACKET:
989 case TokenNameRPAREN:
990 this.betweenCatchAndRightParen = false;
994 if (previous == TokenNameIdentifier) {
995 if (this.labelCounterPtr >= 0) this.labelCounterStack[this.labelCounterPtr]++;
998 // case TokenNamethrow:
999 // this.throwBracketDepth= bracketDepth;
1005 * Return whether the given ast node contains the completion node.
1007 private boolean containsCompletionNode(AstNode ast) {
1008 if (this.assistNode == null || ast instanceof Literal) {
1011 if (this.assistNode == ast) {
1014 if (ast instanceof Reference || ast instanceof TypeReference) {
1015 return ast == this.assistNode;
1017 if (ast instanceof Assignment) {
1018 Assignment assign = (Assignment)ast;
1019 return containsCompletionNode(assign.lhs) || containsCompletionNode(assign.expression);
1021 if (ast instanceof UnaryExpression) {
1022 UnaryExpression unary = (UnaryExpression)ast;
1023 return containsCompletionNode(unary.expression);
1025 if (ast instanceof BinaryExpression) {
1026 BinaryExpression binary = (BinaryExpression)ast;
1027 return containsCompletionNode(binary.left) || containsCompletionNode(binary.right);
1029 if (ast instanceof InstanceOfExpression) {
1030 InstanceOfExpression instanceOfExpr = (InstanceOfExpression)ast;
1031 return containsCompletionNode(instanceOfExpr.expression) || containsCompletionNode(instanceOfExpr.type);
1033 if (ast instanceof ConditionalExpression) {
1034 ConditionalExpression conditional = (ConditionalExpression)ast;
1035 return containsCompletionNode(conditional.condition) || containsCompletionNode(conditional.valueIfTrue) || containsCompletionNode(conditional.valueIfFalse);
1037 if (ast instanceof AllocationExpression) {
1038 AllocationExpression alloc = (AllocationExpression)ast;
1039 return containsCompletionNode(alloc.type);
1041 if (ast instanceof CastExpression) {
1042 CastExpression cast = (CastExpression)ast;
1043 return containsCompletionNode(cast.expression) || containsCompletionNode(cast.type);
1045 if (ast instanceof ExplicitConstructorCall) {
1046 ExplicitConstructorCall call = (ExplicitConstructorCall)ast;
1047 Expression[] arguments = call.arguments;
1048 if (arguments != null) {
1049 for (int i = 0; i < arguments.length; i++) {
1050 if (containsCompletionNode(arguments[i])) {
1059 public ImportReference createAssistImportReference(char[][] tokens, long[] positions){
1060 return new CompletionOnImportReference(tokens, positions);
1062 public ImportReference createAssistPackageReference(char[][] tokens, long[] positions){
1063 return new CompletionOnPackageReference(tokens, positions);
1065 public NameReference createQualifiedAssistNameReference(char[][] previousIdentifiers, char[] name, long[] positions){
1066 return new CompletionOnQualifiedNameReference(
1067 previousIdentifiers,
1071 public TypeReference createQualifiedAssistTypeReference(char[][] previousIdentifiers, char[] name, long[] positions){
1072 return this.betweenCatchAndRightParen || this.nextTypeReferenceIsException // check for exception scenario
1073 ? new CompletionOnQualifiedExceptionReference(
1074 previousIdentifiers,
1077 : this.nextTypeReferenceIsInterface
1078 ? new CompletionOnQualifiedInterfaceReference(
1079 previousIdentifiers,
1082 : this.nextTypeReferenceIsClass
1083 ? new CompletionOnQualifiedClassReference(
1084 previousIdentifiers,
1087 : new CompletionOnQualifiedTypeReference(
1088 previousIdentifiers,
1092 public NameReference createSingleAssistNameReference(char[] name, long position) {
1093 return new CompletionOnSingleNameReference(name, position);
1095 public TypeReference createSingleAssistTypeReference(char[] name, long position) {
1096 return this.betweenCatchAndRightParen || this.nextTypeReferenceIsException // check for exception scenario
1097 ? new CompletionOnExceptionReference(name, position)
1098 : this.nextTypeReferenceIsInterface
1099 ? new CompletionOnInterfaceReference(name, position)
1100 : this.nextTypeReferenceIsClass
1101 ? new CompletionOnClassReference(name, position)
1102 : new CompletionOnSingleTypeReference(name, position);
1104 public CompilationUnitDeclaration dietParse(ICompilationUnit sourceUnit, CompilationResult compilationResult, int cursorLocation) {
1106 this.cursorLocation = cursorLocation;
1107 CompletionScanner completionScanner = (CompletionScanner)this.scanner;
1108 completionScanner.completionIdentifier = null;
1109 completionScanner.cursorLocation = cursorLocation;
1110 return this.dietParse(sourceUnit, compilationResult);
1113 * Flush parser/scanner state regarding to code assist
1115 public void flushAssistState() {
1117 super.flushAssistState();
1118 this.isOrphanCompletionNode = false;
1119 CompletionScanner completionScanner = (CompletionScanner)this.scanner;
1120 completionScanner.completedIdentifierStart = 0;
1121 completionScanner.completedIdentifierEnd = -1;
1123 protected NameReference getUnspecifiedReferenceOptimized() {
1124 if (this.identifierLengthStack[this.identifierLengthPtr] > 1) { // reducing a qualified name
1125 // potential receiver is being poped, so reset potential receiver
1126 this.invocationType = NO_RECEIVER;
1128 return super.getUnspecifiedReferenceOptimized();
1131 * Return whether the given ast node has information interresting for code completion.
1133 private boolean hasCompletionInformation(AstNode ast) {
1135 ast instanceof AbstractMethodDeclaration ||
1136 ast instanceof AbstractVariableDeclaration ||
1137 ast instanceof LabeledStatement ||
1138 ast instanceof TypeDeclaration);
1140 public void initialize() {
1142 this.initializeForBlockStatements();
1143 this.labelCounterPtr = -1;
1146 * Initializes the state of the parser that is about to go for BlockStatements.
1148 private void initializeForBlockStatements() {
1149 this.previousToken = -1;
1150 this.previousIdentifierPtr = -1;
1151 this.completionBehindDot = false;
1152 this.betweenNewAndLeftBraket = false;
1153 this.betweenCatchAndRightParen = false;
1154 this.bracketDepth = 0;
1155 this.throwBracketDepth = -1;
1156 this.invocationType = NO_RECEIVER;
1157 this.qualifier = -1;
1158 this.blockInvocationPtr = -1;
1160 public void initializeScanner(){
1161 this.scanner = new CompletionScanner(this.assertMode);
1164 * Returns whether the completion is just after an array type
1165 * eg. String[].[cursor]
1167 private boolean isAfterArrayType() {
1168 // TBD: The following relies on the fact that array dimensions are small: it says that if the
1169 // top of the intStack is less than 11, then it must be a dimension
1170 // (smallest position of array type in a compilation unit is 11 as in "class X{Y[]")
1171 if ((this.intPtr > -1) && (this.intStack[this.intPtr] < 11)) {
1176 private boolean isEmptyNameCompletion() {
1178 this.assistNode != null &&
1179 this.assistNode instanceof CompletionOnSingleNameReference &&
1180 (((CompletionOnSingleNameReference)this.assistNode).token.length == 0);
1182 public CompilationUnitDeclaration parse(ICompilationUnit sourceUnit, CompilationResult compilationResult, int cursorLocation) {
1184 this.cursorLocation = cursorLocation;
1185 CompletionScanner completionScanner = (CompletionScanner)this.scanner;
1186 completionScanner.completionIdentifier = null;
1187 completionScanner.cursorLocation = cursorLocation;
1188 return this.parse(sourceUnit, compilationResult);
1191 * Prepares the state of the parser to go for BlockStatements.
1193 protected void prepareForBlockStatements() {
1194 super.prepareForBlockStatements();
1195 this.initializeForBlockStatements();
1197 protected void pushBlockInvocationPtr() {
1199 this.blockInvocationStack[++this.blockInvocationPtr] = this.invocationPtr+1;
1200 } catch (IndexOutOfBoundsException e) {
1201 int oldStackLength = this.blockInvocationStack.length;
1202 int[] oldStack = this.blockInvocationStack;
1203 this.blockInvocationStack = new int[oldStackLength + StackIncrement];
1204 System.arraycopy(oldStack, 0, this.blockInvocationStack, 0, oldStackLength);
1205 this.blockInvocationStack[this.blockInvocationPtr] = this.invocationPtr+1;
1209 * Creates a completion on member access node and push it
1210 * on the expression stack.
1212 private void pushCompletionOnMemberAccessOnExpressionStack(boolean isSuperAccess) {
1213 char[] source = identifierStack[identifierPtr];
1214 long pos = identifierPositionStack[identifierPtr--];
1215 CompletionOnMemberAccess fr = new CompletionOnMemberAccess(source, pos);
1216 this.assistNode = fr;
1217 this.lastCheckPoint = fr.sourceEnd + 1;
1218 identifierLengthPtr--;
1219 if (isSuperAccess) { //considerates the fieldReference beginning at the 'super' ....
1220 fr.sourceStart = intStack[intPtr--];
1221 fr.receiver = new SuperReference(fr.sourceStart, endPosition);
1222 pushOnExpressionStack(fr);
1223 } else { //optimize push/pop
1224 if ((fr.receiver = expressionStack[expressionPtr]).isThis()) { //fieldreference begins at the this
1225 fr.sourceStart = fr.receiver.sourceStart;
1227 expressionStack[expressionPtr] = fr;
1230 protected void pushNewLabelCounter() {
1232 this.labelCounterStack[++this.labelCounterPtr] = 0;
1233 } catch (IndexOutOfBoundsException e) {
1234 int oldStackLength = this.labelCounterStack.length;
1235 int[] oldStack = this.labelCounterStack;
1236 this.labelCounterStack = new int[oldStackLength + StackIncrement];
1237 System.arraycopy(oldStack, 0, this.labelCounterStack, 0, oldStackLength);
1238 this.labelCounterStack[this.labelCounterPtr] = 0;
1242 * Pushes the given invocation type (one of the invocation type constants) on the invocation type stack,
1243 * and the given qualifier (an expression pointer to the expression stack) on the qualifier stack.
1245 protected void pushOnInvocationStacks(int invocationType, int qualifierExprPtr) {
1246 // NB: invocationPtr has already been incremented by a call to pushOnSelectorStack()
1248 this.invocationTypeStack[this.invocationPtr] = invocationType;
1249 this.qualifierStack[this.invocationPtr] = qualifierExprPtr;
1250 } catch (IndexOutOfBoundsException e) {
1251 int oldStackLength = this.invocationTypeStack.length;
1252 int oldInvocationTypeStack[] = this.invocationTypeStack;
1253 int oldQualifierStack[] = this.qualifierStack;
1254 this.invocationTypeStack = new int[oldStackLength + StackIncrement];
1255 this.qualifierStack = new int[oldStackLength + StackIncrement];
1256 System.arraycopy(oldInvocationTypeStack, 0, this.invocationTypeStack, 0, oldStackLength);
1257 System.arraycopy(oldQualifierStack, 0, this.qualifierStack, 0, oldStackLength);
1258 this.invocationTypeStack[this.invocationPtr] = invocationType;
1259 this.qualifierStack[this.invocationPtr] = qualifierExprPtr;
1262 public void recordCompletionOnReference(){
1264 if (currentElement instanceof RecoveredType){
1265 RecoveredType recoveredType = (RecoveredType)currentElement;
1267 /* filter out cases where scanner is still inside type header */
1268 if (!recoveredType.foundOpeningBrace) return;
1270 /* generate a pseudo field with a completion on type reference */
1272 new CompletionOnFieldType(this.getTypeReference(0), false), 0);
1275 if (!diet) return; // only record references attached to types
1278 protected void reportSyntaxError(int act, int currentKind, int stateStackTop) {
1280 /* Intercept error state on EOF inside method bodies, due to
1281 cursor location being used as an EOF position.
1283 if (!diet && currentToken == TokenNameEOF) return;
1284 super.reportSyntaxError(act, currentKind, stateStackTop);
1287 * Reset internal state after completion is over
1290 public void reset() {
1292 this.cursorLocation = 0;
1295 * Reset internal state after completion is over
1298 public void resetAfterCompletion() {
1299 this.cursorLocation = 0;
1300 this.flushAssistState();
1303 * Reset context so as to resume to regular parse loop
1304 * If unable to reset for resuming, answers false.
1306 * Move checkpoint location, reset internal stacks and
1307 * decide which grammar goal is activated.
1309 protected boolean resumeAfterRecovery() {
1310 if (this.assistNode != null) {
1311 /* if reached [eof] inside method body, but still inside nested type,
1312 or inside a field initializer, should continue in diet mode until
1313 the end of the method body or compilation unit */
1314 if ((scanner.eofPosition == cursorLocation+1)
1315 && (!(referenceContext instanceof CompilationUnitDeclaration)
1316 || insideFieldInitialization())) {
1318 /* disabled since does not handle possible field/message refs, i.e. Obj[ASSIST HERE]ect.registerNatives()
1319 // consume extra tokens which were part of the qualified reference
1320 // so that the replaced source comprises them as well
1321 if (this.assistNode instanceof NameReference){
1322 int oldEof = scanner.eofPosition;
1323 scanner.eofPosition = currentElement.topElement().sourceEnd()+1;
1324 scanner.currentPosition = this.cursorLocation+1;
1328 // first token might not have to be a dot
1329 if (token >= 0 || !this.completionBehindDot){
1330 if ((token = scanner.getNextToken()) != TokenNameDOT) break;
1332 if ((token = scanner.getNextToken()) != TokenNameIdentifier) break;
1333 this.assistNode.sourceEnd = scanner.currentPosition - 1;
1334 } while (token != TokenNameEOF);
1335 } catch (InvalidInputException e){
1337 scanner.eofPosition = oldEof;
1341 /* restart in diet mode for finding sibling constructs */
1342 if (currentElement.enclosingType() != null){
1343 lastCheckPoint = this.assistNode.sourceEnd+1;
1344 int end = currentElement.topElement().sourceEnd();
1345 scanner.eofPosition = end < Integer.MAX_VALUE ? end + 1 : end;
1352 return super.resumeAfterRecovery();
1354 public void setAssistIdentifier(char[] assistIdent){
1355 ((CompletionScanner)scanner).completionIdentifier = assistIdent;
1358 * Stores the labels left on the identifier stack if they have not been stored yet.
1360 private void storeLabelsIfNeeded() {
1361 // int counter = this.labelCounterPtr >= 0 ? this.labelCounterStack[this.labelCounterPtr] : 0;
1362 // if (this.labels == null && this.identifierPtr >= 0) {
1363 // this.labels = new char[counter][];
1364 // System.arraycopy(this.identifierStack, this.identifierPtr - counter + 1, this.labels, 0, counter);
1366 // this.identifierPtr -= counter;
1367 // this.identifierLengthPtr -= counter; // labels have not been concatenated yet
1370 * Update recovery state based on current parser/scanner state
1372 protected void updateRecoveryState() {
1374 /* expose parser state to recovery state */
1375 currentElement.updateFromParserState();
1377 /* may be able to retrieve completionNode as an orphan, and then attach it */
1378 this.completionIdentifierCheck();
1379 this.attachOrphanCompletionNode();
1381 // if an assist node has been found and a recovered element exists,
1382 // mark enclosing blocks as to be preserved
1383 if (this.assistNode != null && this.currentElement != null) {
1384 currentElement.preserveEnclosingBlocks();
1387 /* check and update recovered state based on current token,
1388 this action is also performed when shifting token after recovery
1391 this.recoveryTokenCheck();
1394 protected LocalDeclaration createLocalDeclaration(Expression initialization, char[] name, int sourceStart, int sourceEnd) {
1395 if (this.indexOfAssistIdentifier() < 0) {
1396 return super.createLocalDeclaration(initialization, name, sourceStart, sourceEnd);
1398 CompletionOnLocalName local = new CompletionOnLocalName(initialization, name, sourceStart, sourceEnd);
1399 this.assistNode = local;
1400 this.lastCheckPoint = sourceEnd + 1;
1405 protected FieldDeclaration createFieldDeclaration(Expression initialization, char[] name, int sourceStart, int sourceEnd) {
1406 if (this.indexOfAssistIdentifier() < 0) {
1407 return super.createFieldDeclaration(initialization, name, sourceStart, sourceEnd);
1409 CompletionOnFieldName field = new CompletionOnFieldName(initialization, name, sourceStart, sourceEnd);
1410 this.assistNode = field;
1411 this.lastCheckPoint = sourceEnd + 1;