blob: e664afbfef1aec8fa34be5729c22790289aef5c7 [file] [log] [blame]
package org.eclipse.jdt.internal.codeassist.complete;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
/*
* Parser able to build specific completion parse nodes, given a cursorLocation.
*
* Cursor location denotes the position of the last character behind which completion
* got requested:
* -1 means completion at the very beginning of the source
* 0 means completion behind the first character
* n means completion behind the n-th character
*/
import org.eclipse.jdt.internal.compiler.*;
import org.eclipse.jdt.internal.compiler.env.*;
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.parser.*;
import org.eclipse.jdt.internal.compiler.problem.*;
import org.eclipse.jdt.internal.codeassist.impl.*;
public class CompletionParser extends AssistParser {
/* public fields */
public int cursorLocation;
public char[][] labels; // the visible labels up to the cursor location
public AstNode assistNodeParent; // the parent node of assist node
/* the following fields are internal flags */
boolean betweenNewAndLeftBraket; // whether we are between the keyword 'new' and the following left braket, ie. '[', '(' or '{'
boolean betweenCatchAndRightParen; // whether we are between the keyword 'catch' and the following ')'
boolean completionBehindDot; // true when completion identifier immediately follows a dot
boolean nextTypeReferenceIsClass;
boolean nextTypeReferenceIsException;
boolean nextTypeReferenceIsInterface;
int bracketDepth;
int throwBracketDepth;
// the stacks of types and qualifiers for invocations (ie. method invocations, allocation expressions and
// explicit constructor invocations). They use the same stack pointer as the selector stack (ie. invocationPtr)
// the invocation type stack contains one of the invocation type constants below
// the qualifier stack contains pointers to the expression stack or -1 if there is no qualifier
// (a qualifier is the expression that qualifies a 'new', a 'super' constructor or a 'this' constructor
// or it is the receiver of a message send)
int[] invocationTypeStack = new int[StackIncrement];
int[] qualifierStack = new int[StackIncrement];
// invocation type constants
static final int EXPLICIT_RECEIVER = 0;
static final int NO_RECEIVER = -1;
static final int SUPER_RECEIVER = -2;
static final int NAME_RECEIVER = -3;
static final int ALLOCATION = -4;
static final int QUALIFIED_ALLOCATION = -5;
// the type of the current invocation (one of the invocation type constants)
int invocationType;
// a pointer in the expression stack to the qualifier of a invocation
int qualifier;
// a stack of label counters
// a new counter is pushed on the stack each time when a method (or a constructor) is entered,
// it is poped when the method (or constructor) is exited,
// it is incremented when a new label is defined
int labelCounterPtr;
int[] labelCounterStack = new int[StackIncrement];
// a stack of invocationPtr: contains the first invocationPtr of a block
// the current invocationPtr+1 is pushed when a block is entered
// it is poped when a block is exited
int blockInvocationPtr;
int[] blockInvocationStack = new int[StackIncrement];
// last modifiers info
int lastModifiers = AccDefault;
int lastModifiersStart = -1;
public CompletionParser(ProblemReporter problemReporter, boolean assertMode) {
super(problemReporter, assertMode);
}
public char[] assistIdentifier(){
return ((CompletionScanner)scanner).completionIdentifier;
}
protected void attachOrphanCompletionNode(){
if (this.isOrphanCompletionNode) {
AstNode orphan = this.assistNode;
this.isOrphanCompletionNode = false;
/* if in context of a type, then persists the identifier into a fake field return type */
if (currentElement instanceof RecoveredType){
RecoveredType recoveredType = (RecoveredType)currentElement;
/* filter out cases where scanner is still inside type header */
if (recoveredType.foundOpeningBrace) {
/* generate a pseudo field with a completion on type reference */
if (orphan instanceof TypeReference){
CompletionOnFieldType fieldDeclaration = new CompletionOnFieldType((TypeReference)orphan, false);
// retrieve available modifiers if any
if (intPtr >= 2 && intStack[intPtr-1] == this.lastModifiersStart && intStack[intPtr-2] == this.lastModifiers){
fieldDeclaration.modifiersSourceStart = intStack[intPtr-1];
fieldDeclaration.modifiers = intStack[intPtr-2];
}
currentElement = currentElement.add(fieldDeclaration, 0);
return;
}
}
}
/* if in context of a method, persists if inside arguments as a type */
if (currentElement instanceof RecoveredMethod){
RecoveredMethod recoveredMethod = (RecoveredMethod)currentElement;
/* only consider if inside method header */
if (!recoveredMethod.foundOpeningBrace) {
//if (rParenPos < lParenPos){ // inside arguments
if (orphan instanceof TypeReference){
currentElement = currentElement.parent.add(
new CompletionOnFieldType((TypeReference)orphan, true), 0);
return;
}
}
}
// add the completion node to the method declaration or constructor declaration
if (orphan instanceof Statement) {
/* check for completion at the beginning of method body
behind an invalid signature
*/
RecoveredMethod method = currentElement.enclosingMethod();
if (method != null){
AbstractMethodDeclaration methodDecl = method.methodDeclaration;
if ((methodDecl.bodyStart == methodDecl.sourceEnd+1) // was missing opening brace
&& (scanner.getLineNumber(orphan.sourceStart) == scanner.getLineNumber(methodDecl.sourceEnd))){
return;
}
}
// add the completion node as a statement to the list of block statements
currentElement = currentElement.add((Statement)orphan, 0);
return;
}
}
// the following code applies only in methods, constructors or initializers
if ((!this.inMethodStack[this.inMethodPtr] && !this.inFieldInitializationStack[this.inFieldInitializationPtr])) {
return;
}
// push top expression on ast stack if it contains the completion node
Expression expression;
if (this.expressionPtr > -1 && containsCompletionNode(expression = this.expressionStack[this.expressionPtr])) {
/* check for completion at the beginning of method body
behind an invalid signature
*/
RecoveredMethod method = currentElement.enclosingMethod();
if (method != null){
AbstractMethodDeclaration methodDecl = method.methodDeclaration;
if ((methodDecl.bodyStart == methodDecl.sourceEnd+1) // was missing opening brace
&& (scanner.getLineNumber(expression.sourceStart) == scanner.getLineNumber(methodDecl.sourceEnd))){
return;
}
}
if (expression instanceof AllocationExpression) {
// keep the context if it is an allocation expression
Statement statement = (Statement)wrapWithExplicitConstructorCallIfNeeded(expression);
currentElement = currentElement.add(statement, 0);
} else {
Statement statement = (Statement)wrapWithExplicitConstructorCallIfNeeded(this.assistNode);
currentElement = currentElement.add(statement, 0);
}
}
}
public int bodyEnd(AbstractMethodDeclaration method){
return cursorLocation;
}
public int bodyEnd(Initializer initializer){
return cursorLocation;
}
/**
* Checks if the completion is on the exception type of a catch clause.
* Returns whether we found a completion node.
*/
private boolean checkCatchClause() {
if (this.betweenCatchAndRightParen && this.identifierPtr > -1) {
// NB: if the cursor is on the variable, then it has been reduced (so identifierPtr is -1),
// thus this can only be a completion on the type of the catch clause
this.assistNode = getTypeReference(0);
this.lastCheckPoint = this.assistNode.sourceEnd + 1;
this.isOrphanCompletionNode = true;
return true;
}
return false;
}
/**
* Checks if the completion is on the type following a 'new'.
* Returns whether we found a completion node.
*/
private boolean checkClassInstanceCreation() {
if (this.betweenNewAndLeftBraket) {
// completion on type inside an allocation expression
if(this.throwBracketDepth != -1 && this.throwBracketDepth == this.bracketDepth) {
this.nextTypeReferenceIsException = true;
}
TypeReference type = getTypeReference(0);
this.nextTypeReferenceIsException = false;
this.assistNode = type;
this.lastCheckPoint = type.sourceEnd + 1;
if (this.invocationType == ALLOCATION) {
// non qualified allocation expression
AllocationExpression allocExpr = new AllocationExpression();
allocExpr.type = type;
allocExpr.sourceStart = type.sourceStart;
allocExpr.sourceEnd = type.sourceEnd;
pushOnExpressionStack(allocExpr);
this.isOrphanCompletionNode = false;
} else {
// qualified allocation expression
QualifiedAllocationExpression allocExpr = new QualifiedAllocationExpression();
allocExpr.type = type;
allocExpr.enclosingInstance = this.expressionStack[this.qualifier];
allocExpr.sourceStart = this.intStack[this.intPtr--];
allocExpr.sourceEnd = type.sourceEnd;
this.expressionStack[this.qualifier] = allocExpr; // attach it now (it replaces the qualifier expression)
this.isOrphanCompletionNode = false;
}
return true;
}
return false;
}
/**
* Checks if the completion is on the dot following an array type,
* a primitive type or an primitive array type.
* Returns whether we found a completion node.
*/
private boolean checkClassLiteralAccess() {
if (this.identifierLengthPtr >= 1 && this.previousToken == TokenNameDOT) { // (NB: the top id length is 1 and it is for the completion identifier)
int length;
// if the penultimate id length is negative,
// the completion is after a primitive type or a primitive array type
if ((length = this.identifierLengthStack[this.identifierLengthPtr-1]) < 0) {
// build the primitive type node
int dim = this.isAfterArrayType() ? this.intStack[this.intPtr--] : 0;
SingleTypeReference typeRef = (SingleTypeReference)TypeReference.baseTypeReference(-length, dim);
typeRef.sourceStart = this.intStack[this.intPtr--];
if (dim == 0) {
typeRef.sourceEnd = this.intStack[this.intPtr--];
} else {
this.intPtr--;
typeRef.sourceEnd = this.endPosition;
}
//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
// find the completion identifier and its source positions
char[] source = identifierStack[identifierPtr];
long pos = this.identifierPositionStack[this.identifierPtr--];
this.identifierLengthPtr--; // it can only be a simple identifier (so its length is one)
// build the completion on class literal access node
CompletionOnClassLiteralAccess access = new CompletionOnClassLiteralAccess(pos, typeRef);
access.completionIdentifier = source;
this.identifierLengthPtr--; // pop the length that was used to say it is a primitive type
this.assistNode = access;
this.isOrphanCompletionNode = true;
return true;
}
// if the completion is after a regular array type
if (isAfterArrayType()) {
// find the completion identifier and its source positions
char[] source = identifierStack[identifierPtr];
long pos = this.identifierPositionStack[this.identifierPtr--];
this.identifierLengthPtr--; // it can only be a simple identifier (so its length is one)
// get the type reference
TypeReference typeRef = getTypeReference(this.intPtr--);
// build the completion on class literal access node
CompletionOnClassLiteralAccess access = new CompletionOnClassLiteralAccess(pos, typeRef);
access.completionIdentifier = source;
this.assistNode = access;
this.isOrphanCompletionNode = true;
return true;
}
}
return false;
}
/**
* Checks if the completion is inside a method invocation or a constructor invocation.
* Returns whether we found a completion node.
*/
private boolean checkInvocation() {
Expression topExpression = this.expressionPtr >= 0 ?
this.expressionStack[this.expressionPtr] :
null;
boolean isEmptyNameCompletion = false;
boolean isEmptyAssistIdentifier = false;
int startInvocationPtr = this.blockInvocationPtr >= 0 ? this.blockInvocationStack[this.blockInvocationPtr] : 0;
if (this.invocationPtr >= startInvocationPtr
&& ((isEmptyNameCompletion = topExpression == this.assistNode && this.isEmptyNameCompletion()) // eg. it is something like "this.fred([cursor]" but it is not something like "this.fred(1 + [cursor]"
|| (isEmptyAssistIdentifier = this.indexOfAssistIdentifier() >= 0 && this.identifierStack[this.identifierPtr].length == 0))) { // eg. it is something like "this.fred(1 [cursor]"
// pop empty name completion
if (isEmptyNameCompletion) {
this.expressionPtr--;
this.expressionLengthStack[this.expressionLengthPtr]--;
} else if (isEmptyAssistIdentifier) {
this.identifierPtr--;
this.identifierLengthPtr--;
}
// find receiver and qualifier
int invocationType = this.invocationTypeStack[this.invocationPtr];
int qualifierExprPtr = this.qualifierStack[this.invocationPtr];
// find arguments
int numArgs = this.expressionPtr - qualifierExprPtr;
int argStart = qualifierExprPtr + 1;
Expression[] arguments = null;
if (numArgs > 0) {
// remember the arguments
arguments = new Expression[numArgs];
System.arraycopy(this.expressionStack, argStart, arguments, 0, numArgs);
// consume the expression arguments
this.expressionPtr -= numArgs;
int count = numArgs;
while (count > 0) {
count -= this.expressionLengthStack[this.expressionLengthPtr--];
}
}
// build ast node
if (invocationType != ALLOCATION && invocationType != QUALIFIED_ALLOCATION) {
// creates completion on message send
CompletionOnMessageSend messageSend = new CompletionOnMessageSend();
messageSend.arguments = arguments;
switch (invocationType) {
case NO_RECEIVER:
// implicit this
messageSend.receiver = ThisReference.ThisImplicit;
break;
case NAME_RECEIVER:
// remove special flags for primitive types
while (this.identifierLengthPtr >= 0 && this.identifierLengthStack[this.identifierLengthPtr] < 0) {
this.identifierLengthPtr--;
}
// remove selector
this.identifierPtr--;
this.identifierLengthStack[this.identifierLengthPtr]--;
// consume the receiver
messageSend.receiver = this.getUnspecifiedReference();
break;
case SUPER_RECEIVER:
messageSend.receiver = SuperReference.Super;
break;
case EXPLICIT_RECEIVER:
messageSend.receiver = this.expressionStack[qualifierExprPtr];
}
// set selector
int selectorPtr = this.selectorStack[this.invocationPtr];
messageSend.selector = this.identifierStack[selectorPtr];
// remove selector
if (this.identifierLengthPtr >=0 && this.identifierLengthStack[this.identifierLengthPtr] == 1) {
this.identifierPtr--;
this.identifierLengthPtr--;
}
// the entire message may be replaced in case qualification is needed
messageSend.sourceStart = (int)(this.identifierPositionStack[selectorPtr] >> 32); //this.cursorLocation + 1;
messageSend.sourceEnd = this.cursorLocation;
// remember the message send as an orphan completion node
this.assistNode = messageSend;
this.lastCheckPoint = messageSend.sourceEnd + 1;
this.isOrphanCompletionNode = true;
return true;
} else {
int selectorPtr = this.selectorStack[this.invocationPtr];
if (selectorPtr == THIS_CONSTRUCTOR || selectorPtr == SUPER_CONSTRUCTOR) {
// creates an explicit constructor call
CompletionOnExplicitConstructorCall call = new CompletionOnExplicitConstructorCall(
(selectorPtr == THIS_CONSTRUCTOR) ? ExplicitConstructorCall.This : ExplicitConstructorCall.Super);
call.arguments = arguments;
if (invocationType == QUALIFIED_ALLOCATION) {
call.qualification = this.expressionStack[qualifierExprPtr];
}
// no source is going to be replaced
call.sourceStart = this.cursorLocation + 1;
call.sourceEnd = this.cursorLocation;
// remember the explicit constructor call as an orphan completion node
this.assistNode = call;
this.lastCheckPoint = call.sourceEnd + 1;
this.isOrphanCompletionNode = true;
return true;
} else {
// creates an allocation expression
CompletionOnQualifiedAllocationExpression allocExpr = new CompletionOnQualifiedAllocationExpression();
allocExpr.arguments = arguments;
allocExpr.type = super.getTypeReference(0); // we don't want a completion node here, so call super
if (invocationType == QUALIFIED_ALLOCATION) {
allocExpr.enclosingInstance = this.expressionStack[qualifierExprPtr];
}
// no source is going to be replaced
allocExpr.sourceStart = this.cursorLocation + 1;
allocExpr.sourceEnd = this.cursorLocation;
// remember the allocation expression as an orphan completion node
this.assistNode = allocExpr;
this.lastCheckPoint = allocExpr.sourceEnd + 1;
this.isOrphanCompletionNode = true;
return true;
}
}
}
return false;
}
/**
* Checks if the completion is on a member access (ie. in an identifier following a dot).
* Returns whether we found a completion node.
*/
private boolean checkMemberAccess() {
if (this.previousToken == TokenNameDOT && this.qualifier > -1 && this.expressionPtr == this.qualifier) {
// the receiver is an expression
pushCompletionOnMemberAccessOnExpressionStack(false);
return true;
}
return false;
}
/**
* Checks if the completion is on a name reference.
* Returns whether we found a completion node.
*/
private boolean checkNameCompletion() {
/*
We didn't find any other completion, but the completion identifier is on the identifier stack,
so it can only be a completion on name.
Note that we allow the completion on a name even if nothing is expected (eg. foo() b[cursor] would
be a completion on 'b'). This policy gives more to the user than he/she would expect, but this
simplifies the problem. To fix this, the recovery must be changed to work at a 'statement' granularity
instead of at the 'expression' granularity as it does right now.
*/
// NB: at this point the completion identifier is on the identifier stack
this.assistNode = getUnspecifiedReferenceOptimized();
this.lastCheckPoint = this.assistNode.sourceEnd + 1;
this.isOrphanCompletionNode = true;
return true;
}
/**
* Checks if the completion is in the context of a method and on the type of one of its arguments
* Returns whether we found a completion node.
*/
private boolean checkRecoveredMethod() {
if (currentElement instanceof RecoveredMethod){
/* check if current awaiting identifier is the completion identifier */
if (this.indexOfAssistIdentifier() < 0) return false;
/* check if on line with an error already - to avoid completing inside
illegal type names e.g. int[<cursor> */
if (lastErrorEndPosition <= cursorLocation+1
&& scanner.getLineNumber(lastErrorEndPosition)
== scanner.getLineNumber(((CompletionScanner)scanner).completedIdentifierStart)){
return false;
}
RecoveredMethod recoveredMethod = (RecoveredMethod)currentElement;
/* only consider if inside method header */
if (!recoveredMethod.foundOpeningBrace
&& lastIgnoredToken == -1) {
//if (rParenPos < lParenPos){ // inside arguments
this.assistNode = this.getTypeReference(0);
this.lastCheckPoint = this.assistNode.sourceEnd + 1;
this.isOrphanCompletionNode = true;
return true;
}
}
return false;
}
/**
* Checks if the completion is in the context of a type and on a type reference in this type.
* Persists the identifier into a fake field return type
* Returns whether we found a completion node.
*/
private boolean checkRecoveredType() {
if (currentElement instanceof RecoveredType){
/* check if current awaiting identifier is the completion identifier */
if (this.indexOfAssistIdentifier() < 0) return false;
/* check if on line with an error already - to avoid completing inside
illegal type names e.g. int[<cursor> */
if ((lastErrorEndPosition <= cursorLocation+1)
&& scanner.getLineNumber(lastErrorEndPosition)
== scanner.getLineNumber(((CompletionScanner)scanner).completedIdentifierStart)){
return false;
}
RecoveredType recoveredType = (RecoveredType)currentElement;
/* filter out cases where scanner is still inside type header */
if (recoveredType.foundOpeningBrace) {
this.assistNode = this.getTypeReference(0);
this.lastCheckPoint = this.assistNode.sourceEnd + 1;
this.isOrphanCompletionNode = true;
return true;
}
}
return false;
}
/*
* Check whether about to shift beyond the completion token.
* If so, depending on the context, a special node might need to be created
* and attached to the existing recovered structure so as to be remember in the
* resulting parsed structure.
*/
public void completionIdentifierCheck(){
if (checkRecoveredType()) return;
if (checkRecoveredMethod()) return;
// if not in a method in non diet mode and if not inside a field initializer, only record references attached to types
if (!(this.inMethodStack[this.inMethodPtr] && !this.diet)
&& !insideFieldInitialization()) return;
/*
In some cases, the completion identifier may not have yet been consumed,
e.g. int.[cursor]
This is because the grammar does not allow any (empty) identifier to follow
a base type. We thus have to manually force the identifier to be consumed
(i.e. pushed).
*/
if (assistIdentifier() == null && this.currentToken == TokenNameIdentifier) { // Test below copied from CompletionScanner.getCurrentIdentifierSource()
if (cursorLocation < this.scanner.startPosition && this.scanner.currentPosition == this.scanner.startPosition){ // fake empty identifier got issued
this.pushIdentifier();
} else if (cursorLocation+1 >= this.scanner.startPosition && cursorLocation < this.scanner.currentPosition){
this.pushIdentifier();
}
}
// check for different scenarii
try {
// no need to go further if we found a non empty completion node
// (we still need to store labels though)
if (this.assistNode != null) {
// however inside an invocation, the completion identifier may already have been consumed into an empty name
// completion, so this check should be before we check that we are at the cursor location
if (!isEmptyNameCompletion() || checkInvocation()) return;
}
// no need to check further if we are not at the cursor location
if (this.indexOfAssistIdentifier() < 0) return;
if (checkClassInstanceCreation()) return;
if (checkCatchClause()) return;
if (checkMemberAccess()) return;
if (checkClassLiteralAccess()) return;
// if the completion was not on an empty name, it can still be inside an invocation (eg. this.fred("abc"[cursor])
// (NB: Put this check before checkNameCompletion() because the selector of the invocation can be on the identifier stack)
if (checkInvocation()) return;
if (checkNameCompletion()) return;
} finally {
storeLabelsIfNeeded();
}
}
protected void consumeCaseLabel() {
Expression caseExpression = this.expressionStack[this.expressionPtr];
if (caseExpression instanceof SingleNameReference || caseExpression instanceof QualifiedNameReference) {
// label counter was wrongly incremented in consumeToken
if (this.labelCounterPtr >= 0) this.labelCounterStack[this.labelCounterPtr]--;
}
super.consumeCaseLabel();
}
protected void consumeClassHeaderExtends() {
this.nextTypeReferenceIsClass = true;
super.consumeClassHeaderExtends();
this.nextTypeReferenceIsClass = false;
}
protected void consumeClassTypeElt() {
this.nextTypeReferenceIsException = true;
super.consumeClassTypeElt();
this.nextTypeReferenceIsException = false;
}
protected void consumeConditionalExpression(int op) {
Expression valueIfTrue = this.expressionStack[this.expressionPtr - 1];
if (valueIfTrue instanceof SingleNameReference || valueIfTrue instanceof QualifiedNameReference) {
// label counter was wrongly incremented in consumeToken
if (this.labelCounterPtr >= 0) this.labelCounterStack[this.labelCounterPtr]--;
}
super.consumeConditionalExpression(op);
}
protected void consumeConstructorBody() {
super.consumeConstructorBody();
this.labelCounterPtr--;
if (this.blockInvocationPtr >= 0) this.blockInvocationPtr--;
}
protected void consumeConstructorHeader() {
super.consumeConstructorHeader();
pushBlockInvocationPtr();
}
protected void consumeConstructorHeaderName() {
/* no need to take action if not inside assist identifiers */
if (indexOfAssistIdentifier() < 0) {
super.consumeConstructorHeaderName();
return;
}
/* force to start recovering in order to get fake field behavior */
if (currentElement == null){
this.hasReportedError = true; // do not report any error
}
this.restartRecovery = true;
}
protected void consumeEnterVariable() {
identifierPtr--;
identifierLengthPtr--;
boolean isLocalDeclaration = nestedMethod[nestedType] != 0;
int variableIndex = variablesCounter[nestedType];
int extendedDimension = intStack[intPtr + 1];
if(isLocalDeclaration || indexOfAssistIdentifier() < 0 || variableIndex != 0 || extendedDimension != 0) {
identifierPtr++;
identifierLengthPtr++;
super.consumeEnterVariable();
} else {
restartRecovery = true;
// recovery
if (currentElement != null) {
int nameSourceStart = (int)(identifierPositionStack[identifierPtr] >>> 32);
intPtr--;
TypeReference type = getTypeReference(intStack[intPtr--]);
intPtr--;
if (!(currentElement instanceof RecoveredType)
&& (currentToken == TokenNameDOT
|| (scanner.getLineNumber(type.sourceStart)
!= scanner.getLineNumber(nameSourceStart)))){
lastCheckPoint = nameSourceStart;
restartRecovery = true;
return;
}
FieldDeclaration completionFieldDecl = new CompletionOnFieldType(type, false);
completionFieldDecl.modifiers = intStack[intPtr--];
assistNode = completionFieldDecl;
lastCheckPoint = type.sourceEnd + 1;
currentElement = currentElement.add(completionFieldDecl, 0);
lastIgnoredToken = -1;
}
}
}
protected void consumeExitVariableWithInitialization() {
super.consumeExitVariableWithInitialization();
// does not keep the initialization if completion is not inside
AbstractVariableDeclaration variable = (AbstractVariableDeclaration) astStack[astPtr];
if (cursorLocation + 1 < variable.initialization.sourceStart ||
cursorLocation > variable.initialization.sourceEnd) {
variable.initialization = null;
}
}
/*
* Copy of code from superclass with the following change:
* If the cursor location is on the field access, then create a
* CompletionOnMemberAccess instead.
*/
protected void consumeFieldAccess(boolean isSuperAccess) {
// FieldAccess ::= Primary '.' 'Identifier'
// FieldAccess ::= 'super' '.' 'Identifier'
// potential receiver is being poped, so reset potential receiver
this.invocationType = NO_RECEIVER;
if (this.indexOfAssistIdentifier() < 0) {
super.consumeFieldAccess(isSuperAccess);
} else {
this.pushCompletionOnMemberAccessOnExpressionStack(isSuperAccess);
}
}
protected void consumeFormalParameter() {
if (this.indexOfAssistIdentifier() < 0) {
super.consumeFormalParameter();
} else {
identifierLengthPtr--;
char[] name = identifierStack[identifierPtr];
long namePositions = identifierPositionStack[identifierPtr--];
TypeReference type = getTypeReference(intStack[intPtr--] + intStack[intPtr--]);
intPtr -= 2;
Argument arg =
new CompletionOnArgumentName(
name,
namePositions,
type,
intStack[intPtr + 1] & ~AccDeprecated); // modifiers
pushOnAstStack(arg);
assistNode = arg;
this.lastCheckPoint = (int) namePositions;
isOrphanCompletionNode = true;
/* if incomplete method header, listLength counter will not have been reset,
indicating that some arguments are available on the stack */
listLength++;
}
}
protected void consumeInterfaceType() {
this.nextTypeReferenceIsInterface = true;
super.consumeInterfaceType();
this.nextTypeReferenceIsInterface = false;
}
protected void consumeMethodHeaderName() {
if(this.indexOfAssistIdentifier() < 0) {
identifierPtr--;
identifierLengthPtr--;
if(this.indexOfAssistIdentifier() != 0) {
identifierPtr++;
identifierLengthPtr++;
super.consumeMethodHeaderName();
} else {
restartRecovery = true;
// recovery
if (currentElement != null) {
//name
char[] selector = identifierStack[identifierPtr + 1];
long selectorSource = identifierPositionStack[identifierPtr + 1];
//type
TypeReference type = getTypeReference(intStack[intPtr--]);
((CompletionOnSingleTypeReference)type).isCompletionNode = false;
//modifiers
int declarationSourceStart = intStack[intPtr--];
int modifiers = intStack[intPtr--];
if(scanner.getLineNumber(type.sourceStart) != scanner.getLineNumber((int) (selectorSource >>> 32))) {
FieldDeclaration completionFieldDecl = new CompletionOnFieldType(type, false);
completionFieldDecl.modifiers = modifiers;
assistNode = completionFieldDecl;
lastCheckPoint = type.sourceEnd + 1;
currentElement = currentElement.add(completionFieldDecl, 0);
lastIgnoredToken = -1;
} else {
CompletionOnMethodReturnType md = new CompletionOnMethodReturnType(type, this.compilationUnit.compilationResult);
md.selector = selector;
md.declarationSourceStart = declarationSourceStart;
md.modifiers = modifiers;
md.bodyStart = lParenPos+1;
listLength = 0; // initialize listLength before reading parameters/throws
assistNode = md;
this.lastCheckPoint = md.bodyStart;
currentElement = currentElement.add(md, 0);
lastIgnoredToken = -1;
}
}
}
} else {
// MethodHeaderName ::= Modifiersopt Type 'Identifier' '('
CompletionOnMethodName md = new CompletionOnMethodName(this.compilationUnit.compilationResult);
//name
md.selector = identifierStack[identifierPtr];
long selectorSource = identifierPositionStack[identifierPtr--];
//type
md.returnType = getTypeReference(intStack[intPtr--]);
//modifiers
md.declarationSourceStart = intStack[intPtr--];
md.modifiers = intStack[intPtr--];
//highlight starts at selector start
md.sourceStart = (int) (selectorSource >>> 32);
md.selectorEnd = (int) selectorSource;
pushOnAstStack(md);
md.sourceEnd = lParenPos;
md.bodyStart = lParenPos+1;
listLength = 0; // initialize listLength before reading parameters/throws
this.assistNode = md;
this.lastCheckPoint = md.sourceEnd;
// recovery
if (currentElement != null){
if (currentElement instanceof RecoveredType
//|| md.modifiers != 0
|| (scanner.getLineNumber(md.returnType.sourceStart)
== scanner.getLineNumber(md.sourceStart))){
lastCheckPoint = md.bodyStart;
currentElement = currentElement.add(md, 0);
lastIgnoredToken = -1;
} else {
lastCheckPoint = md.sourceStart;
restartRecovery = true;
}
}
}
}
protected void consumeMethodBody() {
super.consumeMethodBody();
this.labelCounterPtr--;
if (this.blockInvocationPtr >= 0) this.blockInvocationPtr--;
}
protected void consumeMethodHeader() {
super.consumeMethodHeader();
pushBlockInvocationPtr();
}
protected void consumeModifiers() {
super.consumeModifiers();
// save from stack values
this.lastModifiersStart = intStack[intPtr];
this.lastModifiers = intStack[intPtr-1];
}
protected void consumeNestedMethod() {
super.consumeNestedMethod();
this.pushNewLabelCounter();
}
protected void consumeStatementLabel() {
super.consumeStatementLabel();
if (this.labelCounterPtr >= 0) this.labelCounterStack[this.labelCounterPtr]--;
}
protected void consumeToken(int token) {
int previous = this.previousToken;
int previousIdentifierPtr = this.previousIdentifierPtr;
super.consumeToken(token);
// if in field initializer (directly or not), on the completion identifier and not in recovery mode yet
// then position end of file at cursor location (so that we have the same behavior as
// in method bodies)
if (token == TokenNameIdentifier
&& this.identifierStack[this.identifierPtr] == assistIdentifier()
&& this.currentElement == null
&& this.insideFieldInitialization()) {
this.scanner.eofPosition = cursorLocation < Integer.MAX_VALUE ? cursorLocation+1 : cursorLocation;
}
// if in a method or if in a field initializer
if (this.inMethodStack[this.inMethodPtr] || this.inFieldInitializationStack[this.inFieldInitializationPtr]) {
switch (token) {
case TokenNameDOT:
switch (previous) {
case TokenNamethis: // eg. this[.]fred()
this.invocationType = EXPLICIT_RECEIVER;
break;
case TokenNamesuper: // eg. super[.]fred()
this.invocationType = SUPER_RECEIVER;
break;
case TokenNameIdentifier: // eg. bar[.]fred()
if (!this.betweenNewAndLeftBraket) { // eg. not new z.y[.]X()
if (this.identifierPtr != previousIdentifierPtr) { // if identifier has been consumed, eg. this.x[.]fred()
this.invocationType = EXPLICIT_RECEIVER;
} else {
this.invocationType = NAME_RECEIVER;
}
}
break;
}
break;
case TokenNameIdentifier:
if (previous == TokenNameDOT) { // eg. foo().[fred]()
// if current identifier is the empty completion one
if (identifierStack[identifierPtr] == CompletionScanner.EmptyCompletionIdentifier){
this.completionBehindDot = true;
}
if (this.invocationType != SUPER_RECEIVER // eg. not super.[fred]()
&& this.invocationType != NAME_RECEIVER // eg. not bar.[fred]()
&& this.invocationType != ALLOCATION // eg. not new foo.[Bar]()
&& this.invocationType != QUALIFIED_ALLOCATION) { // eg. not fred().new foo.[Bar]()
this.invocationType = EXPLICIT_RECEIVER;
this.qualifier = this.expressionPtr;
}
}
break;
case TokenNamenew:
this.betweenNewAndLeftBraket = true;
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
if (previous == TokenNameDOT) { // eg. fred().[new] X()
this.invocationType = QUALIFIED_ALLOCATION;
} else { // eg. [new] X()
this.invocationType = ALLOCATION;
}
break;
case TokenNamethis:
if (previous == TokenNameDOT) { // eg. fred().[this]()
this.invocationType = QUALIFIED_ALLOCATION;
this.qualifier = this.expressionPtr;
}
break;
case TokenNamesuper:
if (previous == TokenNameDOT) { // eg. fred().[super]()
this.invocationType = QUALIFIED_ALLOCATION;
this.qualifier = this.expressionPtr;
}
break;
case TokenNamecatch:
this.betweenCatchAndRightParen = true;
break;
case TokenNameLPAREN:
this.betweenNewAndLeftBraket = false;
this.bracketDepth++;
if (this.invocationType == NO_RECEIVER || this.invocationType == NAME_RECEIVER) {
this.qualifier = this.expressionPtr; // remenber the last expression so that arguments are correctly computed
}
switch (previous) {
case TokenNameIdentifier: // eg. fred[(]) or foo.fred[(])
this.pushOnInvocationStacks(this.invocationType, this.qualifier);
this.invocationType = NO_RECEIVER;
break;
case TokenNamethis: // explicit constructor invocation, eg. this[(]1, 2)
this.pushOnInvocationStacks(
(this.invocationType == QUALIFIED_ALLOCATION) ? QUALIFIED_ALLOCATION : ALLOCATION,
this.qualifier);
this.invocationType = NO_RECEIVER;
break;
case TokenNamesuper: // explicit constructor invocation, eg. super[(]1, 2)
this.pushOnInvocationStacks(
(this.invocationType == QUALIFIED_ALLOCATION) ? QUALIFIED_ALLOCATION : ALLOCATION,
this.qualifier);
this.invocationType = NO_RECEIVER;
break;
}
break;
case TokenNameLBRACE:
this.betweenNewAndLeftBraket = false;
this.bracketDepth++;
this.pushBlockInvocationPtr();
break;
case TokenNameLBRACKET:
this.betweenNewAndLeftBraket = false;
this.bracketDepth++;
break;
case TokenNameRBRACE:
this.bracketDepth--;
if (this.blockInvocationPtr >= 0) this.blockInvocationPtr--;
break;
case TokenNameRBRACKET:
this.bracketDepth--;
break;
case TokenNameRPAREN:
this.betweenCatchAndRightParen = false;
this.bracketDepth--;
break;
case TokenNameCOLON:
if (previous == TokenNameIdentifier) {
if (this.labelCounterPtr >= 0) this.labelCounterStack[this.labelCounterPtr]++;
}
break;
case TokenNamethrow:
this.throwBracketDepth= bracketDepth;
break;
}
}
}
/**
* Return whether the given ast node contains the completion node.
*/
private boolean containsCompletionNode(AstNode ast) {
if (this.assistNode == null || ast instanceof Literal) {
return false;
}
if (this.assistNode == ast) {
return true;
}
if (ast instanceof Reference || ast instanceof TypeReference) {
return ast == this.assistNode;
}
if (ast instanceof Assignment) {
Assignment assign = (Assignment)ast;
return containsCompletionNode(assign.lhs) || containsCompletionNode(assign.expression);
}
if (ast instanceof UnaryExpression) {
UnaryExpression unary = (UnaryExpression)ast;
return containsCompletionNode(unary.expression);
}
if (ast instanceof BinaryExpression) {
BinaryExpression binary = (BinaryExpression)ast;
return containsCompletionNode(binary.left) || containsCompletionNode(binary.right);
}
if (ast instanceof InstanceOfExpression) {
InstanceOfExpression instanceOfExpr = (InstanceOfExpression)ast;
return containsCompletionNode(instanceOfExpr.expression) || containsCompletionNode(instanceOfExpr.type);
}
if (ast instanceof ConditionalExpression) {
ConditionalExpression conditional = (ConditionalExpression)ast;
return containsCompletionNode(conditional.condition) || containsCompletionNode(conditional.valueIfTrue) || containsCompletionNode(conditional.valueIfFalse);
}
if (ast instanceof AllocationExpression) {
AllocationExpression alloc = (AllocationExpression)ast;
return containsCompletionNode(alloc.type);
}
if (ast instanceof CastExpression) {
CastExpression cast = (CastExpression)ast;
return containsCompletionNode(cast.expression) || containsCompletionNode(cast.type);
}
if (ast instanceof ExplicitConstructorCall) {
ExplicitConstructorCall call = (ExplicitConstructorCall)ast;
Expression[] arguments = call.arguments;
if (arguments != null) {
for (int i = 0; i < arguments.length; i++) {
if (containsCompletionNode(arguments[i])) {
return true;
}
}
return false;
}
}
return false;
}
public ImportReference createAssistImportReference(char[][] tokens, long[] positions){
return new CompletionOnImportReference(tokens, positions);
}
public ImportReference createAssistPackageReference(char[][] tokens, long[] positions){
return new CompletionOnPackageReference(tokens, positions);
}
public NameReference createQualifiedAssistNameReference(char[][] previousIdentifiers, char[] name, long[] positions){
return new CompletionOnQualifiedNameReference(
previousIdentifiers,
name,
positions);
}
public TypeReference createQualifiedAssistTypeReference(char[][] previousIdentifiers, char[] name, long[] positions){
return this.betweenCatchAndRightParen || this.nextTypeReferenceIsException // check for exception scenario
? new CompletionOnQualifiedExceptionReference(
previousIdentifiers,
name,
positions)
: this.nextTypeReferenceIsInterface
? new CompletionOnQualifiedInterfaceReference(
previousIdentifiers,
name,
positions)
: this.nextTypeReferenceIsClass
? new CompletionOnQualifiedClassReference(
previousIdentifiers,
name,
positions)
: new CompletionOnQualifiedTypeReference(
previousIdentifiers,
name,
positions);
}
public NameReference createSingleAssistNameReference(char[] name, long position) {
return new CompletionOnSingleNameReference(name, position);
}
public TypeReference createSingleAssistTypeReference(char[] name, long position) {
return this.betweenCatchAndRightParen || this.nextTypeReferenceIsException // check for exception scenario
? new CompletionOnExceptionReference(name, position)
: this.nextTypeReferenceIsInterface
? new CompletionOnInterfaceReference(name, position)
: this.nextTypeReferenceIsClass
? new CompletionOnClassReference(name, position)
: new CompletionOnSingleTypeReference(name, position);
}
public CompilationUnitDeclaration dietParse(ICompilationUnit sourceUnit, CompilationResult compilationResult, int cursorLocation) {
this.cursorLocation = cursorLocation;
CompletionScanner completionScanner = (CompletionScanner)this.scanner;
completionScanner.completionIdentifier = null;
completionScanner.cursorLocation = cursorLocation;
return this.dietParse(sourceUnit, compilationResult);
}
/*
* Flush parser/scanner state regarding to code assist
*/
public void flushAssistState() {
super.flushAssistState();
this.isOrphanCompletionNode = false;
CompletionScanner completionScanner = (CompletionScanner)this.scanner;
completionScanner.completedIdentifierStart = 0;
completionScanner.completedIdentifierEnd = -1;
}
protected NameReference getUnspecifiedReferenceOptimized() {
if (this.identifierLengthStack[this.identifierLengthPtr] > 1) { // reducing a qualified name
// potential receiver is being poped, so reset potential receiver
this.invocationType = NO_RECEIVER;
}
return super.getUnspecifiedReferenceOptimized();
}
/**
* Return whether the given ast node has information interresting for code completion.
*/
private boolean hasCompletionInformation(AstNode ast) {
return (
ast instanceof AbstractMethodDeclaration ||
ast instanceof AbstractVariableDeclaration ||
ast instanceof LabeledStatement ||
ast instanceof TypeDeclaration);
}
public void initialize() {
super.initialize();
this.initializeForBlockStatements();
this.labelCounterPtr = -1;
}
/*
* Initializes the state of the parser that is about to go for BlockStatements.
*/
private void initializeForBlockStatements() {
this.previousToken = -1;
this.previousIdentifierPtr = -1;
this.completionBehindDot = false;
this.betweenNewAndLeftBraket = false;
this.betweenCatchAndRightParen = false;
this.bracketDepth = 0;
this.throwBracketDepth = -1;
this.invocationType = NO_RECEIVER;
this.qualifier = -1;
this.blockInvocationPtr = -1;
}
public void initializeScanner(){
this.scanner = new CompletionScanner(this.assertMode);
}
/**
* Returns whether the completion is just after an array type
* eg. String[].[cursor]
*/
private boolean isAfterArrayType() {
// TBD: The following relies on the fact that array dimensions are small: it says that if the
// top of the intStack is less than 11, then it must be a dimension
// (smallest position of array type in a compilation unit is 11 as in "class X{Y[]")
if ((this.intPtr > -1) && (this.intStack[this.intPtr] < 11)) {
return true;
}
return false;
}
private boolean isEmptyNameCompletion() {
return
this.assistNode != null &&
this.assistNode instanceof CompletionOnSingleNameReference &&
(((CompletionOnSingleNameReference)this.assistNode).token.length == 0);
}
public CompilationUnitDeclaration parse(ICompilationUnit sourceUnit, CompilationResult compilationResult, int cursorLocation) {
this.cursorLocation = cursorLocation;
CompletionScanner completionScanner = (CompletionScanner)this.scanner;
completionScanner.completionIdentifier = null;
completionScanner.cursorLocation = cursorLocation;
return this.parse(sourceUnit, compilationResult);
}
/*
* Prepares the state of the parser to go for BlockStatements.
*/
protected void prepareForBlockStatements() {
super.prepareForBlockStatements();
this.initializeForBlockStatements();
}
protected void pushBlockInvocationPtr() {
try {
this.blockInvocationStack[++this.blockInvocationPtr] = this.invocationPtr+1;
} catch (IndexOutOfBoundsException e) {
int oldStackLength = this.blockInvocationStack.length;
int[] oldStack = this.blockInvocationStack;
this.blockInvocationStack = new int[oldStackLength + StackIncrement];
System.arraycopy(oldStack, 0, this.blockInvocationStack, 0, oldStackLength);
this.blockInvocationStack[this.blockInvocationPtr] = this.invocationPtr+1;
}
}
/**
* Creates a completion on member access node and push it
* on the expression stack.
*/
private void pushCompletionOnMemberAccessOnExpressionStack(boolean isSuperAccess) {
char[] source = identifierStack[identifierPtr];
long pos = identifierPositionStack[identifierPtr--];
CompletionOnMemberAccess fr = new CompletionOnMemberAccess(source, pos);
this.assistNode = fr;
this.lastCheckPoint = fr.sourceEnd + 1;
identifierLengthPtr--;
if (isSuperAccess) { //considerates the fieldReference beginning at the 'super' ....
fr.sourceStart = intStack[intPtr--];
fr.receiver = new SuperReference(fr.sourceStart, endPosition);
pushOnExpressionStack(fr);
} else { //optimize push/pop
if ((fr.receiver = expressionStack[expressionPtr]).isThis()) { //fieldreference begins at the this
fr.sourceStart = fr.receiver.sourceStart;
}
expressionStack[expressionPtr] = fr;
}
}
protected void pushNewLabelCounter() {
try {
this.labelCounterStack[++this.labelCounterPtr] = 0;
} catch (IndexOutOfBoundsException e) {
int oldStackLength = this.labelCounterStack.length;
int[] oldStack = this.labelCounterStack;
this.labelCounterStack = new int[oldStackLength + StackIncrement];
System.arraycopy(oldStack, 0, this.labelCounterStack, 0, oldStackLength);
this.labelCounterStack[this.labelCounterPtr] = 0;
}
}
/**
* Pushes the given invocation type (one of the invocation type constants) on the invocation type stack,
* and the given qualifier (an expression pointer to the expression stack) on the qualifier stack.
*/
protected void pushOnInvocationStacks(int invocationType, int qualifierExprPtr) {
// NB: invocationPtr has already been incremented by a call to pushOnSelectorStack()
try {
this.invocationTypeStack[this.invocationPtr] = invocationType;
this.qualifierStack[this.invocationPtr] = qualifierExprPtr;
} catch (IndexOutOfBoundsException e) {
int oldStackLength = this.invocationTypeStack.length;
int oldInvocationTypeStack[] = this.invocationTypeStack;
int oldQualifierStack[] = this.qualifierStack;
this.invocationTypeStack = new int[oldStackLength + StackIncrement];
this.qualifierStack = new int[oldStackLength + StackIncrement];
System.arraycopy(oldInvocationTypeStack, 0, this.invocationTypeStack, 0, oldStackLength);
System.arraycopy(oldQualifierStack, 0, this.qualifierStack, 0, oldStackLength);
this.invocationTypeStack[this.invocationPtr] = invocationType;
this.qualifierStack[this.invocationPtr] = qualifierExprPtr;
}
}
public void recordCompletionOnReference(){
if (currentElement instanceof RecoveredType){
RecoveredType recoveredType = (RecoveredType)currentElement;
/* filter out cases where scanner is still inside type header */
if (!recoveredType.foundOpeningBrace) return;
/* generate a pseudo field with a completion on type reference */
currentElement.add(
new CompletionOnFieldType(this.getTypeReference(0), false), 0);
return;
}
if (!diet) return; // only record references attached to types
}
protected void reportSyntaxError(int act, int currentKind, int stateStackTop) {
/* Intercept error state on EOF inside method bodies, due to
cursor location being used as an EOF position.
*/
if (!diet && currentToken == TokenNameEOF) return;
super.reportSyntaxError(act, currentKind, stateStackTop);
}
/*
* Reset internal state after completion is over
*/
public void reset() {
super.reset();
this.cursorLocation = 0;
}
/*
* Reset internal state after completion is over
*/
public void resetAfterCompletion() {
this.cursorLocation = 0;
this.flushAssistState();
}
/*
* Reset context so as to resume to regular parse loop
* If unable to reset for resuming, answers false.
*
* Move checkpoint location, reset internal stacks and
* decide which grammar goal is activated.
*/
protected boolean resumeAfterRecovery() {
if (this.assistNode != null) {
/* if reached [eof] inside method body, but still inside nested type,
or inside a field initializer, should continue in diet mode until
the end of the method body or compilation unit */
if ((scanner.eofPosition == cursorLocation+1)
&& (!(referenceContext instanceof CompilationUnitDeclaration)
|| insideFieldInitialization())) {
/* disabled since does not handle possible field/message refs, i.e. Obj[ASSIST HERE]ect.registerNatives()
// consume extra tokens which were part of the qualified reference
// so that the replaced source comprises them as well
if (this.assistNode instanceof NameReference){
int oldEof = scanner.eofPosition;
scanner.eofPosition = currentElement.topElement().sourceEnd()+1;
scanner.currentPosition = this.cursorLocation+1;
int token = -1;
try {
do {
// first token might not have to be a dot
if (token >= 0 || !this.completionBehindDot){
if ((token = scanner.getNextToken()) != TokenNameDOT) break;
}
if ((token = scanner.getNextToken()) != TokenNameIdentifier) break;
this.assistNode.sourceEnd = scanner.currentPosition - 1;
} while (token != TokenNameEOF);
} catch (InvalidInputException e){
} finally {
scanner.eofPosition = oldEof;
}
}
*/
/* restart in diet mode for finding sibling constructs */
if (currentElement.enclosingType() != null){
lastCheckPoint = this.assistNode.sourceEnd+1;
int end = currentElement.topElement().sourceEnd();
scanner.eofPosition = end < Integer.MAX_VALUE ? end + 1 : end;
} else {
this.resetStacks();
return false;
}
}
}
return super.resumeAfterRecovery();
}
public void setAssistIdentifier(char[] assistIdent){
((CompletionScanner)scanner).completionIdentifier = assistIdent;
}
/**
* Stores the labels left on the identifier stack if they have not been stored yet.
*/
private void storeLabelsIfNeeded() {
// int counter = this.labelCounterPtr >= 0 ? this.labelCounterStack[this.labelCounterPtr] : 0;
// if (this.labels == null && this.identifierPtr >= 0) {
// this.labels = new char[counter][];
// System.arraycopy(this.identifierStack, this.identifierPtr - counter + 1, this.labels, 0, counter);
// }
// this.identifierPtr -= counter;
// this.identifierLengthPtr -= counter; // labels have not been concatenated yet
}
/*
* Update recovery state based on current parser/scanner state
*/
protected void updateRecoveryState() {
/* expose parser state to recovery state */
currentElement.updateFromParserState();
/* may be able to retrieve completionNode as an orphan, and then attach it */
this.completionIdentifierCheck();
this.attachOrphanCompletionNode();
// if an assist node has been found and a recovered element exists,
// mark enclosing blocks as to be preserved
if (this.assistNode != null && this.currentElement != null) {
currentElement.preserveEnclosingBlocks();
}
/* check and update recovered state based on current token,
this action is also performed when shifting token after recovery
got activated once.
*/
this.recoveryTokenCheck();
}
protected LocalDeclaration createLocalDeclaration(Expression initialization, char[] name, int sourceStart, int sourceEnd) {
if (this.indexOfAssistIdentifier() < 0) {
return super.createLocalDeclaration(initialization, name, sourceStart, sourceEnd);
} else {
CompletionOnLocalName local = new CompletionOnLocalName(initialization, name, sourceStart, sourceEnd);
this.assistNode = local;
this.lastCheckPoint = sourceEnd + 1;
return local;
}
}
protected FieldDeclaration createFieldDeclaration(Expression initialization, char[] name, int sourceStart, int sourceEnd) {
if (this.indexOfAssistIdentifier() < 0) {
return super.createFieldDeclaration(initialization, name, sourceStart, sourceEnd);
} else {
CompletionOnFieldName field = new CompletionOnFieldName(initialization, name, sourceStart, sourceEnd);
this.assistNode = field;
this.lastCheckPoint = sourceEnd + 1;
return field;
}
}
}