/******************************************************************************* * Copyright (c) 2000, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.codeassist.complete; /* * 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 java.util.HashSet; import org.eclipse.jdt.internal.compiler.*; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; 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.compiler.util.HashtableOfObjectToInt; import org.eclipse.jdt.internal.compiler.util.Util; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.codeassist.impl.*; public class CompletionParser extends AssistParser { // OWNER protected static final int COMPLETION_PARSER = 1024; protected static final int COMPLETION_OR_ASSIST_PARSER = ASSIST_PARSER + COMPLETION_PARSER; // KIND : all values known by CompletionParser are between 1025 and 1549 protected static final int K_BLOCK_DELIMITER = COMPLETION_PARSER + 1; // whether we are inside a block protected static final int K_SELECTOR_INVOCATION_TYPE = COMPLETION_PARSER + 2; // whether we are inside a message send protected static final int K_SELECTOR_QUALIFIER = COMPLETION_PARSER + 3; // whether we are inside a message send protected static final int K_BETWEEN_CATCH_AND_RIGHT_PAREN = COMPLETION_PARSER + 4; // whether we are between the keyword 'catch' and the following ')' protected static final int K_NEXT_TYPEREF_IS_CLASS = COMPLETION_PARSER + 5; // whether the next type reference is a class protected static final int K_NEXT_TYPEREF_IS_INTERFACE = COMPLETION_PARSER + 6; // whether the next type reference is an interface protected static final int K_NEXT_TYPEREF_IS_EXCEPTION = COMPLETION_PARSER + 7; // whether the next type reference is an exception protected static final int K_BETWEEN_NEW_AND_LEFT_BRACKET = COMPLETION_PARSER + 8; // whether we are between the keyword 'new' and the following left braket, i.e. '[', '(' or '{' protected static final int K_INSIDE_THROW_STATEMENT = COMPLETION_PARSER + 9; // whether we are between the keyword 'throw' and the end of a throw statement protected static final int K_INSIDE_RETURN_STATEMENT = COMPLETION_PARSER + 10; // whether we are between the keyword 'return' and the end of a return statement protected static final int K_CAST_STATEMENT = COMPLETION_PARSER + 11; // whether we are between ')' and the end of a cast statement protected static final int K_LOCAL_INITIALIZER_DELIMITER = COMPLETION_PARSER + 12; protected static final int K_ARRAY_INITIALIZER = COMPLETION_PARSER + 13; protected static final int K_ARRAY_CREATION = COMPLETION_PARSER + 14; protected static final int K_UNARY_OPERATOR = COMPLETION_PARSER + 15; protected static final int K_BINARY_OPERATOR = COMPLETION_PARSER + 16; protected static final int K_ASSISGNMENT_OPERATOR = COMPLETION_PARSER + 17; protected static final int K_CONDITIONAL_OPERATOR = COMPLETION_PARSER + 18; protected static final int K_BETWEEN_IF_AND_RIGHT_PAREN = COMPLETION_PARSER + 19; protected static final int K_BETWEEN_WHILE_AND_RIGHT_PAREN = COMPLETION_PARSER + 20; protected static final int K_BETWEEN_FOR_AND_RIGHT_PAREN = COMPLETION_PARSER + 21; protected static final int K_BETWEEN_SWITCH_AND_RIGHT_PAREN = COMPLETION_PARSER + 22; protected static final int K_BETWEEN_SYNCHRONIZED_AND_RIGHT_PAREN = COMPLETION_PARSER + 23; protected static final int K_INSIDE_ASSERT_STATEMENT = COMPLETION_PARSER + 24; protected static final int K_SWITCH_LABEL= COMPLETION_PARSER + 25; protected static final int K_BETWEEN_CASE_AND_COLON = COMPLETION_PARSER + 26; protected static final int K_BETWEEN_DEFAULT_AND_COLON = COMPLETION_PARSER + 27; protected static final int K_BETWEEN_LEFT_AND_RIGHT_BRACKET = COMPLETION_PARSER + 28; protected static final int K_EXTENDS_KEYWORD = COMPLETION_PARSER + 29; protected static final int K_PARAMETERIZED_METHOD_INVOCATION = COMPLETION_PARSER + 30; protected static final int K_PARAMETERIZED_ALLOCATION = COMPLETION_PARSER + 31; protected static final int K_PARAMETERIZED_CAST = COMPLETION_PARSER + 32; protected static final int K_BETWEEN_ANNOTATION_NAME_AND_RPAREN = COMPLETION_PARSER + 33; protected static final int K_INSIDE_BREAK_STATEMENT = COMPLETION_PARSER + 34; protected static final int K_INSIDE_CONTINUE_STATEMENT = COMPLETION_PARSER + 35; protected static final int K_LABEL = COMPLETION_PARSER + 36; protected static final int K_MEMBER_VALUE_ARRAY_INITIALIZER = COMPLETION_PARSER + 37; protected static final int K_CONTROL_STATEMENT_DELIMITER = COMPLETION_PARSER + 38; protected static final int K_INSIDE_ASSERT_EXCEPTION = COMPLETION_PARSER + 39; protected static final int K_INSIDE_FOR_CONDITIONAL = COMPLETION_PARSER + 40; // added for https://bugs.eclipse.org/bugs/show_bug.cgi?id=261534 protected static final int K_BETWEEN_INSTANCEOF_AND_RPAREN = COMPLETION_PARSER + 41; public final static char[] FAKE_TYPE_NAME = new char[]{' '}; public final static char[] FAKE_METHOD_NAME = new char[]{' '}; public final static char[] FAKE_ARGUMENT_NAME = new char[]{' '}; public final static char[] VALUE = new char[]{'v', 'a', 'l', 'u', 'e'}; /* public fields */ public int cursorLocation; public ASTNode assistNodeParent; // the parent node of assist node public ASTNode enclosingNode; // an enclosing node used by proposals inference /* the following fields are internal flags */ // block kind static final int IF = 1; static final int TRY = 2; static final int CATCH = 3; static final int WHILE = 4; static final int SWITCH = 5; static final int FOR = 6; static final int DO = 7; static final int SYNCHRONIZED = 8; // label kind static final int DEFAULT = 1; // 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; static final int QUESTION = 1; static final int COLON = 2; // K_BETWEEN_ANNOTATION_NAME_AND_RPAREN arguments static final int LPAREN_NOT_CONSUMED = 1; static final int LPAREN_CONSUMED = 2; static final int ANNOTATION_NAME_COMPLETION = 4; // K_PARAMETERIZED_METHOD_INVOCATION arguments static final int INSIDE_NAME = 1; // 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; // used to find if there is unused modifiers when building completion inside a method or an initializer boolean hasUnusedModifiers; // show if the current token can be an explicit constructor int canBeExplicitConstructor = NO; static final int NO = 0; static final int NEXTTOKEN = 1; static final int YES = 2; protected static final int LabelStackIncrement = 10; char[][] labelStack = new char[LabelStackIncrement][]; int labelPtr = -1; boolean isAlreadyAttached; public boolean record = false; public boolean skipRecord = false; public int recordFrom; public int recordTo; public int potentialVariableNamesPtr; public char[][] potentialVariableNames; public int[] potentialVariableNameStarts; public int[] potentialVariableNameEnds; CompletionOnAnnotationOfType pendingAnnotation; private boolean storeSourceEnds; public HashtableOfObjectToInt sourceEnds; public CompletionParser(ProblemReporter problemReporter, boolean storeExtraSourceEnds) { super(problemReporter); this.reportSyntaxErrorIsRequired = false; this.javadocParser.checkDocComment = true; this.annotationRecoveryActivated = false; if (storeExtraSourceEnds) { this.storeSourceEnds = true; this.sourceEnds = new HashtableOfObjectToInt(); } } private void addPotentialName(char[] potentialVariableName, int start, int end) { int length = this.potentialVariableNames.length; if (this.potentialVariableNamesPtr >= length - 1) { System.arraycopy( this.potentialVariableNames, 0, this.potentialVariableNames = new char[length * 2][], 0, length); System.arraycopy( this.potentialVariableNameStarts, 0, this.potentialVariableNameStarts = new int[length * 2], 0, length); System.arraycopy( this.potentialVariableNameEnds, 0, this.potentialVariableNameEnds = new int[length * 2], 0, length); } this.potentialVariableNames[++this.potentialVariableNamesPtr] = potentialVariableName; this.potentialVariableNameStarts[this.potentialVariableNamesPtr] = start; this.potentialVariableNameEnds[this.potentialVariableNamesPtr] = end; } public void startRecordingIdentifiers(int from, int to) { this.record = true; this.skipRecord = false; this.recordFrom = from; this.recordTo = to; this.potentialVariableNamesPtr = -1; this.potentialVariableNames = new char[10][]; this.potentialVariableNameStarts = new int[10]; this.potentialVariableNameEnds = new int[10]; } public void stopRecordingIdentifiers() { this.record = true; this.skipRecord = false; } public char[] assistIdentifier(){ return ((CompletionScanner)this.scanner).completionIdentifier; } protected void attachOrphanCompletionNode(){ if(this.assistNode == null || this.isAlreadyAttached) return; this.isAlreadyAttached = true; if (this.isOrphanCompletionNode) { ASTNode orphan = this.assistNode; this.isOrphanCompletionNode = false; if (this.currentElement instanceof RecoveredUnit){ if (orphan instanceof ImportReference){ this.currentElement.add((ImportReference)orphan, 0); } } /* if in context of a type, then persists the identifier into a fake field return type */ if (this.currentElement instanceof RecoveredType){ RecoveredType recoveredType = (RecoveredType)this.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){ TypeReference fieldType; int kind = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER); int info = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER); if(kind == K_BINARY_OPERATOR && info == LESS && this.identifierPtr > -1) { if(this.genericsLengthStack[this.genericsLengthPtr] > 0) { consumeTypeArguments(); } pushOnGenericsStack(orphan); consumeTypeArguments(); fieldType = getTypeReference(0); this.assistNodeParent = fieldType; } else { fieldType = (TypeReference)orphan; } CompletionOnFieldType fieldDeclaration = new CompletionOnFieldType(fieldType, false); // retrieve annotations if any int length; if ((length = this.expressionLengthStack[this.expressionLengthPtr]) != 0 && this.expressionStack[this.expressionPtr] instanceof Annotation) { System.arraycopy( this.expressionStack, this.expressionPtr - length + 1, fieldDeclaration.annotations = new Annotation[length], 0, length); } // retrieve available modifiers if any if (this.intPtr >= 2 && this.intStack[this.intPtr-1] == this.lastModifiersStart && this.intStack[this.intPtr-2] == this.lastModifiers){ fieldDeclaration.modifiersSourceStart = this.intStack[this.intPtr-1]; fieldDeclaration.modifiers = this.intStack[this.intPtr-2]; } this.currentElement = this.currentElement.add(fieldDeclaration, 0); return; } } } /* if in context of a method, persists if inside arguments as a type */ if (this.currentElement instanceof RecoveredMethod){ RecoveredMethod recoveredMethod = (RecoveredMethod)this.currentElement; /* only consider if inside method header */ if (!recoveredMethod.foundOpeningBrace) { //if (rParenPos < lParenPos){ // inside arguments if (orphan instanceof TypeReference){ this.currentElement = this.currentElement.parent.add( new CompletionOnFieldType((TypeReference)orphan, true), 0); return; } if(orphan instanceof Annotation) { CompletionOnAnnotationOfType fakeType = new CompletionOnAnnotationOfType( FAKE_TYPE_NAME, this.compilationUnit.compilationResult(), (Annotation)orphan); fakeType.isParameter = true; this.currentElement.parent.add(fakeType, 0); this.pendingAnnotation = fakeType; return; } } } if(orphan instanceof MemberValuePair) { buildMoreAnnotationCompletionContext((MemberValuePair) orphan); return; } if(orphan instanceof Annotation) { popUntilCompletedAnnotationIfNecessary(); CompletionOnAnnotationOfType fakeType = new CompletionOnAnnotationOfType( FAKE_TYPE_NAME, this.compilationUnit.compilationResult(), (Annotation)orphan); this.currentElement.add(fakeType, 0); if (!isInsideAnnotation()) { this.pendingAnnotation = fakeType; } return; } if ((topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BETWEEN_CATCH_AND_RIGHT_PAREN)) { if (this.assistNode instanceof CompletionOnSingleTypeReference && ((CompletionOnSingleTypeReference)this.assistNode).isException()) { buildMoreTryStatementCompletionContext((TypeReference)this.assistNode); return; } else if (this.assistNode instanceof CompletionOnQualifiedTypeReference && ((CompletionOnQualifiedTypeReference)this.assistNode).isException()) { buildMoreTryStatementCompletionContext((TypeReference)this.assistNode); return; } else if (this.assistNode instanceof CompletionOnParameterizedQualifiedTypeReference && ((CompletionOnParameterizedQualifiedTypeReference)this.assistNode).isException()) { buildMoreTryStatementCompletionContext((TypeReference)this.assistNode); 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 = this.currentElement.enclosingMethod(); if (method != null){ AbstractMethodDeclaration methodDecl = method.methodDeclaration; if ((methodDecl.bodyStart == methodDecl.sourceEnd+1) // was missing opening brace && (Util.getLineNumber(orphan.sourceStart, this.scanner.lineEnds, 0, this.scanner.linePtr) == Util.getLineNumber(methodDecl.sourceEnd, this.scanner.lineEnds, 0, this.scanner.linePtr))){ return; } } // add the completion node as a statement to the list of block statements this.currentElement = this.currentElement.add((Statement)orphan, 0); return; } } if (isInsideAnnotation()) { // push top expression on ast stack if it contains the completion node Expression expression; if (this.expressionPtr > -1) { expression = this.expressionStack[this.expressionPtr]; if(expression == this.assistNode) { if (this.topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_MEMBER_VALUE_ARRAY_INITIALIZER ) { ArrayInitializer arrayInitializer = new ArrayInitializer(); arrayInitializer.expressions = new Expression[]{expression}; MemberValuePair valuePair = new MemberValuePair(VALUE, expression.sourceStart, expression.sourceEnd, arrayInitializer); buildMoreAnnotationCompletionContext(valuePair); } else if(this.topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BETWEEN_ANNOTATION_NAME_AND_RPAREN) { if (expression instanceof SingleNameReference) { SingleNameReference nameReference = (SingleNameReference) expression; CompletionOnMemberValueName memberValueName = new CompletionOnMemberValueName(nameReference.token, nameReference.sourceStart, nameReference.sourceEnd); buildMoreAnnotationCompletionContext(memberValueName); return; } else if (expression instanceof QualifiedNameReference || expression instanceof StringLiteral) { MemberValuePair valuePair = new MemberValuePair(VALUE, expression.sourceStart, expression.sourceEnd, expression); buildMoreAnnotationCompletionContext(valuePair); } } else { int index; if((index = lastIndexOfElement(K_ATTRIBUTE_VALUE_DELIMITER)) != -1) { int attributeIndentifierPtr = this.elementInfoStack[index]; int identLengthPtr = this.identifierLengthPtr; int identPtr = this.identifierPtr; while (attributeIndentifierPtr < identPtr) { identPtr -= this.identifierLengthStack[identLengthPtr--]; } if(attributeIndentifierPtr != identPtr) return; this.identifierLengthPtr = identLengthPtr; this.identifierPtr = identPtr; this.identifierLengthPtr--; MemberValuePair memberValuePair = new MemberValuePair( this.identifierStack[this.identifierPtr--], expression.sourceStart, expression.sourceEnd, expression); buildMoreAnnotationCompletionContext(memberValuePair); return; } } } else { CompletionNodeDetector detector = new CompletionNodeDetector(this.assistNode, expression); if(detector.containsCompletionNode()) { MemberValuePair valuePair = new MemberValuePair(VALUE, expression.sourceStart, expression.sourceEnd, expression); buildMoreAnnotationCompletionContext(valuePair); } } } if (this.astPtr > -1) { ASTNode node = this.astStack[this.astPtr]; if(node instanceof MemberValuePair) { MemberValuePair memberValuePair = (MemberValuePair) node; CompletionNodeDetector detector = new CompletionNodeDetector(this.assistNode, memberValuePair); if(detector.containsCompletionNode()) { buildMoreAnnotationCompletionContext(memberValuePair); this.assistNodeParent = detector.getCompletionNodeParent(); return; } } } } if(this.genericsPtr > -1) { ASTNode node = this.genericsStack[this.genericsPtr]; if(node instanceof Wildcard && ((Wildcard)node).bound == this.assistNode){ int kind = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER); if (kind == K_BINARY_OPERATOR) { int info = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER); if (info == LESS) { buildMoreGenericsCompletionContext(node, true); return; } } if(this.identifierLengthPtr > -1 && this.identifierLengthStack[this.identifierLengthPtr]!= 0) { this.pushOnElementStack(K_BINARY_OPERATOR, LESS); buildMoreGenericsCompletionContext(node, false); return; } } } if(this.currentElement instanceof RecoveredType || this.currentElement instanceof RecoveredMethod) { if(this.currentElement instanceof RecoveredType) { RecoveredType recoveredType = (RecoveredType)this.currentElement; if(recoveredType.foundOpeningBrace && this.genericsPtr > -1) { if(this.genericsStack[this.genericsPtr] instanceof TypeParameter) { TypeParameter typeParameter = (TypeParameter) this.genericsStack[this.genericsPtr]; CompletionNodeDetector detector = new CompletionNodeDetector(this.assistNode, typeParameter); if(detector.containsCompletionNode()) { this.currentElement.add(new CompletionOnMethodTypeParameter(new TypeParameter[]{typeParameter},this.compilationUnit.compilationResult()), 0); } return; } } } if ((!isInsideMethod() && !isInsideFieldInitialization())) { if(this.genericsPtr > -1 && this.genericsLengthPtr > -1 && this.genericsIdentifiersLengthPtr > -1) { int kind = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER); int info = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER); if(kind == K_BINARY_OPERATOR && info == LESS) { consumeTypeArguments(); } int numberOfIdentifiers = this.genericsIdentifiersLengthStack[this.genericsIdentifiersLengthPtr]; int genPtr = this.genericsPtr; done : for(int i = 0; i <= this.identifierLengthPtr && numberOfIdentifiers > 0; i++){ int identifierLength = this.identifierLengthStack[this.identifierLengthPtr - i]; int length = this.genericsLengthStack[this.genericsLengthPtr - i]; for(int j = 0; j < length; j++) { ASTNode node = this.genericsStack[genPtr - j]; CompletionNodeDetector detector = new CompletionNodeDetector(this.assistNode, node); if(detector.containsCompletionNode()) { if(node == this.assistNode){ if(this.identifierLengthPtr > -1 && this.identifierLengthStack[this.identifierLengthPtr]!= 0) { TypeReference ref = this.getTypeReference(0); this.assistNodeParent = ref; } } else { this.assistNodeParent = detector.getCompletionNodeParent(); } break done; } } genPtr -= length; numberOfIdentifiers -= identifierLength; } if(this.assistNodeParent != null && this.assistNodeParent instanceof TypeReference) { if(this.currentElement instanceof RecoveredType) { this.currentElement = this.currentElement.add(new CompletionOnFieldType((TypeReference)this.assistNodeParent, false), 0); } else { this.currentElement = this.currentElement.add((TypeReference)this.assistNodeParent, 0); } } } } } // the following code applies only in methods, constructors or initializers if ((!isInsideMethod() && !isInsideFieldInitialization() && !isInsideAttributeValue())) { return; } if(this.genericsPtr > -1) { ASTNode node = this.genericsStack[this.genericsPtr]; CompletionNodeDetector detector = new CompletionNodeDetector(this.assistNode, node); if(detector.containsCompletionNode()) { /* check for completion at the beginning of method body behind an invalid signature */ RecoveredMethod method = this.currentElement.enclosingMethod(); if (method != null){ AbstractMethodDeclaration methodDecl = method.methodDeclaration; if ((methodDecl.bodyStart == methodDecl.sourceEnd+1) // was missing opening brace && (Util.getLineNumber(node.sourceStart, this.scanner.lineEnds, 0, this.scanner.linePtr) == Util.getLineNumber(methodDecl.sourceEnd, this.scanner.lineEnds, 0, this.scanner.linePtr))){ return; } } if(node == this.assistNode){ buildMoreGenericsCompletionContext(node, true); } } } // push top expression on ast stack if it contains the completion node Expression expression; if (this.expressionPtr > -1) { expression = this.expressionStack[this.expressionPtr]; CompletionNodeDetector detector = new CompletionNodeDetector(this.assistNode, expression); if(detector.containsCompletionNode()) { /* check for completion at the beginning of method body behind an invalid signature */ RecoveredMethod method = this.currentElement.enclosingMethod(); if (method != null){ AbstractMethodDeclaration methodDecl = method.methodDeclaration; if ((methodDecl.bodyStart == methodDecl.sourceEnd+1) // was missing opening brace && (Util.getLineNumber(expression.sourceStart, this.scanner.lineEnds, 0, this.scanner.linePtr) == Util.getLineNumber(methodDecl.sourceEnd, this.scanner.lineEnds, 0, this.scanner.linePtr))){ return; } } if(expression == this.assistNode || (expression instanceof Assignment // https://bugs.eclipse.org/bugs/show_bug.cgi?id=287939 && ((Assignment)expression).expression == this.assistNode && ((this.expressionPtr > 0 && stackHasInstanceOfExpression(this.expressionStack, this.expressionPtr - 1)) // In case of error in compilation unit, expression stack might not have instanceof exp, so try elementObjectInfoStack || (this.elementPtr >= 0 && stackHasInstanceOfExpression(this.elementObjectInfoStack, this.elementPtr)))) || (expression instanceof AllocationExpression && ((AllocationExpression)expression).type == this.assistNode) || (expression instanceof AND_AND_Expression && (this.elementPtr >= 0 && this.elementObjectInfoStack[this.elementPtr] instanceof InstanceOfExpression))){ buildMoreCompletionContext(expression); if (this.assistNodeParent == null && expression instanceof Assignment) { this.assistNodeParent = detector.getCompletionNodeParent(); } return; } else { this.assistNodeParent = detector.getCompletionNodeParent(); if(this.assistNodeParent != null) { this.currentElement = this.currentElement.add((Statement)this.assistNodeParent, 0); } else { this.currentElement = this.currentElement.add(expression, 0); } return; } } } if (this.astPtr > -1 && this.astStack[this.astPtr] instanceof LocalDeclaration) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=287939 // To take care of: if (a instance of X) int i = a.| LocalDeclaration local = (LocalDeclaration) this.astStack[this.astPtr]; if (local.initialization == this.assistNode) { Statement enclosing = buildMoreCompletionEnclosingContext(local); if (enclosing instanceof IfStatement) { if (this.currentElement instanceof RecoveredBlock) { // RecoveredLocalVariable must be removed from its parent because the IfStatement will be added instead RecoveredBlock recoveredBlock = (RecoveredBlock) this.currentElement; recoveredBlock.statements[--recoveredBlock.statementCount] = null; this.currentElement = this.currentElement.add(enclosing, 0); } } } } } public Object becomeSimpleParser() { CompletionScanner completionScanner = (CompletionScanner)this.scanner; int[] parserState = new int[] {this.cursorLocation, completionScanner.cursorLocation}; this.cursorLocation = Integer.MAX_VALUE; completionScanner.cursorLocation = Integer.MAX_VALUE; return parserState; } private void buildMoreAnnotationCompletionContext(MemberValuePair memberValuePair) { if(this.identifierPtr < 0 || this.identifierLengthPtr < 0 ) return; TypeReference typeReference = getAnnotationType(); int nodesToRemove = this.astPtr > -1 && this.astStack[this.astPtr] == memberValuePair ? 1 : 0; NormalAnnotation annotation; if (memberValuePair instanceof CompletionOnMemberValueName) { MemberValuePair[] memberValuePairs = null; int length; if (this.astLengthPtr > -1 && (length = this.astLengthStack[this.astLengthPtr--]) > nodesToRemove) { if (this.astStack[this.astPtr] instanceof MemberValuePair) { System.arraycopy( this.astStack, (this.astPtr -= length) + 1, memberValuePairs = new MemberValuePair[length - nodesToRemove], 0, length - nodesToRemove); } } annotation = new CompletionOnAnnotationMemberValuePair( typeReference, this.intStack[this.intPtr--], memberValuePairs, memberValuePair); this.assistNode = memberValuePair; this.assistNodeParent = annotation; if (memberValuePair.sourceEnd >= this.lastCheckPoint) { this.lastCheckPoint = memberValuePair.sourceEnd + 1; } } else { MemberValuePair[] memberValuePairs = null; int length = 0; if (this.astLengthPtr > -1 && (length = this.astLengthStack[this.astLengthPtr--]) > nodesToRemove) { if (this.astStack[this.astPtr] instanceof MemberValuePair) { System.arraycopy( this.astStack, (this.astPtr -= length) + 1, memberValuePairs = new MemberValuePair[length - nodesToRemove + 1], 0, length - nodesToRemove); } if(memberValuePairs != null) { memberValuePairs[length - nodesToRemove] = memberValuePair; } else { memberValuePairs = new MemberValuePair[]{memberValuePair}; } } else { memberValuePairs = new MemberValuePair[]{memberValuePair}; } annotation = new NormalAnnotation( typeReference, this.intStack[this.intPtr--]); annotation.memberValuePairs = memberValuePairs; } CompletionOnAnnotationOfType fakeType = new CompletionOnAnnotationOfType( FAKE_TYPE_NAME, this.compilationUnit.compilationResult(), annotation); this.currentElement.add(fakeType, 0); this.pendingAnnotation = fakeType; } private void buildMoreCompletionContext(Expression expression) { Statement statement = expression; int kind = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER); if(kind != 0) { int info = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER); nextElement : switch (kind) { case K_SELECTOR_QUALIFIER : int selector = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER, 2); if(selector == THIS_CONSTRUCTOR || selector == SUPER_CONSTRUCTOR) { ExplicitConstructorCall call = new ExplicitConstructorCall( (selector == THIS_CONSTRUCTOR) ? ExplicitConstructorCall.This : ExplicitConstructorCall.Super ); call.arguments = new Expression[] {expression}; call.sourceStart = expression.sourceStart; call.sourceEnd = expression.sourceEnd; this.assistNodeParent = call; } else { int invocType = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER,1); int qualifierExprPtr = info; // find arguments int length = this.expressionLengthStack[this.expressionLengthPtr]; // search previous arguments if missing if(this.expressionPtr > 0 && this.expressionLengthPtr > 0 && length == 1) { int start = (int) (this.identifierPositionStack[selector] >>> 32); if(this.expressionStack[this.expressionPtr-1] != null && this.expressionStack[this.expressionPtr-1].sourceStart > start) { length += this.expressionLengthStack[this.expressionLengthPtr-1]; } } Expression[] arguments = null; if (length != 0) { arguments = new Expression[length]; this.expressionPtr -= length; System.arraycopy(this.expressionStack, this.expressionPtr + 1, arguments, 0, length-1); arguments[length-1] = expression; } if(invocType != ALLOCATION && invocType != QUALIFIED_ALLOCATION) { MessageSend messageSend = new MessageSend(); messageSend.selector = this.identifierStack[selector]; messageSend.arguments = arguments; // find receiver switch (invocType) { case NO_RECEIVER: messageSend.receiver = ThisReference.implicitThis(); 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--; if(this.genericsPtr > -1 && this.genericsLengthPtr > -1 && this.genericsLengthStack[this.genericsLengthPtr] > 0) { // is inside a paremeterized method: bar.<X>.foo this.identifierLengthPtr--; } else { this.identifierLengthStack[this.identifierLengthPtr]--; } // consume the receiver int identifierLength = this.identifierLengthStack[this.identifierLengthPtr]; if(this.identifierPtr > -1 && identifierLength > 0 && this.identifierPtr + 1 >= identifierLength) { messageSend.receiver = getUnspecifiedReference(); } else { messageSend = null; } break; case SUPER_RECEIVER: messageSend.receiver = new SuperReference(0, 0); break; case EXPLICIT_RECEIVER: messageSend.receiver = this.expressionStack[qualifierExprPtr]; break; default : messageSend.receiver = ThisReference.implicitThis(); break; } this.assistNodeParent = messageSend; } else { if(invocType == ALLOCATION) { AllocationExpression allocationExpr = new AllocationExpression(); allocationExpr.arguments = arguments; pushOnGenericsIdentifiersLengthStack(this.identifierLengthStack[this.identifierLengthPtr]); pushOnGenericsLengthStack(0); allocationExpr.type = getTypeReference(0); this.assistNodeParent = allocationExpr; } else { QualifiedAllocationExpression allocationExpr = new QualifiedAllocationExpression(); allocationExpr.enclosingInstance = this.expressionStack[qualifierExprPtr]; allocationExpr.arguments = arguments; pushOnGenericsIdentifiersLengthStack(this.identifierLengthStack[this.identifierLengthPtr]); pushOnGenericsLengthStack(0); allocationExpr.type = getTypeReference(0); this.assistNodeParent = allocationExpr; } } } break nextElement; case K_INSIDE_RETURN_STATEMENT : if(info == this.bracketDepth) { ReturnStatement returnStatement = new ReturnStatement(expression, expression.sourceStart, expression.sourceEnd); this.assistNodeParent = returnStatement; } break nextElement; case K_CAST_STATEMENT : Expression castType; if(this.expressionPtr > 0 && ((castType = this.expressionStack[this.expressionPtr-1]) instanceof TypeReference)) { CastExpression cast = new CastExpression(expression, (TypeReference) castType); cast.sourceStart = castType.sourceStart; cast.sourceEnd= expression.sourceEnd; this.assistNodeParent = cast; } break nextElement; case K_UNARY_OPERATOR : if(this.expressionPtr > -1) { Expression operatorExpression = null; switch (info) { case PLUS_PLUS : operatorExpression = new PrefixExpression(expression,IntLiteral.One, PLUS, expression.sourceStart); break; case MINUS_MINUS : operatorExpression = new PrefixExpression(expression,IntLiteral.One, MINUS, expression.sourceStart); break; default : operatorExpression = new UnaryExpression(expression, info); break; } this.assistNodeParent = operatorExpression; } break nextElement; case K_BINARY_OPERATOR : if(this.expressionPtr > -1) { Expression operatorExpression = null; Expression left = null; if(this.expressionPtr == 0) { // it is a ***_NotName rule if(this.identifierPtr > -1) { left = getUnspecifiedReferenceOptimized(); } } else { left = this.expressionStack[this.expressionPtr-1]; // is it a ***_NotName rule ? if(this.identifierPtr > -1) { int start = (int) (this.identifierPositionStack[this.identifierPtr] >>> 32); if(left.sourceStart < start) { left = getUnspecifiedReferenceOptimized(); } } } if(left != null) { switch (info) { case AND_AND : operatorExpression = new AND_AND_Expression(left, expression, info); break; case OR_OR : operatorExpression = new OR_OR_Expression(left, expression, info); break; case EQUAL_EQUAL : case NOT_EQUAL : operatorExpression = new EqualExpression(left, expression, info); break; default : operatorExpression = new BinaryExpression(left, expression, info); break; } } if(operatorExpression != null) { this.assistNodeParent = operatorExpression; } } break nextElement; case K_ARRAY_INITIALIZER : ArrayInitializer arrayInitializer = new ArrayInitializer(); arrayInitializer.expressions = new Expression[]{expression}; this.expressionPtr -= this.expressionLengthStack[this.expressionLengthPtr--]; if(this.expressionLengthPtr > -1 && this.expressionPtr > -1 && this.expressionStack[this.expressionPtr] != null && this.expressionStack[this.expressionPtr].sourceStart > info) { this.expressionLengthPtr--; } if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER, 1) == K_ARRAY_CREATION) { ArrayAllocationExpression allocationExpression = new ArrayAllocationExpression(); pushOnGenericsLengthStack(0); pushOnGenericsIdentifiersLengthStack(this.identifierLengthStack[this.identifierLengthPtr]); allocationExpression.type = getTypeReference(0); allocationExpression.type.bits |= ASTNode.IgnoreRawTypeCheck; // no need to worry about raw type usage int length = this.expressionLengthStack[this.expressionLengthPtr]; allocationExpression.dimensions = new Expression[length]; allocationExpression.initializer = arrayInitializer; this.assistNodeParent = allocationExpression; } else if(this.currentElement instanceof RecoveredField && !(this.currentElement instanceof RecoveredInitializer)) { RecoveredField recoveredField = (RecoveredField) this.currentElement; if(recoveredField.fieldDeclaration.type.dimensions() == 0) { Block block = new Block(0); block.sourceStart = info; this.currentElement = this.currentElement.add(block, 1); } else { statement = arrayInitializer; } } else if(this.currentElement instanceof RecoveredLocalVariable) { RecoveredLocalVariable recoveredLocalVariable = (RecoveredLocalVariable) this.currentElement; if(recoveredLocalVariable.localDeclaration.type.dimensions() == 0) { Block block = new Block(0); block.sourceStart = info; this.currentElement = this.currentElement.add(block, 1); } else { statement = arrayInitializer; } } else { statement = arrayInitializer; } break nextElement; case K_ARRAY_CREATION : ArrayAllocationExpression allocationExpression = new ArrayAllocationExpression(); allocationExpression.type = getTypeReference(0); allocationExpression.dimensions = new Expression[]{expression}; this.assistNodeParent = allocationExpression; break nextElement; case K_ASSISGNMENT_OPERATOR : if(this.expressionPtr > 0 && this.expressionStack[this.expressionPtr - 1] != null) { Assignment assignment; if(info == EQUAL) { assignment = new Assignment( this.expressionStack[this.expressionPtr - 1], expression, expression.sourceEnd ); } else { assignment = new CompoundAssignment( this.expressionStack[this.expressionPtr - 1], expression, info, expression.sourceEnd ); } this.assistNodeParent = assignment; } break nextElement; case K_CONDITIONAL_OPERATOR : if(info == QUESTION) { if(this.expressionPtr > 0) { this.expressionPtr--; this.expressionLengthPtr--; this.expressionStack[this.expressionPtr] = this.expressionStack[this.expressionPtr+1]; popElement(K_CONDITIONAL_OPERATOR); buildMoreCompletionContext(expression); return; } } else { if(this.expressionPtr > 1) { this.expressionPtr = this.expressionPtr - 2; this.expressionLengthPtr = this.expressionLengthPtr - 2; this.expressionStack[this.expressionPtr] = this.expressionStack[this.expressionPtr+2]; popElement(K_CONDITIONAL_OPERATOR); buildMoreCompletionContext(expression); return; } } break nextElement; case K_BETWEEN_LEFT_AND_RIGHT_BRACKET : ArrayReference arrayReference; if(this.identifierPtr < 0 && this.expressionPtr > 0 && this.expressionStack[this.expressionPtr] == expression) { arrayReference = new ArrayReference( this.expressionStack[this.expressionPtr-1], expression); } else { arrayReference = new ArrayReference( getUnspecifiedReferenceOptimized(), expression); } this.assistNodeParent = arrayReference; break; case K_BETWEEN_CASE_AND_COLON : if(this.expressionPtr > 0) { SwitchStatement switchStatement = new SwitchStatement(); switchStatement.expression = this.expressionStack[this.expressionPtr - 1]; if(this.astLengthPtr > -1 && this.astPtr > -1) { int length = this.astLengthStack[this.astLengthPtr]; int newAstPtr = this.astPtr - length; ASTNode firstNode = this.astStack[newAstPtr + 1]; if(length != 0 && firstNode.sourceStart > switchStatement.expression.sourceEnd) { switchStatement.statements = new Statement[length + 1]; System.arraycopy( this.astStack, newAstPtr + 1, switchStatement.statements, 0, length); } } CaseStatement caseStatement = new CaseStatement(expression, expression.sourceStart, expression.sourceEnd); if(switchStatement.statements == null) { switchStatement.statements = new Statement[]{caseStatement}; } else { switchStatement.statements[switchStatement.statements.length - 1] = caseStatement; } this.assistNodeParent = switchStatement; } break; case K_BETWEEN_IF_AND_RIGHT_PAREN : IfStatement ifStatement = new IfStatement(expression, new EmptyStatement(expression.sourceEnd, expression.sourceEnd), expression.sourceStart, expression.sourceEnd); this.assistNodeParent = ifStatement; break nextElement; case K_BETWEEN_WHILE_AND_RIGHT_PAREN : WhileStatement whileStatement = new WhileStatement(expression, new EmptyStatement(expression.sourceEnd, expression.sourceEnd), expression.sourceStart, expression.sourceEnd); this.assistNodeParent = whileStatement; break nextElement; case K_INSIDE_FOR_CONDITIONAL: // https://bugs.eclipse.org/bugs/show_bug.cgi?id=253008 ForStatement forStatement = new ForStatement(new Statement[0], expression, new Statement[0], new EmptyStatement(expression.sourceEnd, expression.sourceEnd), false, expression.sourceStart, expression.sourceEnd); this.assistNodeParent = forStatement; break nextElement; case K_BETWEEN_SWITCH_AND_RIGHT_PAREN: SwitchStatement switchStatement = new SwitchStatement(); switchStatement.expression = expression; switchStatement.statements = new Statement[0]; this.assistNodeParent = switchStatement; break nextElement; case K_BETWEEN_SYNCHRONIZED_AND_RIGHT_PAREN : SynchronizedStatement synchronizedStatement = new SynchronizedStatement(expression, new Block(0), expression.sourceStart, expression.sourceEnd); this.assistNodeParent = synchronizedStatement; break nextElement; case K_INSIDE_THROW_STATEMENT: if(info == this.bracketDepth) { ThrowStatement throwStatement = new ThrowStatement(expression, expression.sourceStart, expression.sourceEnd); this.assistNodeParent = throwStatement; } break nextElement; case K_INSIDE_ASSERT_STATEMENT: if(info == this.bracketDepth) { AssertStatement assertStatement = new AssertStatement(expression, expression.sourceStart); this.assistNodeParent = assertStatement; } break nextElement; case K_INSIDE_ASSERT_EXCEPTION: if(info == this.bracketDepth) { AssertStatement assertStatement = new AssertStatement(expression, new TrueLiteral(expression.sourceStart, expression.sourceStart), expression.sourceStart); this.assistNodeParent = assertStatement; } break nextElement; } } if(this.assistNodeParent != null) { this.currentElement = this.currentElement.add(buildMoreCompletionEnclosingContext((Statement)this.assistNodeParent), 0); } else { if(this.currentElement instanceof RecoveredField && !(this.currentElement instanceof RecoveredInitializer) && ((RecoveredField) this.currentElement).fieldDeclaration.initialization == null) { this.assistNodeParent = ((RecoveredField) this.currentElement).fieldDeclaration; this.currentElement = this.currentElement.add(buildMoreCompletionEnclosingContext(statement), 0); } else if(this.currentElement instanceof RecoveredLocalVariable && ((RecoveredLocalVariable) this.currentElement).localDeclaration.initialization == null) { this.assistNodeParent = ((RecoveredLocalVariable) this.currentElement).localDeclaration; this.currentElement = this.currentElement.add(buildMoreCompletionEnclosingContext(statement), 0); } else { this.currentElement = this.currentElement.add(buildMoreCompletionEnclosingContext(expression), 0); } } } private Statement buildMoreCompletionEnclosingContext(Statement statement) { IfStatement ifStatement = null; int blockIndex = lastIndexOfElement(K_BLOCK_DELIMITER); int controlIndex = lastIndexOfElement(K_CONTROL_STATEMENT_DELIMITER); int index; if (controlIndex != -1) { index = blockIndex != -1 && controlIndex < blockIndex ? blockIndex : controlIndex; } else { // To handle the case when the completion is requested before enclosing R_PAREN // and an instanceof expression is also present // https://bugs.eclipse.org/bugs/show_bug.cgi?id=261534 int instanceOfIndex = lastIndexOfElement(K_BETWEEN_INSTANCEOF_AND_RPAREN); index = blockIndex != -1 && instanceOfIndex < blockIndex ? blockIndex : instanceOfIndex; } while (index >= 0) { // Try to find an enclosing if statement even if one is not found immediately preceding the completion node. if (index != -1 && this.elementInfoStack[index] == IF && this.elementObjectInfoStack[index] != null) { Expression condition = (Expression)this.elementObjectInfoStack[index]; // If currentElement is a RecoveredLocalVariable then it can be contained in the if statement if (this.currentElement instanceof RecoveredLocalVariable && this.currentElement.parent instanceof RecoveredBlock) { RecoveredLocalVariable recoveredLocalVariable = (RecoveredLocalVariable) this.currentElement; if (recoveredLocalVariable.localDeclaration.initialization == null && statement instanceof Expression && condition.sourceStart < recoveredLocalVariable.localDeclaration.sourceStart) { this.currentElement.add(statement, 0); statement = recoveredLocalVariable.updatedStatement(0, new HashSet()); // RecoveredLocalVariable must be removed from its parent because the IfStatement will be added instead RecoveredBlock recoveredBlock = (RecoveredBlock) recoveredLocalVariable.parent; recoveredBlock.statements[--recoveredBlock.statementCount] = null; this.currentElement = recoveredBlock; } } if (statement instanceof AND_AND_Expression && this.assistNode instanceof Statement) { statement = (Statement) this.assistNode; } ifStatement = new IfStatement( condition, statement, condition.sourceStart, statement.sourceEnd); index--; break; } index--; } if (ifStatement == null) { return statement; } // collect all if statements with instanceof expressions that enclose the completion node // https://bugs.eclipse.org/bugs/show_bug.cgi?id=304006 while (index >= 0) { if (this.elementInfoStack[index] == IF && this.elementObjectInfoStack[index] instanceof InstanceOfExpression) { InstanceOfExpression condition = (InstanceOfExpression)this.elementObjectInfoStack[index]; ifStatement = new IfStatement( condition, ifStatement, condition.sourceStart, ifStatement.sourceEnd); } index--; } this.enclosingNode = ifStatement; return ifStatement; } private void buildMoreGenericsCompletionContext(ASTNode node, boolean consumeTypeArguments) { int kind = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER); if(kind != 0) { int info = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER); nextElement : switch (kind) { case K_BINARY_OPERATOR : int prevKind = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER, 1); switch (prevKind) { case K_PARAMETERIZED_ALLOCATION : if(this.invocationType == ALLOCATION || this.invocationType == QUALIFIED_ALLOCATION) { this.currentElement = this.currentElement.add((TypeReference)node, 0); } break nextElement; case K_PARAMETERIZED_METHOD_INVOCATION : if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER, 1) == 0) { this.currentElement = this.currentElement.add((TypeReference)node, 0); break nextElement; } } if(info == LESS && node instanceof TypeReference) { if(this.identifierLengthPtr > -1 && this.identifierLengthStack[this.identifierLengthPtr]!= 0) { if (consumeTypeArguments) consumeTypeArguments(); TypeReference ref = this.getTypeReference(0); if(prevKind == K_PARAMETERIZED_CAST) { ref = computeQualifiedGenericsFromRightSide(ref, 0); } if(this.currentElement instanceof RecoveredType) { this.currentElement = this.currentElement.add(new CompletionOnFieldType(ref, false), 0); } else { if (prevKind == K_BETWEEN_NEW_AND_LEFT_BRACKET) { AllocationExpression exp; if (this.expressionPtr > -1 && this.expressionStack[this.expressionPtr] instanceof AllocationExpression) { exp = new QualifiedAllocationExpression(); exp.type = ref; ((QualifiedAllocationExpression)exp).enclosingInstance = this.expressionStack[this.expressionPtr]; } else { exp = new AllocationExpression(); exp.type = ref; } if (isInsideReturn()) { ReturnStatement returnStatement = new ReturnStatement(exp, exp.sourceStart, exp.sourceEnd); this.enclosingNode = returnStatement; this.currentElement = this.currentElement.add(returnStatement,0); } else if (this.currentElement instanceof RecoveredLocalVariable) { if (((RecoveredLocalVariable)this.currentElement).localDeclaration.initialization == null) { this.enclosingNode = ((RecoveredLocalVariable) this.currentElement).localDeclaration; this.currentElement = this.currentElement.add(exp, 0); } } else if (this.currentElement instanceof RecoveredField) { if (((RecoveredField) this.currentElement).fieldDeclaration.initialization == null) { this.enclosingNode = ((RecoveredField) this.currentElement).fieldDeclaration; this.currentElement = this.currentElement.add(exp, 0); } } else { this.currentElement = this.currentElement.add(ref, 0); } } else { this.currentElement = this.currentElement.add(ref, 0); } } } else if (this.currentElement.enclosingMethod() != null && this.currentElement.enclosingMethod().methodDeclaration.isConstructor()) { this.currentElement = this.currentElement.add((TypeReference)node, 0); } } break; } } } private void buildMoreTryStatementCompletionContext(TypeReference exceptionRef) { if (this.astLengthPtr > 0 && this.astPtr > 2 && this.astStack[this.astPtr -1] instanceof Block && this.astStack[this.astPtr - 2] instanceof Argument) { TryStatement tryStatement = new TryStatement(); int newAstPtr = this.astPtr - 1; int length = this.astLengthStack[this.astLengthPtr - 1]; Block[] bks = (tryStatement.catchBlocks = new Block[length + 1]); Argument[] args = (tryStatement.catchArguments = new Argument[length + 1]); if (length != 0) { while (length-- > 0) { bks[length] = (Block) this.astStack[newAstPtr--]; bks[length].statements = null; // statements of catch block won't be used args[length] = (Argument) this.astStack[newAstPtr--]; } } bks[bks.length - 1] = new Block(0); if (this.astStack[this.astPtr] instanceof UnionTypeReference) { UnionTypeReference unionTypeReference = (UnionTypeReference) this.astStack[this.astPtr]; args[args.length - 1] = new Argument(FAKE_ARGUMENT_NAME,0,unionTypeReference,0); } else { args[args.length - 1] = new Argument(FAKE_ARGUMENT_NAME,0,exceptionRef,0); } tryStatement.tryBlock = (Block) this.astStack[newAstPtr--]; this.assistNodeParent = tryStatement; this.currentElement.add(tryStatement, 0); } else if (this.astLengthPtr > -1 && this.astPtr > 0 && this.astStack[this.astPtr - 1] instanceof Block) { TryStatement tryStatement = new TryStatement(); int newAstPtr = this.astPtr - 1; Block[] bks = (tryStatement.catchBlocks = new Block[1]); Argument[] args = (tryStatement.catchArguments = new Argument[1]); bks[0] = new Block(0); if (this.astStack[this.astPtr] instanceof UnionTypeReference) { UnionTypeReference unionTypeReference = (UnionTypeReference) this.astStack[this.astPtr]; args[0] = new Argument(FAKE_ARGUMENT_NAME,0,unionTypeReference,0); } else { args[0] = new Argument(FAKE_ARGUMENT_NAME,0,exceptionRef,0); } tryStatement.tryBlock = (Block) this.astStack[newAstPtr--]; this.assistNodeParent = tryStatement; this.currentElement.add(tryStatement, 0); }else { this.currentElement = this.currentElement.add(exceptionRef, 0); } } public int bodyEnd(AbstractMethodDeclaration method){ return this.cursorLocation; } public int bodyEnd(Initializer initializer){ return this.cursorLocation; } protected void checkAndSetModifiers(int flag) { super.checkAndSetModifiers(flag); if (isInsideMethod()) { this.hasUnusedModifiers = true; } } /** * Checks if the completion is on the type following a 'new'. * Returns whether we found a completion node. */ private boolean checkClassInstanceCreation() { if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BETWEEN_NEW_AND_LEFT_BRACKET) { int length = this.identifierLengthStack[this.identifierLengthPtr]; int numberOfIdentifiers = this.genericsIdentifiersLengthStack[this.genericsIdentifiersLengthPtr]; if (length != numberOfIdentifiers || this.genericsLengthStack[this.genericsLengthPtr] != 0) { // no class instance creation with a parameterized type return true; } // completion on type inside an allocation expression TypeReference type; if (this.invocationType == ALLOCATION) { // non qualified allocation expression AllocationExpression allocExpr = new AllocationExpression(); if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER, 1) == K_INSIDE_THROW_STATEMENT && topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER, 1) == this.bracketDepth) { pushOnElementStack(K_NEXT_TYPEREF_IS_EXCEPTION); type = getTypeReference(0); popElement(K_NEXT_TYPEREF_IS_EXCEPTION); } else { type = getTypeReference(0); } if(type instanceof CompletionOnSingleTypeReference) { ((CompletionOnSingleTypeReference)type).isConstructorType = true; } else if (type instanceof CompletionOnQualifiedTypeReference) { ((CompletionOnQualifiedTypeReference)type).isConstructorType = true; } allocExpr.type = type; allocExpr.sourceStart = type.sourceStart; allocExpr.sourceEnd = type.sourceEnd; pushOnExpressionStack(allocExpr); this.isOrphanCompletionNode = false; } else { // qualified allocation expression QualifiedAllocationExpression allocExpr = new QualifiedAllocationExpression(); pushOnGenericsIdentifiersLengthStack(this.identifierLengthStack[this.identifierLengthPtr]); pushOnGenericsLengthStack(0); if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER, 1) == K_INSIDE_THROW_STATEMENT && topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER, 1) == this.bracketDepth) { pushOnElementStack(K_NEXT_TYPEREF_IS_EXCEPTION); type = getTypeReference(0); popElement(K_NEXT_TYPEREF_IS_EXCEPTION); } else { type = getTypeReference(0); } if(type instanceof CompletionOnSingleTypeReference) { ((CompletionOnSingleTypeReference)type).isConstructorType = true; } 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; } this.assistNode = type; this.lastCheckPoint = type.sourceEnd + 1; popElement(K_BETWEEN_NEW_AND_LEFT_BRACKET); 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 = 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 = this.identifierStack[this.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 = this.identifierStack[this.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 pushOnGenericsIdentifiersLengthStack(this.identifierLengthStack[this.identifierLengthPtr]); pushOnGenericsLengthStack(0); TypeReference typeRef = getTypeReference(this.intStack[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; } private boolean checkKeyword() { if (this.currentElement instanceof RecoveredUnit) { RecoveredUnit unit = (RecoveredUnit) this.currentElement; int index = -1; if ((index = this.indexOfAssistIdentifier()) > -1) { int ptr = this.identifierPtr - this.identifierLengthStack[this.identifierLengthPtr] + index + 1; char[] ident = this.identifierStack[ptr]; long pos = this.identifierPositionStack[ptr]; char[][] keywords = new char[Keywords.COUNT][]; int count = 0; if(unit.typeCount == 0 && this.lastModifiers == ClassFileConstants.AccDefault) { keywords[count++] = Keywords.IMPORT; } if(unit.typeCount == 0 && unit.importCount == 0 && this.lastModifiers == ClassFileConstants.AccDefault && this.compilationUnit.currentPackage == null) { keywords[count++] = Keywords.PACKAGE; } if((this.lastModifiers & ClassFileConstants.AccPublic) == 0) { boolean hasNoPublicType = true; for (int i = 0; i < unit.typeCount; i++) { if((unit.types[i].typeDeclaration.modifiers & ClassFileConstants.AccPublic) != 0) { hasNoPublicType = false; } } if(hasNoPublicType) { keywords[count++] = Keywords.PUBLIC; } } if((this.lastModifiers & ClassFileConstants.AccAbstract) == 0 && (this.lastModifiers & ClassFileConstants.AccFinal) == 0) { keywords[count++] = Keywords.ABSTRACT; } if((this.lastModifiers & ClassFileConstants.AccAbstract) == 0 && (this.lastModifiers & ClassFileConstants.AccFinal) == 0) { keywords[count++] = Keywords.FINAL; } keywords[count++] = Keywords.CLASS; if (this.options.complianceLevel >= ClassFileConstants.JDK1_5) { keywords[count++] = Keywords.ENUM; } if((this.lastModifiers & ClassFileConstants.AccFinal) == 0) { keywords[count++] = Keywords.INTERFACE; } if(count != 0) { System.arraycopy(keywords, 0, keywords = new char[count][], 0, count); this.assistNode = new CompletionOnKeyword2(ident, pos, keywords); this.lastCheckPoint = this.assistNode.sourceEnd + 1; this.isOrphanCompletionNode = true; return true; } } } return false; } private boolean checkInstanceofKeyword() { if(isInsideMethod()) { int kind = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER); int index; if(kind != K_BLOCK_DELIMITER && (index = indexOfAssistIdentifier()) > -1 && this.expressionPtr > -1 && this.expressionLengthStack[this.expressionPtr] == 1) { int ptr = this.identifierPtr - this.identifierLengthStack[this.identifierLengthPtr] + index + 1; if(this.identifierStack[ptr].length > 0 && CharOperation.prefixEquals(this.identifierStack[ptr], Keywords.INSTANCEOF)) { this.assistNode = new CompletionOnKeyword3( this.identifierStack[ptr], this.identifierPositionStack[ptr], Keywords.INSTANCEOF); this.lastCheckPoint = this.assistNode.sourceEnd + 1; 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; if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_SELECTOR_QUALIFIER && ((isEmptyNameCompletion = topExpression == this.assistNode && isEmptyNameCompletion()) // e.g. 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))) { // e.g. 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 invocType = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER, 1); int qualifierExprPtr = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER); // 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 (invocType != ALLOCATION && invocType != QUALIFIED_ALLOCATION) { // creates completion on message send CompletionOnMessageSend messageSend = new CompletionOnMessageSend(); messageSend.arguments = arguments; switch (invocType) { case NO_RECEIVER: // implicit this messageSend.receiver = ThisReference.implicitThis(); 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--; if(this.genericsPtr > -1 && this.genericsLengthPtr > -1 && this.genericsLengthStack[this.genericsLengthPtr] > 0) { // is inside a paremeterized method: bar.<X>.foo this.identifierLengthPtr--; } else { this.identifierLengthStack[this.identifierLengthPtr]--; } // consume the receiver messageSend.receiver = getUnspecifiedReference(); break; case SUPER_RECEIVER: messageSend.receiver = new SuperReference(0, 0); break; case EXPLICIT_RECEIVER: messageSend.receiver = this.expressionStack[qualifierExprPtr]; } // set selector int selectorPtr = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER, 2); 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 = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER, 2); 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 (invocType == 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; if(this.genericsLengthPtr < 0) { pushOnGenericsLengthStack(0); pushOnGenericsIdentifiersLengthStack(this.identifierLengthStack[this.identifierLengthPtr]); } allocExpr.type = super.getTypeReference(0); // we don't want a completion node here, so call super if (invocType == 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; } private boolean checkLabelStatement() { if(isInsideMethod() || isInsideFieldInitialization()) { int kind = this.topKnownElementKind(COMPLETION_OR_ASSIST_PARSER); if(kind != K_INSIDE_BREAK_STATEMENT && kind != K_INSIDE_CONTINUE_STATEMENT) return false; if (indexOfAssistIdentifier() != 0) return false; char[][] labels = new char[this.labelPtr + 1][]; int labelCount = 0; int labelKind = kind; int index = 1; while(labelKind != 0 && labelKind != K_METHOD_DELIMITER) { labelKind = this.topKnownElementKind(COMPLETION_OR_ASSIST_PARSER, index); if(labelKind == K_LABEL) { int ptr = this.topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER, index); labels[labelCount++] = this.labelStack[ptr]; } index++; } System.arraycopy(labels, 0, labels = new char[labelCount][], 0, labelCount); long position = this.identifierPositionStack[this.identifierPtr]; CompletionOnBranchStatementLabel statementLabel = new CompletionOnBranchStatementLabel( kind == K_INSIDE_BREAK_STATEMENT ? CompletionOnBranchStatementLabel.BREAK : CompletionOnBranchStatementLabel.CONTINUE, this.identifierStack[this.identifierPtr--], (int) (position >>> 32), (int)position, labels); this.assistNode = statementLabel; this.lastCheckPoint = this.assistNode.sourceEnd + 1; this.isOrphanCompletionNode = true; return true; } return false; } /** * Checks if the completion is on a member access (i.e. 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) { if (this.identifierLengthPtr > 1 && this.identifierLengthStack[this.identifierLengthPtr - 1] < 0) { // its not a member access because the receiver is a base type // fix for bug: https://bugs.eclipse.org/bugs/show_bug.cgi?id=137623 return false; } // 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 (e.g. 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; if (this.hasUnusedModifiers && this.assistNode instanceof CompletionOnSingleNameReference) { ((CompletionOnSingleNameReference)this.assistNode).isPrecededByModifiers = true; } return true; } private boolean checkParemeterizedMethodName() { if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_PARAMETERIZED_METHOD_INVOCATION && topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == INSIDE_NAME) { if(this.identifierLengthPtr > -1 && this.genericsLengthPtr > -1 && this.genericsIdentifiersLengthPtr == -1) { CompletionOnMessageSendName m = null; switch (this.invocationType) { case EXPLICIT_RECEIVER: case NO_RECEIVER: // this case occurs with 'bar().foo' if(this.expressionPtr > -1 && this.expressionLengthStack[this.expressionLengthPtr] == 1) { char[] selector = this.identifierStack[this.identifierPtr]; long position = this.identifierPositionStack[this.identifierPtr--]; this.identifierLengthPtr--; int end = (int) position; int start = (int) (position >>> 32); m = new CompletionOnMessageSendName(selector, start, end); // handle type arguments int length = this.genericsLengthStack[this.genericsLengthPtr--]; this.genericsPtr -= length; System.arraycopy(this.genericsStack, this.genericsPtr + 1, m.typeArguments = new TypeReference[length], 0, length); this.intPtr--; m.receiver = this.expressionStack[this.expressionPtr--]; this.expressionLengthPtr--; } break; case NAME_RECEIVER: if(this.identifierPtr > 0) { char[] selector = this.identifierStack[this.identifierPtr]; long position = this.identifierPositionStack[this.identifierPtr--]; this.identifierLengthPtr--; int end = (int) position; int start = (int) (position >>> 32); m = new CompletionOnMessageSendName(selector, start, end); // handle type arguments int length = this.genericsLengthStack[this.genericsLengthPtr--]; this.genericsPtr -= length; System.arraycopy(this.genericsStack, this.genericsPtr + 1, m.typeArguments = new TypeReference[length], 0, length); this.intPtr--; m.receiver = getUnspecifiedReference(); } break; case SUPER_RECEIVER: char[] selector = this.identifierStack[this.identifierPtr]; long position = this.identifierPositionStack[this.identifierPtr--]; this.identifierLengthPtr--; int end = (int) position; int start = (int) (position >>> 32); m = new CompletionOnMessageSendName(selector, start, end); // handle type arguments int length = this.genericsLengthStack[this.genericsLengthPtr--]; this.genericsPtr -= length; System.arraycopy(this.genericsStack, this.genericsPtr + 1, m.typeArguments = new TypeReference[length], 0, length); this.intPtr--; m.receiver = new SuperReference(start, end); break; } if(m != null) { pushOnExpressionStack(m); this.assistNode = m; this.lastCheckPoint = this.assistNode.sourceEnd + 1; this.isOrphanCompletionNode = true; return true; } } } return false; } private boolean checkParemeterizedType() { if(this.identifierLengthPtr > -1 && this.genericsLengthPtr > -1 && this.genericsIdentifiersLengthPtr > -1) { int length = this.identifierLengthStack[this.identifierLengthPtr]; int numberOfIdentifiers = this.genericsIdentifiersLengthStack[this.genericsIdentifiersLengthPtr]; if (length != numberOfIdentifiers || this.genericsLengthStack[this.genericsLengthPtr] != 0) { this.genericsIdentifiersLengthPtr--; this.identifierLengthPtr--; // generic type this.assistNode = getAssistTypeReferenceForGenericType(0, length, numberOfIdentifiers); this.lastCheckPoint = this.assistNode.sourceEnd + 1; this.isOrphanCompletionNode = true; return true; } else if(this.genericsPtr > -1 && this.genericsStack[this.genericsPtr] instanceof TypeReference) { // type of a cast expression numberOfIdentifiers++; this.genericsIdentifiersLengthPtr--; this.identifierLengthPtr--; // generic type this.assistNode = getAssistTypeReferenceForGenericType(0, length, numberOfIdentifiers); this.lastCheckPoint = this.assistNode.sourceEnd + 1; this.isOrphanCompletionNode = true; return true; } } return false; } /** * 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 (this.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 (this.lastErrorEndPosition <= this.cursorLocation && Util.getLineNumber(this.lastErrorEndPosition, this.scanner.lineEnds, 0, this.scanner.linePtr) == Util.getLineNumber(((CompletionScanner)this.scanner).completedIdentifierStart, this.scanner.lineEnds, 0, this.scanner.linePtr)){ return false; } RecoveredMethod recoveredMethod = (RecoveredMethod)this.currentElement; /* only consider if inside method header */ if (!recoveredMethod.foundOpeningBrace && this.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; } private boolean checkMemberValueName() { /* check if current awaiting identifier is the completion identifier */ if (this.indexOfAssistIdentifier() < 0) return false; if (this.topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) != K_BETWEEN_ANNOTATION_NAME_AND_RPAREN) return false; if(this.identifierPtr > -1 && this.identifierLengthPtr > -1 && this.identifierLengthStack[this.identifierLengthPtr] == 1) { char[] simpleName = this.identifierStack[this.identifierPtr]; long position = this.identifierPositionStack[this.identifierPtr--]; this.identifierLengthPtr--; int end = (int) position; int start = (int) (position >>> 32); CompletionOnMemberValueName memberValueName = new CompletionOnMemberValueName(simpleName,start, end); this.assistNode = memberValueName; 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 (this.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 (this.lastErrorEndPosition <= this.cursorLocation && ((RecoveredType)this.currentElement).lastMemberEnd() < this.lastErrorEndPosition && Util.getLineNumber(this.lastErrorEndPosition, this.scanner.lineEnds, 0, this.scanner.linePtr) == Util.getLineNumber(((CompletionScanner)this.scanner).completedIdentifierStart, this.scanner.lineEnds, 0, this.scanner.linePtr)){ return false; } RecoveredType recoveredType = (RecoveredType)this.currentElement; /* filter out cases where scanner is still inside type header */ if (recoveredType.foundOpeningBrace) { // complete generics stack if necessary if((this.genericsIdentifiersLengthPtr < 0 && this.identifierPtr > -1) || (this.genericsIdentifiersLengthStack[this.genericsIdentifiersLengthPtr] <= this.identifierPtr)) { pushOnGenericsIdentifiersLengthStack(this.identifierLengthStack[this.identifierLengthPtr]); pushOnGenericsLengthStack(0); // handle type arguments } this.assistNode = this.getTypeReference(0); this.lastCheckPoint = this.assistNode.sourceEnd + 1; this.isOrphanCompletionNode = true; return true; } else { if(recoveredType.typeDeclaration.superclass == null && this.topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_EXTENDS_KEYWORD) { consumeClassOrInterfaceName(); this.pushOnElementStack(K_NEXT_TYPEREF_IS_CLASS); this.assistNode = this.getTypeReference(0); popElement(K_NEXT_TYPEREF_IS_CLASS); this.lastCheckPoint = this.assistNode.sourceEnd + 1; this.isOrphanCompletionNode = true; return true; } } } return false; } private void classHeaderExtendsOrImplements(boolean isInterface) { if (this.currentElement != null && this.currentToken == TokenNameIdentifier && this.cursorLocation+1 >= this.scanner.startPosition && this.cursorLocation < this.scanner.currentPosition){ this.pushIdentifier(); int index = -1; /* check if current awaiting identifier is the completion identifier */ if ((index = this.indexOfAssistIdentifier()) > -1) { int ptr = this.identifierPtr - this.identifierLengthStack[this.identifierLengthPtr] + index + 1; RecoveredType recoveredType = (RecoveredType)this.currentElement; /* filter out cases where scanner is still inside type header */ if (!recoveredType.foundOpeningBrace) { TypeDeclaration type = recoveredType.typeDeclaration; if(!isInterface) { char[][] keywords = new char[Keywords.COUNT][]; int count = 0; if(type.superInterfaces == null) { if(type.superclass == null) { keywords[count++] = Keywords.EXTENDS; } keywords[count++] = Keywords.IMPLEMENTS; } System.arraycopy(keywords, 0, keywords = new char[count][], 0, count); if(count > 0) { CompletionOnKeyword1 completionOnKeyword = new CompletionOnKeyword1( this.identifierStack[ptr], this.identifierPositionStack[ptr], keywords); completionOnKeyword.canCompleteEmptyToken = true; type.superclass = completionOnKeyword; type.superclass.bits |= ASTNode.IsSuperType; this.assistNode = completionOnKeyword; this.lastCheckPoint = completionOnKeyword.sourceEnd + 1; } } else { if(type.superInterfaces == null) { CompletionOnKeyword1 completionOnKeyword = new CompletionOnKeyword1( this.identifierStack[ptr], this.identifierPositionStack[ptr], Keywords.EXTENDS); completionOnKeyword.canCompleteEmptyToken = true; type.superInterfaces = new TypeReference[]{completionOnKeyword}; type.superInterfaces[0].bits |= ASTNode.IsSuperType; this.assistNode = completionOnKeyword; this.lastCheckPoint = completionOnKeyword.sourceEnd + 1; } } } } } } /* * 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 (assistNode != null) return; if (checkMemberValueName()) return; if (checkKeyword()) return; 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 (!(isInsideMethod() && !this.diet) && !isIndirectlyInsideFieldInitialization() && !isInsideAttributeValue()) 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 (that is, pushed). */ if (assistIdentifier() == null && this.currentToken == TokenNameIdentifier) { // Test below copied from CompletionScanner.getCurrentIdentifierSource() if (this.cursorLocation < this.scanner.startPosition && this.scanner.currentPosition == this.scanner.startPosition){ // fake empty identifier got issued this.pushIdentifier(); } else if (this.cursorLocation+1 >= this.scanner.startPosition && this.cursorLocation < this.scanner.currentPosition){ this.pushIdentifier(); } } // check for different scenarii // 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 (checkMemberAccess()) return; if (checkClassLiteralAccess()) return; if (checkInstanceofKeyword()) return; // if the completion was not on an empty name, it can still be inside an invocation (e.g. 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 (checkParemeterizedType()) return; if (checkParemeterizedMethodName()) return; if (checkLabelStatement()) return; if (checkNameCompletion()) return; } protected void consumeArrayCreationExpressionWithInitializer() { super.consumeArrayCreationExpressionWithInitializer(); popElement(K_ARRAY_CREATION); } protected void consumeArrayCreationExpressionWithoutInitializer() { super.consumeArrayCreationExpressionWithoutInitializer(); popElement(K_ARRAY_CREATION); } protected void consumeArrayCreationHeader() { // nothing to do } protected void consumeAssignment() { popElement(K_ASSISGNMENT_OPERATOR); super.consumeAssignment(); } protected void consumeAssignmentOperator(int pos) { super.consumeAssignmentOperator(pos); pushOnElementStack(K_ASSISGNMENT_OPERATOR, pos); } protected void consumeBinaryExpression(int op) { super.consumeBinaryExpression(op); popElement(K_BINARY_OPERATOR); if(this.expressionStack[this.expressionPtr] instanceof BinaryExpression) { BinaryExpression exp = (BinaryExpression) this.expressionStack[this.expressionPtr]; if(this.assistNode != null && exp.right == this.assistNode) { this.assistNodeParent = exp; } } } protected void consumeBinaryExpressionWithName(int op) { super.consumeBinaryExpressionWithName(op); popElement(K_BINARY_OPERATOR); if(this.expressionStack[this.expressionPtr] instanceof BinaryExpression) { BinaryExpression exp = (BinaryExpression) this.expressionStack[this.expressionPtr]; if(this.assistNode != null && exp.right == this.assistNode) { this.assistNodeParent = exp; } } } protected void consumeCaseLabel() { super.consumeCaseLabel(); if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) != K_SWITCH_LABEL) { pushOnElementStack(K_SWITCH_LABEL); } } protected void consumeCastExpressionWithPrimitiveType() { popElement(K_CAST_STATEMENT); Expression exp; Expression cast; TypeReference castType; this.expressionPtr--; this.expressionLengthPtr--; this.expressionStack[this.expressionPtr] = cast = new CastExpression(exp = this.expressionStack[this.expressionPtr+1], castType = (TypeReference) this.expressionStack[this.expressionPtr]); cast.sourceStart = castType.sourceStart - 1; cast.sourceEnd = exp.sourceEnd; } protected void consumeCastExpressionWithGenericsArray() { popElement(K_CAST_STATEMENT); Expression exp; Expression cast; TypeReference castType; this.expressionPtr--; this.expressionLengthPtr--; this.expressionStack[this.expressionPtr] = cast = new CastExpression(exp = this.expressionStack[this.expressionPtr + 1], castType = (TypeReference) this.expressionStack[this.expressionPtr]); cast.sourceStart = castType.sourceStart - 1; cast.sourceEnd = exp.sourceEnd; } protected void consumeCastExpressionWithQualifiedGenericsArray() { popElement(K_CAST_STATEMENT); Expression exp; Expression cast; TypeReference castType; this.expressionPtr--; this.expressionLengthPtr--; this.expressionStack[this.expressionPtr] = cast = new CastExpression(exp = this.expressionStack[this.expressionPtr + 1], castType = (TypeReference) this.expressionStack[this.expressionPtr]); cast.sourceStart = castType.sourceStart - 1; cast.sourceEnd = exp.sourceEnd; } protected void consumeCastExpressionWithNameArray() { // CastExpression ::= PushLPAREN Name Dims PushRPAREN InsideCastExpression UnaryExpressionNotPlusMinus popElement(K_CAST_STATEMENT); Expression exp; Expression cast; TypeReference castType; this.expressionPtr--; this.expressionLengthPtr--; this.expressionStack[this.expressionPtr] = cast = new CastExpression(exp = this.expressionStack[this.expressionPtr+1], castType = (TypeReference) this.expressionStack[this.expressionPtr]); cast.sourceStart = castType.sourceStart - 1; cast.sourceEnd = exp.sourceEnd; } protected void consumeCastExpressionLL1() { popElement(K_CAST_STATEMENT); super.consumeCastExpressionLL1(); } protected void consumeCatchFormalParameter() { if (this.indexOfAssistIdentifier() < 0) { super.consumeCatchFormalParameter(); if (this.pendingAnnotation != null) { this.pendingAnnotation.potentialAnnotatedNode = this.astStack[this.astPtr]; this.pendingAnnotation = null; } } else { this.identifierLengthPtr--; char[] identifierName = this.identifierStack[this.identifierPtr]; long namePositions = this.identifierPositionStack[this.identifierPtr--]; this.intPtr--; // dimension from the variabledeclaratorid TypeReference type = (TypeReference) this.astStack[this.astPtr--]; this.intPtr -= 2; CompletionOnArgumentName arg = new CompletionOnArgumentName( identifierName, namePositions, type, this.intStack[this.intPtr + 1] & ~ClassFileConstants.AccDeprecated); // modifiers arg.bits &= ~ASTNode.IsArgument; // consume annotations int length; if ((length = this.expressionLengthStack[this.expressionLengthPtr--]) != 0) { System.arraycopy( this.expressionStack, (this.expressionPtr -= length) + 1, arg.annotations = new Annotation[length], 0, length); } arg.isCatchArgument = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BETWEEN_CATCH_AND_RIGHT_PAREN; pushOnAstStack(arg); this.assistNode = arg; this.lastCheckPoint = (int) namePositions; this.isOrphanCompletionNode = true; /* if incomplete method header, listLength counter will not have been reset, indicating that some arguments are available on the stack */ this.listLength++; } } protected void consumeClassBodyDeclaration() { popElement(K_BLOCK_DELIMITER); super.consumeClassBodyDeclaration(); this.pendingAnnotation = null; // the pending annotation cannot be attached to next nodes } protected void consumeClassBodyopt() { popElement(K_SELECTOR_QUALIFIER); popElement(K_SELECTOR_INVOCATION_TYPE); super.consumeClassBodyopt(); } /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.parser.Parser#consumeClassDeclaration() */ protected void consumeClassDeclaration() { if (this.astPtr >= 0) { int length = this.astLengthStack[this.astLengthPtr]; TypeDeclaration typeDeclaration = (TypeDeclaration) this.astStack[this.astPtr-length]; this.javadoc = null; CompletionJavadocParser completionJavadocParser = (CompletionJavadocParser)this.javadocParser; completionJavadocParser.allPossibleTags = true; checkComment(); if (this.javadoc != null && this.cursorLocation > this.javadoc.sourceStart && this.cursorLocation < this.javadoc.sourceEnd) { // completion is in an orphan javadoc comment => replace in last read declaration to allow completion resolution typeDeclaration.javadoc = this.javadoc; } completionJavadocParser.allPossibleTags = false; } super.consumeClassDeclaration(); } protected void consumeClassHeaderName1() { super.consumeClassHeaderName1(); this.hasUnusedModifiers = false; if (this.pendingAnnotation != null) { this.pendingAnnotation.potentialAnnotatedNode = this.astStack[this.astPtr]; this.pendingAnnotation = null; } classHeaderExtendsOrImplements(false); } protected void consumeClassHeaderExtends() { pushOnElementStack(K_NEXT_TYPEREF_IS_CLASS); super.consumeClassHeaderExtends(); if (this.assistNode != null && this.assistNodeParent == null) { TypeDeclaration typeDecl = (TypeDeclaration) this.astStack[this.astPtr]; if (typeDecl != null && typeDecl.superclass == this.assistNode) this.assistNodeParent = typeDecl; } popElement(K_NEXT_TYPEREF_IS_CLASS); popElement(K_EXTENDS_KEYWORD); if (this.currentElement != null && this.currentToken == TokenNameIdentifier && this.cursorLocation+1 >= this.scanner.startPosition && this.cursorLocation < this.scanner.currentPosition){ this.pushIdentifier(); int index = -1; /* check if current awaiting identifier is the completion identifier */ if ((index = this.indexOfAssistIdentifier()) > -1) { int ptr = this.identifierPtr - this.identifierLengthStack[this.identifierLengthPtr] + index + 1; RecoveredType recoveredType = (RecoveredType)this.currentElement; /* filter out cases where scanner is still inside type header */ if (!recoveredType.foundOpeningBrace) { TypeDeclaration type = recoveredType.typeDeclaration; if(type.superInterfaces == null) { type.superclass = new CompletionOnKeyword1( this.identifierStack[ptr], this.identifierPositionStack[ptr], Keywords.IMPLEMENTS); type.superclass.bits |= ASTNode.IsSuperType; this.assistNode = type.superclass; this.lastCheckPoint = type.superclass.sourceEnd + 1; } } } } } protected void consumeClassHeaderImplements() { super.consumeClassHeaderImplements(); if (this.assistNode != null && this.assistNodeParent == null) { TypeDeclaration typeDecl = (TypeDeclaration) this.astStack[this.astPtr]; if (typeDecl != null) { TypeReference[] superInterfaces = typeDecl.superInterfaces; int length = superInterfaces == null ? 0 : superInterfaces.length; for (int i = 0; i < length; i++) { if (superInterfaces[i] == this.assistNode) { this.assistNodeParent = typeDecl; } } } } } protected void consumeClassTypeElt() { pushOnElementStack(K_NEXT_TYPEREF_IS_EXCEPTION); super.consumeClassTypeElt(); popElement(K_NEXT_TYPEREF_IS_EXCEPTION); } /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.parser.Parser#consumeCompilationUnit() */ protected void consumeCompilationUnit() { this.javadoc = null; checkComment(); if (this.javadoc != null && this.cursorLocation > this.javadoc.sourceStart && this.cursorLocation < this.javadoc.sourceEnd) { // completion is in an orphan javadoc comment => replace compilation unit one to allow completion resolution this.compilationUnit.javadoc = this.javadoc; // create a fake interface declaration to allow resolution if (this.compilationUnit.types == null) { this.compilationUnit.types = new TypeDeclaration[1]; TypeDeclaration declaration = new TypeDeclaration(this.compilationUnit.compilationResult); declaration.name = FAKE_TYPE_NAME; declaration.modifiers = ClassFileConstants.AccDefault | ClassFileConstants.AccInterface; this.compilationUnit.types[0] = declaration; } } super.consumeCompilationUnit(); } protected void consumeConditionalExpression(int op) { popElement(K_CONDITIONAL_OPERATOR); super.consumeConditionalExpression(op); } protected void consumeConditionalExpressionWithName(int op) { popElement(K_CONDITIONAL_OPERATOR); super.consumeConditionalExpressionWithName(op); } protected void consumeConstructorBody() { popElement(K_BLOCK_DELIMITER); super.consumeConstructorBody(); } protected void consumeConstructorHeader() { super.consumeConstructorHeader(); pushOnElementStack(K_BLOCK_DELIMITER); } protected void consumeConstructorHeaderName() { /* no need to take action if not inside assist identifiers */ if (indexOfAssistIdentifier() < 0) { long selectorSourcePositions = this.identifierPositionStack[this.identifierPtr]; int selectorSourceEnd = (int) selectorSourcePositions; int currentAstPtr = this.astPtr; /* recovering - might be an empty message send */ if (this.currentElement != null && this.lastIgnoredToken == TokenNamenew){ // was an allocation expression super.consumeConstructorHeaderName(); } else { super.consumeConstructorHeaderName(); if (this.pendingAnnotation != null) { this.pendingAnnotation.potentialAnnotatedNode = this.astStack[this.astPtr]; this.pendingAnnotation = null; } } if (this.sourceEnds != null && this.astPtr > currentAstPtr) { // if ast node was pushed on the ast stack this.sourceEnds.put(this.astStack[this.astPtr], selectorSourceEnd); } return; } /* force to start recovering in order to get fake field behavior */ if (this.currentElement == null){ this.hasReportedError = true; // do not report any error } pushOnGenericsIdentifiersLengthStack(this.identifierLengthStack[this.identifierLengthPtr]); pushOnGenericsLengthStack(0); // handle type arguments this.restartRecovery = true; } protected void consumeConstructorHeaderNameWithTypeParameters() { long selectorSourcePositions = this.identifierPositionStack[this.identifierPtr]; int selectorSourceEnd = (int) selectorSourcePositions; int currentAstPtr = this.astPtr; if (this.currentElement != null && this.lastIgnoredToken == TokenNamenew){ // was an allocation expression super.consumeConstructorHeaderNameWithTypeParameters(); } else { super.consumeConstructorHeaderNameWithTypeParameters(); if (this.pendingAnnotation != null) { this.pendingAnnotation.potentialAnnotatedNode = this.astStack[this.astPtr]; this.pendingAnnotation = null; } } if (this.sourceEnds != null && this.astPtr > currentAstPtr) { // if ast node was pushed on the ast stack this.sourceEnds.put(this.astStack[this.astPtr], selectorSourceEnd); } } protected void consumeDefaultLabel() { super.consumeDefaultLabel(); if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_SWITCH_LABEL) { popElement(K_SWITCH_LABEL); } pushOnElementStack(K_SWITCH_LABEL, DEFAULT); } protected void consumeDimWithOrWithOutExpr() { // DimWithOrWithOutExpr ::= '[' ']' pushOnExpressionStack(null); } protected void consumeEnhancedForStatement() { super.consumeEnhancedForStatement(); if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_CONTROL_STATEMENT_DELIMITER) { popElement(K_CONTROL_STATEMENT_DELIMITER); } } protected void consumeEnhancedForStatementHeaderInit(boolean hasModifiers) { super.consumeEnhancedForStatementHeaderInit(hasModifiers); this.hasUnusedModifiers = false; if (this.pendingAnnotation != null) { this.pendingAnnotation.potentialAnnotatedNode = this.astStack[this.astPtr]; this.pendingAnnotation = null; } } protected void consumeEnterAnonymousClassBody(boolean qualified) { popElement(K_SELECTOR_QUALIFIER); popElement(K_SELECTOR_INVOCATION_TYPE); super.consumeEnterAnonymousClassBody(qualified); } protected void consumeEnterVariable() { this.identifierPtr--; this.identifierLengthPtr--; boolean isLocalDeclaration = this.nestedMethod[this.nestedType] != 0; int variableIndex = this.variablesCounter[this.nestedType]; this.hasUnusedModifiers = false; if(isLocalDeclaration || indexOfAssistIdentifier() < 0 || variableIndex != 0) { this.identifierPtr++; this.identifierLengthPtr++; if (this.pendingAnnotation != null && this.assistNode != null && this.currentElement != null && this.currentElement instanceof RecoveredMethod && !this.currentElement.foundOpeningBrace && ((RecoveredMethod)this.currentElement).methodDeclaration.declarationSourceEnd == 0) { // this is a method parameter super.consumeEnterVariable(); this.pendingAnnotation.potentialAnnotatedNode = this.astStack[this.astPtr]; this.pendingAnnotation.isParameter = true; this.pendingAnnotation = null; } else { super.consumeEnterVariable(); if (this.pendingAnnotation != null) { this.pendingAnnotation.potentialAnnotatedNode = this.astStack[this.astPtr]; this.pendingAnnotation = null; } } } else { this.restartRecovery = true; // recovery if (this.currentElement != null) { if(!checkKeyword() && !(this.currentElement instanceof RecoveredUnit && ((RecoveredUnit)this.currentElement).typeCount == 0)) { int nameSourceStart = (int)(this.identifierPositionStack[this.identifierPtr] >>> 32); this.intPtr--; TypeReference type = getTypeReference(this.intStack[this.intPtr--]); this.intPtr--; if (!(this.currentElement instanceof RecoveredType) && (this.currentToken == TokenNameDOT || (Util.getLineNumber(type.sourceStart, this.scanner.lineEnds, 0, this.scanner.linePtr) != Util.getLineNumber(nameSourceStart, this.scanner.lineEnds, 0, this.scanner.linePtr)))){ this.lastCheckPoint = nameSourceStart; this.restartRecovery = true; return; } FieldDeclaration completionFieldDecl = new CompletionOnFieldType(type, false); // consume annotations int length; if ((length = this.expressionLengthStack[this.expressionLengthPtr--]) != 0) { System.arraycopy( this.expressionStack, (this.expressionPtr -= length) + 1, completionFieldDecl.annotations = new Annotation[length], 0, length); } completionFieldDecl.modifiers = this.intStack[this.intPtr--]; this.assistNode = completionFieldDecl; this.lastCheckPoint = type.sourceEnd + 1; this.currentElement = this.currentElement.add(completionFieldDecl, 0); this.lastIgnoredToken = -1; } } } } protected void consumeEnumConstantHeaderName() { if (this.currentElement != null) { if (!(this.currentElement instanceof RecoveredType || (this.currentElement instanceof RecoveredField && ((RecoveredField)this.currentElement).fieldDeclaration.type == null)) || (this.lastIgnoredToken == TokenNameDOT)) { super.consumeEnumConstantHeaderName(); return; } } super.consumeEnumConstantHeaderName(); if (this.pendingAnnotation != null) { this.pendingAnnotation.potentialAnnotatedNode = this.astStack[this.astPtr]; this.pendingAnnotation = null; } } protected void consumeEnumConstantNoClassBody() { super.consumeEnumConstantNoClassBody(); if ((this.currentToken == TokenNameCOMMA || this.currentToken == TokenNameSEMICOLON) && this.astStack[this.astPtr] instanceof FieldDeclaration) { if (this.sourceEnds != null) { this.sourceEnds.put(this.astStack[this.astPtr], this.scanner.currentPosition - 1); } } } protected void consumeEnumConstantWithClassBody() { super.consumeEnumConstantWithClassBody(); if ((this.currentToken == TokenNameCOMMA || this.currentToken == TokenNameSEMICOLON) && this.astStack[this.astPtr] instanceof FieldDeclaration) { if (this.sourceEnds != null) { this.sourceEnds.put(this.astStack[this.astPtr], this.scanner.currentPosition - 1); } } } protected void consumeEnumHeaderName() { super.consumeEnumHeaderName(); this.hasUnusedModifiers = false; if (this.pendingAnnotation != null) { this.pendingAnnotation.potentialAnnotatedNode = this.astStack[this.astPtr]; this.pendingAnnotation = null; } } protected void consumeEnumHeaderNameWithTypeParameters() { super.consumeEnumHeaderNameWithTypeParameters(); if (this.pendingAnnotation != null) { this.pendingAnnotation.potentialAnnotatedNode = this.astStack[this.astPtr]; this.pendingAnnotation = null; } } protected void consumeEqualityExpression(int op) { super.consumeEqualityExpression(op); popElement(K_BINARY_OPERATOR); BinaryExpression exp = (BinaryExpression) this.expressionStack[this.expressionPtr]; if(this.assistNode != null && exp.right == this.assistNode) { this.assistNodeParent = exp; } } protected void consumeEqualityExpressionWithName(int op) { super.consumeEqualityExpressionWithName(op); popElement(K_BINARY_OPERATOR); BinaryExpression exp = (BinaryExpression) this.expressionStack[this.expressionPtr]; if(this.assistNode != null && exp.right == this.assistNode) { this.assistNodeParent = exp; } } protected void consumeExitVariableWithInitialization() { super.consumeExitVariableWithInitialization(); if ((this.currentToken == TokenNameCOMMA || this.currentToken == TokenNameSEMICOLON) && this.astStack[this.astPtr] instanceof FieldDeclaration) { if (this.sourceEnds != null) { this.sourceEnds.put(this.astStack[this.astPtr], this.scanner.currentPosition - 1); } } // does not keep the initialization if completion is not inside AbstractVariableDeclaration variable = (AbstractVariableDeclaration) this.astStack[this.astPtr]; if (this.cursorLocation + 1 < variable.initialization.sourceStart || this.cursorLocation > variable.initialization.sourceEnd) { variable.initialization = null; } else if (this.assistNode != null && this.assistNode == variable.initialization) { this.assistNodeParent = variable; } } protected void consumeExitVariableWithoutInitialization() { // ExitVariableWithoutInitialization ::= $empty // do nothing by default super.consumeExitVariableWithoutInitialization(); if ((this.currentToken == TokenNameCOMMA || this.currentToken == TokenNameSEMICOLON) && this.astStack[this.astPtr] instanceof FieldDeclaration) { if (this.sourceEnds != null) { this.sourceEnds.put(this.astStack[this.astPtr], this.scanner.currentPosition - 1); } } } protected void consumeExplicitConstructorInvocation(int flag, int recFlag) { popElement(K_SELECTOR_QUALIFIER); popElement(K_SELECTOR_INVOCATION_TYPE); super.consumeExplicitConstructorInvocation(flag, recFlag); } /* * 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; this.qualifier = -1; if (this.indexOfAssistIdentifier() < 0) { super.consumeFieldAccess(isSuperAccess); } else { pushCompletionOnMemberAccessOnExpressionStack(isSuperAccess); } } protected void consumeForceNoDiet() { super.consumeForceNoDiet(); if (isInsideMethod()) { pushOnElementStack(K_LOCAL_INITIALIZER_DELIMITER); } } protected void consumeFormalParameter(boolean isVarArgs) { if (this.indexOfAssistIdentifier() < 0) { super.consumeFormalParameter(isVarArgs); if (this.pendingAnnotation != null) { this.pendingAnnotation.potentialAnnotatedNode = this.astStack[this.astPtr]; this.pendingAnnotation = null; } } else { this.identifierLengthPtr--; char[] identifierName = this.identifierStack[this.identifierPtr]; long namePositions = this.identifierPositionStack[this.identifierPtr--]; int extendedDimensions = this.intStack[this.intPtr--]; int endOfEllipsis = 0; if (isVarArgs) { endOfEllipsis = this.intStack[this.intPtr--]; } int firstDimensions = this.intStack[this.intPtr--]; final int typeDimensions = firstDimensions + extendedDimensions; TypeReference type = getTypeReference(typeDimensions); if (isVarArgs) { type = copyDims(type, typeDimensions + 1); if (extendedDimensions == 0) { type.sourceEnd = endOfEllipsis; } type.bits |= ASTNode.IsVarArgs; // set isVarArgs } this.intPtr -= 2; CompletionOnArgumentName arg = new CompletionOnArgumentName( identifierName, namePositions, type, this.intStack[this.intPtr + 1] & ~ClassFileConstants.AccDeprecated); // modifiers // consume annotations int length; if ((length = this.expressionLengthStack[this.expressionLengthPtr--]) != 0) { System.arraycopy( this.expressionStack, (this.expressionPtr -= length) + 1, arg.annotations = new Annotation[length], 0, length); } arg.isCatchArgument = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BETWEEN_CATCH_AND_RIGHT_PAREN; pushOnAstStack(arg); this.assistNode = arg; this.lastCheckPoint = (int) namePositions; this.isOrphanCompletionNode = true; /* if incomplete method header, listLength counter will not have been reset, indicating that some arguments are available on the stack */ this.listLength++; } } protected void consumeGenericTypeWithDiamond() { super.consumeGenericTypeWithDiamond(); // we need to pop the <> of the diamond from the stack. // This is not required in usual case when the type argument isn't elided // since the < and > get popped while parsing the type argument. popElement(K_BINARY_OPERATOR); // pop > popElement(K_BINARY_OPERATOR); // pop < } protected void consumeStatementFor() { super.consumeStatementFor(); if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_CONTROL_STATEMENT_DELIMITER) { popElement(K_CONTROL_STATEMENT_DELIMITER); } } protected void consumeStatementIfNoElse() { super.consumeStatementIfNoElse(); if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_CONTROL_STATEMENT_DELIMITER) { popElement(K_CONTROL_STATEMENT_DELIMITER); } } protected void consumeStatementIfWithElse() { super.consumeStatementIfWithElse(); if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_CONTROL_STATEMENT_DELIMITER) { popElement(K_CONTROL_STATEMENT_DELIMITER); } } protected void consumeInsideCastExpression() { int end = this.intStack[this.intPtr--]; boolean isParameterized =(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_PARAMETERIZED_CAST); if(isParameterized) { popElement(K_PARAMETERIZED_CAST); if(this.identifierLengthStack[this.identifierLengthPtr] > 0) { pushOnGenericsIdentifiersLengthStack(this.identifierLengthStack[this.identifierLengthPtr]); } } else { if(this.identifierLengthStack[this.identifierLengthPtr] > 0) { pushOnGenericsIdentifiersLengthStack(this.identifierLengthStack[this.identifierLengthPtr]); pushOnGenericsLengthStack(0); } } Expression castType = getTypeReference(this.intStack[this.intPtr--]); if(isParameterized) { this.intPtr--; } castType.sourceEnd = end - 1; castType.sourceStart = this.intStack[this.intPtr--] + 1; pushOnExpressionStack(castType); pushOnElementStack(K_CAST_STATEMENT); } protected void consumeInsideCastExpressionLL1() { if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_PARAMETERIZED_CAST) { popElement(K_PARAMETERIZED_CAST); } if (!this.record) { super.consumeInsideCastExpressionLL1(); } else { boolean temp = this.skipRecord; try { this.skipRecord = true; super.consumeInsideCastExpressionLL1(); if (this.record) { Expression typeReference = this.expressionStack[this.expressionPtr]; if (!isAlreadyPotentialName(typeReference.sourceStart)) { addPotentialName(null, typeReference.sourceStart, typeReference.sourceEnd); } } } finally { this.skipRecord = temp; } } pushOnElementStack(K_CAST_STATEMENT); } protected void consumeInsideCastExpressionWithQualifiedGenerics() { popElement(K_PARAMETERIZED_CAST); Expression castType; int end = this.intStack[this.intPtr--]; int dim = this.intStack[this.intPtr--]; TypeReference rightSide = getTypeReference(0); castType = computeQualifiedGenericsFromRightSide(rightSide, dim); this.intPtr--; castType.sourceEnd = end - 1; castType.sourceStart = this.intStack[this.intPtr--] + 1; pushOnExpressionStack(castType); pushOnElementStack(K_CAST_STATEMENT); } protected void consumeInstanceOfExpression() { super.consumeInstanceOfExpression(); popElement(K_BINARY_OPERATOR); // to handle https://bugs.eclipse.org/bugs/show_bug.cgi?id=261534 if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BETWEEN_IF_AND_RIGHT_PAREN) { pushOnElementStack(K_BETWEEN_INSTANCEOF_AND_RPAREN, IF, this.expressionStack[this.expressionPtr]); } InstanceOfExpression exp = (InstanceOfExpression) this.expressionStack[this.expressionPtr]; if(this.assistNode != null && exp.type == this.assistNode) { this.assistNodeParent = exp; } } protected void consumeInstanceOfExpressionWithName() { super.consumeInstanceOfExpressionWithName(); popElement(K_BINARY_OPERATOR); InstanceOfExpression exp = (InstanceOfExpression) this.expressionStack[this.expressionPtr]; if(this.assistNode != null && exp.type == this.assistNode) { this.assistNodeParent = exp; } } protected void consumeInterfaceHeaderName1() { super.consumeInterfaceHeaderName1(); this.hasUnusedModifiers = false; if (this.pendingAnnotation != null) { this.pendingAnnotation.potentialAnnotatedNode = this.astStack[this.astPtr]; this.pendingAnnotation = null; } classHeaderExtendsOrImplements(true); } protected void consumeInterfaceHeaderExtends() { super.consumeInterfaceHeaderExtends(); popElement(K_EXTENDS_KEYWORD); } protected void consumeInterfaceType() { pushOnElementStack(K_NEXT_TYPEREF_IS_INTERFACE); super.consumeInterfaceType(); popElement(K_NEXT_TYPEREF_IS_INTERFACE); } protected void consumeMethodInvocationName() { popElement(K_SELECTOR_QUALIFIER); popElement(K_SELECTOR_INVOCATION_TYPE); super.consumeMethodInvocationName(); } protected void consumeMethodInvocationNameWithTypeArguments() { popElement(K_SELECTOR_QUALIFIER); popElement(K_SELECTOR_INVOCATION_TYPE); super.consumeMethodInvocationNameWithTypeArguments(); } protected void consumeMethodInvocationPrimary() { popElement(K_SELECTOR_QUALIFIER); popElement(K_SELECTOR_INVOCATION_TYPE); super.consumeMethodInvocationPrimary(); } protected void consumeMethodInvocationPrimaryWithTypeArguments() { popElement(K_SELECTOR_QUALIFIER); popElement(K_SELECTOR_INVOCATION_TYPE); super.consumeMethodInvocationPrimaryWithTypeArguments(); } protected void consumeMethodInvocationSuper() { popElement(K_SELECTOR_QUALIFIER); popElement(K_SELECTOR_INVOCATION_TYPE); super.consumeMethodInvocationSuper(); } protected void consumeMethodInvocationSuperWithTypeArguments() { popElement(K_SELECTOR_QUALIFIER); popElement(K_SELECTOR_INVOCATION_TYPE); super.consumeMethodInvocationSuperWithTypeArguments(); } protected void consumeMethodHeaderName(boolean isAnnotationMethod) { if(this.indexOfAssistIdentifier() < 0) { this.identifierPtr--; this.identifierLengthPtr--; if(this.indexOfAssistIdentifier() != 0 || this.identifierLengthStack[this.identifierLengthPtr] != this.genericsIdentifiersLengthStack[this.genericsIdentifiersLengthPtr]) { this.identifierPtr++; this.identifierLengthPtr++; long selectorSourcePositions = this.identifierPositionStack[this.identifierPtr]; int selectorSourceEnd = (int) selectorSourcePositions; int currentAstPtr = this.astPtr; super.consumeMethodHeaderName(isAnnotationMethod); if (this.sourceEnds != null && this.astPtr > currentAstPtr) { // if ast node was pushed on the ast stack this.sourceEnds.put(this.astStack[this.astPtr], selectorSourceEnd); } if (this.pendingAnnotation != null) { this.pendingAnnotation.potentialAnnotatedNode = this.astStack[this.astPtr]; this.pendingAnnotation = null; } } else { this.restartRecovery = true; // recovery if (this.currentElement != null) { //name char[] selector = this.identifierStack[this.identifierPtr + 1]; long selectorSource = this.identifierPositionStack[this.identifierPtr + 1]; //type TypeReference type = getTypeReference(this.intStack[this.intPtr--]); ((CompletionOnSingleTypeReference)type).isCompletionNode = false; //modifiers int declarationSourceStart = this.intStack[this.intPtr--]; int mod = this.intStack[this.intPtr--]; if(Util.getLineNumber(type.sourceStart, this.scanner.lineEnds, 0, this.scanner.linePtr) != Util.getLineNumber((int) (selectorSource >>> 32), this.scanner.lineEnds, 0, this.scanner.linePtr)) { FieldDeclaration completionFieldDecl = new CompletionOnFieldType(type, false); // consume annotations int length; if ((length = this.expressionLengthStack[this.expressionLengthPtr--]) != 0) { System.arraycopy( this.expressionStack, (this.expressionPtr -= length) + 1, completionFieldDecl.annotations = new Annotation[length], 0, length); } completionFieldDecl.modifiers = mod; this.assistNode = completionFieldDecl; this.lastCheckPoint = type.sourceEnd + 1; this.currentElement = this.currentElement.add(completionFieldDecl, 0); this.lastIgnoredToken = -1; } else { CompletionOnMethodReturnType md = new CompletionOnMethodReturnType(type, this.compilationUnit.compilationResult); // consume annotations int length; if ((length = this.expressionLengthStack[this.expressionLengthPtr--]) != 0) { System.arraycopy( this.expressionStack, (this.expressionPtr -= length) + 1, md.annotations = new Annotation[length], 0, length); } md.selector = selector; md.declarationSourceStart = declarationSourceStart; md.modifiers = mod; md.bodyStart = this.lParenPos+1; this.listLength = 0; // initialize listLength before reading parameters/throws this.assistNode = md; this.lastCheckPoint = md.bodyStart; this.currentElement = this.currentElement.add(md, 0); this.lastIgnoredToken = -1; // javadoc md.javadoc = this.javadoc; this.javadoc = null; } } } } else { // MethodHeaderName ::= Modifiersopt Type 'Identifier' '(' CompletionOnMethodName md = new CompletionOnMethodName(this.compilationUnit.compilationResult); //name md.selector = this.identifierStack[this.identifierPtr]; long selectorSource = this.identifierPositionStack[this.identifierPtr--]; this.identifierLengthPtr--; //type md.returnType = getTypeReference(this.intStack[this.intPtr--]); //modifiers md.declarationSourceStart = this.intStack[this.intPtr--]; md.modifiers = this.intStack[this.intPtr--]; // consume annotations int length; if ((length = this.expressionLengthStack[this.expressionLengthPtr--]) != 0) { System.arraycopy( this.expressionStack, (this.expressionPtr -= length) + 1, md.annotations = new Annotation[length], 0, length); } // javadoc md.javadoc = this.javadoc; this.javadoc = null; //highlight starts at selector start md.sourceStart = (int) (selectorSource >>> 32); md.selectorEnd = (int) selectorSource; pushOnAstStack(md); md.sourceEnd = this.lParenPos; md.bodyStart = this.lParenPos+1; this.listLength = 0; // initialize listLength before reading parameters/throws this.assistNode = md; this.lastCheckPoint = md.sourceEnd; // recovery if (this.currentElement != null){ if (this.currentElement instanceof RecoveredType //|| md.modifiers != 0 || (Util.getLineNumber(md.returnType.sourceStart, this.scanner.lineEnds, 0, this.scanner.linePtr) == Util.getLineNumber(md.sourceStart, this.scanner.lineEnds, 0, this.scanner.linePtr))){ this.lastCheckPoint = md.bodyStart; this.currentElement = this.currentElement.add(md, 0); this.lastIgnoredToken = -1; } else { this.lastCheckPoint = md.sourceStart; this.restartRecovery = true; } } } } protected void consumeMethodHeaderNameWithTypeParameters( boolean isAnnotationMethod) { long selectorSourcePositions = this.identifierPositionStack[this.identifierPtr]; int selectorSourceEnd = (int) selectorSourcePositions; int currentAstPtr = this.astPtr; super.consumeMethodHeaderNameWithTypeParameters(isAnnotationMethod); if (this.sourceEnds != null && this.astPtr > currentAstPtr) {// if ast node was pushed on the ast stack this.sourceEnds.put(this.astStack[this.astPtr], selectorSourceEnd); } if (this.pendingAnnotation != null) { this.pendingAnnotation.potentialAnnotatedNode = this.astStack[this.astPtr]; this.pendingAnnotation = null; } } protected void consumeMethodHeaderRightParen() { super.consumeMethodHeaderRightParen(); if (this.currentElement != null && this.currentToken == TokenNameIdentifier && this.cursorLocation+1 >= this.scanner.startPosition && this.cursorLocation < this.scanner.currentPosition){ this.pushIdentifier(); int index = -1; /* check if current awaiting identifier is the completion identifier */ if ((index = this.indexOfAssistIdentifier()) > -1) { int ptr = this.identifierPtr - this.identifierLengthStack[this.identifierLengthPtr] + index + 1; if (this.currentElement instanceof RecoveredMethod){ RecoveredMethod recoveredMethod = (RecoveredMethod)this.currentElement; /* filter out cases where scanner is still inside type header */ if (!recoveredMethod.foundOpeningBrace) { AbstractMethodDeclaration method = recoveredMethod.methodDeclaration; if(method.thrownExceptions == null) { CompletionOnKeyword1 completionOnKeyword = new CompletionOnKeyword1( this.identifierStack[ptr], this.identifierPositionStack[ptr], Keywords.THROWS); method.thrownExceptions = new TypeReference[]{completionOnKeyword}; recoveredMethod.foundOpeningBrace = true; this.assistNode = completionOnKeyword; this.lastCheckPoint = completionOnKeyword.sourceEnd + 1; } } } } } } protected void consumeMethodHeaderExtendedDims() { super.consumeMethodHeaderExtendedDims(); if (this.currentElement != null && this.currentToken == TokenNameIdentifier && this.cursorLocation+1 >= this.scanner.startPosition && this.cursorLocation < this.scanner.currentPosition){ this.pushIdentifier(); int index = -1; /* check if current awaiting identifier is the completion identifier */ if ((index = this.indexOfAssistIdentifier()) > -1) { int ptr = this.identifierPtr - this.identifierLengthStack[this.identifierLengthPtr] + index + 1; RecoveredMethod recoveredMethod = (RecoveredMethod)this.currentElement; /* filter out cases where scanner is still inside type header */ if (!recoveredMethod.foundOpeningBrace) { AbstractMethodDeclaration method = recoveredMethod.methodDeclaration; if(method.thrownExceptions == null) { CompletionOnKeyword1 completionOnKeyword = new CompletionOnKeyword1( this.identifierStack[ptr], this.identifierPositionStack[ptr], Keywords.THROWS); method.thrownExceptions = new TypeReference[]{completionOnKeyword}; recoveredMethod.foundOpeningBrace = true; this.assistNode = completionOnKeyword; this.lastCheckPoint = completionOnKeyword.sourceEnd + 1; } } } } } protected void consumeAnnotationAsModifier() { super.consumeAnnotationAsModifier(); if (isInsideMethod()) { this.hasUnusedModifiers = true; } } protected void consumeAdditionalBound() { super.consumeAdditionalBound(); ASTNode node = this.genericsStack[this.genericsPtr]; if (node instanceof CompletionOnSingleTypeReference) { ((CompletionOnSingleTypeReference) node).setKind(CompletionOnQualifiedTypeReference.K_INTERFACE); } else if (node instanceof CompletionOnQualifiedTypeReference) { ((CompletionOnQualifiedTypeReference) node).setKind(CompletionOnQualifiedTypeReference.K_INTERFACE); } } protected void consumeAdditionalBound1() { super.consumeAdditionalBound1(); ASTNode node = this.genericsStack[this.genericsPtr]; if (node instanceof CompletionOnSingleTypeReference) { ((CompletionOnSingleTypeReference) node).setKind(CompletionOnQualifiedTypeReference.K_INTERFACE); } else if (node instanceof CompletionOnQualifiedTypeReference) { ((CompletionOnQualifiedTypeReference) node).setKind(CompletionOnQualifiedTypeReference.K_INTERFACE); } } protected void consumeAnnotationName() { int index; if ((index = this.indexOfAssistIdentifier()) < 0) { super.consumeAnnotationName(); this.pushOnElementStack(K_BETWEEN_ANNOTATION_NAME_AND_RPAREN, LPAREN_NOT_CONSUMED); return; } MarkerAnnotation markerAnnotation = null; int length = this.identifierLengthStack[this.identifierLengthPtr]; TypeReference typeReference; /* retrieve identifiers subset and whole positions, the assist node positions should include the entire replaced source. */ char[][] subset = identifierSubSet(index); this.identifierLengthPtr--; this.identifierPtr -= length; long[] positions = new long[length]; System.arraycopy( this.identifierPositionStack, this.identifierPtr + 1, positions, 0, length); /* build specific assist on type reference */ if (index == 0) { /* assist inside first identifier */ typeReference = createSingleAssistTypeReference( assistIdentifier(), positions[0]); } else { /* assist inside subsequent identifier */ typeReference = createQualifiedAssistTypeReference( subset, assistIdentifier(), positions); } markerAnnotation = new CompletionOnMarkerAnnotationName(typeReference, typeReference.sourceStart); this.intPtr--; markerAnnotation.declarationSourceEnd = markerAnnotation.sourceEnd; pushOnExpressionStack(markerAnnotation); this.assistNode = markerAnnotation; this.isOrphanCompletionNode = true; this.lastCheckPoint = markerAnnotation.sourceEnd + 1; this.pushOnElementStack(K_BETWEEN_ANNOTATION_NAME_AND_RPAREN, LPAREN_NOT_CONSUMED | ANNOTATION_NAME_COMPLETION); } protected void consumeAnnotationTypeDeclarationHeaderName() { super.consumeAnnotationTypeDeclarationHeaderName(); this.hasUnusedModifiers = false; if (this.pendingAnnotation != null) { this.pendingAnnotation.potentialAnnotatedNode = this.astStack[this.astPtr]; this.pendingAnnotation = null; } } protected void consumeAnnotationTypeDeclarationHeaderNameWithTypeParameters() { super.consumeAnnotationTypeDeclarationHeaderNameWithTypeParameters(); this.hasUnusedModifiers = false; if (this.pendingAnnotation != null) { this.pendingAnnotation.potentialAnnotatedNode = this.astStack[this.astPtr]; this.pendingAnnotation = null; } } protected void consumeLabel() { super.consumeLabel(); pushOnLabelStack(this.identifierStack[this.identifierPtr]); this.pushOnElementStack(K_LABEL, this.labelPtr); } protected void consumeMarkerAnnotation() { if (this.topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BETWEEN_ANNOTATION_NAME_AND_RPAREN && (this.topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) & ANNOTATION_NAME_COMPLETION) != 0 ) { popElement(K_BETWEEN_ANNOTATION_NAME_AND_RPAREN); this.restartRecovery = true; } else { popElement(K_BETWEEN_ANNOTATION_NAME_AND_RPAREN); super.consumeMarkerAnnotation(); } } protected void consumeMemberValuePair() { /* check if current awaiting identifier is the completion identifier */ if (this.indexOfAssistIdentifier() < 0){ super.consumeMemberValuePair(); MemberValuePair memberValuePair = (MemberValuePair) this.astStack[this.astPtr]; if(this.assistNode != null && memberValuePair.value == this.assistNode) { this.assistNodeParent = memberValuePair; } return; } char[] simpleName = this.identifierStack[this.identifierPtr]; long position = this.identifierPositionStack[this.identifierPtr--]; this.identifierLengthPtr--; int end = (int) position; int start = (int) (position >>> 32); this.expressionPtr--; this.expressionLengthPtr--; CompletionOnMemberValueName memberValueName = new CompletionOnMemberValueName(simpleName,start, end); pushOnAstStack(memberValueName); this.assistNode = memberValueName; this.lastCheckPoint = this.assistNode.sourceEnd + 1; this.isOrphanCompletionNode = true; this.restartRecovery = true; } protected void consumeMemberValueAsName() { if ((indexOfAssistIdentifier()) < 0) { super.consumeMemberValueAsName(); } else { super.consumeMemberValueAsName(); if(this.topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BETWEEN_ANNOTATION_NAME_AND_RPAREN) { this.restartRecovery = true; } } } protected void consumeMethodBody() { popElement(K_BLOCK_DELIMITER); super.consumeMethodBody(); } protected void consumeMethodHeader() { super.consumeMethodHeader(); pushOnElementStack(K_BLOCK_DELIMITER); } protected void consumeMethodDeclaration(boolean isNotAbstract) { if (!isNotAbstract) { popElement(K_BLOCK_DELIMITER); } super.consumeMethodDeclaration(isNotAbstract); } protected void consumeModifiers() { super.consumeModifiers(); // save from stack values this.lastModifiersStart = this.intStack[this.intPtr]; this.lastModifiers = this.intStack[this.intPtr-1]; } protected void consumeReferenceType() { if (this.identifierLengthStack[this.identifierLengthPtr] > 1) { // reducing a qualified name // potential receiver is being poped, so reset potential receiver this.invocationType = NO_RECEIVER; this.qualifier = -1; } super.consumeReferenceType(); } protected void consumeRestoreDiet() { super.consumeRestoreDiet(); if (isInsideMethod()) { popElement(K_LOCAL_INITIALIZER_DELIMITER); } } protected void consumeSingleMemberAnnotation() { if (this.topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BETWEEN_ANNOTATION_NAME_AND_RPAREN && (this.topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) & ANNOTATION_NAME_COMPLETION) != 0 ) { popElement(K_BETWEEN_ANNOTATION_NAME_AND_RPAREN); this.restartRecovery = true; } else { popElement(K_BETWEEN_ANNOTATION_NAME_AND_RPAREN); super.consumeSingleMemberAnnotation(); } } protected void consumeSingleStaticImportDeclarationName() { super.consumeSingleStaticImportDeclarationName(); this.pendingAnnotation = null; // the pending annotation cannot be attached to next nodes } protected void consumeSingleTypeImportDeclarationName() { super.consumeSingleTypeImportDeclarationName(); this.pendingAnnotation = null; // the pending annotation cannot be attached to next nodes } protected void consumeStatementBreakWithLabel() { super.consumeStatementBreakWithLabel(); if (this.record) { ASTNode breakStatement = this.astStack[this.astPtr]; if (!isAlreadyPotentialName(breakStatement.sourceStart)) { addPotentialName(null, breakStatement.sourceStart, breakStatement.sourceEnd); } } } protected void consumeStatementLabel() { popElement(K_LABEL); super.consumeStatementLabel(); } protected void consumeStatementSwitch() { super.consumeStatementSwitch(); if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_SWITCH_LABEL) { popElement(K_SWITCH_LABEL); popElement(K_BLOCK_DELIMITER); } } protected void consumeStatementWhile() { super.consumeStatementWhile(); if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_CONTROL_STATEMENT_DELIMITER) { popElement(K_CONTROL_STATEMENT_DELIMITER); } } protected void consumeStaticImportOnDemandDeclarationName() { super.consumeStaticImportOnDemandDeclarationName(); this.pendingAnnotation = null; // the pending annotation cannot be attached to next nodes } protected void consumeStaticInitializer() { super.consumeStaticInitializer(); this.pendingAnnotation = null; // the pending annotation cannot be attached to next nodes } protected void consumeNestedMethod() { super.consumeNestedMethod(); if(!(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BLOCK_DELIMITER)) pushOnElementStack(K_BLOCK_DELIMITER); } protected void consumeNormalAnnotation() { if (this.topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BETWEEN_ANNOTATION_NAME_AND_RPAREN && (this.topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) & ANNOTATION_NAME_COMPLETION) != 0 ) { popElement(K_BETWEEN_ANNOTATION_NAME_AND_RPAREN); this.restartRecovery = true; } else { popElement(K_BETWEEN_ANNOTATION_NAME_AND_RPAREN); super.consumeNormalAnnotation(); } } protected void consumePackageDeclarationName() { super.consumePackageDeclarationName(); if (this.pendingAnnotation != null) { this.pendingAnnotation.potentialAnnotatedNode = this.compilationUnit.currentPackage; this.pendingAnnotation = null; } } protected void consumePackageDeclarationNameWithModifiers() { super.consumePackageDeclarationNameWithModifiers(); if (this.pendingAnnotation != null) { this.pendingAnnotation.potentialAnnotatedNode = this.compilationUnit.currentPackage; this.pendingAnnotation = null; } } protected void consumePrimaryNoNewArrayName() { // this is class literal access, so reset potential receiver this.invocationType = NO_RECEIVER; this.qualifier = -1; super.consumePrimaryNoNewArrayName(); } protected void consumePrimaryNoNewArrayNameSuper() { // this is class literal access, so reset potential receiver this.invocationType = NO_RECEIVER; this.qualifier = -1; super.consumePrimaryNoNewArrayNameSuper(); } protected void consumePrimaryNoNewArrayNameThis() { // this is class literal access, so reset potential receiver this.invocationType = NO_RECEIVER; this.qualifier = -1; super.consumePrimaryNoNewArrayNameThis(); } protected void consumePushPosition() { super.consumePushPosition(); if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BINARY_OPERATOR) { int info = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER); popElement(K_BINARY_OPERATOR); pushOnElementStack(K_UNARY_OPERATOR, info); } } protected void consumeToken(int token) { if(this.isFirst) { super.consumeToken(token); return; } if(this.canBeExplicitConstructor == NEXTTOKEN) { this.canBeExplicitConstructor = YES; } else { this.canBeExplicitConstructor = NO; } int previous = this.previousToken; int prevIdentifierPtr = this.previousIdentifierPtr; if (isInsideMethod() || isInsideFieldInitialization() || isInsideAnnotation()) { switch(token) { case TokenNameLPAREN: if(previous == TokenNameIdentifier && topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_PARAMETERIZED_METHOD_INVOCATION) { popElement(K_PARAMETERIZED_METHOD_INVOCATION); } else { popElement(K_BETWEEN_NEW_AND_LEFT_BRACKET); } break; case TokenNameLBRACE: popElement(K_BETWEEN_NEW_AND_LEFT_BRACKET); break; case TokenNameLBRACKET: if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BETWEEN_NEW_AND_LEFT_BRACKET) { popElement(K_BETWEEN_NEW_AND_LEFT_BRACKET); pushOnElementStack(K_ARRAY_CREATION); } break; case TokenNameRBRACE: int kind = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER); switch (kind) { case K_BLOCK_DELIMITER: popElement(K_BLOCK_DELIMITER); break; case K_MEMBER_VALUE_ARRAY_INITIALIZER: popElement(K_MEMBER_VALUE_ARRAY_INITIALIZER); break; default: popElement(K_ARRAY_INITIALIZER); break; } break; case TokenNameRBRACKET: if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BETWEEN_LEFT_AND_RIGHT_BRACKET) { popElement(K_BETWEEN_LEFT_AND_RIGHT_BRACKET); } break; } } 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 && isIndirectlyInsideFieldInitialization()) { this.scanner.eofPosition = this.cursorLocation < Integer.MAX_VALUE ? this.cursorLocation+1 : this.cursorLocation; } // if in a method or if in a field initializer if (isInsideMethod() || isInsideFieldInitialization() || isInsideAttributeValue()) { switch (token) { case TokenNameDOT: switch (previous) { case TokenNamethis: // e.g. this[.]fred() this.invocationType = EXPLICIT_RECEIVER; break; case TokenNamesuper: // e.g. super[.]fred() this.invocationType = SUPER_RECEIVER; break; case TokenNameIdentifier: // e.g. bar[.]fred() if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) != K_BETWEEN_NEW_AND_LEFT_BRACKET) { if (this.identifierPtr != prevIdentifierPtr) { // if identifier has been consumed, e.g. this.x[.]fred() this.invocationType = EXPLICIT_RECEIVER; } else { this.invocationType = NAME_RECEIVER; } } break; } break; case TokenNameIdentifier: if (previous == TokenNameDOT) { // e.g. foo().[fred]() if (this.invocationType != SUPER_RECEIVER // e.g. not super.[fred]() && this.invocationType != NAME_RECEIVER // e.g. not bar.[fred]() && this.invocationType != ALLOCATION // e.g. not new foo.[Bar]() && this.invocationType != QUALIFIED_ALLOCATION) { // e.g. not fred().new foo.[Bar]() this.invocationType = EXPLICIT_RECEIVER; this.qualifier = this.expressionPtr; } } if (previous == TokenNameGREATER) { // e.g. foo().<X>[fred]() if (this.invocationType != SUPER_RECEIVER // e.g. not super.<X>[fred]() && this.invocationType != NAME_RECEIVER // e.g. not bar.<X>[fred]() && this.invocationType != ALLOCATION // e.g. not new foo.<X>[Bar]() && this.invocationType != QUALIFIED_ALLOCATION) { // e.g. not fred().new foo.<X>[Bar]() if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_PARAMETERIZED_METHOD_INVOCATION) { this.invocationType = EXPLICIT_RECEIVER; this.qualifier = this.expressionPtr; } } } break; case TokenNamenew: pushOnElementStack(K_BETWEEN_NEW_AND_LEFT_BRACKET); 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) { // e.g. fred().[new] X() this.invocationType = QUALIFIED_ALLOCATION; } else { // e.g. [new] X() this.invocationType = ALLOCATION; } break; case TokenNamethis: if (previous == TokenNameDOT) { // e.g. fred().[this]() this.invocationType = QUALIFIED_ALLOCATION; this.qualifier = this.expressionPtr; } break; case TokenNamesuper: if (previous == TokenNameDOT) { // e.g. fred().[super]() this.invocationType = QUALIFIED_ALLOCATION; this.qualifier = this.expressionPtr; } break; case TokenNamecatch: pushOnElementStack(K_BETWEEN_CATCH_AND_RIGHT_PAREN); break; case TokenNameLPAREN: if (this.invocationType == NO_RECEIVER || this.invocationType == NAME_RECEIVER || this.invocationType == SUPER_RECEIVER) { this.qualifier = this.expressionPtr; // remenber the last expression so that arguments are correctly computed } switch (previous) { case TokenNameIdentifier: // e.g. fred[(]) or foo.fred[(]) if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_SELECTOR) { int info = 0; if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER,1) == K_BETWEEN_ANNOTATION_NAME_AND_RPAREN && (info=topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER,1) & LPAREN_NOT_CONSUMED) != 0) { popElement(K_SELECTOR); popElement(K_BETWEEN_ANNOTATION_NAME_AND_RPAREN); if ((info & ANNOTATION_NAME_COMPLETION) != 0) { this.pushOnElementStack(K_BETWEEN_ANNOTATION_NAME_AND_RPAREN, LPAREN_CONSUMED | ANNOTATION_NAME_COMPLETION); } else { this.pushOnElementStack(K_BETWEEN_ANNOTATION_NAME_AND_RPAREN, LPAREN_CONSUMED); } } else { this.pushOnElementStack(K_SELECTOR_INVOCATION_TYPE, this.invocationType); this.pushOnElementStack(K_SELECTOR_QUALIFIER, this.qualifier); } } this.qualifier = -1; this.invocationType = NO_RECEIVER; break; case TokenNamethis: // explicit constructor invocation, e.g. this[(]1, 2) if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_SELECTOR) { this.pushOnElementStack(K_SELECTOR_INVOCATION_TYPE, (this.invocationType == QUALIFIED_ALLOCATION) ? QUALIFIED_ALLOCATION : ALLOCATION); this.pushOnElementStack(K_SELECTOR_QUALIFIER, this.qualifier); } this.qualifier = -1; this.invocationType = NO_RECEIVER; break; case TokenNamesuper: // explicit constructor invocation, e.g. super[(]1, 2) if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_SELECTOR) { this.pushOnElementStack(K_SELECTOR_INVOCATION_TYPE, (this.invocationType == QUALIFIED_ALLOCATION) ? QUALIFIED_ALLOCATION : ALLOCATION); this.pushOnElementStack(K_SELECTOR_QUALIFIER, this.qualifier); } this.qualifier = -1; this.invocationType = NO_RECEIVER; break; case TokenNameGREATER: // explicit constructor invocation, e.g. Fred<X>[(]1, 2) case TokenNameRIGHT_SHIFT: // or fred<X<X>>[(]1, 2) case TokenNameUNSIGNED_RIGHT_SHIFT: //or Fred<X<X<X>>>[(]1, 2) if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_SELECTOR) { int info; if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER, 1) == K_BINARY_OPERATOR && ((info = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER, 1)) == GREATER || info == RIGHT_SHIFT || info == UNSIGNED_RIGHT_SHIFT)) { // it's not a selector invocation popElement(K_SELECTOR); } else { this.pushOnElementStack(K_SELECTOR_INVOCATION_TYPE, (this.invocationType == QUALIFIED_ALLOCATION) ? QUALIFIED_ALLOCATION : ALLOCATION); this.pushOnElementStack(K_SELECTOR_QUALIFIER, this.qualifier); } } this.qualifier = -1; this.invocationType = NO_RECEIVER; break; } break; case TokenNameLBRACE: int kind = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER); if(kind == K_FIELD_INITIALIZER_DELIMITER || kind == K_LOCAL_INITIALIZER_DELIMITER || kind == K_ARRAY_CREATION) { pushOnElementStack(K_ARRAY_INITIALIZER, this.endPosition); } else if (kind == K_BETWEEN_ANNOTATION_NAME_AND_RPAREN) { pushOnElementStack(K_MEMBER_VALUE_ARRAY_INITIALIZER, this.endPosition); } else { if (kind == K_CONTROL_STATEMENT_DELIMITER) { int info = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER); popElement(K_CONTROL_STATEMENT_DELIMITER); if (info == IF) { pushOnElementStack(K_BLOCK_DELIMITER, IF, this.expressionStack[this.expressionPtr]); } else { pushOnElementStack(K_BLOCK_DELIMITER, info); } } else { switch(previous) { case TokenNameRPAREN : switch(this.previousKind) { case K_BETWEEN_CATCH_AND_RIGHT_PAREN : pushOnElementStack(K_BLOCK_DELIMITER, CATCH); break; case K_BETWEEN_SWITCH_AND_RIGHT_PAREN : pushOnElementStack(K_BLOCK_DELIMITER, SWITCH); break; case K_BETWEEN_SYNCHRONIZED_AND_RIGHT_PAREN : pushOnElementStack(K_BLOCK_DELIMITER, SYNCHRONIZED); break; default : pushOnElementStack(K_BLOCK_DELIMITER); break; } break; case TokenNametry : pushOnElementStack(K_BLOCK_DELIMITER, TRY); break; case TokenNamedo: pushOnElementStack(K_BLOCK_DELIMITER, DO); break; default : pushOnElementStack(K_BLOCK_DELIMITER); break; } } } break; case TokenNameLBRACKET: if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) != K_ARRAY_CREATION) { pushOnElementStack(K_BETWEEN_LEFT_AND_RIGHT_BRACKET); } else { switch (previous) { case TokenNameIdentifier: case TokenNameboolean: case TokenNamebyte: case TokenNamechar: case TokenNamedouble: case TokenNamefloat: case TokenNameint: case TokenNamelong: case TokenNameshort: case TokenNameGREATER: case TokenNameRIGHT_SHIFT: case TokenNameUNSIGNED_RIGHT_SHIFT: this.invocationType = NO_RECEIVER; this.qualifier = -1; break; } } break; case TokenNameRPAREN: switch(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER)) { case K_BETWEEN_CATCH_AND_RIGHT_PAREN : popElement(K_BETWEEN_CATCH_AND_RIGHT_PAREN); break; case K_BETWEEN_INSTANCEOF_AND_RPAREN : popElement(K_BETWEEN_INSTANCEOF_AND_RPAREN); //$FALL-THROUGH$ case K_BETWEEN_IF_AND_RIGHT_PAREN : if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == this.bracketDepth) { popElement(K_BETWEEN_IF_AND_RIGHT_PAREN); pushOnElementStack(K_CONTROL_STATEMENT_DELIMITER, IF, this.expressionStack[this.expressionPtr]); } break; case K_BETWEEN_WHILE_AND_RIGHT_PAREN : if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == this.bracketDepth) { popElement(K_BETWEEN_WHILE_AND_RIGHT_PAREN); pushOnElementStack(K_CONTROL_STATEMENT_DELIMITER, WHILE); } break; case K_BETWEEN_FOR_AND_RIGHT_PAREN : if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == this.bracketDepth) { popElement(K_BETWEEN_FOR_AND_RIGHT_PAREN); pushOnElementStack(K_CONTROL_STATEMENT_DELIMITER, FOR); } break; case K_BETWEEN_SWITCH_AND_RIGHT_PAREN : if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == this.bracketDepth) { popElement(K_BETWEEN_SWITCH_AND_RIGHT_PAREN); } break; case K_BETWEEN_SYNCHRONIZED_AND_RIGHT_PAREN : if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == this.bracketDepth) { popElement(K_BETWEEN_SYNCHRONIZED_AND_RIGHT_PAREN); } break; } break; case TokenNamethrow: pushOnElementStack(K_INSIDE_THROW_STATEMENT, this.bracketDepth); break; case TokenNameSEMICOLON: switch(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER)) { case K_INSIDE_THROW_STATEMENT : if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == this.bracketDepth) { popElement(K_INSIDE_THROW_STATEMENT); } break; case K_INSIDE_RETURN_STATEMENT : if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == this.bracketDepth) { popElement(K_INSIDE_RETURN_STATEMENT); } break; case K_INSIDE_ASSERT_STATEMENT : if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == this.bracketDepth) { popElement(K_INSIDE_ASSERT_STATEMENT); } break; case K_INSIDE_ASSERT_EXCEPTION : if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == this.bracketDepth) { popElement(K_INSIDE_ASSERT_EXCEPTION); popElement(K_INSIDE_ASSERT_STATEMENT); } break; case K_INSIDE_BREAK_STATEMENT: if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == this.bracketDepth) { popElement(K_INSIDE_BREAK_STATEMENT); } break; case K_INSIDE_CONTINUE_STATEMENT: if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == this.bracketDepth) { popElement(K_INSIDE_CONTINUE_STATEMENT); } break; case K_BETWEEN_FOR_AND_RIGHT_PAREN: if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == this.bracketDepth - 1) { popElement(K_BETWEEN_FOR_AND_RIGHT_PAREN); pushOnElementStack(K_INSIDE_FOR_CONDITIONAL, this.bracketDepth - 1); } break; case K_INSIDE_FOR_CONDITIONAL: if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == this.bracketDepth - 1) { popElement(K_INSIDE_FOR_CONDITIONAL); pushOnElementStack(K_BETWEEN_FOR_AND_RIGHT_PAREN, this.bracketDepth - 1); } break; } break; case TokenNamereturn: pushOnElementStack(K_INSIDE_RETURN_STATEMENT, this.bracketDepth); break; case TokenNameMULTIPLY: pushOnElementStack(K_BINARY_OPERATOR, MULTIPLY); break; case TokenNameDIVIDE: pushOnElementStack(K_BINARY_OPERATOR, DIVIDE); break; case TokenNameREMAINDER: pushOnElementStack(K_BINARY_OPERATOR, REMAINDER); break; case TokenNamePLUS: pushOnElementStack(K_BINARY_OPERATOR, PLUS); break; case TokenNameMINUS: pushOnElementStack(K_BINARY_OPERATOR, MINUS); break; case TokenNameLEFT_SHIFT: pushOnElementStack(K_BINARY_OPERATOR, LEFT_SHIFT); break; case TokenNameRIGHT_SHIFT: pushOnElementStack(K_BINARY_OPERATOR, RIGHT_SHIFT); break; case TokenNameUNSIGNED_RIGHT_SHIFT: pushOnElementStack(K_BINARY_OPERATOR, UNSIGNED_RIGHT_SHIFT); break; case TokenNameLESS: switch(previous) { case TokenNameDOT : pushOnElementStack(K_PARAMETERIZED_METHOD_INVOCATION); break; case TokenNamenew : pushOnElementStack(K_PARAMETERIZED_ALLOCATION); break; } pushOnElementStack(K_BINARY_OPERATOR, LESS); break; case TokenNameGREATER: pushOnElementStack(K_BINARY_OPERATOR, GREATER); break; case TokenNameLESS_EQUAL: pushOnElementStack(K_BINARY_OPERATOR, LESS_EQUAL); break; case TokenNameGREATER_EQUAL: pushOnElementStack(K_BINARY_OPERATOR, GREATER_EQUAL); break; case TokenNameAND: pushOnElementStack(K_BINARY_OPERATOR, AND); break; case TokenNameXOR: pushOnElementStack(K_BINARY_OPERATOR, XOR); break; case TokenNameOR: // Don't push the OR operator used for union types in a catch declaration if (topKnownElementKind(COMPLETION_PARSER) != K_BETWEEN_CATCH_AND_RIGHT_PAREN) pushOnElementStack(K_BINARY_OPERATOR, OR); break; case TokenNameAND_AND: pushOnElementStack(K_BINARY_OPERATOR, AND_AND); break; case TokenNameOR_OR: pushOnElementStack(K_BINARY_OPERATOR, OR_OR); break; case TokenNamePLUS_PLUS: pushOnElementStack(K_UNARY_OPERATOR, PLUS_PLUS); break; case TokenNameMINUS_MINUS: pushOnElementStack(K_UNARY_OPERATOR, MINUS_MINUS); break; case TokenNameTWIDDLE: pushOnElementStack(K_UNARY_OPERATOR, TWIDDLE); break; case TokenNameNOT: pushOnElementStack(K_UNARY_OPERATOR, NOT); break; case TokenNameEQUAL_EQUAL: pushOnElementStack(K_BINARY_OPERATOR, EQUAL_EQUAL); break; case TokenNameNOT_EQUAL: pushOnElementStack(K_BINARY_OPERATOR, NOT_EQUAL); break; case TokenNameinstanceof: pushOnElementStack(K_BINARY_OPERATOR, INSTANCEOF); break; case TokenNameQUESTION: if(previous != TokenNameLESS && previous != TokenNameCOMMA) { pushOnElementStack(K_CONDITIONAL_OPERATOR, QUESTION); } break; case TokenNameCOLON: switch (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER)) { case K_CONDITIONAL_OPERATOR: if (topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == QUESTION) { popElement(K_CONDITIONAL_OPERATOR); pushOnElementStack(K_CONDITIONAL_OPERATOR, COLON); } break; case K_BETWEEN_CASE_AND_COLON: popElement(K_BETWEEN_CASE_AND_COLON); break; case K_BETWEEN_DEFAULT_AND_COLON: popElement(K_BETWEEN_DEFAULT_AND_COLON); break; case K_INSIDE_ASSERT_STATEMENT: pushOnElementStack(K_INSIDE_ASSERT_EXCEPTION, this.bracketDepth); break; } break; case TokenNameif: pushOnElementStack(K_BETWEEN_IF_AND_RIGHT_PAREN, this.bracketDepth); break; case TokenNameelse: if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_CONTROL_STATEMENT_DELIMITER) { popElement(K_CONTROL_STATEMENT_DELIMITER); } pushOnElementStack(K_CONTROL_STATEMENT_DELIMITER); break; case TokenNamewhile: pushOnElementStack(K_BETWEEN_WHILE_AND_RIGHT_PAREN, this.bracketDepth); break; case TokenNamefor: pushOnElementStack(K_BETWEEN_FOR_AND_RIGHT_PAREN, this.bracketDepth); break; case TokenNameswitch: pushOnElementStack(K_BETWEEN_SWITCH_AND_RIGHT_PAREN, this.bracketDepth); break; case TokenNamesynchronized: pushOnElementStack(K_BETWEEN_SYNCHRONIZED_AND_RIGHT_PAREN, this.bracketDepth); break; case TokenNameassert: pushOnElementStack(K_INSIDE_ASSERT_STATEMENT, this.bracketDepth); break; case TokenNamecase : pushOnElementStack(K_BETWEEN_CASE_AND_COLON); break; case TokenNamedefault : pushOnElementStack(K_BETWEEN_DEFAULT_AND_COLON); break; case TokenNameextends: pushOnElementStack(K_EXTENDS_KEYWORD); break; case TokenNamebreak: pushOnElementStack(K_INSIDE_BREAK_STATEMENT, this.bracketDepth); break; case TokenNamecontinue: pushOnElementStack(K_INSIDE_CONTINUE_STATEMENT, this.bracketDepth); break; } } else if (isInsideAnnotation()){ switch (token) { case TokenNameLBRACE: this.bracketDepth++; int kind = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER); if (kind == K_BETWEEN_ANNOTATION_NAME_AND_RPAREN) { pushOnElementStack(K_MEMBER_VALUE_ARRAY_INITIALIZER, this.endPosition); } break; } } else { switch(token) { case TokenNameextends: pushOnElementStack(K_EXTENDS_KEYWORD); break; case TokenNameLESS: pushOnElementStack(K_BINARY_OPERATOR, LESS); break; case TokenNameGREATER: pushOnElementStack(K_BINARY_OPERATOR, GREATER); break; case TokenNameRIGHT_SHIFT: pushOnElementStack(K_BINARY_OPERATOR, RIGHT_SHIFT); break; case TokenNameUNSIGNED_RIGHT_SHIFT: pushOnElementStack(K_BINARY_OPERATOR, UNSIGNED_RIGHT_SHIFT); break; } } } protected void consumeOnlySynchronized() { super.consumeOnlySynchronized(); this.hasUnusedModifiers = false; } protected void consumeOnlyTypeArguments() { super.consumeOnlyTypeArguments(); popElement(K_BINARY_OPERATOR); if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_PARAMETERIZED_METHOD_INVOCATION) { popElement(K_PARAMETERIZED_METHOD_INVOCATION); pushOnElementStack(K_PARAMETERIZED_METHOD_INVOCATION, INSIDE_NAME); } else { popElement(K_PARAMETERIZED_ALLOCATION); } } protected void consumeOnlyTypeArgumentsForCastExpression() { super.consumeOnlyTypeArgumentsForCastExpression(); pushOnElementStack(K_PARAMETERIZED_CAST); } protected void consumeOpenFakeBlock() { super.consumeOpenFakeBlock(); pushOnElementStack(K_BLOCK_DELIMITER); } protected void consumeRightParen() { super.consumeRightParen(); } protected void consumeReferenceType1() { super.consumeReferenceType1(); popElement(K_BINARY_OPERATOR); } protected void consumeReferenceType2() { super.consumeReferenceType2(); popElement(K_BINARY_OPERATOR); } protected void consumeReferenceType3() { super.consumeReferenceType3(); popElement(K_BINARY_OPERATOR); } protected void consumeTypeArgumentReferenceType1() { super.consumeTypeArgumentReferenceType1(); popElement(K_BINARY_OPERATOR); } protected void consumeTypeArgumentReferenceType2() { super.consumeTypeArgumentReferenceType2(); popElement(K_BINARY_OPERATOR); } protected void consumeTypeArguments() { super.consumeTypeArguments(); popElement(K_BINARY_OPERATOR); } protected void consumeTypeHeaderNameWithTypeParameters() { super.consumeTypeHeaderNameWithTypeParameters(); TypeDeclaration typeDecl = (TypeDeclaration)this.astStack[this.astPtr]; classHeaderExtendsOrImplements((typeDecl.modifiers & ClassFileConstants.AccInterface) != 0); } protected void consumeTypeImportOnDemandDeclarationName() { super.consumeTypeImportOnDemandDeclarationName(); this.pendingAnnotation = null; // the pending annotation cannot be attached to next nodes } protected void consumeTypeParameters() { super.consumeTypeParameters(); popElement(K_BINARY_OPERATOR); } protected void consumeTypeParameterHeader() { super.consumeTypeParameterHeader(); TypeParameter typeParameter = (TypeParameter) this.genericsStack[this.genericsPtr]; if(typeParameter.type != null || (typeParameter.bounds != null && typeParameter.bounds.length > 0)) return; if (assistIdentifier() == null && this.currentToken == TokenNameIdentifier) { // Test below copied from CompletionScanner.getCurrentIdentifierSource() if (this.cursorLocation < this.scanner.startPosition && this.scanner.currentPosition == this.scanner.startPosition){ // fake empty identifier got issued this.pushIdentifier(); } else if (this.cursorLocation+1 >= this.scanner.startPosition && this.cursorLocation < this.scanner.currentPosition){ this.pushIdentifier(); } else { return; } } else { return; } CompletionOnKeyword1 keyword = new CompletionOnKeyword1( this.identifierStack[this.identifierPtr], this.identifierPositionStack[this.identifierPtr], Keywords.EXTENDS); keyword.canCompleteEmptyToken = true; typeParameter.type = keyword; this.identifierPtr--; this.identifierLengthPtr--; this.assistNode = typeParameter.type; this.lastCheckPoint = typeParameter.type.sourceEnd + 1; } protected void consumeTypeParameter1() { super.consumeTypeParameter1(); popElement(K_BINARY_OPERATOR); } protected void consumeTypeParameterWithExtends() { super.consumeTypeParameterWithExtends(); if (this.assistNode != null && this.assistNodeParent == null) { TypeParameter typeParameter = (TypeParameter) this.genericsStack[this.genericsPtr]; if (typeParameter != null && typeParameter.type == this.assistNode) this.assistNodeParent = typeParameter; } popElement(K_EXTENDS_KEYWORD); } protected void consumeTypeParameterWithExtendsAndBounds() { super.consumeTypeParameterWithExtendsAndBounds(); if (this.assistNode != null && this.assistNodeParent == null) { TypeParameter typeParameter = (TypeParameter) this.genericsStack[this.genericsPtr]; if (typeParameter != null && typeParameter.type == this.assistNode) this.assistNodeParent = typeParameter; } popElement(K_EXTENDS_KEYWORD); } protected void consumeTypeParameter1WithExtends() { super.consumeTypeParameter1WithExtends(); if (this.assistNode != null && this.assistNodeParent == null) { TypeParameter typeParameter = (TypeParameter) this.genericsStack[this.genericsPtr]; if (typeParameter != null && typeParameter.type == this.assistNode) this.assistNodeParent = typeParameter; } popElement(K_EXTENDS_KEYWORD); } protected void consumeTypeParameter1WithExtendsAndBounds() { super.consumeTypeParameter1WithExtendsAndBounds(); if (this.assistNode != null && this.assistNodeParent == null) { TypeParameter typeParameter = (TypeParameter) this.genericsStack[this.genericsPtr]; if (typeParameter != null && typeParameter.type == this.assistNode) this.assistNodeParent = typeParameter; } popElement(K_EXTENDS_KEYWORD); } protected void consumeUnionType() { pushOnElementStack(K_NEXT_TYPEREF_IS_EXCEPTION); super.consumeUnionType(); popElement(K_NEXT_TYPEREF_IS_EXCEPTION); } protected void consumeUnionTypeAsClassType() { pushOnElementStack(K_NEXT_TYPEREF_IS_EXCEPTION); super.consumeUnionTypeAsClassType(); popElement(K_NEXT_TYPEREF_IS_EXCEPTION); } protected void consumeWildcard() { super.consumeWildcard(); if (assistIdentifier() == null && this.currentToken == TokenNameIdentifier) { // Test below copied from CompletionScanner.getCurrentIdentifierSource() if (this.cursorLocation < this.scanner.startPosition && this.scanner.currentPosition == this.scanner.startPosition){ // fake empty identifier got issued this.pushIdentifier(); } else if (this.cursorLocation+1 >= this.scanner.startPosition && this.cursorLocation < this.scanner.currentPosition){ this.pushIdentifier(); } else { return; } } else { return; } Wildcard wildcard = (Wildcard) this.genericsStack[this.genericsPtr]; CompletionOnKeyword1 keyword = new CompletionOnKeyword1( this.identifierStack[this.identifierPtr], this.identifierPositionStack[this.identifierPtr], new char[][]{Keywords.EXTENDS, Keywords.SUPER} ); keyword.canCompleteEmptyToken = true; wildcard.kind = Wildcard.EXTENDS; wildcard.bound = keyword; this.identifierPtr--; this.identifierLengthPtr--; this.assistNode = wildcard.bound; this.lastCheckPoint = wildcard.bound.sourceEnd + 1; } protected void consumeWildcard1() { super.consumeWildcard1(); popElement(K_BINARY_OPERATOR); } protected void consumeWildcard2() { super.consumeWildcard2(); popElement(K_BINARY_OPERATOR); } protected void consumeWildcard3() { super.consumeWildcard3(); popElement(K_BINARY_OPERATOR); } protected void consumeWildcardBoundsExtends() { super.consumeWildcardBoundsExtends(); if (this.assistNode != null && this.assistNodeParent == null) { Wildcard wildcard = (Wildcard) this.genericsStack[this.genericsPtr]; if (wildcard != null && wildcard.bound == this.assistNode) this.assistNodeParent = wildcard; } popElement(K_EXTENDS_KEYWORD); } protected void consumeWildcardBounds1Extends() { super.consumeWildcardBounds1Extends(); if (this.assistNode != null && this.assistNodeParent == null) { Wildcard wildcard = (Wildcard) this.genericsStack[this.genericsPtr]; if (wildcard != null && wildcard.bound == this.assistNode) this.assistNodeParent = wildcard; } popElement(K_EXTENDS_KEYWORD); } protected void consumeWildcardBounds2Extends() { super.consumeWildcardBounds2Extends(); if (this.assistNode != null && this.assistNodeParent == null) { Wildcard wildcard = (Wildcard) this.genericsStack[this.genericsPtr]; if (wildcard != null && wildcard.bound == this.assistNode) this.assistNodeParent = wildcard; } popElement(K_EXTENDS_KEYWORD); } protected void consumeWildcardBounds3Extends() { super.consumeWildcardBounds3Extends(); if (this.assistNode != null && this.assistNodeParent == null) { Wildcard wildcard = (Wildcard) this.genericsStack[this.genericsPtr]; if (wildcard != null && wildcard.bound == this.assistNode) this.assistNodeParent = wildcard; } popElement(K_EXTENDS_KEYWORD); } protected void consumeUnaryExpression(int op) { super.consumeUnaryExpression(op); popElement(K_UNARY_OPERATOR); if(this.expressionStack[this.expressionPtr] instanceof UnaryExpression) { UnaryExpression exp = (UnaryExpression) this.expressionStack[this.expressionPtr]; if(this.assistNode != null && exp.expression == this.assistNode) { this.assistNodeParent = exp; } } } protected void consumeUnaryExpression(int op, boolean post) { super.consumeUnaryExpression(op, post); popElement(K_UNARY_OPERATOR); if(this.expressionStack[this.expressionPtr] instanceof UnaryExpression) { UnaryExpression exp = (UnaryExpression) this.expressionStack[this.expressionPtr]; if(this.assistNode != null && exp.expression == this.assistNode) { this.assistNodeParent = exp; } } } public MethodDeclaration convertToMethodDeclaration(ConstructorDeclaration c, CompilationResult compilationResult) { MethodDeclaration methodDeclaration = super.convertToMethodDeclaration(c, compilationResult); if (this.sourceEnds != null) { int selectorSourceEnd = this.sourceEnds.removeKey(c); if (selectorSourceEnd != -1) this.sourceEnds.put(methodDeclaration, selectorSourceEnd); } return methodDeclaration; } public ImportReference createAssistImportReference(char[][] tokens, long[] positions, int mod){ return new CompletionOnImportReference(tokens, positions, mod); } public ImportReference createAssistPackageReference(char[][] tokens, long[] positions){ return new CompletionOnPackageReference(tokens, positions); } public NameReference createQualifiedAssistNameReference(char[][] previousIdentifiers, char[] assistName, long[] positions){ return new CompletionOnQualifiedNameReference( previousIdentifiers, assistName, positions, isInsideAttributeValue()); } public TypeReference createQualifiedAssistTypeReference(char[][] previousIdentifiers, char[] assistName, long[] positions){ switch (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER)) { case K_NEXT_TYPEREF_IS_EXCEPTION : if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER, 1) == K_BETWEEN_CATCH_AND_RIGHT_PAREN) this.isOrphanCompletionNode = true; return new CompletionOnQualifiedTypeReference( previousIdentifiers, assistName, positions, CompletionOnQualifiedTypeReference.K_EXCEPTION); case K_NEXT_TYPEREF_IS_CLASS : return new CompletionOnQualifiedTypeReference( previousIdentifiers, assistName, positions, CompletionOnQualifiedTypeReference.K_CLASS); case K_NEXT_TYPEREF_IS_INTERFACE : return new CompletionOnQualifiedTypeReference( previousIdentifiers, assistName, positions, CompletionOnQualifiedTypeReference.K_INTERFACE); default : return new CompletionOnQualifiedTypeReference( previousIdentifiers, assistName, positions); } } public TypeReference createParameterizedQualifiedAssistTypeReference(char[][] previousIdentifiers, TypeReference[][] typeArguments, char[] assistName, TypeReference[] assistTypeArguments, long[] positions) { boolean isParameterized = false; for (int i = 0; i < typeArguments.length; i++) { if(typeArguments[i] != null) { isParameterized = true; } } if(!isParameterized) { return createQualifiedAssistTypeReference(previousIdentifiers, assistName, positions); } else { switch (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER)) { case K_NEXT_TYPEREF_IS_EXCEPTION : if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER, 1) == K_BETWEEN_CATCH_AND_RIGHT_PAREN) this.isOrphanCompletionNode = true; return new CompletionOnParameterizedQualifiedTypeReference( previousIdentifiers, typeArguments, assistName, positions, CompletionOnParameterizedQualifiedTypeReference.K_EXCEPTION); case K_NEXT_TYPEREF_IS_CLASS : return new CompletionOnParameterizedQualifiedTypeReference( previousIdentifiers, typeArguments, assistName, positions, CompletionOnParameterizedQualifiedTypeReference.K_CLASS); case K_NEXT_TYPEREF_IS_INTERFACE : return new CompletionOnParameterizedQualifiedTypeReference( previousIdentifiers, typeArguments, assistName, positions, CompletionOnParameterizedQualifiedTypeReference.K_INTERFACE); default : return new CompletionOnParameterizedQualifiedTypeReference( previousIdentifiers, typeArguments, assistName, positions); } } } public NameReference createSingleAssistNameReference(char[] assistName, long position) { int kind = topKnownElementKind(COMPLETION_OR_ASSIST_PARSER); if(!isInsideMethod()) { if (isInsideFieldInitialization()) { return new CompletionOnSingleNameReference( assistName, position, new char[][]{Keywords.FALSE, Keywords.TRUE}, false, isInsideAttributeValue()); } return new CompletionOnSingleNameReference(assistName, position, isInsideAttributeValue()); } else { boolean canBeExplicitConstructorCall = false; if(kind == K_BLOCK_DELIMITER && this.previousKind == K_BLOCK_DELIMITER && this.previousInfo == DO) { return new CompletionOnKeyword3(assistName, position, Keywords.WHILE); } else if(kind == K_BLOCK_DELIMITER && this.previousKind == K_BLOCK_DELIMITER && this.previousInfo == TRY) { return new CompletionOnKeyword3(assistName, position, new char[][]{Keywords.CATCH, Keywords.FINALLY}); } else if(kind == K_BLOCK_DELIMITER && topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == SWITCH) { return new CompletionOnKeyword3(assistName, position, new char[][]{Keywords.CASE, Keywords.DEFAULT}); } else { char[][] keywords = new char[Keywords.COUNT][]; int count = 0; if((this.lastModifiers & ClassFileConstants.AccStatic) == 0) { keywords[count++]= Keywords.SUPER; keywords[count++]= Keywords.THIS; } keywords[count++]= Keywords.NEW; // https://bugs.eclipse.org/bugs/show_bug.cgi?id=269493: Keywords are not proposed in a for // loop without block. Completion while at K_CONTROL_STATEMENT_DELIMITER case needs to handled // similar to the K_BLOCK_DELIMITER with minor differences. if(kind == K_BLOCK_DELIMITER || kind == K_CONTROL_STATEMENT_DELIMITER) { if(this.canBeExplicitConstructor == YES) { canBeExplicitConstructorCall = true; } if (this.options.complianceLevel >= ClassFileConstants.JDK1_4) { keywords[count++]= Keywords.ASSERT; } keywords[count++]= Keywords.DO; keywords[count++]= Keywords.FOR; keywords[count++]= Keywords.IF; keywords[count++]= Keywords.RETURN; keywords[count++]= Keywords.SWITCH; keywords[count++]= Keywords.SYNCHRONIZED; keywords[count++]= Keywords.THROW; keywords[count++]= Keywords.TRY; keywords[count++]= Keywords.WHILE; keywords[count++]= Keywords.FINAL; keywords[count++]= Keywords.CLASS; if(this.previousKind == K_BLOCK_DELIMITER) { switch (this.previousInfo) { case IF : keywords[count++]= Keywords.ELSE; break; case CATCH : keywords[count++]= Keywords.CATCH; keywords[count++]= Keywords.FINALLY; break; } } else if(this.previousKind == K_CONTROL_STATEMENT_DELIMITER && this.previousInfo == IF) { keywords[count++]= Keywords.ELSE; } if(isInsideLoop()) { keywords[count++]= Keywords.CONTINUE; } if(isInsideBreakable()) { keywords[count++]= Keywords.BREAK; } } else if(kind != K_BETWEEN_CASE_AND_COLON && kind != K_BETWEEN_DEFAULT_AND_COLON) { keywords[count++]= Keywords.TRUE; keywords[count++]= Keywords.FALSE; keywords[count++]= Keywords.NULL; if(kind == K_SWITCH_LABEL) { if(topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) != DEFAULT) { keywords[count++]= Keywords.DEFAULT; } keywords[count++]= Keywords.BREAK; keywords[count++]= Keywords.CASE; if (this.options.complianceLevel >= ClassFileConstants.JDK1_4) { keywords[count++]= Keywords.ASSERT; } keywords[count++]= Keywords.DO; keywords[count++]= Keywords.FOR; keywords[count++]= Keywords.IF; keywords[count++]= Keywords.RETURN; keywords[count++]= Keywords.SWITCH; keywords[count++]= Keywords.SYNCHRONIZED; keywords[count++]= Keywords.THROW; keywords[count++]= Keywords.TRY; keywords[count++]= Keywords.WHILE; keywords[count++]= Keywords.FINAL; keywords[count++]= Keywords.CLASS; if(isInsideLoop()) { keywords[count++]= Keywords.CONTINUE; } } } System.arraycopy(keywords, 0 , keywords = new char[count][], 0, count); return new CompletionOnSingleNameReference(assistName, position, keywords, canBeExplicitConstructorCall, isInsideAttributeValue()); } } } public TypeReference createSingleAssistTypeReference(char[] assistName, long position) { switch (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER)) { case K_NEXT_TYPEREF_IS_EXCEPTION : if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER, 1) == K_BETWEEN_CATCH_AND_RIGHT_PAREN) this.isOrphanCompletionNode = true; return new CompletionOnSingleTypeReference(assistName, position, CompletionOnSingleTypeReference.K_EXCEPTION) ; case K_NEXT_TYPEREF_IS_CLASS : return new CompletionOnSingleTypeReference(assistName, position, CompletionOnSingleTypeReference.K_CLASS); case K_NEXT_TYPEREF_IS_INTERFACE : return new CompletionOnSingleTypeReference(assistName, position, CompletionOnSingleTypeReference.K_INTERFACE); default : return new CompletionOnSingleTypeReference(assistName, position); } } public TypeReference createParameterizedSingleAssistTypeReference(TypeReference[] typeArguments, char[] assistName, long position) { return createSingleAssistTypeReference(assistName, position); } protected StringLiteral createStringLiteral(char[] token, int start, int end, int lineNumber) { if (start <= this.cursorLocation && this.cursorLocation <= end){ char[] source = this.scanner.source; int contentStart = start; int contentEnd = end; // " could be as unicode \u0022 int pos = contentStart; if(source[pos] == '\"') { contentStart = pos + 1; } else if(source[pos] == '\\' && source[pos+1] == 'u') { pos += 2; while (source[pos] == 'u') { pos++; } if(source[pos] == 0 && source[pos + 1] == 0 && source[pos + 2] == 2 && source[pos + 3] == 2) { contentStart = pos + 4; } } pos = contentEnd; if(source[pos] == '\"') { contentEnd = pos - 1; } else if(source.length > 5 && source[pos-4] == 'u') { if(source[pos - 3] == 0 && source[pos - 2] == 0 && source[pos - 1] == 2 && source[pos] == 2) { pos -= 5; while (pos > -1 && source[pos] == 'u') { pos--; } if(pos > -1 && source[pos] == '\\') { contentEnd = pos - 1; } } } if(contentEnd < start) { contentEnd = end; } if(this.cursorLocation != end || end == contentEnd) { CompletionOnStringLiteral stringLiteral = new CompletionOnStringLiteral( token, start, end, contentStart, contentEnd, lineNumber); this.assistNode = stringLiteral; this.restartRecovery = true; this.lastCheckPoint = end; return stringLiteral; } } return super.createStringLiteral(token, start, end, lineNumber); } protected TypeReference copyDims(TypeReference typeRef, int dim) { if (this.assistNode == typeRef) { return typeRef; } TypeReference result = super.copyDims(typeRef, dim); if (this.assistNodeParent == typeRef) { this.assistNodeParent = result; } return result; } public CompilationUnitDeclaration dietParse(ICompilationUnit sourceUnit, CompilationResult compilationResult, int cursorLoc) { this.cursorLocation = cursorLoc; CompletionScanner completionScanner = (CompletionScanner)this.scanner; completionScanner.completionIdentifier = null; completionScanner.cursorLocation = cursorLoc; return this.dietParse(sourceUnit, compilationResult); } /* * Flush parser/scanner state regarding to code assist */ public void flushAssistState() { super.flushAssistState(); this.isOrphanCompletionNode = false; this.isAlreadyAttached = false; this.assistNodeParent = null; CompletionScanner completionScanner = (CompletionScanner)this.scanner; completionScanner.completedIdentifierStart = 0; completionScanner.completedIdentifierEnd = -1; } protected TypeReference getTypeReferenceForGenericType(int dim, int identifierLength, int numberOfIdentifiers) { TypeReference ref = super.getTypeReferenceForGenericType(dim, identifierLength, numberOfIdentifiers); // in completion case we might have encountered the assist node before really parsing // the complete class instance creation, and so a separate check for diamond is needed here. // https://bugs.eclipse.org/bugs/show_bug.cgi?id=346454 checkForDiamond(ref); if(this.assistNode != null) { if (identifierLength == 1 && numberOfIdentifiers == 1) { ParameterizedSingleTypeReference singleRef = (ParameterizedSingleTypeReference) ref; TypeReference[] typeArguments = singleRef.typeArguments; for (int i = 0; i < typeArguments.length; i++) { if(typeArguments[i] == this.assistNode) { this.assistNodeParent = ref; return ref; } } } else { ParameterizedQualifiedTypeReference qualifiedRef = (ParameterizedQualifiedTypeReference) ref; TypeReference[][] typeArguments = qualifiedRef.typeArguments; for (int i = 0; i < typeArguments.length; i++) { if(typeArguments[i] != null) { for (int j = 0; j < typeArguments[i].length; j++) { if(typeArguments[i][j] == this.assistNode) { this.assistNodeParent = ref; return ref; } } } } } } return ref; } protected NameReference getUnspecifiedReference() { NameReference nameReference = super.getUnspecifiedReference(); if (this.record) { recordReference(nameReference); } return nameReference; } 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; this.qualifier = -1; } NameReference nameReference = super.getUnspecifiedReferenceOptimized(); if (this.record) { recordReference(nameReference); } return nameReference; } private boolean isAlreadyPotentialName(int identifierStart) { if (this.potentialVariableNamesPtr < 0) return false; return identifierStart <= this.potentialVariableNameEnds[this.potentialVariableNamesPtr]; } protected int indexOfAssistIdentifier(boolean useGenericsStack) { if (this.record) return -1; // when names are recorded there is no assist identifier return super.indexOfAssistIdentifier(useGenericsStack); } public void initialize() { super.initialize(); this.labelPtr = -1; initializeForBlockStatements(); } public void initialize(boolean initializeNLS) { super.initialize(initializeNLS); this.labelPtr = -1; initializeForBlockStatements(); } /* * Initializes the state of the parser that is about to go for BlockStatements. */ private void initializeForBlockStatements() { this.previousToken = -1; this.previousIdentifierPtr = -1; this.invocationType = NO_RECEIVER; this.qualifier = -1; popUntilElement(K_SWITCH_LABEL); if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) != K_SWITCH_LABEL) { if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_ARRAY_INITIALIZER) { // if recovery is taking place in an array initializer, we should prevent popping // up to the enclosing block until the array initializer is properly closed // https://bugs.eclipse.org/bugs/show_bug.cgi?id=249704 popUntilElement(K_ARRAY_INITIALIZER); } else { popUntilElement(K_BLOCK_DELIMITER); } } } public void initializeScanner(){ this.scanner = new CompletionScanner(this.options.sourceLevel); } /** * Returns whether the completion is just after an array type * e.g. 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); } protected boolean isInsideAnnotation() { int i = this.elementPtr; while(i > -1) { if(this.elementKindStack[i] == K_BETWEEN_ANNOTATION_NAME_AND_RPAREN) return true; i--; } return false; } protected boolean isIndirectlyInsideBlock(){ int i = this.elementPtr; while(i > -1) { if(this.elementKindStack[i] == K_BLOCK_DELIMITER) return true; i--; } return false; } protected boolean isInsideBlock(){ int i = this.elementPtr; while(i > -1) { switch (this.elementKindStack[i]) { case K_TYPE_DELIMITER : return false; case K_METHOD_DELIMITER : return false; case K_FIELD_INITIALIZER_DELIMITER : return false; case K_BLOCK_DELIMITER : return true; } i--; } return false; } protected boolean isInsideBreakable(){ int i = this.elementPtr; while(i > -1) { switch (this.elementKindStack[i]) { case K_TYPE_DELIMITER : return false; case K_METHOD_DELIMITER : return false; case K_FIELD_INITIALIZER_DELIMITER : return false; case K_SWITCH_LABEL : return true; case K_BLOCK_DELIMITER : case K_CONTROL_STATEMENT_DELIMITER: switch(this.elementInfoStack[i]) { case FOR : case DO : case WHILE : return true; } } i--; } return false; } protected boolean isInsideLoop(){ int i = this.elementPtr; while(i > -1) { switch (this.elementKindStack[i]) { case K_TYPE_DELIMITER : return false; case K_METHOD_DELIMITER : return false; case K_FIELD_INITIALIZER_DELIMITER : return false; case K_BLOCK_DELIMITER : case K_CONTROL_STATEMENT_DELIMITER: switch(this.elementInfoStack[i]) { case FOR : case DO : case WHILE : return true; } } i--; } return false; } protected boolean isInsideReturn(){ int i = this.elementPtr; while(i > -1) { switch (this.elementKindStack[i]) { case K_TYPE_DELIMITER : return false; case K_METHOD_DELIMITER : return false; case K_FIELD_INITIALIZER_DELIMITER : return false; case K_BLOCK_DELIMITER : return false; case K_CONTROL_STATEMENT_DELIMITER: return false; // FWIW case K_INSIDE_RETURN_STATEMENT : return true; } i--; } return false; } public CompilationUnitDeclaration parse(ICompilationUnit sourceUnit, CompilationResult compilationResult, int cursorLoc) { this.cursorLocation = cursorLoc; CompletionScanner completionScanner = (CompletionScanner)this.scanner; completionScanner.completionIdentifier = null; completionScanner.cursorLocation = cursorLoc; return this.parse(sourceUnit, compilationResult); } public void parseBlockStatements( ConstructorDeclaration cd, CompilationUnitDeclaration unit) { this.canBeExplicitConstructor = 1; super.parseBlockStatements(cd, unit); } public MethodDeclaration parseSomeStatements(int start, int end, int fakeBlocksCount, CompilationUnitDeclaration unit) { this.methodRecoveryActivated = true; initialize(); // simulate goForMethodBody except that we don't want to balance brackets because they are not going to be balanced goForBlockStatementsopt(); MethodDeclaration fakeMethod = new MethodDeclaration(unit.compilationResult()); fakeMethod.selector = FAKE_METHOD_NAME; fakeMethod.bodyStart = start; fakeMethod.bodyEnd = end; fakeMethod.declarationSourceStart = start; fakeMethod.declarationSourceEnd = end; fakeMethod.sourceStart = start; fakeMethod.sourceEnd = start; //fake method must ignore the method header this.referenceContext = fakeMethod; this.compilationUnit = unit; this.diet = false; this.restartRecovery = true; this.scanner.resetTo(start, end); consumeNestedMethod(); for (int i = 0; i < fakeBlocksCount; i++) { consumeOpenFakeBlock(); } try { parse(); } catch (AbortCompilation ex) { this.lastAct = ERROR_ACTION; } finally { this.nestedMethod[this.nestedType]--; } if (!this.hasError) { int length; if (this.astLengthPtr > -1 && (length = this.astLengthStack[this.astLengthPtr--]) != 0) { System.arraycopy( this.astStack, (this.astPtr -= length) + 1, fakeMethod.statements = new Statement[length], 0, length); } } return fakeMethod; } protected void popUntilCompletedAnnotationIfNecessary() { if(this.elementPtr < 0) return; int i = this.elementPtr; while(i > -1 && (this.elementKindStack[i] != K_BETWEEN_ANNOTATION_NAME_AND_RPAREN || (this.elementInfoStack[i] & ANNOTATION_NAME_COMPLETION) == 0)) { i--; } if(i >= 0) { this.previousKind = this.elementKindStack[i]; this.previousInfo = this.elementInfoStack[i]; this.previousObjectInfo = this.elementObjectInfoStack[i]; for (int j = i; j <= this.elementPtr; j++) { this.elementObjectInfoStack[j] = null; } this.elementPtr = i - 1; } } /* * Prepares the state of the parser to go for BlockStatements. */ protected void prepareForBlockStatements() { this.nestedMethod[this.nestedType = 0] = 1; this.variablesCounter[this.nestedType] = 0; this.realBlockStack[this.realBlockPtr = 1] = 0; initializeForBlockStatements(); } protected void pushOnLabelStack(char[] label){ if (this.labelPtr < -1) return; int stackLength = this.labelStack.length; if (++this.labelPtr >= stackLength) { System.arraycopy( this.labelStack, 0, this.labelStack = new char[stackLength + LabelStackIncrement][], 0, stackLength); } this.labelStack[this.labelPtr] = label; } /** * Creates a completion on member access node and push it * on the expression stack. */ private void pushCompletionOnMemberAccessOnExpressionStack(boolean isSuperAccess) { char[] source = this.identifierStack[this.identifierPtr]; long pos = this.identifierPositionStack[this.identifierPtr--]; CompletionOnMemberAccess fr = new CompletionOnMemberAccess(source, pos, isInsideAnnotation()); this.assistNode = fr; this.lastCheckPoint = fr.sourceEnd + 1; this.identifierLengthPtr--; if (isSuperAccess) { //considerates the fieldReference beginning at the 'super' .... fr.sourceStart = this.intStack[this.intPtr--]; fr.receiver = new SuperReference(fr.sourceStart, this.endPosition); pushOnExpressionStack(fr); } else { //optimize push/pop if ((fr.receiver = this.expressionStack[this.expressionPtr]).isThis()) { //fieldreference begins at the this fr.sourceStart = fr.receiver.sourceStart; } this.expressionStack[this.expressionPtr] = fr; } } private void recordReference(NameReference nameReference) { if (!this.skipRecord && this.recordFrom <= nameReference.sourceStart && nameReference.sourceEnd <= this.recordTo && !isAlreadyPotentialName(nameReference.sourceStart)) { char[] token; if (nameReference instanceof SingleNameReference) { token = ((SingleNameReference) nameReference).token; } else { token = ((QualifiedNameReference) nameReference).tokens[0]; } // Most of the time a name which start with an uppercase is a type name. // As we don't want to resolve names to avoid to slow down performances then this name will be ignored if (Character.isUpperCase(token[0])) return; addPotentialName(token, nameReference.sourceStart, nameReference.sourceEnd); } } public void recoveryExitFromVariable() { if(this.currentElement != null && this.currentElement instanceof RecoveredLocalVariable) { RecoveredElement oldElement = this.currentElement; super.recoveryExitFromVariable(); if(oldElement != this.currentElement) { popElement(K_LOCAL_INITIALIZER_DELIMITER); } } else { super.recoveryExitFromVariable(); } } public void recoveryTokenCheck() { RecoveredElement oldElement = this.currentElement; switch (this.currentToken) { case TokenNameLBRACE : if(!this.ignoreNextOpeningBrace) { this.pendingAnnotation = null; // the pending annotation cannot be attached to next nodes } super.recoveryTokenCheck(); break; case TokenNameRBRACE : super.recoveryTokenCheck(); if(this.currentElement != oldElement && oldElement instanceof RecoveredBlock) { if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_ARRAY_INITIALIZER) { // When inside an array initializer, we should not prematurely pop the enclosing block // https://bugs.eclipse.org/bugs/show_bug.cgi?id=249704 popElement(K_ARRAY_INITIALIZER); } else { popElement(K_BLOCK_DELIMITER); } } break; case TokenNamecase : super.recoveryTokenCheck(); if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BLOCK_DELIMITER && topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == SWITCH) { pushOnElementStack(K_SWITCH_LABEL); } break; case TokenNamedefault : super.recoveryTokenCheck(); if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BLOCK_DELIMITER && topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) == SWITCH) { pushOnElementStack(K_SWITCH_LABEL, DEFAULT); } else if(topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_SWITCH_LABEL) { popElement(K_SWITCH_LABEL); pushOnElementStack(K_SWITCH_LABEL, DEFAULT); } break; default : super.recoveryTokenCheck(); break; } } /* * Reset internal state after completion is over */ public void reset() { super.reset(); this.cursorLocation = 0; if (this.storeSourceEnds) { this.sourceEnds = new HashtableOfObjectToInt(); } } /* * Reset internal state after completion is over */ public void resetAfterCompletion() { this.cursorLocation = 0; flushAssistState(); } public void restoreAssistParser(Object parserState) { int[] state = (int[]) parserState; CompletionScanner completionScanner = (CompletionScanner)this.scanner; this.cursorLocation = state[0]; completionScanner.cursorLocation = state[1]; } /* * 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() { this.hasUnusedModifiers = false; 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 ((this.scanner.eofPosition == this.cursorLocation+1) && (!(this.referenceContext instanceof CompilationUnitDeclaration) || isIndirectlyInsideFieldInitialization() || this.assistNodeParent instanceof FieldDeclaration && !(this.assistNodeParent instanceof Initializer))) { /* disabled since does not handle possible field/message refs, that is, 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 (this.currentElement instanceof RecoveredType || this.currentElement.enclosingType() != null){ this.pendingAnnotation = null; if(this.lastCheckPoint <= this.assistNode.sourceEnd) { this.lastCheckPoint = this.assistNode.sourceEnd+1; } int end = this.currentElement.topElement().sourceEnd(); this.scanner.eofPosition = end < Integer.MAX_VALUE ? end + 1 : end; } else { resetStacks(); return false; } } } return super.resumeAfterRecovery(); } public void setAssistIdentifier(char[] assistIdent){ ((CompletionScanner)this.scanner).completionIdentifier = assistIdent; } public String toString() { StringBuffer buffer = new StringBuffer(); buffer.append("elementKindStack : int[] = {"); //$NON-NLS-1$ for (int i = 0; i <= this.elementPtr; i++) { buffer.append(String.valueOf(this.elementKindStack[i])).append(','); } buffer.append("}\n"); //$NON-NLS-1$ buffer.append("elementInfoStack : int[] = {"); //$NON-NLS-1$ for (int i = 0; i <= this.elementPtr; i++) { buffer.append(String.valueOf(this.elementInfoStack[i])).append(','); } buffer.append("}\n"); //$NON-NLS-1$ buffer.append(super.toString()); return String.valueOf(buffer); } /* * Update recovery state based on current parser/scanner state */ protected void updateRecoveryState() { /* expose parser state to recovery state */ this.currentElement.updateFromParserState(); /* may be able to retrieve completionNode as an orphan, and then attach it */ completionIdentifierCheck(); 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) { this.currentElement.preserveEnclosingBlocks(); } /* check and update recovered state based on current token, this action is also performed when shifting token after recovery got activated once. */ recoveryTokenCheck(); recoveryExitFromVariable(); } protected LocalDeclaration createLocalDeclaration(char[] assistName, int sourceStart, int sourceEnd) { if (this.indexOfAssistIdentifier() < 0) { return super.createLocalDeclaration(assistName, sourceStart, sourceEnd); } else { CompletionOnLocalName local = new CompletionOnLocalName(assistName, sourceStart, sourceEnd); this.assistNode = local; this.lastCheckPoint = sourceEnd + 1; return local; } } protected JavadocParser createJavadocParser() { return new CompletionJavadocParser(this); } protected FieldDeclaration createFieldDeclaration(char[] assistName, int sourceStart, int sourceEnd) { if (this.indexOfAssistIdentifier() < 0 || (this.currentElement instanceof RecoveredUnit && ((RecoveredUnit)this.currentElement).typeCount == 0)) { return super.createFieldDeclaration(assistName, sourceStart, sourceEnd); } else { CompletionOnFieldName field = new CompletionOnFieldName(assistName, sourceStart, sourceEnd); this.assistNode = field; this.lastCheckPoint = sourceEnd + 1; return field; } } /* * To find out if the given stack has an instanceof expression * at the given startIndex or at one prior to that */ private boolean stackHasInstanceOfExpression(Object[] stackToSearch, int startIndex) { int indexInstanceOf = startIndex; while (indexInstanceOf >= 0) { if (stackToSearch[indexInstanceOf] instanceof InstanceOfExpression) { return true; } indexInstanceOf--; } return false; } }