blob: d330454bae242b3da6f724b50c8592f9b5125edc [file] [log] [blame]
package org.eclipse.jdt.internal.compiler.parser;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.lookup.*;
import org.eclipse.jdt.internal.compiler.util.*;
/**
* Internal method structure for parsing recovery
*/
public class RecoveredMethod extends RecoveredElement implements CompilerModifiers, TerminalSymbols, BaseTypes {
public AbstractMethodDeclaration methodDeclaration;
public RecoveredType[] localTypes;
public int localTypeCount;
public RecoveredBlock methodBody;
public boolean discardBody = true;
public RecoveredMethod(AbstractMethodDeclaration methodDeclaration, RecoveredElement parent, int bracketBalance){
this(methodDeclaration, parent, bracketBalance, null);
}
public RecoveredMethod(AbstractMethodDeclaration methodDeclaration, RecoveredElement parent, int bracketBalance, Parser parser){
super(parent, bracketBalance, parser);
this.methodDeclaration = methodDeclaration;
this.foundOpeningBrace = !bodyStartsAtHeaderEnd();
if(this.foundOpeningBrace) {
this.bracketBalance++;
}
}
/*
* Record a nested block declaration
*/
public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalance) {
/* default behavior is to delegate recording to parent if any,
do not consider elements passed the known end (if set)
it must be belonging to an enclosing element
*/
if (methodDeclaration.declarationSourceEnd > 0
&& nestedBlockDeclaration.sourceStart
> methodDeclaration.declarationSourceEnd){
return this.parent.add(nestedBlockDeclaration, bracketBalance);
}
/* consider that if the opening brace was not found, it is there */
if (!foundOpeningBrace){
foundOpeningBrace = true;
this.bracketBalance++;
}
methodBody = new RecoveredBlock(nestedBlockDeclaration, this, bracketBalance);
if (nestedBlockDeclaration.sourceEnd == 0) return methodBody;
return this;
}
/*
* Record a field declaration
*/
public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalance) {
/* local variables inside method can only be final and non void */
char[][] fieldTypeName;
if ((fieldDeclaration.modifiers & ~AccFinal) != 0 /* local var can only be final */
|| (fieldDeclaration.type == null) // initializer
|| ((fieldTypeName = fieldDeclaration.type.getTypeName()).length == 1 // non void
&& CharOperation.equals(fieldTypeName[0], VoidBinding.sourceName()))){
this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(fieldDeclaration.declarationSourceStart - 1));
return this.parent.add(fieldDeclaration, bracketBalance);
}
/* default behavior is to delegate recording to parent if any,
do not consider elements passed the known end (if set)
it must be belonging to an enclosing element
*/
if (methodDeclaration.declarationSourceEnd > 0
&& fieldDeclaration.declarationSourceStart
> methodDeclaration.declarationSourceEnd){
return this.parent.add(fieldDeclaration, bracketBalance);
}
/* consider that if the opening brace was not found, it is there */
if (!foundOpeningBrace){
foundOpeningBrace = true;
this.bracketBalance++;
}
// still inside method, treat as local variable
return this; // ignore
}
/*
* Record a local declaration - regular method should have been created a block body
*/
public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalance) {
/* do not consider a type starting passed the type end (if set)
it must be belonging to an enclosing type */
if (methodDeclaration.declarationSourceEnd != 0
&& localDeclaration.declarationSourceStart > methodDeclaration.declarationSourceEnd){
if (parent == null) {
return this; // ignore
} else {
return this.parent.add(localDeclaration, bracketBalance);
}
}
/* method body should have been created */
Block block = new Block(0);
block.sourceStart = methodDeclaration.bodyStart;
RecoveredElement element = this.add(block, 1);
return element.add(localDeclaration, bracketBalance);
}
/*
* Record a statement - regular method should have been created a block body
*/
public RecoveredElement add(Statement statement, int bracketBalance) {
/* do not consider a type starting passed the type end (if set)
it must be belonging to an enclosing type */
if (methodDeclaration.declarationSourceEnd != 0
&& statement.sourceStart > methodDeclaration.declarationSourceEnd){
if (parent == null) {
return this; // ignore
} else {
return this.parent.add(statement, bracketBalance);
}
}
/* method body should have been created */
Block block = new Block(0);
block.sourceStart = methodDeclaration.bodyStart;
RecoveredElement element = this.add(block, 1);
return element.add(statement, bracketBalance);
}
public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalance) {
/* do not consider a type starting passed the type end (if set)
it must be belonging to an enclosing type */
if (methodDeclaration.declarationSourceEnd != 0
&& typeDeclaration.declarationSourceStart > methodDeclaration.declarationSourceEnd){
if (parent == null) {
return this; // ignore
} else {
return this.parent.add(typeDeclaration, bracketBalance);
}
}
if (typeDeclaration instanceof LocalTypeDeclaration){
/* method body should have been created */
Block block = new Block(0);
block.sourceStart = methodDeclaration.bodyStart;
RecoveredElement element = this.add(block, 1);
return element.add(typeDeclaration, bracketBalance);
}
if (localTypes == null) {
localTypes = new RecoveredType[5];
localTypeCount = 0;
} else {
if (localTypeCount == localTypes.length) {
System.arraycopy(
localTypes,
0,
(localTypes = new RecoveredType[2 * localTypeCount]),
0,
localTypeCount);
}
}
RecoveredType element = new RecoveredType(typeDeclaration, this, bracketBalance);
localTypes[localTypeCount++] = element;
/* consider that if the opening brace was not found, it is there */
if (!foundOpeningBrace){
foundOpeningBrace = true;
this.bracketBalance++;
}
return element;
}
public boolean bodyStartsAtHeaderEnd(){
return methodDeclaration.bodyStart == methodDeclaration.sourceEnd+1;
}
/*
* Answer the associated parsed structure
*/
public AstNode parseTree(){
return methodDeclaration;
}
/*
* Answer the very source end of the corresponding parse node
*/
public int sourceEnd(){
return this.methodDeclaration.declarationSourceEnd;
}
public String toString(int tab) {
StringBuffer result = new StringBuffer(tabString(tab));
result.append("Recovered method:\n"/*nonNLS*/);
result.append(this.methodDeclaration.toString(tab + 1));
if (this.localTypes != null) {
for (int i = 0; i < this.localTypeCount; i++) {
result.append("\n"/*nonNLS*/);
result.append(this.localTypes[i].toString(tab + 1));
}
}
if (this.methodBody != null) {
result.append("\n"/*nonNLS*/);
result.append(this.methodBody.toString(tab + 1));
}
return result.toString();
}
/*
* Update the bodyStart of the corresponding parse node
*/
public void updateBodyStart(int bodyStart){
this.foundOpeningBrace = true;
this.methodDeclaration.bodyStart = bodyStart;
}
public AbstractMethodDeclaration updatedMethodDeclaration(){
if (methodBody != null){
Block block = methodBody.updatedBlock();
if (block != null){
methodDeclaration.statements = block.statements;
/* first statement might be an explict constructor call destinated to a special slot */
if (methodDeclaration.isConstructor()
&& methodDeclaration.statements != null
&& methodDeclaration.statements[0] instanceof ExplicitConstructorCall){
((ConstructorDeclaration)methodDeclaration).constructorCall = (ExplicitConstructorCall)methodDeclaration.statements[0];
int length = methodDeclaration.statements.length;
System.arraycopy(
methodDeclaration.statements,
1,
(methodDeclaration.statements = new Statement[length-1]),
0,
length-1);
}
}
}
return methodDeclaration;
}
/*
* Update the corresponding parse node from parser state which
* is about to disappear because of restarting recovery
*/
public void updateFromParserState(){
if(this.bodyStartsAtHeaderEnd()){
Parser parser = this.parser();
/* might want to recover arguments or thrown exceptions */
if (parser.listLength > 0){ // awaiting interface type references
/* has consumed the arguments - listed elements must be thrown exceptions */
if (methodDeclaration.sourceEnd == parser.rParenPos){
parser.consumeMethodHeaderThrowsClause();
// will reset typeListLength to zero
// thus this check will only be performed on first errorCheck after void foo() throws X, Y,
} else {
/* has not consumed arguments yet, listed elements must be arguments */
if (parser.currentToken == TokenNameLPAREN || parser.currentToken == TokenNameSEMICOLON){
/* if currentToken is parenthesis this last argument is a method/field signature */
parser.astLengthStack[parser.astLengthPtr] --;
parser.astPtr --;
parser.listLength --;
parser.currentToken = 0;
}
int argLength = parser.astLengthStack[parser.astLengthPtr];
int argStart = parser.astPtr - argLength + 1;
int count;
for (count = 0; count < argLength; count++){
Argument argument = (Argument)parser.astStack[argStart+count];
/* cannot be an argument if non final */
char[][] argTypeName = argument.type.getTypeName();
if ((argument.modifiers & ~AccFinal) != 0
|| (argTypeName.length == 1
&& CharOperation.equals(argTypeName[0], VoidBinding.sourceName()))){
parser.astLengthStack[parser.astLengthPtr] = count-1;
parser.astPtr = argStart+count-1;
parser.listLength = count-1;
parser.currentToken = 0;
break;
}
count++;
}
if (parser.listLength > 0){
parser.consumeMethodHeaderParameters();
/* fix-up positions, given they were updated against rParenPos, which did not get set */
if (parser.currentElement == this){ // parameter addition might have added an awaiting (no return type) method - see 1FVXQZ4 */
methodDeclaration.sourceEnd = methodDeclaration.arguments[methodDeclaration.arguments.length-1].sourceEnd;
methodDeclaration.bodyStart = methodDeclaration.sourceEnd+1;
parser.lastCheckPoint = methodDeclaration.bodyStart;
}
}
}
}
}
}
/*
* An opening brace got consumed, might be the expected opening one of the current element,
* in which case the bodyStart is updated.
*/
public RecoveredElement updateOnOpeningBrace(int braceEnd){
/* in case the opening brace is close enough to the signature */
if (bracketBalance == 0){
/*
if (parser.scanner.searchLineNumber(methodDeclaration.sourceEnd)
!= parser.scanner.searchLineNumber(braceEnd)){
*/
switch(parser().lastIgnoredToken){
case -1 :
case TokenNamethrows :
break;
default:
this.foundOpeningBrace = true;
bracketBalance = 1; // pretend the brace was already there
}
}
return super.updateOnOpeningBrace(braceEnd);
}
public void updateParseTree(){
this.updatedMethodDeclaration();
}
/*
* Update the declarationSourceEnd of the corresponding parse node
*/
public void updateSourceEndIfNecessary(int sourceEnd){
if (this.methodDeclaration.declarationSourceEnd == 0) {
this.methodDeclaration.declarationSourceEnd = sourceEnd;
}
}
}