| /******************************************************************************* |
| * Copyright (c) 2000, 2016 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.select; |
| |
| /* |
| * Parser able to build specific completion parse nodes, given a cursorLocation. |
| * |
| * Cursor location denotes the position of the last character behind which completion |
| * got requested: |
| * -1 means completion at the very beginning of the source |
| * 0 means completion behind the first character |
| * n means completion behind the n-th character |
| */ |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.codeassist.impl.AssistParser; |
| import org.eclipse.jdt.internal.compiler.CompilationResult; |
| import org.eclipse.jdt.internal.compiler.ast.ASTNode; |
| import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; |
| import org.eclipse.jdt.internal.compiler.ast.Annotation; |
| import org.eclipse.jdt.internal.compiler.ast.Argument; |
| import org.eclipse.jdt.internal.compiler.ast.ArrayAllocationExpression; |
| import org.eclipse.jdt.internal.compiler.ast.CaseStatement; |
| import org.eclipse.jdt.internal.compiler.ast.CastExpression; |
| import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall; |
| import org.eclipse.jdt.internal.compiler.ast.Expression; |
| import org.eclipse.jdt.internal.compiler.ast.FieldReference; |
| import org.eclipse.jdt.internal.compiler.ast.ImportReference; |
| import org.eclipse.jdt.internal.compiler.ast.LambdaExpression; |
| import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.MarkerAnnotation; |
| import org.eclipse.jdt.internal.compiler.ast.MemberValuePair; |
| import org.eclipse.jdt.internal.compiler.ast.MessageSend; |
| import org.eclipse.jdt.internal.compiler.ast.ModuleDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.ModuleReference; |
| import org.eclipse.jdt.internal.compiler.ast.NameReference; |
| import org.eclipse.jdt.internal.compiler.ast.NormalAnnotation; |
| import org.eclipse.jdt.internal.compiler.ast.QualifiedAllocationExpression; |
| import org.eclipse.jdt.internal.compiler.ast.Reference; |
| import org.eclipse.jdt.internal.compiler.ast.ReferenceExpression; |
| import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; |
| import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation; |
| import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; |
| import org.eclipse.jdt.internal.compiler.ast.Statement; |
| import org.eclipse.jdt.internal.compiler.ast.SuperReference; |
| import org.eclipse.jdt.internal.compiler.ast.SwitchStatement; |
| import org.eclipse.jdt.internal.compiler.ast.ThisReference; |
| import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.TypeReference; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; |
| import org.eclipse.jdt.internal.compiler.lookup.BlockScope; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; |
| import org.eclipse.jdt.internal.compiler.parser.JavadocParser; |
| import org.eclipse.jdt.internal.compiler.parser.RecoveredType; |
| import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; |
| import org.eclipse.jdt.internal.compiler.util.Util; |
| |
| public class SelectionParser extends AssistParser { |
| // OWNER |
| protected static final int SELECTION_PARSER = 1024; |
| protected static final int SELECTION_OR_ASSIST_PARSER = ASSIST_PARSER + SELECTION_PARSER; |
| |
| // KIND : all values known by SelectionParser are between 1025 and 1549 |
| protected static final int K_BETWEEN_CASE_AND_COLON = SELECTION_PARSER + 1; // whether we are inside a block |
| protected static final int K_INSIDE_RETURN_STATEMENT = SELECTION_PARSER + 2; // whether we are between the keyword 'return' and the end of a return statement |
| protected static final int K_CAST_STATEMENT = SELECTION_PARSER + 3; // whether we are between ')' and the end of a cast statement |
| |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=476693 |
| private boolean selectionNodeFound; |
| public ASTNode assistNodeParent; // the parent node of assist node |
| |
| /* public fields */ |
| |
| public int selectionStart, selectionEnd; |
| public static final char[] SUPER = "super".toCharArray(); //$NON-NLS-1$ |
| public static final char[] THIS = "this".toCharArray(); //$NON-NLS-1$ |
| |
| public SelectionParser(ProblemReporter problemReporter) { |
| super(problemReporter); |
| this.javadocParser.checkDocComment = true; |
| } |
| public char[] assistIdentifier(){ |
| return ((SelectionScanner)this.scanner).selectionIdentifier; |
| } |
| protected void attachOrphanCompletionNode(){ |
| if (this.isOrphanCompletionNode){ |
| ASTNode orphan = this.assistNode; |
| this.isOrphanCompletionNode = false; |
| |
| |
| /* if in context of a type, then persists the identifier into a fake field return type */ |
| if (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){ |
| this.currentElement = this.currentElement.add(new SelectionOnFieldType((TypeReference)orphan), 0); |
| return; |
| } |
| } |
| } |
| |
| if (orphan instanceof Expression) { |
| buildMoreCompletionContext((Expression)orphan); |
| } else { |
| if (lastIndexOfElement(K_LAMBDA_EXPRESSION_DELIMITER) < 0) { // lambdas are recovered up to the containing expression statement and will carry along the assist node anyways. |
| Statement statement = (Statement) orphan; |
| this.currentElement = this.currentElement.add(statement, 0); |
| } |
| } |
| if (isIndirectlyInsideLambdaExpression()) { |
| if (this.currentToken == TokenNameLBRACE) |
| this.ignoreNextOpeningBrace = true; |
| else if (this.currentToken == TokenNameRBRACE) |
| this.ignoreNextClosingBrace = true; |
| } else { |
| this.currentToken = 0; // given we are not on an eof, we do not want side effects caused by looked-ahead token |
| } |
| } |
| } |
| private void buildMoreCompletionContext(Expression expression) { |
| ASTNode parentNode = null; |
| |
| int kind = topKnownElementKind(SELECTION_OR_ASSIST_PARSER); |
| if(kind != 0) { |
| int info = topKnownElementInfo(SELECTION_OR_ASSIST_PARSER); |
| nextElement : switch (kind) { |
| 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; |
| } |
| parentNode = switchStatement; |
| this.assistNodeParent = parentNode; |
| } |
| break nextElement; |
| case K_INSIDE_RETURN_STATEMENT : |
| if(info == this.bracketDepth) { |
| ReturnStatement returnStatement = new ReturnStatement(expression, expression.sourceStart, expression.sourceEnd); |
| parentNode = returnStatement; |
| this.assistNodeParent = parentNode; |
| } |
| 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; |
| parentNode = cast; |
| this.assistNodeParent = parentNode; |
| } |
| break nextElement; |
| } |
| } |
| // Do not add assist node/parent into the recovery system if we are inside a lambda. The lambda will be fully recovered including the containing statement and added. |
| if (lastIndexOfElement(K_LAMBDA_EXPRESSION_DELIMITER) < 0) { |
| if(parentNode != null) { |
| this.currentElement = this.currentElement.add((Statement)parentNode, 0); |
| } else { |
| this.currentElement = this.currentElement.add((Statement)wrapWithExplicitConstructorCallIfNeeded(expression), 0); |
| if(this.lastCheckPoint < expression.sourceEnd) { |
| this.lastCheckPoint = expression.sourceEnd + 1; |
| } |
| } |
| } |
| } |
| private boolean checkRecoveredType() { |
| if (this.currentElement instanceof RecoveredType){ |
| /* check if current awaiting identifier is the completion identifier */ |
| if (this.indexOfAssistIdentifier() < 0) return false; |
| |
| if ((this.lastErrorEndPosition >= this.selectionStart) |
| && (this.lastErrorEndPosition <= this.selectionEnd+1)){ |
| return false; |
| } |
| RecoveredType recoveredType = (RecoveredType)this.currentElement; |
| /* filter out cases where scanner is still inside type header */ |
| if (recoveredType.foundOpeningBrace) { |
| this.assistNode = this.getTypeReference(0); |
| this.lastCheckPoint = this.assistNode.sourceEnd + 1; |
| this.isOrphanCompletionNode = true; |
| return true; |
| } |
| } |
| return false; |
| } |
| protected void classInstanceCreation(boolean hasClassBody) { |
| |
| // ClassInstanceCreationExpression ::= 'new' ClassType '(' ArgumentListopt ')' ClassBodyopt |
| |
| // ClassBodyopt produces a null item on the astStak if it produces NO class body |
| // An empty class body produces a 0 on the length stack..... |
| |
| |
| if ((this.astLengthStack[this.astLengthPtr] == 1) |
| && (this.astStack[this.astPtr] == null)) { |
| |
| |
| int index; |
| if ((index = this.indexOfAssistIdentifier()) < 0) { |
| super.classInstanceCreation(hasClassBody); |
| return; |
| } else if(this.identifierLengthPtr > -1 && |
| (this.identifierLengthStack[this.identifierLengthPtr] - 1) != index) { |
| super.classInstanceCreation(hasClassBody); |
| return; |
| } |
| QualifiedAllocationExpression alloc; |
| this.astPtr--; |
| this.astLengthPtr--; |
| alloc = new SelectionOnQualifiedAllocationExpression(); |
| alloc.sourceEnd = this.endPosition; //the position has been stored explicitly |
| |
| int length; |
| if ((length = this.expressionLengthStack[this.expressionLengthPtr--]) != 0) { |
| this.expressionPtr -= length; |
| System.arraycopy( |
| this.expressionStack, |
| this.expressionPtr + 1, |
| alloc.arguments = new Expression[length], |
| 0, |
| length); |
| } |
| // trick to avoid creating a selection on type reference |
| char [] oldIdent = assistIdentifier(); |
| setAssistIdentifier(null); |
| alloc.type = getTypeReference(0); |
| checkForDiamond(alloc.type); |
| |
| setAssistIdentifier(oldIdent); |
| |
| //the default constructor with the correct number of argument |
| //will be created and added by the TC (see createsInternalConstructorWithBinding) |
| alloc.sourceStart = this.intStack[this.intPtr--]; |
| pushOnExpressionStack(alloc); |
| |
| this.assistNode = alloc; |
| this.lastCheckPoint = alloc.sourceEnd + 1; |
| if (!this.diet){ |
| this.restartRecovery = true; // force to restart in recovery mode |
| this.lastIgnoredToken = -1; |
| } |
| this.isOrphanCompletionNode = true; |
| } else { |
| super.classInstanceCreation(hasClassBody); |
| } |
| } |
| protected void consumeArrayCreationExpressionWithoutInitializer() { |
| // ArrayCreationWithoutArrayInitializer ::= 'new' PrimitiveType DimWithOrWithOutExprs |
| // ArrayCreationWithoutArrayInitializer ::= 'new' ClassOrInterfaceType DimWithOrWithOutExprs |
| |
| super.consumeArrayCreationExpressionWithoutInitializer(); |
| |
| ArrayAllocationExpression alloc = (ArrayAllocationExpression)this.expressionStack[this.expressionPtr]; |
| if (alloc.type == this.assistNode){ |
| if (!this.diet){ |
| this.restartRecovery = true; // force to restart in recovery mode |
| this.lastIgnoredToken = -1; |
| } |
| this.isOrphanCompletionNode = true; |
| } |
| } |
| protected void consumeArrayCreationExpressionWithInitializer() { |
| // ArrayCreationWithArrayInitializer ::= 'new' ClassOrInterfaceType DimWithOrWithOutExprs ArrayInitializer |
| |
| super.consumeArrayCreationExpressionWithInitializer(); |
| |
| ArrayAllocationExpression alloc = (ArrayAllocationExpression)this.expressionStack[this.expressionPtr]; |
| if (alloc.type == this.assistNode){ |
| if (!this.diet){ |
| this.restartRecovery = true; // force to restart in recovery mode |
| this.lastIgnoredToken = -1; |
| } |
| this.isOrphanCompletionNode = true; |
| } |
| } |
| protected void consumeCastExpressionLL1() { |
| popElement(K_CAST_STATEMENT); |
| super.consumeCastExpressionLL1(); |
| } |
| protected void consumeCastExpressionLL1WithBounds() { |
| popElement(K_CAST_STATEMENT); |
| super.consumeCastExpressionLL1WithBounds(); |
| } |
| protected void consumeCastExpressionWithGenericsArray() { |
| popElement(K_CAST_STATEMENT); |
| super.consumeCastExpressionWithGenericsArray(); |
| } |
| protected void consumeCastExpressionWithNameArray() { |
| popElement(K_CAST_STATEMENT); |
| super.consumeCastExpressionWithNameArray(); |
| } |
| protected void consumeCastExpressionWithPrimitiveType() { |
| popElement(K_CAST_STATEMENT); |
| super.consumeCastExpressionWithPrimitiveType(); |
| } |
| protected void consumeCastExpressionWithQualifiedGenericsArray() { |
| popElement(K_CAST_STATEMENT); |
| super.consumeCastExpressionWithQualifiedGenericsArray(); |
| } |
| protected void consumeCatchFormalParameter() { |
| if (this.indexOfAssistIdentifier() < 0) { |
| super.consumeCatchFormalParameter(); |
| if((!this.diet || this.dietInt != 0) && this.astPtr > -1) { |
| Argument argument = (Argument) this.astStack[this.astPtr]; |
| if(argument.type == this.assistNode) { |
| this.isOrphanCompletionNode = true; |
| this.restartRecovery = true; // force to restart in recovery mode |
| this.lastIgnoredToken = -1; |
| } |
| } |
| } 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.astLengthPtr --; |
| int modifierPositions = this.intStack[this.intPtr--]; |
| this.intPtr--; |
| Argument arg = |
| new SelectionOnArgumentName( |
| identifierName, |
| namePositions, |
| type, |
| this.intStack[this.intPtr + 1] & ~ClassFileConstants.AccDeprecated); // modifiers |
| arg.bits &= ~ASTNode.IsArgument; |
| arg.declarationSourceStart = modifierPositions; |
| |
| // 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); |
| } |
| |
| pushOnAstStack(arg); |
| |
| this.assistNode = arg; |
| this.lastCheckPoint = (int) namePositions; |
| this.isOrphanCompletionNode = true; |
| |
| if (!this.diet){ |
| this.restartRecovery = true; // force to restart in recovery mode |
| this.lastIgnoredToken = -1; |
| } |
| |
| /* if incomplete method header, listLength counter will not have been reset, |
| indicating that some arguments are available on the stack */ |
| this.listLength++; |
| } |
| } |
| protected void consumeClassInstanceCreationExpressionQualifiedWithTypeArguments() { |
| // ClassInstanceCreationExpression ::= Primary '.' 'new' TypeArguments SimpleName '(' ArgumentListopt ')' ClassBodyopt |
| // ClassInstanceCreationExpression ::= ClassInstanceCreationExpressionName 'new' TypeArguments SimpleName '(' ArgumentListopt ')' ClassBodyopt |
| |
| QualifiedAllocationExpression alloc; |
| int length; |
| if (((length = this.astLengthStack[this.astLengthPtr]) == 1) && (this.astStack[this.astPtr] == null)) { |
| |
| if (this.indexOfAssistIdentifier() < 0) { |
| super.consumeClassInstanceCreationExpressionQualifiedWithTypeArguments(); |
| return; |
| } |
| |
| //NO ClassBody |
| this.astPtr--; |
| this.astLengthPtr--; |
| alloc = new SelectionOnQualifiedAllocationExpression(); |
| alloc.sourceEnd = this.endPosition; //the position has been stored explicitly |
| |
| if ((length = this.expressionLengthStack[this.expressionLengthPtr--]) != 0) { |
| this.expressionPtr -= length; |
| System.arraycopy( |
| this.expressionStack, |
| this.expressionPtr + 1, |
| alloc.arguments = new Expression[length], |
| 0, |
| length); |
| } |
| |
| // trick to avoid creating a selection on type reference |
| char [] oldIdent = assistIdentifier(); |
| setAssistIdentifier(null); |
| alloc.type = getTypeReference(0); |
| checkForDiamond(alloc.type); |
| |
| setAssistIdentifier(oldIdent); |
| |
| length = this.genericsLengthStack[this.genericsLengthPtr--]; |
| this.genericsPtr -= length; |
| System.arraycopy(this.genericsStack, this.genericsPtr + 1, alloc.typeArguments = new TypeReference[length], 0, length); |
| this.intPtr--; // remove the position of the '<' |
| |
| //the default constructor with the correct number of argument |
| //will be created and added by the TC (see createsInternalConstructorWithBinding) |
| alloc.sourceStart = this.intStack[this.intPtr--]; |
| pushOnExpressionStack(alloc); |
| |
| this.assistNode = alloc; |
| this.lastCheckPoint = alloc.sourceEnd + 1; |
| if (!this.diet){ |
| this.restartRecovery = true; // force to restart in recovery mode |
| this.lastIgnoredToken = -1; |
| } |
| this.isOrphanCompletionNode = true; |
| } else { |
| super.consumeClassInstanceCreationExpressionQualifiedWithTypeArguments(); |
| } |
| |
| this.expressionLengthPtr--; |
| QualifiedAllocationExpression qae = |
| (QualifiedAllocationExpression) this.expressionStack[this.expressionPtr--]; |
| qae.enclosingInstance = this.expressionStack[this.expressionPtr]; |
| this.expressionStack[this.expressionPtr] = qae; |
| qae.sourceStart = qae.enclosingInstance.sourceStart; |
| } |
| protected void consumeClassInstanceCreationExpressionWithTypeArguments() { |
| // ClassInstanceCreationExpression ::= 'new' TypeArguments ClassType '(' ArgumentListopt ')' ClassBodyopt |
| AllocationExpression alloc; |
| int length; |
| if (((length = this.astLengthStack[this.astLengthPtr]) == 1) |
| && (this.astStack[this.astPtr] == null)) { |
| |
| if (this.indexOfAssistIdentifier() < 0) { |
| super.consumeClassInstanceCreationExpressionWithTypeArguments(); |
| return; |
| } |
| |
| //NO ClassBody |
| this.astPtr--; |
| this.astLengthPtr--; |
| alloc = new SelectionOnQualifiedAllocationExpression(); |
| alloc.sourceEnd = this.endPosition; //the position has been stored explicitly |
| |
| if ((length = this.expressionLengthStack[this.expressionLengthPtr--]) != 0) { |
| this.expressionPtr -= length; |
| System.arraycopy( |
| this.expressionStack, |
| this.expressionPtr + 1, |
| alloc.arguments = new Expression[length], |
| 0, |
| length); |
| } |
| |
| // trick to avoid creating a selection on type reference |
| char [] oldIdent = assistIdentifier(); |
| setAssistIdentifier(null); |
| alloc.type = getTypeReference(0); |
| checkForDiamond(alloc.type); |
| |
| setAssistIdentifier(oldIdent); |
| |
| length = this.genericsLengthStack[this.genericsLengthPtr--]; |
| this.genericsPtr -= length; |
| System.arraycopy(this.genericsStack, this.genericsPtr + 1, alloc.typeArguments = new TypeReference[length], 0, length); |
| this.intPtr--; // remove the position of the '<' |
| |
| //the default constructor with the correct number of argument |
| //will be created and added by the TC (see createsInternalConstructorWithBinding) |
| alloc.sourceStart = this.intStack[this.intPtr--]; |
| pushOnExpressionStack(alloc); |
| |
| this.assistNode = alloc; |
| this.lastCheckPoint = alloc.sourceEnd + 1; |
| if (!this.diet){ |
| this.restartRecovery = true; // force to restart in recovery mode |
| this.lastIgnoredToken = -1; |
| } |
| this.isOrphanCompletionNode = true; |
| } else { |
| super.consumeClassInstanceCreationExpressionWithTypeArguments(); |
| } |
| } |
| protected void consumeEnterAnonymousClassBody(boolean qualified) { |
| // EnterAnonymousClassBody ::= $empty |
| |
| if (this.indexOfAssistIdentifier() < 0) { |
| super.consumeEnterAnonymousClassBody(qualified); |
| return; |
| } |
| |
| // trick to avoid creating a selection on type reference |
| char [] oldIdent = assistIdentifier(); |
| setAssistIdentifier(null); |
| TypeReference typeReference = getTypeReference(0); |
| setAssistIdentifier(oldIdent); |
| |
| TypeDeclaration anonymousType = new TypeDeclaration(this.compilationUnit.compilationResult); |
| anonymousType.name = CharOperation.NO_CHAR; |
| anonymousType.bits |= (ASTNode.IsAnonymousType|ASTNode.IsLocalType); |
| QualifiedAllocationExpression alloc = new SelectionOnQualifiedAllocationExpression(anonymousType); |
| markEnclosingMemberWithLocalType(); |
| pushOnAstStack(anonymousType); |
| |
| alloc.sourceEnd = this.rParenPos; //the position has been stored explicitly |
| int argumentLength; |
| if ((argumentLength = this.expressionLengthStack[this.expressionLengthPtr--]) != 0) { |
| this.expressionPtr -= argumentLength; |
| System.arraycopy( |
| this.expressionStack, |
| this.expressionPtr + 1, |
| alloc.arguments = new Expression[argumentLength], |
| 0, |
| argumentLength); |
| } |
| |
| if (qualified) { |
| this.expressionLengthPtr--; |
| alloc.enclosingInstance = this.expressionStack[this.expressionPtr--]; |
| } |
| |
| alloc.type = typeReference; |
| |
| anonymousType.sourceEnd = alloc.sourceEnd; |
| //position at the type while it impacts the anonymous declaration |
| anonymousType.sourceStart = anonymousType.declarationSourceStart = alloc.type.sourceStart; |
| alloc.sourceStart = this.intStack[this.intPtr--]; |
| pushOnExpressionStack(alloc); |
| |
| this.assistNode = alloc; |
| this.lastCheckPoint = alloc.sourceEnd + 1; |
| if (!this.diet){ |
| this.restartRecovery = true; // force to restart in recovery mode |
| this.lastIgnoredToken = -1; |
| if (isIndirectlyInsideLambdaExpression()) |
| this.ignoreNextOpeningBrace = true; |
| else |
| this.currentToken = 0; // opening brace already taken into account. |
| this.hasReportedError = true; |
| } |
| |
| anonymousType.bodyStart = this.scanner.currentPosition; |
| this.listLength = 0; // will be updated when reading super-interfaces |
| // recovery |
| if (this.currentElement != null){ |
| this.lastCheckPoint = anonymousType.bodyStart; |
| this.currentElement = this.currentElement.add(anonymousType, 0); |
| if (isIndirectlyInsideLambdaExpression()) |
| this.ignoreNextOpeningBrace = true; |
| else |
| this.currentToken = 0; // opening brace already taken into account. |
| this.lastIgnoredToken = -1; |
| } |
| } |
| protected void consumeEnterVariable() { |
| // EnterVariable ::= $empty |
| // do nothing by default |
| |
| super.consumeEnterVariable(); |
| |
| AbstractVariableDeclaration variable = (AbstractVariableDeclaration) this.astStack[this.astPtr]; |
| if (variable.type == this.assistNode){ |
| if (!this.diet){ |
| this.restartRecovery = true; // force to restart in recovery mode |
| this.lastIgnoredToken = -1; |
| } |
| this.isOrphanCompletionNode = false; // already attached inside variable decl |
| } |
| } |
| |
| protected void consumeExitVariableWithInitialization() { |
| super.consumeExitVariableWithInitialization(); |
| |
| // does not keep the initialization if selection is not inside |
| AbstractVariableDeclaration variable = (AbstractVariableDeclaration) this.astStack[this.astPtr]; |
| int start = variable.initialization.sourceStart; |
| int end = variable.initialization.sourceEnd; |
| if ((this.selectionStart < start) && (this.selectionEnd < start) || |
| (this.selectionStart > end) && (this.selectionEnd > end)) { |
| variable.initialization = null; |
| } |
| triggerRecoveryUponLambdaClosure(variable, false); |
| } |
| |
| protected void consumeFieldAccess(boolean isSuperAccess) { |
| // FieldAccess ::= Primary '.' 'Identifier' |
| // FieldAccess ::= 'super' '.' 'Identifier' |
| |
| if (this.indexOfAssistIdentifier() < 0) { |
| super.consumeFieldAccess(isSuperAccess); |
| return; |
| } |
| FieldReference fieldReference = |
| new SelectionOnFieldReference( |
| this.identifierStack[this.identifierPtr], |
| this.identifierPositionStack[this.identifierPtr--]); |
| this.identifierLengthPtr--; |
| if (isSuperAccess) { //considerates the fieldReferenceerence beginning at the 'super' .... |
| fieldReference.sourceStart = this.intStack[this.intPtr--]; |
| fieldReference.receiver = new SuperReference(fieldReference.sourceStart, this.endPosition); |
| pushOnExpressionStack(fieldReference); |
| } else { //optimize push/pop |
| if ((fieldReference.receiver = this.expressionStack[this.expressionPtr]).isThis()) { //fieldReferenceerence begins at the this |
| fieldReference.sourceStart = fieldReference.receiver.sourceStart; |
| } |
| this.expressionStack[this.expressionPtr] = fieldReference; |
| } |
| this.assistNode = fieldReference; |
| this.lastCheckPoint = fieldReference.sourceEnd + 1; |
| if (!this.diet){ |
| this.restartRecovery = true; // force to restart in recovery mode |
| this.lastIgnoredToken = -1; |
| } |
| this.isOrphanCompletionNode = true; |
| } |
| protected void consumeFormalParameter(boolean isVarArgs) { |
| if (this.indexOfAssistIdentifier() < 0) { |
| super.consumeFormalParameter(isVarArgs); |
| if((!this.diet || this.dietInt != 0) && this.astPtr > -1) { |
| Argument argument = (Argument) this.astStack[this.astPtr]; |
| if(argument.type == this.assistNode) { |
| this.isOrphanCompletionNode = true; |
| this.restartRecovery = true; // force to restart in recovery mode |
| this.lastIgnoredToken = -1; |
| } |
| } |
| } else { |
| boolean isReceiver = this.intStack[this.intPtr--] == 0; |
| if (isReceiver) { |
| this.expressionPtr--; |
| this.expressionLengthPtr --; |
| } |
| this.identifierLengthPtr--; |
| char[] identifierName = this.identifierStack[this.identifierPtr]; |
| long namePositions = this.identifierPositionStack[this.identifierPtr--]; |
| int extendedDimensions = this.intStack[this.intPtr--]; |
| Annotation [][] annotationsOnExtendedDimensions = extendedDimensions == 0 ? null : getAnnotationsOnDimensions(extendedDimensions); |
| Annotation [] varArgsAnnotations = null; |
| int length; |
| int endOfEllipsis = 0; |
| if (isVarArgs) { |
| endOfEllipsis = this.intStack[this.intPtr--]; |
| if ((length = this.typeAnnotationLengthStack[this.typeAnnotationLengthPtr--]) != 0) { |
| System.arraycopy( |
| this.typeAnnotationStack, |
| (this.typeAnnotationPtr -= length) + 1, |
| varArgsAnnotations = new Annotation[length], |
| 0, |
| length); |
| } |
| } |
| int firstDimensions = this.intStack[this.intPtr--]; |
| TypeReference type = getTypeReference(firstDimensions); |
| |
| if (isVarArgs || extendedDimensions != 0) { |
| if (isVarArgs) { |
| type = augmentTypeWithAdditionalDimensions(type, 1, varArgsAnnotations != null ? new Annotation[][] { varArgsAnnotations } : null, true); |
| } |
| if (extendedDimensions != 0) { // combination illegal. |
| type = augmentTypeWithAdditionalDimensions(type, extendedDimensions, annotationsOnExtendedDimensions, false); |
| } |
| type.sourceEnd = type.isParameterizedTypeReference() ? this.endStatementPosition : this.endPosition; |
| } |
| if (isVarArgs) { |
| if (extendedDimensions == 0) { |
| type.sourceEnd = endOfEllipsis; |
| } |
| type.bits |= ASTNode.IsVarArgs; // set isVarArgs |
| } |
| int modifierPositions = this.intStack[this.intPtr--]; |
| this.intPtr--; |
| Argument arg = |
| new SelectionOnArgumentName( |
| identifierName, |
| namePositions, |
| type, |
| this.intStack[this.intPtr + 1] & ~ClassFileConstants.AccDeprecated); // modifiers |
| arg.declarationSourceStart = modifierPositions; |
| |
| // consume annotations |
| if ((length = this.expressionLengthStack[this.expressionLengthPtr--]) != 0) { |
| System.arraycopy( |
| this.expressionStack, |
| (this.expressionPtr -= length) + 1, |
| arg.annotations = new Annotation[length], |
| 0, |
| length); |
| RecoveredType currentRecoveryType = this.currentRecoveryType(); |
| if (currentRecoveryType != null) |
| currentRecoveryType.annotationsConsumed(arg.annotations); |
| } |
| |
| pushOnAstStack(arg); |
| |
| this.assistNode = arg; |
| this.lastCheckPoint = (int) namePositions; |
| this.isOrphanCompletionNode = true; |
| |
| if (!this.diet){ |
| this.restartRecovery = true; // force to restart in recovery mode |
| this.lastIgnoredToken = -1; |
| } |
| |
| /* if incomplete method header, listLength counter will not have been reset, |
| indicating that some arguments are available on the stack */ |
| this.listLength++; |
| } |
| } |
| protected void consumeInsideCastExpression() { |
| super.consumeInsideCastExpression(); |
| pushOnElementStack(K_CAST_STATEMENT); |
| } |
| protected void consumeInsideCastExpressionLL1() { |
| super.consumeInsideCastExpressionLL1(); |
| pushOnElementStack(K_CAST_STATEMENT); |
| } |
| protected void consumeInsideCastExpressionLL1WithBounds() { |
| super.consumeInsideCastExpressionLL1WithBounds(); |
| pushOnElementStack(K_CAST_STATEMENT); |
| } |
| protected void consumeInsideCastExpressionWithQualifiedGenerics() { |
| super.consumeInsideCastExpressionWithQualifiedGenerics(); |
| pushOnElementStack(K_CAST_STATEMENT); |
| } |
| protected void consumeInstanceOfExpression() { |
| if (indexOfAssistIdentifier() < 0) { |
| super.consumeInstanceOfExpression(); |
| } else { |
| getTypeReference(this.intStack[this.intPtr--]); |
| this.isOrphanCompletionNode = true; |
| this.restartRecovery = true; |
| this.lastIgnoredToken = -1; |
| } |
| } |
| protected void consumeInstanceOfExpressionWithName() { |
| if (indexOfAssistIdentifier() < 0) { |
| super.consumeInstanceOfExpressionWithName(); |
| } else { |
| getTypeReference(this.intStack[this.intPtr--]); |
| this.isOrphanCompletionNode = true; |
| this.restartRecovery = true; |
| this.lastIgnoredToken = -1; |
| } |
| } |
| @Override |
| protected void consumeLambdaExpression() { |
| super.consumeLambdaExpression(); |
| LambdaExpression expression = (LambdaExpression) this.expressionStack[this.expressionPtr]; |
| int arrowEnd = expression.arrowPosition(); |
| int arrowStart = arrowEnd - 1; |
| if (this.selectionStart == arrowStart || this.selectionStart == arrowEnd) { |
| if (this.selectionEnd == arrowStart || this.selectionEnd == arrowEnd) { |
| this.expressionStack[this.expressionPtr] = new SelectionOnLambdaExpression(expression); |
| } |
| } |
| if (!(this.selectionStart >= expression.sourceStart && this.selectionEnd <= expression.sourceEnd)) |
| popElement(K_LAMBDA_EXPRESSION_DELIMITER); |
| } |
| @Override |
| protected void consumeReferenceExpression(ReferenceExpression referenceExpression) { |
| int kolonKolonStart = this.colonColonStart; |
| int kolonKolonEnd = kolonKolonStart + 1; |
| this.colonColonStart = -1; |
| if (this.selectionStart == kolonKolonStart || this.selectionStart == kolonKolonEnd) { |
| if (this.selectionEnd == kolonKolonStart || this.selectionEnd == kolonKolonEnd) { |
| referenceExpression = new SelectionOnReferenceExpression(referenceExpression); |
| } |
| } |
| super.consumeReferenceExpression(referenceExpression); |
| } |
| |
| protected void consumeLocalVariableDeclarationStatement() { |
| super.consumeLocalVariableDeclarationStatement(); |
| |
| // force to restart in recovery mode if the declaration contains the selection |
| if (!this.diet && this.astStack[this.astPtr] instanceof LocalDeclaration) { |
| LocalDeclaration localDeclaration = (LocalDeclaration) this.astStack[this.astPtr]; |
| if ((this.selectionStart >= localDeclaration.sourceStart) |
| && (this.selectionEnd <= localDeclaration.sourceEnd)) { |
| this.restartRecovery = true; |
| this.lastIgnoredToken = -1; |
| } |
| } |
| if (this.selectionNodeFound) { |
| this.restartRecovery = true; |
| } |
| } |
| protected void consumeAssignment() { |
| super.consumeAssignment(); |
| if (this.selectionNodeFound) { |
| this.restartRecovery = true; |
| } |
| } |
| protected void consumeBlockStatement() { |
| super.consumeBlockStatement(); |
| if (this.selectionNodeFound) { |
| this.restartRecovery = true; |
| } |
| } |
| protected void consumeMarkerAnnotation(boolean isTypeAnnotation) { |
| int index; |
| |
| if ((index = this.indexOfAssistIdentifier()) < 0) { |
| super.consumeMarkerAnnotation(isTypeAnnotation); |
| 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); |
| } |
| this.assistNode = typeReference; |
| this.lastCheckPoint = typeReference.sourceEnd + 1; |
| |
| markerAnnotation = new MarkerAnnotation(typeReference, this.intStack[this.intPtr--]); |
| markerAnnotation.declarationSourceEnd = markerAnnotation.sourceEnd; |
| if (isTypeAnnotation) { |
| pushOnTypeAnnotationStack(markerAnnotation); |
| } else { |
| pushOnExpressionStack(markerAnnotation); |
| } |
| } |
| protected void consumeMemberValuePair() { |
| if (this.indexOfAssistIdentifier() < 0) { |
| super.consumeMemberValuePair(); |
| return; |
| } |
| |
| char[] simpleName = this.identifierStack[this.identifierPtr]; |
| long position = this.identifierPositionStack[this.identifierPtr--]; |
| this.identifierLengthPtr--; |
| int end = (int) position; |
| int start = (int) (position >>> 32); |
| Expression value = this.expressionStack[this.expressionPtr--]; |
| this.expressionLengthPtr--; |
| MemberValuePair memberValuePair = new SelectionOnNameOfMemberValuePair(simpleName, start, end, value); |
| pushOnAstStack(memberValuePair); |
| |
| this.assistNode = memberValuePair; |
| this.lastCheckPoint = memberValuePair.sourceEnd + 1; |
| |
| |
| } |
| protected void consumeMethodInvocationName() { |
| // MethodInvocation ::= Name '(' ArgumentListopt ')' |
| |
| // when the name is only an identifier...we have a message send to "this" (implicit) |
| |
| char[] selector = this.identifierStack[this.identifierPtr]; |
| int accessMode; |
| if(selector == assistIdentifier()) { |
| if(CharOperation.equals(selector, SUPER)) { |
| accessMode = ExplicitConstructorCall.Super; |
| } else if(CharOperation.equals(selector, THIS)) { |
| accessMode = ExplicitConstructorCall.This; |
| } else { |
| super.consumeMethodInvocationName(); |
| return; |
| } |
| } else { |
| super.consumeMethodInvocationName(); |
| if (requireExtendedRecovery()) { |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=430572, compensate for the hacks elsewhere where super/this gets treated as identifier. See getUnspecifiedReference |
| if (this.astPtr >= 0 && this.astStack[this.astPtr] == this.assistNode && this.assistNode instanceof ThisReference) { |
| MessageSend messageSend = (MessageSend) this.expressionStack[this.expressionPtr]; |
| if (messageSend.receiver instanceof SingleNameReference) { |
| SingleNameReference snr = (SingleNameReference) messageSend.receiver; |
| if (snr.token == CharOperation.NO_CHAR) { // dummy reference created by getUnspecifiedReference ??? |
| messageSend.receiver = (Expression) this.astStack[this.astPtr--]; |
| } |
| } |
| } |
| } |
| return; |
| } |
| |
| final ExplicitConstructorCall constructorCall = new SelectionOnExplicitConstructorCall(accessMode); |
| constructorCall.sourceEnd = this.rParenPos; |
| constructorCall.sourceStart = (int) (this.identifierPositionStack[this.identifierPtr] >>> 32); |
| int length; |
| if ((length = this.expressionLengthStack[this.expressionLengthPtr--]) != 0) { |
| this.expressionPtr -= length; |
| System.arraycopy(this.expressionStack, this.expressionPtr + 1, constructorCall.arguments = new Expression[length], 0, length); |
| } |
| |
| if (!this.diet){ |
| pushOnAstStack(constructorCall); |
| this.restartRecovery = true; // force to restart in recovery mode |
| this.lastIgnoredToken = -1; |
| } else { |
| pushOnExpressionStack(new Expression(){ |
| public TypeBinding resolveType(BlockScope scope) { |
| constructorCall.resolve(scope); |
| return null; |
| } |
| public StringBuffer printExpression(int indent, StringBuffer output) { |
| return output; |
| } |
| }); |
| } |
| this.assistNode = constructorCall; |
| this.lastCheckPoint = constructorCall.sourceEnd + 1; |
| this.isOrphanCompletionNode = true; |
| } |
| protected void consumeMethodInvocationPrimary() { |
| //optimize the push/pop |
| //MethodInvocation ::= Primary '.' 'Identifier' '(' ArgumentListopt ')' |
| |
| char[] selector = this.identifierStack[this.identifierPtr]; |
| int accessMode; |
| if(selector == assistIdentifier()) { |
| if(CharOperation.equals(selector, SUPER)) { |
| accessMode = ExplicitConstructorCall.Super; |
| } else if(CharOperation.equals(selector, THIS)) { |
| accessMode = ExplicitConstructorCall.This; |
| } else { |
| super.consumeMethodInvocationPrimary(); |
| return; |
| } |
| } else { |
| super.consumeMethodInvocationPrimary(); |
| return; |
| } |
| |
| final ExplicitConstructorCall constructorCall = new SelectionOnExplicitConstructorCall(accessMode); |
| constructorCall.sourceEnd = this.rParenPos; |
| int length; |
| if ((length = this.expressionLengthStack[this.expressionLengthPtr--]) != 0) { |
| this.expressionPtr -= length; |
| System.arraycopy(this.expressionStack, this.expressionPtr + 1, constructorCall.arguments = new Expression[length], 0, length); |
| } |
| constructorCall.qualification = this.expressionStack[this.expressionPtr--]; |
| constructorCall.sourceStart = constructorCall.qualification.sourceStart; |
| |
| if (!this.diet){ |
| pushOnAstStack(constructorCall); |
| this.restartRecovery = true; // force to restart in recovery mode |
| this.lastIgnoredToken = -1; |
| } else { |
| pushOnExpressionStack(new Expression(){ |
| public TypeBinding resolveType(BlockScope scope) { |
| constructorCall.resolve(scope); |
| return null; |
| } |
| public StringBuffer printExpression(int indent, StringBuffer output) { |
| return output; |
| } |
| }); |
| } |
| |
| this.assistNode = constructorCall; |
| this.lastCheckPoint = constructorCall.sourceEnd + 1; |
| this.isOrphanCompletionNode = true; |
| } |
| protected void consumeNormalAnnotation(boolean isTypeAnnotation) { |
| int index; |
| |
| if ((index = this.indexOfAssistIdentifier()) < 0) { |
| super.consumeNormalAnnotation(isTypeAnnotation); |
| return; |
| } |
| |
| NormalAnnotation normalAnnotation = 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); |
| } |
| this.assistNode = typeReference; |
| this.lastCheckPoint = typeReference.sourceEnd + 1; |
| |
| normalAnnotation = new NormalAnnotation(typeReference, this.intStack[this.intPtr--]); |
| if ((length = this.astLengthStack[this.astLengthPtr--]) != 0) { |
| System.arraycopy( |
| this.astStack, |
| (this.astPtr -= length) + 1, |
| normalAnnotation.memberValuePairs = new MemberValuePair[length], |
| 0, |
| length); |
| } |
| normalAnnotation.declarationSourceEnd = this.rParenPos; |
| if (isTypeAnnotation) { |
| pushOnTypeAnnotationStack(normalAnnotation); |
| } else { |
| pushOnExpressionStack(normalAnnotation); |
| } |
| } |
| protected void consumeSingleMemberAnnotation(boolean isTypeAnnotation) { |
| int index; |
| |
| if ((index = this.indexOfAssistIdentifier()) < 0) { |
| super.consumeSingleMemberAnnotation(isTypeAnnotation); |
| return; |
| } |
| |
| SingleMemberAnnotation singleMemberAnnotation = 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); |
| } |
| this.assistNode = typeReference; |
| this.lastCheckPoint = typeReference.sourceEnd + 1; |
| |
| singleMemberAnnotation = new SingleMemberAnnotation(typeReference, this.intStack[this.intPtr--]); |
| singleMemberAnnotation.memberValue = this.expressionStack[this.expressionPtr--]; |
| this.expressionLengthPtr--; |
| singleMemberAnnotation.declarationSourceEnd = this.rParenPos; |
| if (isTypeAnnotation) { |
| pushOnTypeAnnotationStack(singleMemberAnnotation); |
| } else { |
| pushOnExpressionStack(singleMemberAnnotation); |
| } |
| } |
| protected void consumeStaticImportOnDemandDeclarationName() { |
| // TypeImportOnDemandDeclarationName ::= 'import' 'static' Name '.' '*' |
| /* push an ImportRef build from the last name |
| stored in the identifier stack. */ |
| |
| int index; |
| |
| /* no need to take action if not inside assist identifiers */ |
| if ((index = indexOfAssistIdentifier()) < 0) { |
| super.consumeStaticImportOnDemandDeclarationName(); |
| return; |
| } |
| /* retrieve identifiers subset and whole positions, the assist node positions |
| should include the entire replaced source. */ |
| int length = this.identifierLengthStack[this.identifierLengthPtr]; |
| char[][] subset = identifierSubSet(index+1); // include the assistIdentifier |
| this.identifierLengthPtr--; |
| this.identifierPtr -= length; |
| long[] positions = new long[length]; |
| System.arraycopy( |
| this.identifierPositionStack, |
| this.identifierPtr + 1, |
| positions, |
| 0, |
| length); |
| |
| /* build specific assist node on import statement */ |
| ImportReference reference = createAssistImportReference(subset, positions, ClassFileConstants.AccStatic); |
| reference.bits |= ASTNode.OnDemand; |
| // star end position |
| reference.trailingStarPosition = this.intStack[this.intPtr--]; |
| this.assistNode = reference; |
| this.lastCheckPoint = reference.sourceEnd + 1; |
| |
| pushOnAstStack(reference); |
| |
| if (this.currentToken == TokenNameSEMICOLON){ |
| reference.declarationSourceEnd = this.scanner.currentPosition - 1; |
| } else { |
| reference.declarationSourceEnd = (int) positions[length-1]; |
| } |
| //endPosition is just before the ; |
| reference.declarationSourceStart = this.intStack[this.intPtr--]; |
| // flush annotations defined prior to import statements |
| reference.declarationSourceEnd = flushCommentsDefinedPriorTo(reference.declarationSourceEnd); |
| |
| // recovery |
| if (this.currentElement != null){ |
| this.lastCheckPoint = reference.declarationSourceEnd+1; |
| this.currentElement = this.currentElement.add(reference, 0); |
| this.lastIgnoredToken = -1; |
| this.restartRecovery = true; // used to avoid branching back into the regular automaton |
| } |
| } |
| protected void consumeToken(int token) { |
| super.consumeToken(token); |
| |
| // if in a method or if in a field initializer |
| if (isInsideMethod() || isInsideFieldInitialization()) { |
| switch (token) { |
| case TokenNamecase : |
| pushOnElementStack(K_BETWEEN_CASE_AND_COLON); |
| break; |
| case TokenNameCOLON: |
| if(topKnownElementKind(SELECTION_OR_ASSIST_PARSER) == K_BETWEEN_CASE_AND_COLON) { |
| popElement(K_BETWEEN_CASE_AND_COLON); |
| } |
| break; |
| case TokenNamereturn: |
| pushOnElementStack(K_INSIDE_RETURN_STATEMENT, this.bracketDepth); |
| break; |
| case TokenNameSEMICOLON: |
| switch(topKnownElementKind(SELECTION_OR_ASSIST_PARSER)) { |
| case K_INSIDE_RETURN_STATEMENT : |
| if(topKnownElementInfo(SELECTION_OR_ASSIST_PARSER) == this.bracketDepth) { |
| popElement(K_INSIDE_RETURN_STATEMENT); |
| } |
| break; |
| } |
| break; |
| } |
| } |
| } |
| protected void consumeTypeImportOnDemandDeclarationName() { |
| // TypeImportOnDemandDeclarationName ::= 'import' Name '.' '*' |
| /* push an ImportRef build from the last name |
| stored in the identifier stack. */ |
| |
| int index; |
| |
| /* no need to take action if not inside assist identifiers */ |
| if ((index = indexOfAssistIdentifier()) < 0) { |
| super.consumeTypeImportOnDemandDeclarationName(); |
| return; |
| } |
| /* retrieve identifiers subset and whole positions, the assist node positions |
| should include the entire replaced source. */ |
| int length = this.identifierLengthStack[this.identifierLengthPtr]; |
| char[][] subset = identifierSubSet(index+1); // include the assistIdentifier |
| this.identifierLengthPtr--; |
| this.identifierPtr -= length; |
| long[] positions = new long[length]; |
| System.arraycopy( |
| this.identifierPositionStack, |
| this.identifierPtr + 1, |
| positions, |
| 0, |
| length); |
| |
| /* build specific assist node on import statement */ |
| ImportReference reference = createAssistImportReference(subset, positions, ClassFileConstants.AccDefault); |
| reference.bits |= ASTNode.OnDemand; |
| // star end position |
| reference.trailingStarPosition = this.intStack[this.intPtr--]; |
| this.assistNode = reference; |
| this.lastCheckPoint = reference.sourceEnd + 1; |
| |
| pushOnAstStack(reference); |
| |
| if (this.currentToken == TokenNameSEMICOLON){ |
| reference.declarationSourceEnd = this.scanner.currentPosition - 1; |
| } else { |
| reference.declarationSourceEnd = (int) positions[length-1]; |
| } |
| //endPosition is just before the ; |
| reference.declarationSourceStart = this.intStack[this.intPtr--]; |
| // flush comments defined prior to import statements |
| reference.declarationSourceEnd = flushCommentsDefinedPriorTo(reference.declarationSourceEnd); |
| |
| // recovery |
| if (this.currentElement != null){ |
| this.lastCheckPoint = reference.declarationSourceEnd+1; |
| this.currentElement = this.currentElement.add(reference, 0); |
| this.lastIgnoredToken = -1; |
| this.restartRecovery = true; // used to avoid branching back into the regular automaton |
| } |
| } |
| protected SelectionParser createSnapShotParser() { |
| return new SelectionParser(this.problemReporter); |
| } |
| public ImportReference createAssistPackageVisibilityReference(char[][] tokens, long[] positions){ |
| return new SelectionOnExportReference(tokens, positions); |
| } |
| public ImportReference createAssistImportReference(char[][] tokens, long[] positions, int mod){ |
| return new SelectionOnImportReference(tokens, positions, mod); |
| } |
| @Override |
| public ModuleDeclaration createAssistModuleDeclaration(CompilationResult compilationResult, char[][] tokens, |
| long[] positions) { |
| return new SelectionOnModuleDeclaration(compilationResult, tokens, positions); |
| } |
| public ImportReference createAssistPackageReference(char[][] tokens, long[] positions){ |
| return new SelectionOnPackageReference(tokens, positions); |
| } |
| protected JavadocParser createJavadocParser() { |
| return new SelectionJavadocParser(this); |
| } |
| protected LocalDeclaration createLocalDeclaration(char[] assistName,int sourceStart,int sourceEnd) { |
| if (this.indexOfAssistIdentifier() < 0) { |
| return super.createLocalDeclaration(assistName, sourceStart, sourceEnd); |
| } else { |
| SelectionOnLocalName local = new SelectionOnLocalName(assistName, sourceStart, sourceEnd); |
| this.assistNode = local; |
| this.lastCheckPoint = sourceEnd + 1; |
| return local; |
| } |
| } |
| public NameReference createQualifiedAssistNameReference(char[][] previousIdentifiers, char[] assistName, long[] positions){ |
| return new SelectionOnQualifiedNameReference( |
| previousIdentifiers, |
| assistName, |
| positions); |
| } |
| public TypeReference createQualifiedAssistTypeReference(char[][] previousIdentifiers, char[] assistName, long[] positions){ |
| return new SelectionOnQualifiedTypeReference( |
| previousIdentifiers, |
| assistName, |
| positions); |
| } |
| public TypeReference createParameterizedQualifiedAssistTypeReference( |
| char[][] tokens, TypeReference[][] typeArguments, char[] assistname, TypeReference[] assistTypeArguments, long[] positions) { |
| return new SelectionOnParameterizedQualifiedTypeReference(tokens, assistname, typeArguments, assistTypeArguments, positions); |
| |
| } |
| public NameReference createSingleAssistNameReference(char[] assistName, long position) { |
| return new SelectionOnSingleNameReference(assistName, position); |
| } |
| public TypeReference createSingleAssistTypeReference(char[] assistName, long position) { |
| return new SelectionOnSingleTypeReference(assistName, position); |
| } |
| public TypeReference createParameterizedSingleAssistTypeReference(TypeReference[] typeArguments, char[] assistName, long position) { |
| return new SelectionOnParameterizedSingleTypeReference(assistName, typeArguments, position); |
| } |
| public CompilationUnitDeclaration dietParse(ICompilationUnit sourceUnit, CompilationResult compilationResult, int start, int end) { |
| |
| this.selectionStart = start; |
| this.selectionEnd = end; |
| SelectionScanner selectionScanner = (SelectionScanner)this.scanner; |
| selectionScanner.selectionIdentifier = null; |
| selectionScanner.selectionStart = start; |
| selectionScanner.selectionEnd = end; |
| return this.dietParse(sourceUnit, compilationResult); |
| } |
| protected NameReference getUnspecifiedReference(boolean rejectTypeAnnotations) { |
| /* build a (unspecified) NameReference which may be qualified*/ |
| |
| int completionIndex; |
| |
| /* no need to take action if not inside completed identifiers */ |
| if ((completionIndex = indexOfAssistIdentifier()) < 0) { |
| return super.getUnspecifiedReference(rejectTypeAnnotations); |
| } |
| |
| if (rejectTypeAnnotations) { |
| consumeNonTypeUseName(); |
| } |
| int length = this.identifierLengthStack[this.identifierLengthPtr]; |
| if (CharOperation.equals(assistIdentifier(), SUPER)){ |
| Reference reference; |
| if (completionIndex > 0){ // qualified super |
| // discard 'super' from identifier stacks |
| // There is some voodoo going on here in combination with SelectionScanne#scanIdentifierOrKeyword, do in Rome as Romans do and leave the stacks at the right depth. |
| this.identifierLengthStack[this.identifierLengthPtr] = completionIndex; |
| int ptr = this.identifierPtr -= (length - completionIndex); |
| pushOnGenericsLengthStack(0); |
| pushOnGenericsIdentifiersLengthStack(this.identifierLengthStack[this.identifierLengthPtr]); |
| for (int i = 0; i < completionIndex; i++) { |
| pushOnTypeAnnotationLengthStack(0); |
| } |
| reference = |
| new SelectionOnQualifiedSuperReference( |
| getTypeReference(0), |
| (int)(this.identifierPositionStack[ptr+1] >>> 32), |
| (int) this.identifierPositionStack[ptr+1]); |
| } else { // standard super |
| this.identifierPtr -= length; |
| this.identifierLengthPtr--; |
| reference = new SelectionOnSuperReference((int)(this.identifierPositionStack[this.identifierPtr+1] >>> 32), (int) this.identifierPositionStack[this.identifierPtr+1]); |
| } |
| pushOnAstStack(reference); |
| this.assistNode = reference; |
| this.lastCheckPoint = reference.sourceEnd + 1; |
| if (!this.diet || this.dietInt != 0){ |
| this.restartRecovery = true; // force to restart in recovery mode |
| this.lastIgnoredToken = -1; |
| } |
| this.isOrphanCompletionNode = true; |
| return new SingleNameReference(CharOperation.NO_CHAR, 0); // dummy reference |
| } |
| NameReference nameReference; |
| /* retrieve identifiers subset and whole positions, the completion node positions |
| should include the entire replaced source. */ |
| char[][] subset = identifierSubSet(completionIndex); |
| this.identifierLengthPtr--; |
| this.identifierPtr -= length; |
| long[] positions = new long[length]; |
| System.arraycopy( |
| this.identifierPositionStack, |
| this.identifierPtr + 1, |
| positions, |
| 0, |
| length); |
| /* build specific completion on name reference */ |
| if (completionIndex == 0) { |
| /* completion inside first identifier */ |
| nameReference = createSingleAssistNameReference(assistIdentifier(), positions[0]); |
| } else { |
| /* completion inside subsequent identifier */ |
| nameReference = createQualifiedAssistNameReference(subset, assistIdentifier(), positions); |
| } |
| this.assistNode = nameReference; |
| this.lastCheckPoint = nameReference.sourceEnd + 1; |
| if (!this.diet){ |
| this.restartRecovery = true; // force to restart in recovery mode |
| this.lastIgnoredToken = -1; |
| } |
| this.isOrphanCompletionNode = true; |
| return nameReference; |
| } |
| /* |
| * Copy of code from superclass with the following change: |
| * In the case of qualified name reference if the cursor location is on the |
| * qualified name reference, then create a CompletionOnQualifiedNameReference |
| * instead. |
| */ |
| protected NameReference getUnspecifiedReferenceOptimized() { |
| |
| int index = indexOfAssistIdentifier(); |
| NameReference reference = super.getUnspecifiedReferenceOptimized(); |
| |
| if (index >= 0){ |
| if (!this.diet){ |
| this.restartRecovery = true; // force to restart in recovery mode |
| this.lastIgnoredToken = -1; |
| } |
| this.isOrphanCompletionNode = true; |
| } |
| return reference; |
| } |
| public void initializeScanner(){ |
| this.scanner = new SelectionScanner(this.options.sourceLevel); |
| } |
| public ReferenceExpression newReferenceExpression() { |
| char[] selector = this.identifierStack[this.identifierPtr]; |
| if (selector != assistIdentifier()){ |
| return super.newReferenceExpression(); |
| } |
| ReferenceExpression referenceExpression = new SelectionOnReferenceExpressionName(); |
| this.assistNode = referenceExpression; |
| return referenceExpression; |
| } |
| protected MessageSend newMessageSend() { |
| // '(' ArgumentListopt ')' |
| // the arguments are on the expression stack |
| |
| char[] selector = this.identifierStack[this.identifierPtr]; |
| if (selector != assistIdentifier()){ |
| return super.newMessageSend(); |
| } |
| MessageSend messageSend = new SelectionOnMessageSend(); |
| int length; |
| if ((length = this.expressionLengthStack[this.expressionLengthPtr--]) != 0) { |
| this.expressionPtr -= length; |
| System.arraycopy( |
| this.expressionStack, |
| this.expressionPtr + 1, |
| messageSend.arguments = new Expression[length], |
| 0, |
| length); |
| } |
| this.assistNode = messageSend; |
| if (!this.diet){ |
| // Don't restart recovery, not yet, until variable decl statement has been consumed. |
| // This is to ensure chained method invocations are taken into account for resolution. |
| this.selectionNodeFound = true; |
| this.lastIgnoredToken = -1; |
| } |
| |
| this.isOrphanCompletionNode = true; |
| return messageSend; |
| } |
| protected MessageSend newMessageSendWithTypeArguments() { |
| char[] selector = this.identifierStack[this.identifierPtr]; |
| if (selector != assistIdentifier()){ |
| return super.newMessageSendWithTypeArguments(); |
| } |
| MessageSend messageSend = new SelectionOnMessageSend(); |
| int length; |
| if ((length = this.expressionLengthStack[this.expressionLengthPtr--]) != 0) { |
| this.expressionPtr -= length; |
| System.arraycopy( |
| this.expressionStack, |
| this.expressionPtr + 1, |
| messageSend.arguments = new Expression[length], |
| 0, |
| length); |
| } |
| this.assistNode = messageSend; |
| if (!this.diet){ |
| // Don't restart recovery, not yet, until variable decl statement has been consumed. |
| // This is to ensure chained method invocations are taken into account for resolution. |
| this.selectionNodeFound = true; |
| this.lastIgnoredToken = -1; |
| } |
| |
| this.isOrphanCompletionNode = true; |
| return messageSend; |
| } |
| public CompilationUnitDeclaration parse(ICompilationUnit sourceUnit, CompilationResult compilationResult, int start, int end) { |
| |
| if (end == -1) return super.parse(sourceUnit, compilationResult, start, end); |
| |
| this.selectionStart = start; |
| this.selectionEnd = end; |
| SelectionScanner selectionScanner = (SelectionScanner)this.scanner; |
| selectionScanner.selectionIdentifier = null; |
| selectionScanner.selectionStart = start; |
| selectionScanner.selectionEnd = end; |
| return super.parse(sourceUnit, compilationResult, -1, -1/*parse without reseting the scanner*/); |
| } |
| |
| /* |
| * 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 int resumeAfterRecovery() { |
| |
| /* if reached assist node inside method body, but still inside nested type, |
| should continue in diet mode until the end of the method body */ |
| if (this.assistNode != null |
| && !(this.referenceContext instanceof CompilationUnitDeclaration)){ |
| this.currentElement.preserveEnclosingBlocks(); |
| if (requireExtendedRecovery()) { |
| if (this.unstackedAct != ERROR_ACTION) { |
| return RESUME; |
| } |
| return super.resumeAfterRecovery(); |
| } |
| if (this.currentElement.enclosingType() == null) { |
| if (!(this.currentElement instanceof RecoveredType)) { |
| resetStacks(); |
| return HALT; |
| } |
| |
| RecoveredType recoveredType = (RecoveredType) this.currentElement; |
| if (recoveredType.typeDeclaration != null && recoveredType.typeDeclaration.allocation == this.assistNode) { |
| resetStacks(); |
| return HALT; |
| } |
| } |
| } |
| return super.resumeAfterRecovery(); |
| } |
| |
| public void selectionIdentifierCheck(){ |
| if (checkRecoveredType()) return; |
| } |
| public void setAssistIdentifier(char[] assistIdent){ |
| ((SelectionScanner)this.scanner).selectionIdentifier = assistIdent; |
| } |
| /* |
| * 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 */ |
| selectionIdentifierCheck(); |
| 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(); |
| } |
| protected Argument typeElidedArgument() { |
| char[] selector = this.identifierStack[this.identifierPtr]; |
| if (selector != assistIdentifier()){ |
| return super.typeElidedArgument(); |
| } |
| this.identifierLengthPtr--; |
| char[] identifierName = this.identifierStack[this.identifierPtr]; |
| long namePositions = this.identifierPositionStack[this.identifierPtr--]; |
| |
| Argument argument = |
| new SelectionOnArgumentName( |
| identifierName, |
| namePositions, |
| null, // elided type |
| ClassFileConstants.AccDefault, |
| true); |
| argument.declarationSourceStart = (int) (namePositions >>> 32); |
| this.assistNode = argument; |
| return argument; |
| } |
| public String toString() { |
| String s = Util.EMPTY_STRING; |
| s = s + "elementKindStack : int[] = {"; //$NON-NLS-1$ |
| for (int i = 0; i <= this.elementPtr; i++) { |
| s = s + String.valueOf(this.elementKindStack[i]) + ","; //$NON-NLS-1$ |
| } |
| s = s + "}\n"; //$NON-NLS-1$ |
| s = s + "elementInfoStack : int[] = {"; //$NON-NLS-1$ |
| for (int i = 0; i <= this.elementPtr; i++) { |
| s = s + String.valueOf(this.elementInfoStack[i]) + ","; //$NON-NLS-1$ |
| } |
| s = s + "}\n"; //$NON-NLS-1$ |
| return s + super.toString(); |
| } |
| @Override |
| public ModuleReference createAssistModuleReference(int index) { |
| // ignore index, all segments of the module name are part of a single identifier. |
| /* retrieve identifiers subset and whole positions, the assist node positions |
| should include the entire replaced source. */ |
| int length; |
| char[][] tokens = new char[length = this.identifierLengthStack[this.identifierLengthPtr--]][]; |
| this.identifierPtr -= length; |
| long[] positions = new long[length]; |
| System.arraycopy(this.identifierStack, this.identifierPtr + 1, tokens, 0, length); |
| System.arraycopy(this.identifierPositionStack, this.identifierPtr + 1, positions, 0, length); |
| return new SelectionOnModuleReference(tokens, positions); |
| } |
| } |