blob: 641356c05af1c9f21036fdaf803adf62248ec00e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2008 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.core.dom;
import java.util.List;
import java.util.Vector;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.compiler.parser.RecoveryScanner;
import org.eclipse.jdt.internal.compiler.parser.RecoveryScannerData;
import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
import org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToIntArray;
/**
* Internal AST visitor for propagating syntax errors.
*/
class ASTRecoveryPropagator extends DefaultASTVisitor {
private static final int NOTHING = -1;
HashtableOfObjectToIntArray endingTokens = new HashtableOfObjectToIntArray();
{
this.endingTokens.put(AnonymousClassDeclaration.class, new int[]{TerminalTokens.TokenNameRBRACE});
this.endingTokens.put(ArrayAccess.class, new int[]{TerminalTokens.TokenNameRBRACKET});
this.endingTokens.put(ArrayCreation.class, new int[]{NOTHING, TerminalTokens.TokenNameRBRACKET});
this.endingTokens.put(ArrayInitializer.class, new int[]{TerminalTokens.TokenNameRBRACE});
this.endingTokens.put(ArrayType.class, new int[]{TerminalTokens.TokenNameRBRACKET});
this.endingTokens.put(AssertStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
this.endingTokens.put(Block.class, new int[]{TerminalTokens.TokenNameRBRACE});
this.endingTokens.put(BooleanLiteral.class, new int[]{TerminalTokens.TokenNamefalse, TerminalTokens.TokenNametrue});
this.endingTokens.put(BreakStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
this.endingTokens.put(CharacterLiteral.class, new int[]{TerminalTokens.TokenNameCharacterLiteral});
this.endingTokens.put(ClassInstanceCreation.class, new int[]{TerminalTokens.TokenNameRBRACE, TerminalTokens.TokenNameRPAREN});
this.endingTokens.put(ConstructorInvocation.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
this.endingTokens.put(ContinueStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
this.endingTokens.put(DoStatement.class, new int[]{TerminalTokens.TokenNameRPAREN});
this.endingTokens.put(EmptyStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
this.endingTokens.put(ExpressionStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
this.endingTokens.put(FieldDeclaration.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
this.endingTokens.put(ImportDeclaration.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
this.endingTokens.put(Initializer.class, new int[]{TerminalTokens.TokenNameRBRACE});
this.endingTokens.put(MethodDeclaration.class, new int[]{NOTHING, TerminalTokens.TokenNameSEMICOLON});
this.endingTokens.put(MethodInvocation.class, new int[]{TerminalTokens.TokenNameRPAREN});
this.endingTokens.put(NullLiteral.class, new int[]{TerminalTokens.TokenNamenull});
this.endingTokens.put(NumberLiteral.class, new int[]{TerminalTokens.TokenNameIntegerLiteral, TerminalTokens.TokenNameLongLiteral, TerminalTokens.TokenNameFloatingPointLiteral, TerminalTokens.TokenNameDoubleLiteral});
this.endingTokens.put(PackageDeclaration.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
this.endingTokens.put(ParenthesizedExpression.class, new int[]{TerminalTokens.TokenNameRPAREN});
this.endingTokens.put(PostfixExpression.class, new int[]{TerminalTokens.TokenNamePLUS_PLUS, TerminalTokens.TokenNameMINUS_MINUS});
this.endingTokens.put(PrimitiveType.class, new int[]{TerminalTokens.TokenNamebyte, TerminalTokens.TokenNameshort, TerminalTokens.TokenNamechar, TerminalTokens.TokenNameint, TerminalTokens.TokenNamelong, TerminalTokens.TokenNamefloat, TerminalTokens.TokenNameboolean, TerminalTokens.TokenNamedouble, TerminalTokens.TokenNamevoid});
this.endingTokens.put(ReturnStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
this.endingTokens.put(SimpleName.class, new int[]{TerminalTokens.TokenNameIdentifier});
this.endingTokens.put(SingleVariableDeclaration.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
this.endingTokens.put(StringLiteral.class, new int[]{TerminalTokens.TokenNameStringLiteral});
this.endingTokens.put(SuperConstructorInvocation.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
this.endingTokens.put(SuperMethodInvocation.class, new int[]{TerminalTokens.TokenNameRPAREN});
this.endingTokens.put(SwitchCase.class, new int[]{TerminalTokens.TokenNameCOLON});
this.endingTokens.put(SwitchStatement.class, new int[]{TerminalTokens.TokenNameRBRACE});
this.endingTokens.put(SynchronizedStatement.class, new int[]{TerminalTokens.TokenNameRBRACE});
this.endingTokens.put(ThisExpression.class, new int[]{TerminalTokens.TokenNamethis});
this.endingTokens.put(ThrowStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
this.endingTokens.put(TypeDeclaration.class, new int[]{TerminalTokens.TokenNameRBRACE});
this.endingTokens.put(TypeLiteral.class, new int[]{TerminalTokens.TokenNameclass});
this.endingTokens.put(VariableDeclarationStatement.class, new int[]{TerminalTokens.TokenNameSEMICOLON});
}
private CategorizedProblem[] problems;
private boolean[] usedOrIrrelevantProblems;
private RecoveryScannerData data;
private int blockDepth = 0;
private int lastEnd;
private int[] insertedTokensKind;
private int[] insertedTokensPosition;
private boolean[] insertedTokensFlagged;
private boolean[] removedTokensFlagged;
private boolean[] replacedTokensFlagged;
private Vector stack = new Vector();
/**
* @noreference This method is not intended to be referenced by clients.
*/
ASTRecoveryPropagator(CategorizedProblem[] problems, RecoveryScannerData data) {
// visit Javadoc.tags() as well
this.problems = problems;
this.usedOrIrrelevantProblems = new boolean[problems.length];
this.data = data;
if(this.data != null) {
int length = 0;
for (int i = 0; i < data.insertedTokensPtr + 1; i++) {
length += data.insertedTokens[i].length;
}
this.insertedTokensKind = new int[length];
this.insertedTokensPosition = new int[length];
this.insertedTokensFlagged = new boolean[length];
int tokenCount = 0;
for (int i = 0; i < data.insertedTokensPtr + 1; i++) {
for (int j = 0; j < data.insertedTokens[i].length; j++) {
this.insertedTokensKind[tokenCount] = data.insertedTokens[i][j];
this.insertedTokensPosition[tokenCount] = data.insertedTokensPosition[i];
tokenCount++;
}
}
if(data.removedTokensPtr != -1) {
this.removedTokensFlagged = new boolean[data.removedTokensPtr + 1];
}
if(data.replacedTokensPtr != -1) {
this.replacedTokensFlagged = new boolean[data.replacedTokensPtr + 1];
}
}
}
public void endVisit(Block node) {
this.blockDepth--;
if(this.blockDepth <= 0) {
flagNodeWithInsertedTokens();
}
super.endVisit(node);
}
public boolean visit(Block node) {
boolean visitChildren = super.visit(node);
this.blockDepth++;
return visitChildren;
}
protected boolean visitNode(ASTNode node) {
if(this.blockDepth > 0) {
int start = node.getStartPosition();
int end = start + node.getLength() - 1;
// continue to visit the node only if it contains tokens modifications
if(this.insertedTokensFlagged != null) {
for (int i = 0; i < this.insertedTokensFlagged.length; i++) {
if(this.insertedTokensPosition[i] >= start &&
this.insertedTokensPosition[i] <= end) {
return true;
}
}
}
if(this.removedTokensFlagged != null) {
for (int i = 0; i <= this.data.removedTokensPtr; i++) {
if(this.data.removedTokensStart[i] >= start &&
this.data.removedTokensEnd[i] <= end) {
return true;
}
}
}
if(this.replacedTokensFlagged != null) {
for (int i = 0; i <= this.data.replacedTokensPtr; i++) {
if(this.data.replacedTokensStart[i] >= start &&
this.data.replacedTokensEnd[i] <= end) {
return true;
}
}
}
return false;
}
return true;
}
protected void endVisitNode(ASTNode node) {
int start = node.getStartPosition();
int end = start + node.getLength() - 1;
// is inside diet part of the ast
if(this.blockDepth < 1) {
switch (node.getNodeType()) {
case ASTNode.ANNOTATION_TYPE_DECLARATION:
case ASTNode.COMPILATION_UNIT:
case ASTNode.ENUM_DECLARATION:
case ASTNode.FIELD_DECLARATION:
case ASTNode.IMPORT_DECLARATION:
case ASTNode.INITIALIZER:
case ASTNode.METHOD_DECLARATION:
case ASTNode.PACKAGE_DECLARATION:
case ASTNode.TYPE_DECLARATION:
case ASTNode.MARKER_ANNOTATION:
case ASTNode.NORMAL_ANNOTATION:
case ASTNode.SINGLE_MEMBER_ANNOTATION:
case ASTNode.BLOCK:
if(markIncludedProblems(start, end)) {
node.setFlags(node.getFlags() | ASTNode.RECOVERED);
}
break;
}
} else {
markIncludedProblems(start, end);
if(this.insertedTokensFlagged != null) {
if(this.lastEnd != end) {
flagNodeWithInsertedTokens();
}
this.stack.add(node);
}
if(this.removedTokensFlagged != null) {
for (int i = 0; i <= this.data.removedTokensPtr; i++) {
if(!this.removedTokensFlagged[i] &&
this.data.removedTokensStart[i] >= start &&
this.data.removedTokensEnd[i] <= end) {
node.setFlags(node.getFlags() | ASTNode.RECOVERED);
this.removedTokensFlagged[i] = true;
}
}
}
if(this.replacedTokensFlagged != null) {
for (int i = 0; i <= this.data.replacedTokensPtr; i++) {
if(!this.replacedTokensFlagged[i] &&
this.data.replacedTokensStart[i] >= start &&
this.data.replacedTokensEnd[i] <= end) {
node.setFlags(node.getFlags() | ASTNode.RECOVERED);
this.replacedTokensFlagged[i] = true;
}
}
}
}
this.lastEnd = end;
}
private void flagNodeWithInsertedTokens() {
if(this.insertedTokensKind != null && this.insertedTokensKind.length > 0) {
int s = this.stack.size();
for (int i = s - 1; i > -1; i--) {
flagNodesWithInsertedTokensAtEnd((ASTNode)this.stack.get(i));
}
for (int i = 0; i < s; i++) {
flagNodesWithInsertedTokensInside((ASTNode)this.stack.get(i));
}
this.stack = new Vector();
}
}
private boolean flagNodesWithInsertedTokensAtEnd(ASTNode node) {
int[] expectedEndingToken = this.endingTokens.get(node.getClass());
if (expectedEndingToken != null) {
int start = node.getStartPosition();
int end = start + node.getLength() - 1;
boolean flagParent = false;
done : for (int i = this.insertedTokensKind.length - 1; i > -1 ; i--) {
if(!this.insertedTokensFlagged[i] &&
this.insertedTokensPosition[i] == end){
this.insertedTokensFlagged[i] = true;
for (int j = 0; j < expectedEndingToken.length; j++) {
if(expectedEndingToken[j] == this.insertedTokensKind[i]) {
node.setFlags(node.getFlags() | ASTNode.RECOVERED);
break done;
}
}
flagParent = true;
}
}
if(flagParent) {
ASTNode parent = node.getParent();
while (parent != null) {
parent.setFlags(node.getFlags() | ASTNode.RECOVERED);
if((parent.getStartPosition() + parent.getLength() - 1) != end) {
parent = null;
} else {
parent = parent.getParent();
}
}
}
}
return true;
}
private boolean flagNodesWithInsertedTokensInside(ASTNode node) {
int start = node.getStartPosition();
int end = start + node.getLength() - 1;
for (int i = 0; i < this.insertedTokensKind.length; i++) {
if(!this.insertedTokensFlagged[i] &&
start <= this.insertedTokensPosition[i] &&
this.insertedTokensPosition[i] < end){
node.setFlags(node.getFlags() | ASTNode.RECOVERED);
this.insertedTokensFlagged[i] = true;
}
}
return true;
}
private boolean markIncludedProblems(int start, int end) {
boolean foundProblems = false;
next: for (int i = 0, max = this.problems.length; i < max; i++) {
CategorizedProblem problem = this.problems[i];
if(this.usedOrIrrelevantProblems[i]) continue next;
switch(problem.getID()) {
case IProblem.ParsingErrorOnKeywordNoSuggestion :
case IProblem.ParsingErrorOnKeyword :
case IProblem.ParsingError :
case IProblem.ParsingErrorNoSuggestion :
case IProblem.ParsingErrorInsertTokenBefore :
case IProblem.ParsingErrorInsertTokenAfter :
case IProblem.ParsingErrorDeleteToken :
case IProblem.ParsingErrorDeleteTokens :
case IProblem.ParsingErrorMergeTokens :
case IProblem.ParsingErrorInvalidToken :
case IProblem.ParsingErrorMisplacedConstruct :
case IProblem.ParsingErrorReplaceTokens :
case IProblem.ParsingErrorNoSuggestionForTokens :
case IProblem.ParsingErrorUnexpectedEOF :
case IProblem.ParsingErrorInsertToComplete :
case IProblem.ParsingErrorInsertToCompleteScope :
case IProblem.ParsingErrorInsertToCompletePhrase :
case IProblem.EndOfSource :
case IProblem.InvalidHexa :
case IProblem.InvalidOctal :
case IProblem.InvalidCharacterConstant :
case IProblem.InvalidEscape :
case IProblem.InvalidInput :
case IProblem.InvalidUnicodeEscape :
case IProblem.InvalidFloat :
case IProblem.NullSourceString :
case IProblem.UnterminatedString :
case IProblem.UnterminatedComment :
case IProblem.InvalidDigit :
break;
default:
this.usedOrIrrelevantProblems[i] = true;
continue next;
}
int problemStart = problem.getSourceStart();
int problemEnd = problem.getSourceEnd();
if ((start <= problemStart) && (problemStart <= end) ||
(start <= problemEnd) && (problemEnd <= end)) {
this.usedOrIrrelevantProblems[i] = true;
foundProblems = true;
}
}
return foundProblems;
}
public void endVisit(ExpressionStatement node) {
endVisitNode(node);
if ((node.getFlags() & ASTNode.RECOVERED) == 0) return;
Expression expression = node.getExpression();
if (expression.getNodeType() == ASTNode.ASSIGNMENT) {
Assignment assignment = (Assignment) expression;
Expression rightHandSide = assignment.getRightHandSide();
if (rightHandSide.getNodeType() == ASTNode.SIMPLE_NAME) {
SimpleName simpleName = (SimpleName) rightHandSide;
if (CharOperation.equals(RecoveryScanner.FAKE_IDENTIFIER, simpleName.getIdentifier().toCharArray())) {
Expression expression2 = assignment.getLeftHandSide();
// unparent the expression to add it in the expression stateemnt
expression2.setParent(null, null);
expression2.setFlags(expression2.getFlags() | ASTNode.RECOVERED);
node.setExpression(expression2);
}
}
}
}
public void endVisit(ForStatement node) {
endVisitNode(node);
List initializers = node.initializers();
if (initializers.size() == 1) {
Expression expression = (Expression) initializers.get(0);
if (expression.getNodeType() == ASTNode.VARIABLE_DECLARATION_EXPRESSION) {
VariableDeclarationExpression variableDeclarationExpression = (VariableDeclarationExpression) expression;
List fragments = variableDeclarationExpression.fragments();
for (int i = 0, max = fragments.size(); i <max; i++) {
VariableDeclarationFragment fragment = (VariableDeclarationFragment) fragments.get(i);
SimpleName simpleName = fragment.getName();
if (CharOperation.equals(RecoveryScanner.FAKE_IDENTIFIER, simpleName.getIdentifier().toCharArray())) {
fragments.remove(fragment);
variableDeclarationExpression.setFlags(variableDeclarationExpression.getFlags() | ASTNode.RECOVERED);
}
}
}
}
}
public void endVisit(VariableDeclarationStatement node) {
endVisitNode(node);
List fragments = node.fragments();
for (int i = 0, max = fragments.size(); i <max; i++) {
VariableDeclarationFragment fragment = (VariableDeclarationFragment) fragments.get(i);
Expression expression = fragment.getInitializer();
if (expression == null) continue;
if ((expression.getFlags() & ASTNode.RECOVERED) == 0) continue;
if (expression.getNodeType() == ASTNode.SIMPLE_NAME) {
SimpleName simpleName = (SimpleName) expression;
if (CharOperation.equals(RecoveryScanner.FAKE_IDENTIFIER, simpleName.getIdentifier().toCharArray())) {
fragment.setInitializer(null);
fragment.setFlags(fragment.getFlags() | ASTNode.RECOVERED);
}
}
}
}
public void endVisit(NormalAnnotation node) {
endVisitNode(node);
// is inside diet part of the ast
if(this.blockDepth < 1) {
List values = node.values();
int size = values.size();
if (size > 0) {
MemberValuePair lastMemberValuePair = (MemberValuePair)values.get(size - 1);
int annotationEnd = node.getStartPosition() + node.getLength();
int lastMemberValuePairEnd = lastMemberValuePair.getStartPosition() + lastMemberValuePair.getLength();
if (annotationEnd == lastMemberValuePairEnd) {
node.setFlags(node.getFlags() | ASTNode.RECOVERED);
}
}
}
}
public void endVisit(SingleMemberAnnotation node) {
endVisitNode(node);
// is inside diet part of the ast
if(this.blockDepth < 1) {
Expression value = node.getValue();
int annotationEnd = node.getStartPosition() + node.getLength();
int valueEnd = value.getStartPosition() + value.getLength();
if (annotationEnd == valueEnd) {
node.setFlags(node.getFlags() | ASTNode.RECOVERED);
}
}
}
}