org.eclipse.jdt.internal.compiler.parser.RecoveredBlock Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ecj Show documentation
Show all versions of ecj Show documentation
Eclipse Compiler for Java(TM)
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.parser;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jdt.core.compiler.*;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Block;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ForeachStatement;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
public class RecoveredBlock extends RecoveredStatement implements TerminalTokens {
public Block blockDeclaration;
public RecoveredStatement[] statements;
public int statementCount;
public boolean preserveContent = false;
public RecoveredLocalVariable pendingArgument;
int pendingModifiers;
int pendingModifersSourceStart = -1;
RecoveredAnnotation[] pendingAnnotations;
int pendingAnnotationCount;
public RecoveredBlock(Block block, RecoveredElement parent, int bracketBalance){
super(block, parent, bracketBalance);
this.blockDeclaration = block;
this.foundOpeningBrace = true;
this.preserveContent = parser().methodRecoveryActivated || parser().statementRecoveryActivated;
}
@Override
public RecoveredElement add(AbstractMethodDeclaration methodDeclaration, int bracketBalanceValue) {
if (this.parent != null && this.parent instanceof RecoveredMethod) {
RecoveredMethod enclosingRecoveredMethod = (RecoveredMethod) this.parent;
if (enclosingRecoveredMethod.methodBody == this && enclosingRecoveredMethod.parent == null) {
resetPendingModifiers();
// the element cannot be added because we are in the body of a top level method
return this; // ignore this element
}
}
return super.add(methodDeclaration, bracketBalanceValue);
}
/*
* Record a nested block declaration
*/
@Override
public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalanceValue) {
resetPendingModifiers();
/* do not consider a nested block starting passed the block end (if set)
it must be belonging to an enclosing block */
if (this.blockDeclaration.sourceEnd != 0
&& nestedBlockDeclaration.sourceStart > this.blockDeclaration.sourceEnd){
return this.parent.add(nestedBlockDeclaration, bracketBalanceValue);
}
RecoveredBlock element = new RecoveredBlock(nestedBlockDeclaration, this, bracketBalanceValue);
// if we have a pending Argument, promote it into the new block
if (this.pendingArgument != null){
element.attach(this.pendingArgument);
this.pendingArgument = null;
}
if(parser().statementRecoveryActivated) {
addBlockStatement(element);
}
attach(element);
if (nestedBlockDeclaration.sourceEnd == 0) return element;
return this;
}
/*
* Record a local declaration
*/
@Override
public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalanceValue) {
return this.add(localDeclaration, bracketBalanceValue, false);
}
/*
* Record a local declaration
*/
public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalanceValue, boolean delegatedByParent) {
if (localDeclaration.isRecoveredFromLoneIdentifier()) {
return this; // skip, the local will be mutated into an assignment and added later, see Parser.consumeLocalVariableDeclarationStatement
}
/* local variables inside method can only be final and non void */
/*
char[][] localTypeName;
if ((localDeclaration.modifiers & ~AccFinal) != 0 // local var can only be final
|| (localDeclaration.type == null) // initializer
|| ((localTypeName = localDeclaration.type.getTypeName()).length == 1 // non void
&& CharOperation.equals(localTypeName[0], VoidBinding.sourceName()))){
if (delegatedByParent){
return this; //ignore
} else {
this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(localDeclaration.declarationSourceStart - 1));
return this.parent.add(localDeclaration, bracketBalance);
}
}
*/
/* do not consider a local variable starting passed the block end (if set)
it must be belonging to an enclosing block */
if (this.blockDeclaration.sourceEnd != 0
&& localDeclaration.declarationSourceStart > this.blockDeclaration.sourceEnd){
resetPendingModifiers();
if (delegatedByParent) return this; //ignore
return this.parent.add(localDeclaration, bracketBalanceValue);
}
RecoveredLocalVariable element = new RecoveredLocalVariable(localDeclaration, this, bracketBalanceValue);
if(this.pendingAnnotationCount > 0) {
element.attach(
this.pendingAnnotations,
this.pendingAnnotationCount,
this.pendingModifiers,
this.pendingModifersSourceStart);
}
resetPendingModifiers();
if (localDeclaration instanceof Argument){
this.pendingArgument = element;
return this;
}
attach(element);
if (localDeclaration.declarationSourceEnd == 0) return element;
return this;
}
/*
* Record a statement declaration
*/
@Override
public RecoveredElement add(Statement stmt, int bracketBalanceValue) {
return this.add(stmt, bracketBalanceValue, false);
}
/*
* Record a statement declaration
*/
public RecoveredElement add(Statement stmt, int bracketBalanceValue, boolean delegatedByParent) {
resetPendingModifiers();
/* do not consider a nested block starting passed the block end (if set)
it must be belonging to an enclosing block */
if (this.blockDeclaration.sourceEnd != 0
&& stmt.sourceStart > this.blockDeclaration.sourceEnd){
if (delegatedByParent) return this; //ignore
return this.parent.add(stmt, bracketBalanceValue);
}
RecoveredStatement element = new RecoveredStatement(stmt, this, bracketBalanceValue);
attach(element);
if (!isEndKnown(stmt)) return element;
return this;
}
boolean isEndKnown(Statement stmt) {
if (stmt instanceof ForeachStatement) {
if (((ForeachStatement) stmt).action == null)
return false;
}
return stmt.sourceEnd != 0;
}
/*
* Addition of a type to an initializer (act like inside method body)
*/
@Override
public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalanceValue) {
return this.add(typeDeclaration, bracketBalanceValue, false);
}
/*
* Addition of a type to an initializer (act like inside method body)
*/
public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalanceValue, boolean delegatedByParent) {
/* do not consider a type starting passed the block end (if set)
it must be belonging to an enclosing block */
if (this.blockDeclaration.sourceEnd != 0
&& typeDeclaration.declarationSourceStart > this.blockDeclaration.sourceEnd){
resetPendingModifiers();
if (delegatedByParent) return this; //ignore
return this.parent.add(typeDeclaration, bracketBalanceValue);
}
RecoveredType element = new RecoveredType(typeDeclaration, this, bracketBalanceValue);
if(this.pendingAnnotationCount > 0) {
element.attach(
this.pendingAnnotations,
this.pendingAnnotationCount,
this.pendingModifiers,
this.pendingModifersSourceStart);
}
resetPendingModifiers();
attach(element);
if (typeDeclaration.declarationSourceEnd == 0) return element;
return this;
}
@Override
public RecoveredElement addAnnotationName(int identifierPtr, int identifierLengthPtr, int annotationStart, int bracketBalanceValue) {
if (this.pendingAnnotations == null) {
this.pendingAnnotations = new RecoveredAnnotation[5];
this.pendingAnnotationCount = 0;
} else {
if (this.pendingAnnotationCount == this.pendingAnnotations.length) {
System.arraycopy(
this.pendingAnnotations,
0,
(this.pendingAnnotations = new RecoveredAnnotation[2 * this.pendingAnnotationCount]),
0,
this.pendingAnnotationCount);
}
}
RecoveredAnnotation element = new RecoveredAnnotation(identifierPtr, identifierLengthPtr, annotationStart, this, bracketBalanceValue);
this.pendingAnnotations[this.pendingAnnotationCount++] = element;
return element;
}
@Override
public void addModifier(int flag, int modifiersSourceStart) {
this.pendingModifiers |= flag;
if (this.pendingModifersSourceStart < 0) {
this.pendingModifersSourceStart = modifiersSourceStart;
}
}
/*
* Attach a recovered statement
*/
void attach(RecoveredStatement recoveredStatement) {
if (this.statements == null) {
this.statements = new RecoveredStatement[5];
this.statementCount = 0;
} else {
if (this.statementCount == this.statements.length) {
System.arraycopy(
this.statements,
0,
(this.statements = new RecoveredStatement[2 * this.statementCount]),
0,
this.statementCount);
}
}
this.statements[this.statementCount++] = recoveredStatement;
}
void attachPendingModifiers(RecoveredAnnotation[] pendingAnnots, int pendingAnnotCount, int pendingMods, int pendingModsSourceStart) {
this.pendingAnnotations = pendingAnnots;
this.pendingAnnotationCount = pendingAnnotCount;
this.pendingModifiers = pendingMods;
this.pendingModifersSourceStart = pendingModsSourceStart;
}
/*
* Answer the associated parsed structure
*/
@Override
public ASTNode parseTree(){
return this.blockDeclaration;
}
@Override
public void resetPendingModifiers() {
this.pendingAnnotations = null;
this.pendingAnnotationCount = 0;
this.pendingModifiers = 0;
this.pendingModifersSourceStart = -1;
}
@Override
public String toString(int tab) {
StringBuffer result = new StringBuffer(tabString(tab));
result.append("Recovered block:\n"); //$NON-NLS-1$
this.blockDeclaration.print(tab + 1, result);
if (this.statements != null) {
for (int i = 0; i < this.statementCount; i++) {
result.append("\n"); //$NON-NLS-1$
result.append(this.statements[i].toString(tab + 1));
}
}
return result.toString();
}
/*
* Rebuild a block from the nested structure which is in scope
*/
public Block updatedBlock(int depth, Set knownTypes){
// if block was not marked to be preserved or empty, then ignore it
if (!this.preserveContent || this.statementCount == 0) return null;
Statement[] updatedStatements = new Statement[this.statementCount];
int updatedCount = 0;
// may need to update the end of the last statement
RecoveredStatement lastStatement = this.statements[this.statementCount - 1];
RecoveredMethod enclosingMethod = enclosingMethod();
RecoveredInitializer enclosingIntializer = enclosingInitializer();
int bodyEndValue = 0;
if(enclosingMethod != null) {
bodyEndValue = enclosingMethod.methodDeclaration.bodyEnd;
if(enclosingIntializer != null && enclosingMethod.methodDeclaration.sourceStart < enclosingIntializer.fieldDeclaration.sourceStart) {
bodyEndValue = enclosingIntializer.fieldDeclaration.declarationSourceEnd;
}
} else if(enclosingIntializer != null) {
bodyEndValue = enclosingIntializer.fieldDeclaration.declarationSourceEnd;
} else {
bodyEndValue = this.blockDeclaration.sourceEnd - 1;
}
if(lastStatement instanceof RecoveredLocalVariable) {
RecoveredLocalVariable lastLocalVariable = (RecoveredLocalVariable) lastStatement;
if(lastLocalVariable.localDeclaration.declarationSourceEnd == 0) {
lastLocalVariable.localDeclaration.declarationSourceEnd = bodyEndValue;
lastLocalVariable.localDeclaration.declarationEnd = bodyEndValue;
}
} else if(lastStatement instanceof RecoveredBlock) {
RecoveredBlock lastBlock = (RecoveredBlock) lastStatement;
if(lastBlock.blockDeclaration.sourceEnd == 0) {
lastBlock.blockDeclaration.sourceEnd = bodyEndValue;
}
} else if(!(lastStatement instanceof RecoveredType)){
if(lastStatement.statement.sourceEnd == 0) {
lastStatement.statement.sourceEnd = bodyEndValue;
}
}
int lastEnd = this.blockDeclaration.sourceStart;
// only collect the non-null updated statements
next:
for (int i = 0; i < this.statementCount; i++){
Statement updatedStatement = this.statements[i].updatedStatement(depth, knownTypes);
if (updatedStatement != null) {
for (int j = 0; j < i; j++) {
if (updatedStatements[j] instanceof LocalDeclaration) {
LocalDeclaration local = (LocalDeclaration) updatedStatements[j];
if (local.initialization != null) {
if (updatedStatement.sourceStart >= local.initialization.sourceStart && updatedStatement.sourceEnd <= local.initialization.sourceEnd)
continue next;
}
}
}
updatedStatements[updatedCount++] = updatedStatement;
if (updatedStatement instanceof LocalDeclaration) {
LocalDeclaration localDeclaration = (LocalDeclaration) updatedStatement;
if(localDeclaration.declarationSourceEnd > lastEnd) {
lastEnd = localDeclaration.declarationSourceEnd;
}
} else if (updatedStatement instanceof TypeDeclaration) {
TypeDeclaration typeDeclaration = (TypeDeclaration) updatedStatement;
if(typeDeclaration.declarationSourceEnd > lastEnd) {
lastEnd = typeDeclaration.declarationSourceEnd;
}
} else {
if (updatedStatement.sourceEnd > lastEnd) {
lastEnd = updatedStatement.sourceEnd;
}
}
}
}
if (updatedCount == 0) return null; // not interesting block
// resize statement collection if necessary
if (updatedCount != this.statementCount){
this.blockDeclaration.statements = new Statement[updatedCount];
System.arraycopy(updatedStatements, 0, this.blockDeclaration.statements, 0, updatedCount);
} else {
this.blockDeclaration.statements = updatedStatements;
}
if (this.blockDeclaration.sourceEnd == 0) {
if(lastEnd < bodyEndValue) {
this.blockDeclaration.sourceEnd = bodyEndValue;
} else {
this.blockDeclaration.sourceEnd = lastEnd;
}
}
return this.blockDeclaration;
}
/*
* Rebuild a statement from the nested structure which is in scope
*/
@Override
public Statement updatedStatement(int depth, Set knownTypes){
return updatedBlock(depth, knownTypes);
}
/*
* A closing brace got consumed, might have closed the current element,
* in which case both the currentElement is exited
*/
@Override
public RecoveredElement updateOnClosingBrace(int braceStart, int braceEnd){
if ((--this.bracketBalance <= 0) && (this.parent != null)){
this.updateSourceEndIfNecessary(braceStart, braceEnd);
/* if the block is the method body, then it closes the method too */
RecoveredMethod method = enclosingMethod();
if (method != null && method.methodBody == this){
return this.parent.updateOnClosingBrace(braceStart, braceEnd);
}
RecoveredInitializer initializer = enclosingInitializer();
if (initializer != null && initializer.initializerBody == this){
return this.parent.updateOnClosingBrace(braceStart, braceEnd);
}
return this.parent;
}
return this;
}
/*
* An opening brace got consumed, might be the expected opening one of the current element,
* in which case the bodyStart is updated.
*/
@Override
public RecoveredElement updateOnOpeningBrace(int braceStart, int braceEnd){
// create a nested block
Block block = new Block(0);
block.sourceStart = parser().scanner.startPosition;
return this.add(block, 1);
}
/*
* Final update the corresponding parse node
*/
@Override
public void updateParseTree(){
updatedBlock(0, new HashSet());
}
/*
* Record a field declaration
*/
@Override
public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalanceValue) {
resetPendingModifiers();
/* local variables inside method can only be final and non void */
char[][] fieldTypeName;
if ((fieldDeclaration.modifiers & ~ClassFileConstants.AccFinal) != 0 // local var can only be final
|| (fieldDeclaration.type == null) // initializer
|| ((fieldTypeName = fieldDeclaration.type.getTypeName()).length == 1 // non void
&& CharOperation.equals(fieldTypeName[0], TypeBinding.VOID.sourceName()))){
this.updateSourceEndIfNecessary(previousAvailableLineEnd(fieldDeclaration.declarationSourceStart - 1));
return this.parent.add(fieldDeclaration, bracketBalanceValue);
}
/* do not consider a local variable starting passed the block end (if set)
it must be belonging to an enclosing block */
if (this.blockDeclaration.sourceEnd != 0
&& fieldDeclaration.declarationSourceStart > this.blockDeclaration.sourceEnd){
return this.parent.add(fieldDeclaration, bracketBalanceValue);
}
// ignore the added field, since indicates a local variable behind recovery point
// which thus got parsed as a field reference. This can happen if restarting after
// having reduced an assistNode to get the following context (see 1GEK7SG)
return this;
}
}