### Eclipse Workspace Patch 1.0 #P org.eclipse.jdt.core Index: model/org/eclipse/jdt/internal/compiler/DocumentElementParser.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/DocumentElementParser.java,v retrieving revision 1.20 diff -u -r1.20 DocumentElementParser.java --- model/org/eclipse/jdt/internal/compiler/DocumentElementParser.java 11 Nov 2005 23:44:39 -0000 1.20 +++ model/org/eclipse/jdt/internal/compiler/DocumentElementParser.java 17 Jan 2006 17:44:48 -0000 @@ -62,6 +62,8 @@ intArrayStack = new int[30][]; this.options = options; this.javadocParser.checkDocComment = false; + + this.setStatementsRecovery(false); } /* * Will clear the comment stack when looking Index: dom/org/eclipse/jdt/core/dom/ASTParser.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTParser.java,v retrieving revision 1.48 diff -u -r1.48 ASTParser.java --- dom/org/eclipse/jdt/core/dom/ASTParser.java 17 Dec 2005 01:20:00 -0000 1.48 +++ dom/org/eclipse/jdt/core/dom/ASTParser.java 17 Jan 2006 17:44:42 -0000 @@ -26,6 +26,9 @@ import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.env.IBinaryType; +import org.eclipse.jdt.internal.compiler.parser.RecoveryScanner; +import org.eclipse.jdt.internal.compiler.parser.RecoveryScannerData; +import org.eclipse.jdt.internal.compiler.parser.Scanner; import org.eclipse.jdt.internal.compiler.util.SuffixConstants; import org.eclipse.jdt.internal.core.*; import org.eclipse.jdt.internal.core.util.CodeSnippetParsingUtil; @@ -914,7 +917,15 @@ } switch(this.astKind) { case K_STATEMENTS : - ConstructorDeclaration constructorDeclaration = codeSnippetParsingUtil.parseStatements(this.rawSource, this.sourceOffset, this.sourceLength, this.compilerOptions, true); + ConstructorDeclaration constructorDeclaration = codeSnippetParsingUtil.parseStatements(this.rawSource, this.sourceOffset, this.sourceLength, this.compilerOptions, true, true); + RecoveryScannerData data = constructorDeclaration.compilationResult.recoveryScannerData; + if(data != null) { + Scanner scanner = converter.scanner; + converter.scanner = new RecoveryScanner(scanner, data.removeUnused()); + converter.docParser.scanner = converter.scanner; + converter.scanner.setSource(scanner.source); + + } RecordedParsingInformation recordedParsingInformation = codeSnippetParsingUtil.recordedParsingInformation; int[][] comments = recordedParsingInformation.commentPositions; if (comments != null) { @@ -1000,6 +1011,8 @@ private void propagateErrors(ASTNode astNode, IProblem[] problems) { ASTSyntaxErrorPropagator syntaxErrorPropagator = new ASTSyntaxErrorPropagator(problems); astNode.accept(syntaxErrorPropagator); + ASTRecoveryPropagator recoveryPropagator = new ASTRecoveryPropagator(problems); + astNode.accept(recoveryPropagator); } private void rootNodeToCompilationUnit(AST ast, CompilationUnit compilationUnit, ASTNode node, RecordedParsingInformation recordedParsingInformation) { Index: dom/org/eclipse/jdt/core/dom/ASTNode.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTNode.java,v retrieving revision 1.66 diff -u -r1.66 ASTNode.java --- dom/org/eclipse/jdt/core/dom/ASTNode.java 17 Dec 2005 03:50:29 -0000 1.66 +++ dom/org/eclipse/jdt/core/dom/ASTNode.java 17 Jan 2006 17:44:41 -0000 @@ -1034,6 +1034,17 @@ public static final int PROTECT = 4; /** + * Flag constant (bit mask, value 8) indicating that this node + * or a part of this node is recovered from source that contains + * a syntax error detected in the vicinity. + *

+ * The standard parser (ASTParser) sets this + * flag on a node to indicate a recovered node. + *

+ */ + public static final int RECOVERED = 8; + + /** * int containing the node type in the top 16 bits and * flags in the bottom 16 bits; none set by default. *

Index: dom/org/eclipse/jdt/core/dom/DefaultASTVisitor.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DefaultASTVisitor.java,v retrieving revision 1.2 diff -u -r1.2 DefaultASTVisitor.java --- dom/org/eclipse/jdt/core/dom/DefaultASTVisitor.java 23 Feb 2005 02:47:27 -0000 1.2 +++ dom/org/eclipse/jdt/core/dom/DefaultASTVisitor.java 17 Jan 2006 17:44:42 -0000 @@ -18,6 +18,14 @@ public DefaultASTVisitor() { super(); } + + /** + * + */ + public DefaultASTVisitor(boolean visitDocTags) { + super(visitDocTags); + } + protected boolean visitNode(ASTNode node) { return true; } Index: dom/org/eclipse/jdt/core/dom/ASTConverter.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java,v retrieving revision 1.223 diff -u -r1.223 ASTConverter.java --- dom/org/eclipse/jdt/core/dom/ASTConverter.java 10 Jan 2006 21:01:06 -0000 1.223 +++ dom/org/eclipse/jdt/core/dom/ASTConverter.java 17 Jan 2006 17:44:40 -0000 @@ -46,6 +46,7 @@ import org.eclipse.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; +import org.eclipse.jdt.internal.compiler.parser.RecoveryScanner; import org.eclipse.jdt.internal.compiler.parser.Scanner; import org.eclipse.jdt.internal.compiler.parser.TerminalTokens; @@ -1190,6 +1191,10 @@ } public CompilationUnit convert(org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration unit, char[] source) { + if(unit.compilationResult.recoveryScannerData != null) { + this.scanner = new RecoveryScanner(this.scanner, unit.compilationResult.recoveryScannerData.removeUnused()); + this.docParser.scanner = this.scanner; + } this.compilationUnitSource = source; this.compilationUnitSourceLength = source.length; this.scanner.setSource(source, unit.compilationResult); @@ -1248,6 +1253,8 @@ } ASTSyntaxErrorPropagator syntaxErrorPropagator = new ASTSyntaxErrorPropagator(resizedProblems); compilationUnit.accept(syntaxErrorPropagator); + ASTRecoveryPropagator recoveryPropagator = new ASTRecoveryPropagator(resizedProblems); + compilationUnit.accept(recoveryPropagator); compilationUnit.setProblems(resizedProblems); } if (this.resolveBindings) { @@ -4237,23 +4244,6 @@ return; } break; - case TerminalTokens.TokenNameLBRACE : - count++; - break; - case TerminalTokens.TokenNameRBRACE : - count--; - break; - case TerminalTokens.TokenNameLPAREN : - count++; - break; - case TerminalTokens.TokenNameRPAREN : - count--; - break; - case TerminalTokens.TokenNameLBRACKET : - count++; - break; - case TerminalTokens.TokenNameRBRACKET : - count--; } } } catch(InvalidInputException e) { Index: compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java,v retrieving revision 1.330 diff -u -r1.330 Parser.java --- compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java 25 Nov 2005 16:44:45 -0000 1.330 +++ compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java 17 Jan 2006 17:44:28 -0000 @@ -22,14 +22,18 @@ import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.compiler.InvalidInputException; +import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.ast.*; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; +import org.eclipse.jdt.internal.compiler.lookup.BlockScope; +import org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; +import org.eclipse.jdt.internal.compiler.lookup.MethodScope; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; import org.eclipse.jdt.internal.compiler.parser.diagnose.DiagnoseParser; @@ -76,6 +80,11 @@ public static byte rhs[] = null; + public static int[] reverse_index = null; + public static char[] recovery_templates_index = null; + public static char[] recovery_templates = null; + public static char[] statements_recovery_filter = null; + public static long rules_compliance[] = null; public static final int RoundBracket = 0; @@ -171,6 +180,17 @@ public boolean reportOnlyOneSyntaxError = false; public boolean reportSyntaxErrorIsRequired = true; protected boolean restartRecovery; + + // statement recovery +// public boolean statementRecoveryEnabled = true; + protected boolean methodRecoveryActivated = false; + protected boolean statementRecoveryActivated = false; + protected TypeDeclaration[] recoveredTypes; + protected int recoveredTypePtr; + protected int nextTypeStart; + + public RecoveryScanner recoveryScanner; + //scanner token public Scanner scanner; protected int[] stack = new int[StackIncrement]; @@ -298,6 +318,76 @@ Collections.sort(entries); buildFile(file, entries); } +private static void buildFilesForRecoveryTemplates( + String indexFilename, + String templatesFilename, + char[] newTerminalIndex, + char[] newNonTerminalIndex, + String[] newName, + char[] newLhs, + String[] tokens) throws IOException { + + int[] newReverse = computeReverseTable(newTerminalIndex, newNonTerminalIndex, newName); + + char[] newRecoveyTemplatesIndex = new char[newNonTerminalIndex.length]; + char[] newRecoveyTemplates = new char[newNonTerminalIndex.length]; + int newRecoveyTemplatesPtr = 0; + + for (int i = 0; i < tokens.length; i = i + 3) { + if("3".equals(tokens[i])) { //$NON-NLS-1$ + int length = newRecoveyTemplates.length; + if(length == newRecoveyTemplatesPtr + 1) { + System.arraycopy(newRecoveyTemplates, 0, newRecoveyTemplates = new char[length * 2], 0, length); + } + newRecoveyTemplates[newRecoveyTemplatesPtr++] = 0; + + int index = newLhs[Integer.parseInt(tokens[i + 1])]; + + newRecoveyTemplatesIndex[index] = (char)newRecoveyTemplatesPtr; + + String token = tokens[i + 2].trim(); + java.util.StringTokenizer st = new java.util.StringTokenizer(new String(token), " "); //$NON-NLS-1$ + String[] terminalNames = new String[st.countTokens()]; + int t = 0; + while (st.hasMoreTokens()) { + terminalNames[t++] = st.nextToken(); + } + + for (int j = 0; j < terminalNames.length; j++) { + int symbol = getSymbol(terminalNames[j], newName, newReverse); + if(symbol > -1) { + length = newRecoveyTemplates.length; + if(length == newRecoveyTemplatesPtr + 1) { + System.arraycopy(newRecoveyTemplates, 0, newRecoveyTemplates = new char[length * 2], 0, length); + } + newRecoveyTemplates[newRecoveyTemplatesPtr++] = (char)symbol; + } + } + } + } + newRecoveyTemplates[newRecoveyTemplatesPtr++] = 0; + System.arraycopy(newRecoveyTemplates, 0, newRecoveyTemplates = new char[newRecoveyTemplatesPtr], 0, newRecoveyTemplatesPtr); + + buildFileForTable(indexFilename, newRecoveyTemplatesIndex); + buildFileForTable(templatesFilename, newRecoveyTemplates); +} +private static void buildFilesForStatementsRecoveryFilter( + String filename, + char[] newNonTerminalIndex, + char[] newLhs, + String[] tokens) throws IOException { + + char[] newStatementsRecoveryFilter = new char[newNonTerminalIndex.length]; + + for (int i = 0; i < tokens.length; i = i + 3) { + if("4".equals(tokens[i])) { //$NON-NLS-1$ + int index = newLhs[Integer.parseInt(tokens[i + 1])]; + + newStatementsRecoveryFilter[index] = 1; + } + } + buildFileForTable(filename, newStatementsRecoveryFilter); + } private static void buildFileForCompliance( String file, int length, @@ -450,7 +540,7 @@ buildFileOfIntFor(prefix + (++i) + ".rsc", "asr", tokens); //$NON-NLS-2$ //$NON-NLS-1$ buildFileOfIntFor(prefix + (++i) + ".rsc", "nasb", tokens); //$NON-NLS-2$ //$NON-NLS-1$ buildFileOfIntFor(prefix + (++i) + ".rsc", "nasr", tokens); //$NON-NLS-2$ //$NON-NLS-1$ - buildFileOfIntFor(prefix + (++i) + ".rsc", "terminal_index", tokens); //$NON-NLS-2$ //$NON-NLS-1$ + char[] newTerminalIndex = buildFileOfIntFor(prefix + (++i) + ".rsc", "terminal_index", tokens); //$NON-NLS-2$ //$NON-NLS-1$ char[] newNonTerminalIndex = buildFileOfIntFor(prefix + (++i) + ".rsc", "non_terminal_index", tokens); //$NON-NLS-1$ //$NON-NLS-2$ buildFileOfIntFor(prefix + (++i) + ".rsc", "term_action", tokens); //$NON-NLS-2$ //$NON-NLS-1$ @@ -475,7 +565,7 @@ System.out.println(Messages.parser_incorrectPath); return; } - st = new java.util.StringTokenizer(new String(contents), "\t\n\r=#"); //$NON-NLS-1$ + st = new java.util.StringTokenizer(new String(contents), "\t\n\r#"); //$NON-NLS-1$ tokens = new String[st.countTokens()]; j = 0; while (st.hasMoreTokens()) { @@ -485,6 +575,22 @@ buildFileForCompliance(prefix + (++i) + ".rsc", newRhs.length, tokens);//$NON-NLS-1$ buildFileForReadableName(READABLE_NAMES_FILE+".properties", newLhs, newNonTerminalIndex, newName, tokens);//$NON-NLS-1$ + buildFilesForRecoveryTemplates( + prefix + (++i) + ".rsc", //$NON-NLS-1$ + prefix + (++i) + ".rsc", //$NON-NLS-1$ + newTerminalIndex, + newNonTerminalIndex, + newName, + newLhs, + tokens); + + buildFilesForStatementsRecoveryFilter( + prefix + (++i) + ".rsc", //$NON-NLS-1$ + newNonTerminalIndex, + newLhs, + tokens); + + System.out.println(Messages.parser_moveFiles); } public static int in_symbol(int state) { @@ -526,6 +632,13 @@ readableName = readReadableNameTable(READABLE_NAMES_FILE_NAME); + reverse_index = computeReverseTable(terminal_index, non_terminal_index, name); + + recovery_templates_index = readTable(prefix + (++i) + ".rsc"); //$NON-NLS-1$ + recovery_templates = readTable(prefix + (++i) + ".rsc"); //$NON-NLS-1$ + + statements_recovery_filter = readTable(prefix + (++i) + ".rsc"); //$NON-NLS-1$ + base_action = lhs; } public static int nasi(int state) { @@ -537,6 +650,36 @@ protected static int original_state(int state) { return -base_check(state); } +protected static int[] computeReverseTable(char[] newTerminalIndex, char[] newNonTerminalIndex, String[] newName) { + int[] newReverseTable = new int[newName.length]; + for (int j = 0; j < newName.length; j++) { + found : { + for (int k = 0; k < newTerminalIndex.length; k++) { + if(newTerminalIndex[k] == j) { + newReverseTable[j] = k; + break found; + } + } + for (int k = 0; k < newNonTerminalIndex.length; k++) { + if(newNonTerminalIndex[k] == j) { + newReverseTable[j] = -k; + break found; + } + } + } + } + return newReverseTable; +} + +private static int getSymbol(String terminalName, String[] newName, int[] newReverse) { + for (int j = 0; j < newName.length; j++) { + if(terminalName.equals(newName[j])) { + return newReverse[j]; + } + } + return -1; +} + protected static byte[] readByteTable(String filename) throws java.io.IOException { //files are located at Parser.class directory @@ -703,6 +846,8 @@ // javadoc support this.javadocParser = new JavadocParser(this); + +// this.statementRecoveryEnabled = this.options.performStatementsRecovery; } protected void annotationRecoveryCheckPoint(int start, int end) { if(this.lastCheckPoint > start && this.lastCheckPoint < end) { @@ -768,6 +913,9 @@ if (this.referenceContext instanceof AbstractMethodDeclaration){ element = new RecoveredMethod((AbstractMethodDeclaration) this.referenceContext, null, 0, this); this.lastCheckPoint = ((AbstractMethodDeclaration) this.referenceContext).bodyStart; + if(this.statementRecoveryActivated) { + element = element.add(new Block(0), 0); + } } else { /* Initializer bodies are parsed in the context of the type declaration, we must thus search it inside */ if (this.referenceContext instanceof TypeDeclaration){ @@ -845,6 +993,29 @@ element = element.add(importRef, 0); this.lastCheckPoint = importRef.declarationSourceEnd + 1; } + if(this.statementRecoveryActivated) { + if(node instanceof Block) { + Block block = (Block) node; + element = element.add(block, 0); + this.lastCheckPoint = block.sourceEnd + 1; + } else if(node instanceof LocalDeclaration) { + LocalDeclaration statement = (LocalDeclaration) node; + element = element.add(statement, 0); + this.lastCheckPoint = statement.sourceEnd + 1; + } else if(node instanceof Expression) { + Expression statement = (Expression) node; + element = element.add(statement, 0); + if(statement.statementEnd != -1) { + this.lastCheckPoint = statement.statementEnd + 1; + } else { + this.lastCheckPoint = statement.sourceEnd + 1; + } + } else if(node instanceof Statement) { + Statement statement = (Statement) node; + element = element.add(statement, 0); + this.lastCheckPoint = statement.sourceEnd + 1; + } + } } return element; } @@ -1133,7 +1304,8 @@ annotationTypeDeclaration.javadoc = this.javadoc; this.javadoc = null; pushOnAstStack(annotationTypeDeclaration); - if(options.sourceLevel < ClassFileConstants.JDK1_5 && + if(!this.statementRecoveryActivated && + options.sourceLevel < ClassFileConstants.JDK1_5 && this.lastErrorEndPositionBeforeRecovery < this.scanner.currentPosition) { this.problemReporter().invalidUsageOfAnnotationDeclarations(annotationTypeDeclaration); } @@ -2421,7 +2593,7 @@ protected void consumeEmptyClassMemberDeclaration() { // ClassMemberDeclaration ::= ';' pushOnAstLengthStack(0); - problemReporter().superfluousSemicolon(this.endPosition+1, this.endStatementPosition); + if(!this.statementRecoveryActivated) problemReporter().superfluousSemicolon(this.endPosition+1, this.endStatementPosition); flushCommentsDefinedPriorTo(this.endStatementPosition); } protected void consumeEmptyMethodHeaderDefaultValue() { @@ -2489,7 +2661,7 @@ protected void consumeEmptyTypeDeclaration() { // TypeDeclaration ::= ';' pushOnAstLengthStack(0); - problemReporter().superfluousSemicolon(this.endPosition+1, this.endStatementPosition); + if(!this.statementRecoveryActivated) problemReporter().superfluousSemicolon(this.endPosition+1, this.endStatementPosition); flushCommentsDefinedPriorTo(this.endStatementPosition); } protected void consumeEnhancedForStatementHeaderInit(boolean hasModifiers) { @@ -2539,6 +2711,8 @@ localDeclaration, this.intStack[this.intPtr--]); pushOnAstStack(iteratorForStatement); + + iteratorForStatement.sourceEnd = localDeclaration.declarationSourceEnd; } protected void consumeEnhancedForStatementHeader(){ // EnhancedForStatementHeader ::= EnhancedForStatementHeaderInit ':' Expression ')' @@ -2547,7 +2721,10 @@ this.expressionLengthPtr--; final Expression collection = this.expressionStack[this.expressionPtr--]; statement.collection = collection; - if(options.sourceLevel < ClassFileConstants.JDK1_5 && + statement.sourceEnd = this.rParenPos; + + if(!this.statementRecoveryActivated && + options.sourceLevel < ClassFileConstants.JDK1_5 && this.lastErrorEndPositionBeforeRecovery < this.scanner.currentPosition) { this.problemReporter().invalidUsageOfForeachStatements(statement.elementVariable, collection); } @@ -2986,7 +3163,8 @@ this.listLength = 0; // will be updated when reading super-interfaces - if(options.sourceLevel < ClassFileConstants.JDK1_5 && + if(!this.statementRecoveryActivated && + options.sourceLevel < ClassFileConstants.JDK1_5 && this.lastErrorEndPositionBeforeRecovery < this.scanner.currentPosition) { //TODO this code will be never run while 'enum' is an identifier in 1.3 scanner this.problemReporter().invalidUsageOfEnumDeclarations(enumDeclaration); @@ -3146,7 +3324,9 @@ protected void consumeExpressionStatement() { // ExpressionStatement ::= StatementExpression ';' this.expressionLengthPtr--; - pushOnAstStack(this.expressionStack[this.expressionPtr--]); + Expression expression = this.expressionStack[this.expressionPtr--]; + expression.statementEnd = this.endStatementPosition; + pushOnAstStack(expression); } protected void consumeFieldAccess(boolean isSuperAccess) { // FieldAccess ::= Primary '.' 'Identifier' @@ -3290,10 +3470,12 @@ this.listLength++; if(isVarArgs) { - if (options.sourceLevel < ClassFileConstants.JDK1_5 && + if (!this.statementRecoveryActivated && + options.sourceLevel < ClassFileConstants.JDK1_5 && this.lastErrorEndPositionBeforeRecovery < this.scanner.currentPosition) { this.problemReporter().invalidUsageOfVarargs(arg); - } else if (extendedDimensions > 0) { + } else if (!this.statementRecoveryActivated && + extendedDimensions > 0) { this.problemReporter().illegalExtendedDimensions(arg); } } @@ -3570,7 +3752,7 @@ protected void consumeInvalidAnnotationTypeDeclaration() { // BlockStatement ::= AnnotationTypeDeclaration TypeDeclaration typeDecl = (TypeDeclaration) this.astStack[this.astPtr]; - problemReporter().illegalLocalTypeDeclaration(typeDecl); + if(!this.statementRecoveryActivated) problemReporter().illegalLocalTypeDeclaration(typeDecl); // remove the ast node created in interface header this.astPtr--; pushOnAstLengthStack(-1); @@ -3623,7 +3805,7 @@ // BlockStatement ::= InvalidInterfaceDeclaration //InterfaceDeclaration ::= Modifiersopt 'interface' 'Identifier' ExtendsInterfacesopt InterfaceHeader InterfaceBody TypeDeclaration typeDecl = (TypeDeclaration) this.astStack[this.astPtr]; - problemReporter().illegalLocalTypeDeclaration(typeDecl); + if(!this.statementRecoveryActivated) problemReporter().illegalLocalTypeDeclaration(typeDecl); // remove the ast node created in interface header this.astPtr--; pushOnAstLengthStack(-1); @@ -3632,7 +3814,7 @@ protected void consumeInvalidEnumDeclaration() { // BlockStatement ::= EnumDeclaration TypeDeclaration typeDecl = (TypeDeclaration) this.astStack[this.astPtr]; - problemReporter().illegalLocalTypeDeclaration(typeDecl); + if(!this.statementRecoveryActivated) problemReporter().illegalLocalTypeDeclaration(typeDecl); // remove the ast node created in interface header this.astPtr--; pushOnAstLengthStack(-1); @@ -3668,7 +3850,7 @@ md.declarationSourceEnd = flushCommentsDefinedPriorTo(this.endStatementPosition); // report the problem and continue the parsing - narrowing the problem onto the method - problemReporter().abstractMethodNeedingNoBody(md); + if(!this.statementRecoveryActivated) problemReporter().abstractMethodNeedingNoBody(md); } protected void consumeLabel() { // Do nothing @@ -3728,7 +3910,8 @@ markerAnnotation = new MarkerAnnotation(typeReference, this.intStack[this.intPtr--]); markerAnnotation.declarationSourceEnd = markerAnnotation.sourceEnd; pushOnExpressionStack(markerAnnotation); - if(options.sourceLevel < ClassFileConstants.JDK1_5 && + if(!this.statementRecoveryActivated && + options.sourceLevel < ClassFileConstants.JDK1_5 && this.lastErrorEndPositionBeforeRecovery < this.scanner.currentPosition) { this.problemReporter().invalidUsageOfAnnotation(markerAnnotation); } @@ -4244,7 +4427,8 @@ annotationRecoveryCheckPoint(normalAnnotation.sourceStart, normalAnnotation.declarationSourceEnd); } - if(options.sourceLevel < ClassFileConstants.JDK1_5 && + if(!this.statementRecoveryActivated && + options.sourceLevel < ClassFileConstants.JDK1_5 && this.lastErrorEndPositionBeforeRecovery < this.scanner.currentPosition) { this.problemReporter().invalidUsageOfAnnotation(normalAnnotation); } @@ -4261,7 +4445,8 @@ this.expressionLengthPtr--; } protected void consumeOnlyTypeArguments() { - if(options.sourceLevel < ClassFileConstants.JDK1_5 && + if(!this.statementRecoveryActivated && + options.sourceLevel < ClassFileConstants.JDK1_5 && this.lastErrorEndPositionBeforeRecovery < this.scanner.currentPosition) { int length = this.genericsLengthStack[this.genericsLengthPtr]; this.problemReporter().invalidUsageOfTypeArguments( @@ -6259,7 +6444,8 @@ annotationRecoveryCheckPoint(singleMemberAnnotation.sourceStart, singleMemberAnnotation.declarationSourceEnd); } - if(options.sourceLevel < ClassFileConstants.JDK1_5 && + if(!this.statementRecoveryActivated && + options.sourceLevel < ClassFileConstants.JDK1_5 && this.lastErrorEndPositionBeforeRecovery < this.scanner.currentPosition) { this.problemReporter().invalidUsageOfAnnotation(singleMemberAnnotation); } @@ -6291,7 +6477,8 @@ //this.endPosition is just before the ; impt.declarationSourceStart = this.intStack[this.intPtr--]; - if(this.options.sourceLevel < ClassFileConstants.JDK1_5 && + if(!this.statementRecoveryActivated && + this.options.sourceLevel < ClassFileConstants.JDK1_5 && this.lastErrorEndPositionBeforeRecovery < this.scanner.currentPosition) { impt.modifiers = ClassFileConstants.AccDefault; // convert the static import reference to a non-static importe reference this.problemReporter().invalidUsageOfStaticImports(impt); @@ -6660,7 +6847,8 @@ //this.endPosition is just before the ; impt.declarationSourceStart = this.intStack[this.intPtr--]; - if(options.sourceLevel < ClassFileConstants.JDK1_5 && + if(!this.statementRecoveryActivated && + options.sourceLevel < ClassFileConstants.JDK1_5 && this.lastErrorEndPositionBeforeRecovery < this.scanner.currentPosition) { impt.modifiers = ClassFileConstants.AccDefault; // convert the static import reference to a non-static importe reference this.problemReporter().invalidUsageOfStaticImports(impt); @@ -6762,12 +6950,12 @@ if (this.scanner.useAssertAsAnIndentifier && this.lastErrorEndPositionBeforeRecovery < this.scanner.currentPosition) { long positions = this.identifierPositionStack[this.identifierPtr]; - problemReporter().useAssertAsAnIdentifier((int) (positions >>> 32), (int) positions); + if(!this.statementRecoveryActivated) problemReporter().useAssertAsAnIdentifier((int) (positions >>> 32), (int) positions); } if (this.scanner.useEnumAsAnIndentifier && this.lastErrorEndPositionBeforeRecovery < this.scanner.currentPosition) { long positions = this.identifierPositionStack[this.identifierPtr]; - problemReporter().useEnumAsAnIdentifier((int) (positions >>> 32), (int) positions); + if(!this.statementRecoveryActivated) problemReporter().useEnumAsAnIdentifier((int) (positions >>> 32), (int) positions); } break; case TokenNameinterface : @@ -7091,7 +7279,8 @@ concatGenericsLists(); intPtr--; - if(options.sourceLevel < ClassFileConstants.JDK1_5 && + if(!this.statementRecoveryActivated && + options.sourceLevel < ClassFileConstants.JDK1_5 && this.lastErrorEndPositionBeforeRecovery < this.scanner.currentPosition) { int length = this.genericsLengthStack[this.genericsLengthPtr]; this.problemReporter().invalidUsageOfTypeArguments( @@ -7202,7 +7391,8 @@ } - if(options.sourceLevel < ClassFileConstants.JDK1_5&& + if(!this.statementRecoveryActivated && + options.sourceLevel < ClassFileConstants.JDK1_5&& this.lastErrorEndPositionBeforeRecovery < this.scanner.currentPosition) { int length = this.genericsLengthStack[this.genericsLengthPtr]; this.problemReporter().invalidUsageOfTypeParameters( @@ -7298,7 +7488,7 @@ if (!post) { this.intPtr--; } - problemReporter().invalidUnaryExpression(leftHandSide); + if(!this.statementRecoveryActivated) problemReporter().invalidUnaryExpression(leftHandSide); } } protected void consumeVariableDeclarators() { @@ -7678,14 +7868,18 @@ this.lastAct = act; - if (this.currentElement != null){ - this.currentElement.topElement().updateParseTree(); + if(this.statementRecoveryActivated) { + RecoveredElement recoveredElement = this.buildInitialRecoveryState(); + recoveredElement.topElement().updateParseTree(); + if(this.hasError) this.resetStacks(); + } else if (this.currentElement != null){ if (VERBOSE_RECOVERY){ System.out.print(Messages.parser_syntaxRecovery); System.out.println("--------------------------"); //$NON-NLS-1$ System.out.println(this.compilationUnit); System.out.println("----------------------------------"); //$NON-NLS-1$ } + this.currentElement.topElement().updateParseTree(); } else { if (this.diet & VERBOSE_RECOVERY){ System.out.print(Messages.parser_regularParse); @@ -7696,7 +7890,7 @@ } persistLineSeparatorPositions(); for (int i = 0; i < this.scanner.foundTaskCount; i++){ - problemReporter().task( + if(!this.statementRecoveryActivated) problemReporter().task( new String(this.scanner.foundTaskTags[i]), new String(this.scanner.foundTaskMessages[i]), this.scanner.foundTaskPriorities[i] == null ? null : new String(this.scanner.foundTaskPriorities[i]), @@ -8187,7 +8381,7 @@ ArrayInitializer arrayInitializer = (ArrayInitializer) this.expressionStack[this.expressionPtr--]; this.expressionLengthPtr -- ; // report a syntax error and abort parsing - problemReporter().arrayConstantsOnlyInArrayInitializers(arrayInitializer.sourceStart, arrayInitializer.sourceEnd); + if(!this.statementRecoveryActivated) problemReporter().arrayConstantsOnlyInArrayInitializers(arrayInitializer.sourceStart, arrayInitializer.sourceEnd); } public void initialize() { this.initialize(false); @@ -8281,6 +8475,51 @@ if (this.diet && (this.dietInt == 0)) this.scanner.diet = true; } +private void jumpOverType(){ + if (this.recoveredTypes != null && this.nextTypeStart > -1 && this.nextTypeStart < this.scanner.currentPosition) { + TypeDeclaration typeDeclaration = this.recoveredTypes[this.recoveredTypePtr]; + boolean isAnonymous = typeDeclaration.allocation != null; + + int end = this.scanner.eofPosition; + this.scanner.resetTo(typeDeclaration.declarationSourceEnd + 1, end - 1); + if(!isAnonymous) { + pushOnAstStack(typeDeclaration); + if(this.astLengthPtr > 0) { + concatNodeLists(); + } + + if(this.currentElement != null) { + this.currentElement = this.currentElement.add(typeDeclaration, 0); + } + + try { + this.currentToken = this.scanner.getNextToken(); + } catch(InvalidInputException e){ + if (!this.hasReportedError){ + this.problemReporter().scannerError(this, e.getMessage()); + this.hasReportedError = true; + } + this.lastCheckPoint = this.scanner.currentPosition; + } + } else { + if(this.astPtr > -1 && this.astStack[this.astPtr] instanceof TypeDeclaration) { + this.astStack[astPtr] = typeDeclaration; + this.expressionStack[this.expressionPtr] = typeDeclaration.allocation; + } + this.currentToken = TokenNameRBRACE; + } + + if(++this.recoveredTypePtr < this.recoveredTypes.length) { + TypeDeclaration nextTypeDeclaration = this.recoveredTypes[this.recoveredTypePtr]; + this.nextTypeStart = + nextTypeDeclaration.allocation == null + ? nextTypeDeclaration.declarationSourceStart + : nextTypeDeclaration.bodyStart; + } else { + this.nextTypeStart = Integer.MAX_VALUE; + } + } +} protected void markEnclosingMemberWithLocalType() { if (this.currentElement != null) return; // this is already done in the recovery code for (int i = this.astPtr; i >= 0; i--) { @@ -8485,6 +8724,7 @@ protected void parse() { if (DEBUG) System.out.println("-- ENTER INSIDE PARSE METHOD --"); //$NON-NLS-1$ boolean isDietParse = this.diet; + boolean jumpOverTypeAfterReduce = false; int oldFirstToken = getFirstToken(); this.hasError = false; @@ -8533,7 +8773,10 @@ } this.lastCheckPoint = this.scanner.currentPosition; this.restartRecovery = true; - } + } + if(this.statementRecoveryActivated) { + jumpOverTypeAfterReduce = true; + } act -= ERROR_ACTION; } else { @@ -8550,6 +8793,9 @@ this.lastCheckPoint = this.scanner.currentPosition; this.restartRecovery = true; } + if(this.statementRecoveryActivated) { + this.jumpOverType(); + } continue ProcessTerminals; } break ProcessTerminals; @@ -8560,6 +8806,12 @@ consumeRule(act); this.stateStackTop -= (rhs[act] - 1); act = ntAction(this.stack[this.stateStackTop], lhs[act]); + if(this.statementRecoveryActivated && act > NUM_RULES) { + if(jumpOverTypeAfterReduce) { + this.jumpOverType(); + jumpOverTypeAfterReduce = false; + } + } } while (act <= NUM_RULES); } endParse(act); @@ -8568,10 +8820,41 @@ if (tags != null) { this.compilationUnit.nlsTags = tags; } + this.scanner.checkNonExternalizedStringLiterals = false; - if (this.reportSyntaxErrorIsRequired && this.hasError) { - reportSyntaxErrors(isDietParse, oldFirstToken); - } + if (this.reportSyntaxErrorIsRequired && this.hasError && !this.statementRecoveryActivated) { + if(!this.options.performStatementsRecovery) { + reportSyntaxErrors(isDietParse, oldFirstToken); + } else { + RecoveryScannerData data = this.referenceContext.compilationResult().recoveryScannerData; + + if(this.recoveryScanner == null) { + this.recoveryScanner = new RecoveryScanner(this.scanner, data); + } else { + this.recoveryScanner.setData(data); + } + + this.recoveryScanner.setSource(scanner.source); + this.recoveryScanner.lineEnds = this.scanner.lineEnds; + this.recoveryScanner.linePtr = this.scanner.linePtr; + + reportSyntaxErrors(isDietParse, oldFirstToken); + + if(data == null) { + this.referenceContext.compilationResult().recoveryScannerData = + this.recoveryScanner.getData(); + } + + if (this.methodRecoveryActivated) { + this.methodRecoveryActivated = false; + this.recoverStatements(); + this.methodRecoveryActivated = true; + + this.lastAct = ERROR_ACTION; + } + } + } + if (DEBUG) System.out.println("-- EXIT FROM PARSE METHOD --"); //$NON-NLS-1$ } public void parse(ConstructorDeclaration cd, CompilationUnitDeclaration unit) { @@ -8583,6 +8866,11 @@ //convert bugs into parse error + boolean oldMethodRecoveryActivated = this.methodRecoveryActivated; + if(this.options.performStatementsRecovery) { + this.methodRecoveryActivated = true; + } + initialize(); goForBlockStatementsopt(); if (recordLineSeparator) { @@ -8601,6 +8889,9 @@ this.lastAct = ERROR_ACTION; } finally { this.nestedMethod[this.nestedType]--; + if(this.options.performStatementsRecovery) { + this.methodRecoveryActivated = oldMethodRecoveryActivated; + } } checkNonNLSAfterBodyEnd(cd.declarationSourceEnd); @@ -8613,7 +8904,7 @@ //statements cd.explicitDeclarations = this.realBlockStack[this.realBlockPtr--]; int length; - if ((length = this.astLengthStack[this.astLengthPtr--]) != 0) { + if (astLengthPtr > -1 && (length = this.astLengthStack[this.astLengthPtr--]) != 0) { this.astPtr -= length; if (this.astStack[this.astPtr + 1] instanceof ExplicitConstructorCall) //avoid a isSomeThing that would only be used here BUT what is faster between two alternatives ? @@ -8747,6 +9038,11 @@ //convert bugs into parse error + boolean oldMethodRecoveryActivated = this.methodRecoveryActivated; + if(this.options.performStatementsRecovery) { + this.methodRecoveryActivated = true; + } + initialize(); goForBlockStatementsopt(); this.nestedMethod[this.nestedType]++; @@ -8762,6 +9058,9 @@ this.lastAct = ERROR_ACTION; } finally { this.nestedMethod[this.nestedType]--; + if(this.options.performStatementsRecovery) { + this.methodRecoveryActivated = oldMethodRecoveryActivated; + } } checkNonNLSAfterBodyEnd(initializer.declarationSourceEnd); @@ -8773,7 +9072,7 @@ //refill statements initializer.block.explicitDeclarations = this.realBlockStack[this.realBlockPtr--]; int length; - if ((length = this.astLengthStack[this.astLengthPtr--]) > 0) { + if (astLengthPtr > -1 && (length = this.astLengthStack[this.astLengthPtr--]) > 0) { System.arraycopy(this.astStack, (this.astPtr -= length) + 1, initializer.block.statements = new Statement[length], 0, length); } else { // check whether this block at least contains some comment in it @@ -8801,6 +9100,11 @@ if ((md.modifiers & ExtraCompilerModifiers.AccSemicolonBody) != 0) return; + boolean oldMethodRecoveryActivated = this.methodRecoveryActivated; + if(this.options.performStatementsRecovery) { + this.methodRecoveryActivated = true; + this.rParenPos = md.sourceEnd; + } initialize(); goForBlockStatementsopt(); this.nestedMethod[this.nestedType]++; @@ -8817,6 +9121,9 @@ this.lastAct = ERROR_ACTION; } finally { this.nestedMethod[this.nestedType]--; + if(this.options.performStatementsRecovery) { + this.methodRecoveryActivated = oldMethodRecoveryActivated; + } } checkNonNLSAfterBodyEnd(md.declarationSourceEnd); @@ -8828,7 +9135,7 @@ //refill statements md.explicitDeclarations = this.realBlockStack[this.realBlockPtr--]; int length; - if ((length = this.astLengthStack[this.astLengthPtr--]) != 0) { + if (astLengthPtr > -1 && (length = this.astLengthStack[this.astLengthPtr--]) != 0) { System.arraycopy( this.astStack, (this.astPtr -= length) + 1, @@ -8871,7 +9178,7 @@ return null; } int astLength; - if ((astLength = this.astLengthStack[this.astLengthPtr--]) != 0) { + if (astLengthPtr > -1 && (astLength = this.astLengthStack[this.astLengthPtr--]) != 0) { ASTNode[] result = new ASTNode[astLength]; this.astPtr -= astLength; System.arraycopy(this.astStack, this.astPtr + 1, result, 0, astLength); @@ -8929,11 +9236,67 @@ return this.expressionStack[this.expressionPtr]; } +public void parseStatements(ReferenceContext rc, int start, int end, TypeDeclaration[] types, CompilationUnitDeclaration unit) { + boolean oldStatementRecoveryEnabled = this.statementRecoveryActivated; + this.statementRecoveryActivated = true; + + initialize(); + + goForBlockStatementsopt(); + this.nestedMethod[this.nestedType]++; + pushOnRealBlockStack(0); + + pushOnAstLengthStack(0); + + this.referenceContext = rc; + this.compilationUnit = unit; + + if(types != null && types.length > 0) { + this.recoveredTypes = types; + this.recoveredTypePtr = 0; + this.nextTypeStart = + this.recoveredTypes[0].allocation == null + ? this.recoveredTypes[0].declarationSourceStart + : this.recoveredTypes[0].bodyStart; + } else { + this.recoveredTypes = null; + this.recoveredTypePtr = -1; + this.nextTypeStart = -1; + } + + this.scanner.resetTo(start, end); + // reset the scanner to parser from { down to } + + this.lastCheckPoint = this.scanner.initialPosition; + + + this.stateStackTop = -1; + + try { + parse(); + } catch (AbortCompilation ex) { + this.lastAct = ERROR_ACTION; + } finally { + this.nestedMethod[this.nestedType]--; + this.recoveredTypes = null; + this.statementRecoveryActivated = oldStatementRecoveryEnabled; + } + + checkNonNLSAfterBodyEnd(end); +} public void persistLineSeparatorPositions() { if (this.scanner.recordLineSeparator) { this.compilationUnit.compilationResult.lineSeparatorPositions = this.scanner.getLineEnds(); } } +/* + * Prepares the state of the parser to go for BlockStatements. + */ +protected void prepareForBlockStatements() { + this.nestedMethod[this.nestedType = 0] = 1; + this.variablesCounter[this.nestedType] = 0; + this.realBlockStack[this.realBlockPtr = 1] = 0; +} /** * Returns this parser's problem reporter initialized with its reference context. * Also it is assumed that a problem is going to be reported, so initializes @@ -9121,6 +9484,166 @@ } this.realBlockStack[this.realBlockPtr] = i; } +protected void recoverStatements() { + class MethodVisitor extends ASTVisitor { + public ASTVisitor typeVisitor; + + TypeDeclaration enclosingType; // used only for initializer + + TypeDeclaration[] types = new TypeDeclaration[0]; + int typePtr = -1; + public boolean visit(ConstructorDeclaration constructorDeclaration, ClassScope scope) { + typePtr = -1; + return true; + } + public boolean visit(Initializer initializer, MethodScope scope) { + typePtr = -1; + return true; + } + public boolean visit(MethodDeclaration methodDeclaration,ClassScope scope) { + typePtr = -1; + return true; + } + public boolean visit(TypeDeclaration typeDeclaration, BlockScope scope) { + return this.visit(typeDeclaration); + } + public boolean visit(TypeDeclaration typeDeclaration, ClassScope scope) { + return this.visit(typeDeclaration); + } + private boolean visit(TypeDeclaration typeDeclaration) { + if(this.types.length <= ++this.typePtr) { + int length = this.typePtr; + System.arraycopy(this.types, 0, this.types = new TypeDeclaration[length * 2 + 1], 0, length); + } + this.types[this.typePtr] = typeDeclaration; + return false; + } + public void endVisit(ConstructorDeclaration constructorDeclaration, ClassScope scope) { + this.endVisitMethod(constructorDeclaration, scope); + } + public void endVisit(MethodDeclaration methodDeclaration, ClassScope scope) { + this.endVisitMethod(methodDeclaration, scope); + } + private void endVisitMethod(AbstractMethodDeclaration methodDeclaration, ClassScope scope) { + TypeDeclaration[] foundTypes = null; + int length = 0; + if(this.typePtr > -1) { + length = this.typePtr + 1; + foundTypes = new TypeDeclaration[length]; + System.arraycopy(this.types, 0, foundTypes, 0, length); + } + ReferenceContext oldContext = Parser.this.referenceContext; + Parser.this.recoveryScanner.resetTo(methodDeclaration.bodyStart, methodDeclaration.bodyEnd); + Scanner oldScanner = Parser.this.scanner; + Parser.this.scanner = Parser.this.recoveryScanner; + Parser.this.parseStatements( + methodDeclaration, + methodDeclaration.bodyStart, + methodDeclaration.bodyEnd, + foundTypes, + compilationUnit); + Parser.this.scanner = oldScanner; + Parser.this.referenceContext = oldContext; + + for (int i = 0; i < length; i++) { + foundTypes[i].traverse(typeVisitor, scope); + } + } + public void endVisit(Initializer initializer, MethodScope scope) { + TypeDeclaration[] foundTypes = null; + int length = 0; + if(this.typePtr > -1) { + length = this.typePtr + 1; + foundTypes = new TypeDeclaration[length]; + System.arraycopy(this.types, 0, foundTypes, 0, length); + } + ReferenceContext oldContext = Parser.this.referenceContext; + Parser.this.recoveryScanner.resetTo(initializer.bodyStart, initializer.bodyEnd); + Scanner oldScanner = Parser.this.scanner; + Parser.this.scanner = Parser.this.recoveryScanner; + Parser.this.parseStatements( + this.enclosingType, + initializer.bodyStart, + initializer.bodyEnd, + foundTypes, + compilationUnit); + Parser.this.scanner = oldScanner; + Parser.this.referenceContext = oldContext; + + for (int i = 0; i < length; i++) { + foundTypes[i].traverse(typeVisitor, scope); + } + } + } + class TypeVisitor extends ASTVisitor { + public MethodVisitor methodVisitor; + + TypeDeclaration[] types = new TypeDeclaration[0]; + int typePtr = -1; + + public void endVisit(TypeDeclaration typeDeclaration, BlockScope scope) { + endVisitType(); + } + public void endVisit(TypeDeclaration typeDeclaration, ClassScope scope) { + endVisitType(); + } + private void endVisitType() { + this.typePtr--; + } + public boolean visit(TypeDeclaration typeDeclaration, BlockScope scope) { + return this.visit(typeDeclaration); + } + public boolean visit(TypeDeclaration typeDeclaration, ClassScope scope) { + return this.visit(typeDeclaration); + } + private boolean visit(TypeDeclaration typeDeclaration) { + if(this.types.length <= ++this.typePtr) { + int length = this.typePtr; + System.arraycopy(this.types, 0, this.types = new TypeDeclaration[length * 2 + 1], 0, length); + } + this.types[this.typePtr] = typeDeclaration; + return true; + } + public boolean visit(ConstructorDeclaration constructorDeclaration, ClassScope scope) { + if(constructorDeclaration.isDefaultConstructor()) return false; + + constructorDeclaration.traverse(methodVisitor, scope); + return false; + } + public boolean visit(Initializer initializer, MethodScope scope) { + methodVisitor.enclosingType = this.types[typePtr]; + initializer.traverse(methodVisitor, scope); + return false; + } + public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) { + methodDeclaration.traverse(methodVisitor, scope); + return false; + } + } + + MethodVisitor methodVisitor = new MethodVisitor(); + TypeVisitor typeVisitor = new TypeVisitor(); + methodVisitor.typeVisitor = typeVisitor; + typeVisitor.methodVisitor = methodVisitor; + + if(this.referenceContext instanceof AbstractMethodDeclaration) { + ((AbstractMethodDeclaration)this.referenceContext).traverse(methodVisitor, (ClassScope)null); + } else if(this.referenceContext instanceof TypeDeclaration) { + TypeDeclaration typeContext = (TypeDeclaration)this.referenceContext; + + int length = typeContext.fields.length; + for (int i = 0; i < length; i++) { + final FieldDeclaration fieldDeclaration = typeContext.fields[i]; + switch(fieldDeclaration.getKind()) { + case AbstractVariableDeclaration.INITIALIZER: + methodVisitor.enclosingType = typeContext; + ((Initializer) fieldDeclaration).traverse(methodVisitor, (MethodScope)null); + break; + } + } + } +} + public void recoveryExitFromVariable() { if(this.currentElement != null && this.currentElement.parent != null) { if(this.currentElement instanceof RecoveredLocalVariable) { @@ -9202,16 +9725,15 @@ int end = this.scanner.eofPosition <= Integer.MAX_VALUE ? this.scanner.eofPosition - 1 : this.scanner.eofPosition; if(isDietParse) { TypeDeclaration[] types = this.compilationUnit.types; - int[][] intervalToSkip = org.eclipse.jdt.internal.compiler.parser.diagnose.RangeUtil.computeDietRange(types); DiagnoseParser diagnoseParser = new DiagnoseParser(this, oldFirstToken, start, end, intervalToSkip[0], intervalToSkip[1], intervalToSkip[2], this.options); - diagnoseParser.diagnoseParse(); + diagnoseParser.diagnoseParse(false); reportSyntaxErrorsForSkippedMethod(types); this.scanner.resetTo(start, end); } else { DiagnoseParser diagnoseParser = new DiagnoseParser(this, oldFirstToken, start, end, this.options); - diagnoseParser.diagnoseParse(); + diagnoseParser.diagnoseParse(this.options.performStatementsRecovery); } } private void reportSyntaxErrorsForSkippedMethod(TypeDeclaration[] types){ @@ -9229,10 +9751,10 @@ if(method.errorInSignature) { if(method.isAnnotationMethod()) { DiagnoseParser diagnoseParser = new DiagnoseParser(this, TokenNameQUESTION, method.declarationSourceStart, method.declarationSourceEnd, this.options); - diagnoseParser.diagnoseParse(); + diagnoseParser.diagnoseParse(this.options.performStatementsRecovery); } else { DiagnoseParser diagnoseParser = new DiagnoseParser(this, TokenNameDIVIDE, method.declarationSourceStart, method.declarationSourceEnd, this.options); - diagnoseParser.diagnoseParse(); + diagnoseParser.diagnoseParse(this.options.performStatementsRecovery); } } @@ -9247,7 +9769,7 @@ Initializer initializer = (Initializer)fields[j]; if(initializer.errorInSignature){ DiagnoseParser diagnoseParser = new DiagnoseParser(this, TokenNameRIGHT_SHIFT, initializer.declarationSourceStart, initializer.declarationSourceEnd, this.options); - diagnoseParser.diagnoseParse(); + diagnoseParser.diagnoseParse(this.options.performStatementsRecovery); } } } @@ -9292,24 +9814,43 @@ * decide which grammar goal is activated. */ protected boolean resumeAfterRecovery() { - - // reset internal stacks - this.resetStacks(); - this.resetModifiers(); + if(!this.methodRecoveryActivated && !this.statementRecoveryActivated) { + + // reset internal stacks + this.resetStacks(); + this.resetModifiers(); + + /* attempt to move checkpoint location */ + if (!this.moveRecoveryCheckpoint()) { + return false; + } - /* attempt to move checkpoint location */ - if (!this.moveRecoveryCheckpoint()) { + // only look for headers + if (this.referenceContext instanceof CompilationUnitDeclaration){ + goForHeaders(); + this.diet = true; // passed this point, will not consider method bodies + return true; + } + + // does not know how to restart return false; - } - - // only look for headers - if (this.referenceContext instanceof CompilationUnitDeclaration){ + } else if(!this.statementRecoveryActivated) { + + // reset internal stacks + this.resetStacks(); + this.resetModifiers(); + + /* attempt to move checkpoint location */ + if (!this.moveRecoveryCheckpoint()) { + return false; + } + + // only look for headers goForHeaders(); - this.diet = true; // passed this point, will not consider method bodies return true; + } else { + return false; } - // does not know how to restart - return false; } protected boolean resumeOnSyntaxError() { this.checkExternalizeStrings = false; @@ -9335,6 +9876,9 @@ /* attempt to reset state in order to resume to parse loop */ return this.resumeAfterRecovery(); } +public void setStatementsRecovery(boolean enabled) { + this.options.performStatementsRecovery = enabled; +} public String toString() { Index: compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredMethod.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredMethod.java,v retrieving revision 1.54 diff -u -r1.54 RecoveredMethod.java --- compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredMethod.java 10 Jan 2006 14:37:28 -0000 1.54 +++ compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredMethod.java 17 Jan 2006 17:44:28 -0000 @@ -207,7 +207,7 @@ } return this.parent.add(typeDeclaration, bracketBalanceValue); } - if ((typeDeclaration.bits & ASTNode.IsLocalType) != 0){ + if ((typeDeclaration.bits & ASTNode.IsLocalType) != 0 || this.parser().methodRecoveryActivated || this.parser().statementRecoveryActivated){ if (methodBody == null){ Block block = new Block(0); block.sourceStart = methodDeclaration.bodyStart; Index: compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredElement.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredElement.java,v retrieving revision 1.27 diff -u -r1.27 RecoveredElement.java --- compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredElement.java 23 Feb 2005 02:47:58 -0000 1.27 +++ compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredElement.java 17 Jan 2006 17:44:28 -0000 @@ -106,6 +106,15 @@ this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(typeDeclaration.declarationSourceStart - 1)); return this.parent.add(typeDeclaration, bracketBalanceValue); } +protected void addBlockStatement(RecoveredBlock recoveredBlock) { + Block block = recoveredBlock.blockDeclaration; + if(block.statements != null) { + Statement[] statements = block.statements; + for (int i = 0; i < statements.length; i++) { + recoveredBlock.add(statements[i], 0); + } + } +} /* * Answer the depth of this element, considering the parent link. */ Index: compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredBlock.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredBlock.java,v retrieving revision 1.34 diff -u -r1.34 RecoveredBlock.java --- compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredBlock.java 10 Jan 2006 14:37:28 -0000 1.34 +++ compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredBlock.java 17 Jan 2006 17:44:28 -0000 @@ -33,6 +33,8 @@ super(block, parent, bracketBalance); this.blockDeclaration = block; this.foundOpeningBrace = true; + + this.preserveContent = this.parser().methodRecoveryActivated || this.parser().statementRecoveryActivated; } /* * Record a nested block declaration @@ -53,6 +55,9 @@ element.attach(this.pendingArgument); this.pendingArgument = null; } + if(this.parser().statementRecoveryActivated) { + this.addBlockStatement(element); + } this.attach(element); if (nestedBlockDeclaration.sourceEnd == 0) return element; return this; Index: compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java,v retrieving revision 1.166 diff -u -r1.166 Scanner.java --- compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java 10 Nov 2005 15:43:36 -0000 1.166 +++ compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java 17 Jan 2006 17:44:31 -0000 @@ -410,7 +410,7 @@ public int getCurrentTokenEndPosition(){ return this.currentPosition - 1; } -public final char[] getCurrentTokenSource() { +public char[] getCurrentTokenSource() { // Return the token REAL source (aka unicodes are precomputed) char[] result; Index: compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredType.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredType.java,v retrieving revision 1.55 diff -u -r1.55 RecoveredType.java --- compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredType.java 27 Oct 2005 13:03:07 -0000 1.55 +++ compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredType.java 17 Jan 2006 17:44:29 -0000 @@ -57,6 +57,8 @@ if(this.foundOpeningBrace) { this.bracketBalance++; } + + this.preserveContent = this.parser().methodRecoveryActivated || this.parser().statementRecoveryActivated; } public RecoveredElement add(AbstractMethodDeclaration methodDeclaration, int bracketBalanceValue) { @@ -311,7 +313,7 @@ return updatedType; } public TypeDeclaration updatedTypeDeclaration(){ - + int lastEnd = typeDeclaration.bodyStart; /* update member types */ if (memberTypeCount > 0){ int existingCount = typeDeclaration.memberTypes == null ? 0 : typeDeclaration.memberTypes.length; @@ -329,6 +331,9 @@ memberTypeDeclarations[existingCount + i] = memberTypes[i].updatedTypeDeclaration(); } typeDeclaration.memberTypes = memberTypeDeclarations; + if(memberTypeDeclarations[memberTypeDeclarations.length - 1].declarationSourceEnd > lastEnd) { + lastEnd = memberTypeDeclarations[memberTypeDeclarations.length - 1].declarationSourceEnd; + } } /* update fields */ if (fieldCount > 0){ @@ -347,6 +352,9 @@ fieldDeclarations[existingCount + i] = fields[i].updatedFieldDeclaration(); } typeDeclaration.fields = fieldDeclarations; + if(fieldDeclarations[fieldDeclarations.length - 1].declarationSourceEnd > lastEnd) { + lastEnd = fieldDeclarations[fieldDeclarations.length - 1].declarationSourceEnd; + } } /* update methods */ int existingCount = typeDeclaration.methods == null ? 0 : typeDeclaration.methods.length; @@ -374,6 +382,9 @@ methodDeclarations[existingCount + i] = updatedMethod; } typeDeclaration.methods = methodDeclarations; + if(methodDeclarations[methodDeclarations.length - 1].declarationSourceEnd > lastEnd) { + lastEnd = methodDeclarations[methodDeclarations.length - 1].declarationSourceEnd; + } if (hasAbstractMethods) typeDeclaration.bits |= ASTNode.HasAbstractMethods; hasConstructor = typeDeclaration.checkConstructors(this.parser()); } else { @@ -428,6 +439,10 @@ } else if (parent instanceof RecoveredMethod){ typeDeclaration.bits |= ASTNode.IsLocalType; } + if(typeDeclaration.declarationSourceEnd == 0) { + typeDeclaration.declarationSourceEnd = lastEnd; + typeDeclaration.bodyEnd = lastEnd; + } return typeDeclaration; } /* Index: grammar/java_1_5.g =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/grammar/java_1_5.g,v retrieving revision 1.58 diff -u -r1.58 java_1_5.g --- grammar/java_1_5.g 7 Nov 2005 09:55:39 -0000 1.58 +++ grammar/java_1_5.g 17 Jan 2006 17:44:44 -0000 @@ -28,11 +28,15 @@ $readableName -/.1#$rule_number=./ +/.1#$rule_number#./ $compliance -/.2#$rule_number=./ +/.2#$rule_number#./ $recovery -/.2#$rule_number= recovery./ +/.2#$rule_number# recovery./ +$recovery_template +/.3#$rule_number#./ +$no_statements_recovery +/.4#$rule_number# 1./ -- here it starts really ------------------------------------------ $Terminals @@ -483,6 +487,7 @@ ClassBody ::= '{' ClassBodyDeclarationsopt '}' /:$readableName ClassBody:/ +/:$no_statements_recovery:/ ClassBodyDeclarations ::= ClassBodyDeclaration ClassBodyDeclarations ::= ClassBodyDeclarations ClassBodyDeclaration @@ -612,6 +617,7 @@ MethodHeaderRightParen ::= ')' /.$putCase consumeMethodHeaderRightParen(); $break ./ /:$readableName ):/ +/:$recovery_template ):/ MethodHeaderExtendedDims ::= Dimsopt /.$putCase consumeMethodHeaderExtendedDims(); $break ./ @@ -656,6 +662,7 @@ MethodBody ::= NestedMethod '{' BlockStatementsopt '}' /.$putCase consumeMethodBody(); $break ./ /:$readableName MethodBody:/ +/:$no_statements_recovery:/ NestedMethod ::= $empty /.$putCase consumeNestedMethod(); $break ./ @@ -810,6 +817,7 @@ ArrayInitializer ::= '{' PushLeftBrace VariableInitializers , '}' /.$putCase consumeArrayInitializer(); $break ./ /:$readableName ArrayInitializer:/ +/:$recovery_template Identifier:/ VariableInitializers ::= VariableInitializer VariableInitializers ::= VariableInitializers ',' VariableInitializer @@ -1087,9 +1095,11 @@ PushLPAREN ::= '(' /.$putCase consumeLeftParen(); $break ./ /:$readableName (:/ +/:$recovery_template (:/ PushRPAREN ::= ')' /.$putCase consumeRightParen(); $break ./ /:$readableName ):/ +/:$recovery_template ):/ Primary -> PrimaryNoNewArray Primary -> ArrayCreationWithArrayInitializer @@ -1174,6 +1184,7 @@ /.$putCase consumeClassBodyopt(); $break ./ ClassBodyopt ::= EnterAnonymousClassBody ClassBody /:$readableName ClassBody:/ +/:$no_statements_recovery:/ EnterAnonymousClassBody ::= $empty /.$putCase consumeEnterAnonymousClassBody(); $break ./ @@ -1448,9 +1459,11 @@ AssignmentOperator ::= '|=' /.$putCase consumeAssignmentOperator(OR); $break ./ /:$readableName AssignmentOperator:/ +/:$recovery_template =:/ Expression -> AssignmentExpression /:$readableName Expression:/ +/:$recovery_template Identifier:/ -- The following rules are for optional nonterminals. -- Index: model/org/eclipse/jdt/internal/core/util/CodeSnippetParsingUtil.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/CodeSnippetParsingUtil.java,v retrieving revision 1.11 diff -u -r1.11 CodeSnippetParsingUtil.java --- model/org/eclipse/jdt/internal/core/util/CodeSnippetParsingUtil.java 28 Sep 2005 15:00:15 -0000 1.11 +++ model/org/eclipse/jdt/internal/core/util/CodeSnippetParsingUtil.java 17 Jan 2006 17:44:49 -0000 @@ -62,7 +62,8 @@ new DefaultProblemFactory(Locale.getDefault())); CommentRecorderParser parser = new CommentRecorderParser(problemReporter, false); - + parser.setStatementsRecovery(false); + ICompilationUnit sourceUnit = new CompilationUnit( source, @@ -158,11 +159,11 @@ return result; } - public ConstructorDeclaration parseStatements(char[] source, Map settings, boolean recordParsingInformation) { - return parseStatements(source, 0, source.length, settings, recordParsingInformation); + public ConstructorDeclaration parseStatements(char[] source, Map settings, boolean recordParsingInformation, boolean enabledStatementRecovery) { + return parseStatements(source, 0, source.length, settings, recordParsingInformation, enabledStatementRecovery); } - public ConstructorDeclaration parseStatements(char[] source, int offset, int length, Map settings, boolean recordParsingInformation) { + public ConstructorDeclaration parseStatements(char[] source, int offset, int length, Map settings, boolean recordParsingInformation, boolean enabledStatementRecovery) { if (source == null) { throw new IllegalArgumentException(); } @@ -172,6 +173,7 @@ compilerOptions, new DefaultProblemFactory(Locale.getDefault())); CommentRecorderParser parser = new CommentRecorderParser(problemReporter, false); + parser.setStatementsRecovery(enabledStatementRecovery); ICompilationUnit sourceUnit = new CompilationUnit( Index: model/org/eclipse/jdt/core/JavaCore.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java,v retrieving revision 1.523 diff -u -r1.523 JavaCore.java --- model/org/eclipse/jdt/core/JavaCore.java 17 Jan 2006 09:35:51 -0000 1.523 +++ model/org/eclipse/jdt/core/JavaCore.java 17 Jan 2006 17:44:47 -0000 @@ -657,6 +657,13 @@ /** * Possible configurable option ID. * @see #getDefaultOptions() + * @since 3.2 + */ + //TODO It is a temporary option. It will removed before 3.2 release + public static final String COMPILER_STATEMENTS_RECOVERY = PLUGIN_ID + ".compiler.statementsRecovery"; //$NON-NLS-1$ + /** + * Possible configurable option ID. + * @see #getDefaultOptions() */ public static final String CORE_JAVA_BUILD_ORDER = PLUGIN_ID + ".computeJavaBuildOrder"; //$NON-NLS-1$ /** Index: model/org/eclipse/jdt/internal/core/builder/AbstractImageBuilder.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/AbstractImageBuilder.java,v retrieving revision 1.81 diff -u -r1.81 AbstractImageBuilder.java --- model/org/eclipse/jdt/internal/core/builder/AbstractImageBuilder.java 13 Jan 2006 09:19:07 -0000 1.81 +++ model/org/eclipse/jdt/internal/core/builder/AbstractImageBuilder.java 17 Jan 2006 17:44:49 -0000 @@ -415,6 +415,7 @@ // enable the compiler reference info support options.produceReferenceInfo = true; + options.performStatementsRecovery = false; org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment env = newCompiler.lookupEnvironment; synchronized (env) { Index: formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatter.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatter.java,v retrieving revision 1.52 diff -u -r1.52 DefaultCodeFormatter.java --- formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatter.java 9 Sep 2005 15:43:05 -0000 1.52 +++ formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatter.java 17 Jan 2006 17:44:43 -0000 @@ -267,7 +267,7 @@ return this.newCodeFormatter2.format(source, (Block) node); } - ConstructorDeclaration constructorDeclaration = this.codeSnippetParsingUtil.parseStatements(source.toCharArray(), getDefaultCompilerOptions(), true); + ConstructorDeclaration constructorDeclaration = this.codeSnippetParsingUtil.parseStatements(source.toCharArray(), getDefaultCompilerOptions(), true, false); if (constructorDeclaration.statements == null) { // a problem occured while parsing the source @@ -469,7 +469,7 @@ } // probe for statements - ConstructorDeclaration constructorDeclaration = this.codeSnippetParsingUtil.parseStatements(source.toCharArray(), getDefaultCompilerOptions(), true); + ConstructorDeclaration constructorDeclaration = this.codeSnippetParsingUtil.parseStatements(source.toCharArray(), getDefaultCompilerOptions(), true, false); if (constructorDeclaration.statements != null) { if (USE_NEW_FORMATTER) { ASTParser parser = ASTParser.newParser(AST.JLS3); Index: compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java,v retrieving revision 1.28 diff -u -r1.28 ForeachStatement.java --- compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java 13 Jan 2006 16:37:16 -0000 1.28 +++ compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java 17 Jan 2006 17:44:20 -0000 @@ -329,7 +329,7 @@ scope = new BlockScope(upperScope); this.elementVariable.resolve(scope); // collection expression can see itemVariable TypeBinding elementType = this.elementVariable.type.resolvedType; - TypeBinding collectionType = this.collection.resolveType(scope); + TypeBinding collectionType = this.collection == null ? null : this.collection.resolveType(scope); boolean hasError = elementType == null || collectionType == null; if (!hasError) { Index: compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java,v retrieving revision 1.91 diff -u -r1.91 Expression.java --- compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java 10 Jan 2006 14:37:27 -0000 1.91 +++ compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java 17 Jan 2006 17:44:20 -0000 @@ -178,6 +178,8 @@ public Constant constant; + public int statementEnd = -1; + //Some expression may not be used - from a java semantic point //of view only - as statements. Other may. In order to avoid the creation //of wrappers around expression in order to tune them as expression Index: compiler/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java,v retrieving revision 1.58 diff -u -r1.58 CompilationUnitDeclaration.java --- compiler/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java 13 Jan 2006 16:37:16 -0000 1.58 +++ compiler/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java 17 Jan 2006 17:44:19 -0000 @@ -130,6 +130,9 @@ localType.enclosingCase = null; } } + + compilationResult.recoveryScannerData = null; // recovery is already done + ClassFile[] classFiles = compilationResult.getClassFiles(); for (int i = 0, max = classFiles.length; i < max; i++) { // clear the classFile back pointer to the bindings Index: compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java,v retrieving revision 1.278 diff -u -r1.278 ProblemReporter.java --- compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java 10 Jan 2006 14:37:28 -0000 1.278 +++ compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java 17 Jan 2006 17:44:36 -0000 @@ -2628,6 +2628,8 @@ public void invalidEnclosingType(Expression expression, TypeBinding type, ReferenceBinding enclosingType) { if (enclosingType.isAnonymousType()) enclosingType = enclosingType.superclass(); + if (enclosingType.sourceName.length == 0) return; + int flag = IProblem.UndefinedType; // default switch (type.problemId()) { case ProblemReasons.NotFound : // 1 @@ -2675,6 +2677,8 @@ expression.sourceEnd); } public void invalidField(FieldReference fieldRef, TypeBinding searchedType) { + if(fieldRef.token.length == 0) return; + int id = IProblem.UndefinedField; FieldBinding field = fieldRef.binding; final int sourceStart= (int) (fieldRef.nameSourcePosition >> 32); @@ -2730,6 +2734,14 @@ fieldRef.sourceEnd); } public void invalidField(NameReference nameRef, FieldBinding field) { + if (nameRef instanceof QualifiedNameReference) { + QualifiedNameReference ref = (QualifiedNameReference) nameRef; + char[] lastToken = ref.tokens[ref.tokens.length - 1]; + if (lastToken.length == 0) return; + } else { + SingleNameReference ref = (SingleNameReference) nameRef; + if (ref.token.length == 0) return; + } int id = IProblem.UndefinedField; switch (field.problemId()) { case ProblemReasons.NotFound : @@ -2786,6 +2798,9 @@ //UndefinedField //NotVisibleField //AmbiguousField + + char[] lastToken = nameRef.tokens[nameRef.tokens.length - 1]; + if (lastToken.length == 0) return; if (searchedType.isBaseType()) { this.handle( @@ -2863,6 +2878,8 @@ annotation.sourceEnd); } public void invalidMethod(MessageSend messageSend, MethodBinding method) { + if(messageSend.selector.length == 0) return; + int id = IProblem.UndefinedMethod; //default... MethodBinding shownMethod = method; switch (method.problemId()) { @@ -3148,26 +3165,31 @@ int end = location.sourceEnd; if (location instanceof QualifiedNameReference) { QualifiedNameReference ref = (QualifiedNameReference) location; + if(ref.tokens[ref.tokens.length - 1].length == 0) return; if (ref.indexOfFirstFieldBinding >= 1) end = (int) ref.sourcePositions[ref.indexOfFirstFieldBinding - 1]; } else if (location instanceof ArrayQualifiedTypeReference) { ArrayQualifiedTypeReference arrayQualifiedTypeReference = (ArrayQualifiedTypeReference) location; + if(arrayQualifiedTypeReference.tokens[arrayQualifiedTypeReference.tokens.length - 1].length == 0) return; long[] positions = arrayQualifiedTypeReference.sourcePositions; end = (int) positions[positions.length - 1]; } else if (location instanceof QualifiedTypeReference) { QualifiedTypeReference ref = (QualifiedTypeReference) location; + if(ref.tokens[ref.tokens.length - 1].length == 0) return; if (type instanceof ReferenceBinding) { char[][] name = ((ReferenceBinding) type).compoundName; end = (int) ref.sourcePositions[name.length - 1]; } } else if (location instanceof ImportReference) { ImportReference ref = (ImportReference) location; + if (ref.tokens[ref.tokens.length - 1].length == 0) return; if (type instanceof ReferenceBinding) { char[][] name = ((ReferenceBinding) type).compoundName; end = (int) ref.sourcePositions[name.length - 1]; } } else if (location instanceof ArrayTypeReference) { ArrayTypeReference arrayTypeReference = (ArrayTypeReference) location; + if (arrayTypeReference.token.length == 0) return; end = arrayTypeReference.originalSourceEnd; } this.handle( @@ -5274,6 +5296,7 @@ } public void undefinedAnnotationValue(TypeBinding annotationType, MemberValuePair memberValuePair) { String name = new String(memberValuePair.name); + if(name.length() == 0) return; this.handle( IProblem.UndefinedAnnotationMember, new String[] { name, new String(annotationType.readableName())}, @@ -5283,6 +5306,7 @@ } public void undefinedLabel(BranchStatement statement) { String[] arguments = new String[] {new String(statement.label)}; + if (statement.label.length == 0) return; this.handle( IProblem.UndefinedLabel, arguments, @@ -5292,6 +5316,7 @@ } // can only occur inside binaries public void undefinedTypeVariableSignature(char[] variableName, ReferenceBinding binaryType) { + if(variableName.length == 0) this.handle( IProblem.UndefinedTypeVariable, new String[] {new String(variableName), new String(binaryType.readableName()) }, @@ -5466,8 +5491,13 @@ int end = nameRef.sourceEnd; if (nameRef instanceof QualifiedNameReference) { QualifiedNameReference ref = (QualifiedNameReference) nameRef; + char[] lastToken = ref.tokens[ref.tokens.length - 1]; + if (lastToken.length == 0) return; if (ref.indexOfFirstFieldBinding >= 1) end = (int) ref.sourcePositions[ref.indexOfFirstFieldBinding - 1]; + } else { + SingleNameReference ref = (SingleNameReference) nameRef; + if (ref.token.length == 0) return; } this.handle( IProblem.UndefinedName, Index: codeassist/org/eclipse/jdt/internal/codeassist/impl/AssistParser.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/impl/AssistParser.java,v retrieving revision 1.73 diff -u -r1.73 AssistParser.java --- codeassist/org/eclipse/jdt/internal/codeassist/impl/AssistParser.java 20 Oct 2005 13:26:47 -0000 1.73 +++ codeassist/org/eclipse/jdt/internal/codeassist/impl/AssistParser.java 17 Jan 2006 17:44:19 -0000 @@ -75,6 +75,8 @@ public AssistParser(ProblemReporter problemReporter) { super(problemReporter, true); this.javadocParser.checkDocComment = false; + + this.setStatementsRecovery(false); } public abstract char[] assistIdentifier(); public int bodyEnd(AbstractMethodDeclaration method){ Index: batch/org/eclipse/jdt/internal/compiler/batch/Main.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java,v retrieving revision 1.224 diff -u -r1.224 Main.java --- batch/org/eclipse/jdt/internal/compiler/batch/Main.java 17 Jan 2006 09:35:50 -0000 1.224 +++ batch/org/eclipse/jdt/internal/compiler/batch/Main.java 17 Jan 2006 17:44:18 -0000 @@ -2795,6 +2795,7 @@ // set the non-externally configurable options. this.compilerOptions.verbose = this.verbose; this.compilerOptions.produceReferenceInfo = this.produceRefInfo; + this.compilerOptions.performStatementsRecovery = false; try { this.logger.startLoggingSources(); batchCompiler.compile(getCompilationUnits()); Index: compiler/org/eclipse/jdt/internal/compiler/parser/diagnose/DiagnoseParser.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/diagnose/DiagnoseParser.java,v retrieving revision 1.21 diff -u -r1.21 DiagnoseParser.java --- compiler/org/eclipse/jdt/internal/compiler/parser/diagnose/DiagnoseParser.java 17 Oct 2005 12:52:29 -0000 1.21 +++ compiler/org/eclipse/jdt/internal/compiler/parser/diagnose/DiagnoseParser.java 17 Jan 2006 17:44:32 -0000 @@ -14,6 +14,7 @@ import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.parser.Parser; import org.eclipse.jdt.internal.compiler.parser.ParserBasicInformation; +import org.eclipse.jdt.internal.compiler.parser.RecoveryScanner; import org.eclipse.jdt.internal.compiler.parser.TerminalTokens; import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; @@ -79,6 +80,10 @@ private Parser parser; + private RecoveryScanner recoveryScanner; + + private boolean reportProblem; + private class RepairCandidate { public int symbol; public int location; @@ -145,6 +150,7 @@ this.parser = parser; this.options = options; this.lexStream = new LexStream(BUFF_SIZE, parser.scanner, intervalStartToSkip, intervalEndToSkip, intervalFlagsToSkip, firstToken, start, end); + this.recoveryScanner = parser.recoveryScanner; } private ProblemReporter problemReporter(){ @@ -179,230 +185,242 @@ } - public void diagnoseParse() { - lexStream.reset(); - - currentToken = lexStream.getToken(); - - int prev_pos; - int pos; - int next_pos; - int act = START_STATE; - - reallocateStacks(); - - // - // Start parsing - // - stateStackTop = 0; - stack[stateStackTop] = act; - - int tok = lexStream.kind(currentToken); - locationStack[stateStackTop] = currentToken; - locationStartStack[stateStackTop] = lexStream.start(currentToken); - - boolean forceRecoveryAfterLBracketMissing = false; -// int forceRecoveryToken = -1; - - // - // Process a terminal - // - do { + public void diagnoseParse(boolean record) { + this.reportProblem = true; + boolean oldRecord = false; + if(this.recoveryScanner != null) { + oldRecord = this.recoveryScanner.record; + this.recoveryScanner.record = record; + } + try { + lexStream.reset(); + + currentToken = lexStream.getToken(); + + int prev_pos; + int pos; + int next_pos; + int act = START_STATE; + + reallocateStacks(); + // - // Synchronize state stacks and update the location stack + // Start parsing // - prev_pos = -1; - prevStackTop = -1; - - next_pos = -1; - nextStackTop = -1; - - pos = stateStackTop; - tempStackTop = stateStackTop - 1; - for (int i = 0; i <= stateStackTop; i++) - tempStack[i] = stack[i]; - - act = Parser.tAction(act, tok); + stateStackTop = 0; + stack[stateStackTop] = act; + + int tok = lexStream.kind(currentToken); + locationStack[stateStackTop] = currentToken; + locationStartStack[stateStackTop] = lexStream.start(currentToken); + + boolean forceRecoveryAfterLBracketMissing = false; + // int forceRecoveryToken = -1; + // - // When a reduce action is encountered, we compute all REDUCE - // and associated goto actions induced by the current token. - // Eventually, a SHIFT, SHIFT-REDUCE, ACCEPT or ERROR action is - // computed... + // Process a terminal // - while (act <= NUM_RULES) { - do { - tempStackTop -= (Parser.rhs[act]-1); - act = Parser.ntAction(tempStack[tempStackTop], Parser.lhs[act]); - } while(act <= NUM_RULES); + do { // - // ... Update the maximum useful position of the - // (STATE_)STACK, push goto state into stack, and - // compute next action on current symbol ... + // Synchronize state stacks and update the location stack // - if (tempStackTop + 1 >= stackLength) - reallocateStacks(); - pos = pos < tempStackTop ? pos : tempStackTop; - tempStack[tempStackTop + 1] = act; + prev_pos = -1; + prevStackTop = -1; + + next_pos = -1; + nextStackTop = -1; + + pos = stateStackTop; + tempStackTop = stateStackTop - 1; + for (int i = 0; i <= stateStackTop; i++) + tempStack[i] = stack[i]; + act = Parser.tAction(act, tok); - } - - // - // At this point, we have a shift, shift-reduce, accept or error - // action. STACK contains the configuration of the state stack - // prior to executing any action on curtok. next_stack contains - // the configuration of the state stack after executing all - // reduce actions induced by curtok. The variable pos indicates - // the highest position in STACK that is still useful after the - // reductions are executed. - // - while(act > ERROR_ACTION || act < ACCEPT_ACTION) { // SHIFT-REDUCE action or SHIFT action ? - nextStackTop = tempStackTop + 1; - for (int i = next_pos + 1; i <= nextStackTop; i++) - nextStack[i] = tempStack[i]; - - for (int i = pos + 1; i <= nextStackTop; i++) { - locationStack[i] = locationStack[stateStackTop]; - locationStartStack[i] = locationStartStack[stateStackTop]; - } - // - // If we have a shift-reduce, process it as well as - // the goto-reduce actions that follow it. + // When a reduce action is encountered, we compute all REDUCE + // and associated goto actions induced by the current token. + // Eventually, a SHIFT, SHIFT-REDUCE, ACCEPT or ERROR action is + // computed... // - if (act > ERROR_ACTION) { - act -= ERROR_ACTION; + while (act <= NUM_RULES) { do { - nextStackTop -= (Parser.rhs[act]-1); - act = Parser.ntAction(nextStack[nextStackTop], Parser.lhs[act]); - } while(act <= NUM_RULES); - pos = pos < nextStackTop ? pos : nextStackTop; - } - - if (nextStackTop + 1 >= stackLength) - reallocateStacks(); - - tempStackTop = nextStackTop; - nextStack[++nextStackTop] = act; - next_pos = nextStackTop; - - // - // Simulate the parser through the next token without - // destroying STACK or next_stack. - // - currentToken = lexStream.getToken(); - tok = lexStream.kind(currentToken); - act = Parser.tAction(act, tok); - while(act <= NUM_RULES) { - // - // ... Process all goto-reduce actions following - // reduction, until a goto action is computed ... - // - do { - int lhs_symbol = Parser.lhs[act]; - if(DEBUG) { - System.out.println(Parser.name[Parser.non_terminal_index[lhs_symbol]]); - } tempStackTop -= (Parser.rhs[act]-1); - act = (tempStackTop > next_pos - ? tempStack[tempStackTop] - : nextStack[tempStackTop]); - act = Parser.ntAction(act, lhs_symbol); - } while(act <= NUM_RULES); - + act = Parser.ntAction(tempStack[tempStackTop], Parser.lhs[act]); + } while(act <= NUM_RULES); // // ... Update the maximum useful position of the - // (STATE_)STACK, push GOTO state into stack, and + // (STATE_)STACK, push goto state into stack, and // compute next action on current symbol ... // if (tempStackTop + 1 >= stackLength) reallocateStacks(); - - next_pos = next_pos < tempStackTop ? next_pos : tempStackTop; + pos = pos < tempStackTop ? pos : tempStackTop; tempStack[tempStackTop + 1] = act; act = Parser.tAction(act, tok); } - -// if((tok != TokenNameRBRACE || (forceRecoveryToken != currentToken && (lexStream.flags(currentToken) & LexStream.LBRACE_MISSING) != 0)) -// && (lexStream.flags(currentToken) & LexStream.IS_AFTER_JUMP) !=0) { -// act = ERROR_ACTION; -// if(forceRecoveryToken != currentToken -// && (lexStream.flags(currentToken) & LexStream.LBRACE_MISSING) != 0) { -// forceRecoveryAfterLBracketMissing = true; -// forceRecoveryToken = currentToken; -// } -// } - - // - // No error was detected, Read next token into - // PREVTOK element, advance CURTOK pointer and - // update stacks. - // - if (act != ERROR_ACTION) { - prevStackTop = stateStackTop; - for (int i = prev_pos + 1; i <= prevStackTop; i++) - prevStack[i] = stack[i]; - prev_pos = pos; - - stateStackTop = nextStackTop; - for (int i = pos + 1; i <= stateStackTop; i++) - stack[i] = nextStack[i]; - locationStack[stateStackTop] = currentToken; - locationStartStack[stateStackTop] = lexStream.start(currentToken); - pos = next_pos; - } - } - - // - // At this stage, either we have an ACCEPT or an ERROR - // action. - // - if (act == ERROR_ACTION) { + // - // An error was detected. + // At this point, we have a shift, shift-reduce, accept or error + // action. STACK contains the configuration of the state stack + // prior to executing any action on curtok. next_stack contains + // the configuration of the state stack after executing all + // reduce actions induced by curtok. The variable pos indicates + // the highest position in STACK that is still useful after the + // reductions are executed. // - RepairCandidate candidate = errorRecovery(currentToken, forceRecoveryAfterLBracketMissing); - - forceRecoveryAfterLBracketMissing = false; - - if(parser.reportOnlyOneSyntaxError) { - return; - } - - if(this.parser.problemReporter().options.maxProblemsPerUnit < this.parser.compilationUnit.compilationResult.problemCount) { - return; + while(act > ERROR_ACTION || act < ACCEPT_ACTION) { // SHIFT-REDUCE action or SHIFT action ? + nextStackTop = tempStackTop + 1; + for (int i = next_pos + 1; i <= nextStackTop; i++) + nextStack[i] = tempStack[i]; + + for (int i = pos + 1; i <= nextStackTop; i++) { + locationStack[i] = locationStack[stateStackTop]; + locationStartStack[i] = locationStartStack[stateStackTop]; + } + + // + // If we have a shift-reduce, process it as well as + // the goto-reduce actions that follow it. + // + if (act > ERROR_ACTION) { + act -= ERROR_ACTION; + do { + nextStackTop -= (Parser.rhs[act]-1); + act = Parser.ntAction(nextStack[nextStackTop], Parser.lhs[act]); + } while(act <= NUM_RULES); + pos = pos < nextStackTop ? pos : nextStackTop; + } + + if (nextStackTop + 1 >= stackLength) + reallocateStacks(); + + tempStackTop = nextStackTop; + nextStack[++nextStackTop] = act; + next_pos = nextStackTop; + + // + // Simulate the parser through the next token without + // destroying STACK or next_stack. + // + currentToken = lexStream.getToken(); + tok = lexStream.kind(currentToken); + act = Parser.tAction(act, tok); + while(act <= NUM_RULES) { + // + // ... Process all goto-reduce actions following + // reduction, until a goto action is computed ... + // + do { + int lhs_symbol = Parser.lhs[act]; + if(DEBUG) { + System.out.println(Parser.name[Parser.non_terminal_index[lhs_symbol]]); + } + tempStackTop -= (Parser.rhs[act]-1); + act = (tempStackTop > next_pos + ? tempStack[tempStackTop] + : nextStack[tempStackTop]); + act = Parser.ntAction(act, lhs_symbol); + } while(act <= NUM_RULES); + + // + // ... Update the maximum useful position of the + // (STATE_)STACK, push GOTO state into stack, and + // compute next action on current symbol ... + // + if (tempStackTop + 1 >= stackLength) + reallocateStacks(); + + next_pos = next_pos < tempStackTop ? next_pos : tempStackTop; + tempStack[tempStackTop + 1] = act; + act = Parser.tAction(act, tok); + } + + // if((tok != TokenNameRBRACE || (forceRecoveryToken != currentToken && (lexStream.flags(currentToken) & LexStream.LBRACE_MISSING) != 0)) + // && (lexStream.flags(currentToken) & LexStream.IS_AFTER_JUMP) !=0) { + // act = ERROR_ACTION; + // if(forceRecoveryToken != currentToken + // && (lexStream.flags(currentToken) & LexStream.LBRACE_MISSING) != 0) { + // forceRecoveryAfterLBracketMissing = true; + // forceRecoveryToken = currentToken; + // } + // } + + // + // No error was detected, Read next token into + // PREVTOK element, advance CURTOK pointer and + // update stacks. + // + if (act != ERROR_ACTION) { + prevStackTop = stateStackTop; + for (int i = prev_pos + 1; i <= prevStackTop; i++) + prevStack[i] = stack[i]; + prev_pos = pos; + + stateStackTop = nextStackTop; + for (int i = pos + 1; i <= stateStackTop; i++) + stack[i] = nextStack[i]; + locationStack[stateStackTop] = currentToken; + locationStartStack[stateStackTop] = lexStream.start(currentToken); + pos = next_pos; + } } - - act = stack[stateStackTop]; - + // - // If the recovery was successful on a nonterminal candidate, - // parse through that candidate and "read" the next token. + // At this stage, either we have an ACCEPT or an ERROR + // action. // - if (candidate.symbol == 0) { - break; - } else if (candidate.symbol > NT_OFFSET) { - int lhs_symbol = candidate.symbol - NT_OFFSET; - if(DEBUG) { - System.out.println(Parser.name[Parser.non_terminal_index[lhs_symbol]]); + if (act == ERROR_ACTION) { + // + // An error was detected. + // + RepairCandidate candidate = errorRecovery(currentToken, forceRecoveryAfterLBracketMissing); + + forceRecoveryAfterLBracketMissing = false; + + if(parser.reportOnlyOneSyntaxError) { + return; } - act = Parser.ntAction(act, lhs_symbol); - while(act <= NUM_RULES) { - stateStackTop -= (Parser.rhs[act]-1); - act = Parser.ntAction(stack[stateStackTop], Parser.lhs[act]); + + if(this.parser.problemReporter().options.maxProblemsPerUnit < this.parser.compilationUnit.compilationResult.problemCount) { + if(this.recoveryScanner == null) return; + this.reportProblem = false; + } + + act = stack[stateStackTop]; + + // + // If the recovery was successful on a nonterminal candidate, + // parse through that candidate and "read" the next token. + // + if (candidate.symbol == 0) { + break; + } else if (candidate.symbol > NT_OFFSET) { + int lhs_symbol = candidate.symbol - NT_OFFSET; + if(DEBUG) { + System.out.println(Parser.name[Parser.non_terminal_index[lhs_symbol]]); + } + act = Parser.ntAction(act, lhs_symbol); + while(act <= NUM_RULES) { + stateStackTop -= (Parser.rhs[act]-1); + act = Parser.ntAction(stack[stateStackTop], Parser.lhs[act]); + } + stack[++stateStackTop] = act; + currentToken = lexStream.getToken(); + tok = lexStream.kind(currentToken); + locationStack[stateStackTop] = currentToken; + locationStartStack[stateStackTop] = lexStream.start(currentToken); + } else { + tok = candidate.symbol; + locationStack[stateStackTop] = candidate.location; + locationStartStack[stateStackTop] = lexStream.start(candidate.location); } - stack[++stateStackTop] = act; - currentToken = lexStream.getToken(); - tok = lexStream.kind(currentToken); - locationStack[stateStackTop] = currentToken; - locationStartStack[stateStackTop] = lexStream.start(currentToken); - } else { - tok = candidate.symbol; - locationStack[stateStackTop] = candidate.location; - locationStartStack[stateStackTop] = lexStream.start(candidate.location); } + } while (act != ACCEPT_ACTION); + } finally { + if(this.recoveryScanner != null) { + this.recoveryScanner.record = oldRecord; } - } while (act != ACCEPT_ACTION); - + } return; } @@ -2084,9 +2102,25 @@ String errorTokenName = Parser.name[Parser.terminal_index[lexStream.kind(token)]]; char[] errorTokenSource = lexStream.name(token); + int addedToken = -1; + if(recoveryScanner != null) { + if (nameIndex >= 0) { + addedToken = Parser.reverse_index[nameIndex]; + } + } switch(msgCode) { case BEFORE_CODE: - problemReporter().parseErrorInsertBeforeToken( + if(recoveryScanner != null) { + if(addedToken > -1) { + recoveryScanner.insertToken(addedToken, -1, errorStart); + } else { + int[] template = getNTermTemplate(-addedToken); + if(template != null) { + recoveryScanner.insertTokens(template, -1, errorStart); + } + } + } + if(this.reportProblem) problemReporter().parseErrorInsertBeforeToken( errorStart, errorEnd, currentKind, @@ -2095,7 +2129,17 @@ name); break; case INSERTION_CODE: - problemReporter().parseErrorInsertAfterToken( + if(recoveryScanner != null) { + if(addedToken > -1) { + recoveryScanner.insertToken(addedToken, -1, errorEnd); + } else { + int[] template = getNTermTemplate(-addedToken); + if(template != null) { + recoveryScanner.insertTokens(template, -1, errorEnd); + } + } + } + if(this.reportProblem) problemReporter().parseErrorInsertAfterToken( errorStart, errorEnd, currentKind, @@ -2104,7 +2148,10 @@ name); break; case DELETION_CODE: - problemReporter().parseErrorDeleteToken( + if(recoveryScanner != null) { + recoveryScanner.removeTokens(errorStart, errorEnd); + } + if(this.reportProblem) problemReporter().parseErrorDeleteToken( errorStart, errorEnd, currentKind, @@ -2113,7 +2160,10 @@ break; case INVALID_CODE: if (name.length() == 0) { - problemReporter().parseErrorReplaceToken( + if(recoveryScanner != null) { + recoveryScanner.removeTokens(errorStart, errorEnd); + } + if(this.reportProblem) problemReporter().parseErrorReplaceToken( errorStart, errorEnd, currentKind, @@ -2121,7 +2171,17 @@ errorTokenName, name); } else { - problemReporter().parseErrorInvalidToken( + if(recoveryScanner != null) { + if(addedToken > -1) { + recoveryScanner.replaceTokens(addedToken, errorStart, errorEnd); + } else { + int[] template = getNTermTemplate(-addedToken); + if(template != null) { + recoveryScanner.replaceTokens(template, errorStart, errorEnd); + } + } + } + if(this.reportProblem) problemReporter().parseErrorInvalidToken( errorStart, errorEnd, currentKind, @@ -2131,7 +2191,17 @@ } break; case SUBSTITUTION_CODE: - problemReporter().parseErrorReplaceToken( + if(recoveryScanner != null) { + if(addedToken > -1) { + recoveryScanner.replaceTokens(addedToken, errorStart, errorEnd); + } else { + int[] template = getNTermTemplate(-addedToken); + if(template != null) { + recoveryScanner.replaceTokens(template, errorStart, errorEnd); + } + } + } + if(this.reportProblem) problemReporter().parseErrorReplaceToken( errorStart, errorEnd, currentKind, @@ -2141,21 +2211,62 @@ break; case SCOPE_CODE: StringBuffer buf = new StringBuffer(); + + int[] addedTokens = null; + int addedTokenCount = 0; + if(this.recoveryScanner != null) { + addedTokens = new int[Parser.scope_rhs.length - Parser.scope_suffix[- nameIndex]]; + } + for (int i = Parser.scope_suffix[- nameIndex]; Parser.scope_rhs[i] != 0; i++) { buf.append(Parser.readableName[Parser.scope_rhs[i]]); if (Parser.scope_rhs[i + 1] != 0) // any more symbols to print? buf.append(' '); - + + if(addedTokens != null) { + int tmpAddedToken = Parser.reverse_index[Parser.scope_rhs[i]]; + if (tmpAddedToken > -1) { + int length = addedTokens.length; + if(addedTokenCount == length) { + System.arraycopy(addedTokens, 0, addedTokens = new int[length * 2], 0, length); + } + addedTokens[addedTokenCount++] = tmpAddedToken; + } else { + int[] template = getNTermTemplate(-tmpAddedToken); + if(template != null) { + for (int j = 0; j < template.length; j++) { + int length = addedTokens.length; + if(addedTokenCount == length) { + System.arraycopy(addedTokens, 0, addedTokens = new int[length * 2], 0, length); + } + addedTokens[addedTokenCount++] = template[j]; + } + } else { + addedTokenCount = 0; + addedTokens = null; + } + } + } } + if(addedTokenCount > 0) { + System.arraycopy(addedTokens, 0, addedTokens = new int[addedTokenCount], 0, addedTokenCount); + + int completedToken = -1; + if(scopeNameIndex != 0) { + completedToken = -Parser.reverse_index[scopeNameIndex]; + } + this.recoveryScanner.insertTokens(addedTokens, completedToken, errorEnd); + } + if (scopeNameIndex != 0) { - problemReporter().parseErrorInsertToComplete( + if(this.reportProblem) problemReporter().parseErrorInsertToComplete( errorStart, errorEnd, buf.toString(), Parser.readableName[scopeNameIndex]); } else { - problemReporter().parseErrorInsertToCompleteScope( + if(this.reportProblem) problemReporter().parseErrorInsertToCompleteScope( errorStart, errorEnd, buf.toString()); @@ -2163,31 +2274,57 @@ break; case EOF_CODE: - problemReporter().parseErrorUnexpectedEnd( + if(this.reportProblem) problemReporter().parseErrorUnexpectedEnd( errorStart, errorEnd); break; case MERGE_CODE: - problemReporter().parseErrorMergeTokens( + if(recoveryScanner != null) { + if(addedToken > -1) { + recoveryScanner.replaceTokens(addedToken, errorStart, errorEnd); + } else { + int[] template = getNTermTemplate(-addedToken); + if(template != null) { + recoveryScanner.replaceTokens(template, errorStart, errorEnd); + } + } + } + if(this.reportProblem) problemReporter().parseErrorMergeTokens( errorStart, errorEnd, name); break; case MISPLACED_CODE: - problemReporter().parseErrorMisplacedConstruct( + if(recoveryScanner != null) { + recoveryScanner.removeTokens(errorStart, errorEnd); + } + if(this.reportProblem) problemReporter().parseErrorMisplacedConstruct( errorStart, errorEnd); break; default: if (name.length() == 0) { - problemReporter().parseErrorNoSuggestion( + if(recoveryScanner != null) { + recoveryScanner.removeTokens(errorStart, errorEnd); + } + if(this.reportProblem) problemReporter().parseErrorNoSuggestion( errorStart, errorEnd, currentKind, errorTokenSource, errorTokenName); } else { - problemReporter().parseErrorReplaceToken( + if(recoveryScanner != null) { + if(addedToken > -1) { + recoveryScanner.replaceTokens(addedToken, errorStart, errorEnd); + } else { + int[] template = getNTermTemplate(-addedToken); + if(template != null) { + recoveryScanner.replaceTokens(template, errorStart, errorEnd); + } + } + } + if(this.reportProblem) problemReporter().parseErrorReplaceToken( errorStart, errorEnd, currentKind, @@ -2230,9 +2367,19 @@ } int errorEnd = lexStream.end(rightToken); + int addedToken = -1; + if(recoveryScanner != null) { + if (nameIndex >= 0) { + addedToken = Parser.reverse_index[nameIndex]; + } + } + switch(msgCode) { case MISPLACED_CODE: - problemReporter().parseErrorMisplacedConstruct( + if(recoveryScanner != null) { + recoveryScanner.removeTokens(errorStart, errorEnd); + } + if(this.reportProblem) problemReporter().parseErrorMisplacedConstruct( errorStart, errorEnd); break; @@ -2241,42 +2388,109 @@ errorStart = lexStream.start(rightToken); StringBuffer buf = new StringBuffer(); + + int[] addedTokens = null; + int addedTokenCount = 0; + if(this.recoveryScanner != null) { + addedTokens = new int[Parser.scope_rhs.length - Parser.scope_suffix[- nameIndex]]; + } + for (int i = Parser.scope_suffix[- nameIndex]; Parser.scope_rhs[i] != 0; i++) { + buf.append(Parser.readableName[Parser.scope_rhs[i]]); if (Parser.scope_rhs[i+1] != 0) buf.append(' '); + + if(addedTokens != null) { + int tmpAddedToken = Parser.reverse_index[Parser.scope_rhs[i]]; + if (tmpAddedToken > -1) { + int length = addedTokens.length; + if(addedTokenCount == length) { + System.arraycopy(addedTokens, 0, addedTokens = new int[length * 2], 0, length); + } + addedTokens[addedTokenCount++] = tmpAddedToken; + } else { + int[] template = getNTermTemplate(-tmpAddedToken); + if(template != null) { + for (int j = 0; j < template.length; j++) { + int length = addedTokens.length; + if(addedTokenCount == length) { + System.arraycopy(addedTokens, 0, addedTokens = new int[length * 2], 0, length); + } + addedTokens[addedTokenCount++] = template[j]; + } + } else { + addedTokenCount = 0; + addedTokens = null; + } + } + } + } + if(addedTokenCount > 0) { + System.arraycopy(addedTokens, 0, addedTokens = new int[addedTokenCount], 0, addedTokenCount); + int completedToken = -1; + if(scopeNameIndex != 0) { + completedToken = -Parser.reverse_index[scopeNameIndex]; + } + this.recoveryScanner.insertTokens(addedTokens, completedToken, errorEnd); } if (scopeNameIndex != 0) { - problemReporter().parseErrorInsertToComplete( + if(this.reportProblem) problemReporter().parseErrorInsertToComplete( errorStart, errorEnd, buf.toString(), Parser.readableName[scopeNameIndex]); } else { - problemReporter().parseErrorInsertToCompletePhrase( + if(this.reportProblem) problemReporter().parseErrorInsertToCompletePhrase( errorStart, errorEnd, buf.toString()); } break; case MERGE_CODE: - problemReporter().parseErrorMergeTokens( + if(recoveryScanner != null) { + if(addedToken > -1) { + recoveryScanner.replaceTokens(addedToken, errorStart, errorEnd); + } else { + int[] template = getNTermTemplate(-addedToken); + if(template != null) { + recoveryScanner.replaceTokens(template, errorStart, errorEnd); + } + } + } + if(this.reportProblem) problemReporter().parseErrorMergeTokens( errorStart, errorEnd, name); break; case DELETION_CODE: - problemReporter().parseErrorDeleteTokens( + if(recoveryScanner != null) { + recoveryScanner.removeTokens(errorStart, errorEnd); + } + if(this.reportProblem) problemReporter().parseErrorDeleteTokens( errorStart, errorEnd); break; default: if (name.length() == 0) { - problemReporter().parseErrorNoSuggestionForTokens( + if(recoveryScanner != null) { + recoveryScanner.removeTokens(errorStart, errorEnd); + } + if(this.reportProblem) problemReporter().parseErrorNoSuggestionForTokens( errorStart, errorEnd); } else { - problemReporter().parseErrorReplaceTokens( + if(recoveryScanner != null) { + if(addedToken > -1) { + recoveryScanner.replaceTokens(addedToken, errorStart, errorEnd); + } else { + int[] template = getNTermTemplate(-addedToken); + if(template != null) { + recoveryScanner.replaceTokens(template, errorStart, errorEnd); + } + } + } + if(this.reportProblem) problemReporter().parseErrorReplaceTokens( errorStart, errorEnd, name); @@ -2285,6 +2499,21 @@ return; } + private int[] getNTermTemplate(int sym) { + int templateIndex = Parser.recovery_templates_index[sym]; + if(templateIndex > 0) { + int[] result = new int[Parser.recovery_templates.length]; + int count = 0; + for(int j = templateIndex; Parser.recovery_templates[j] != 0; j++) { + result[count++] = Parser.recovery_templates[j]; + } + System.arraycopy(result, 0, result = new int[count], 0, count); + return result; + } else { + return null; + } + } + public String toString() { StringBuffer res = new StringBuffer(); Index: compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java,v retrieving revision 1.153 diff -u -r1.153 CompilerOptions.java --- compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java 12 Jan 2006 15:55:44 -0000 1.153 +++ compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java 17 Jan 2006 17:44:21 -0000 @@ -113,6 +113,9 @@ /* should surface ??? */ public static final String OPTION_PrivateConstructorAccess = "org.eclipse.jdt.core.compiler.codegen.constructorAccessEmulation"; //$NON-NLS-1$ + //TODO temporary option + public static final String OPTION_StatementsRecovery = "org.eclipse.jdt.core.compiler.statementsRecovery"; //$NON-NLS-1$ + /** * Possible values for configurable options */ @@ -187,7 +190,7 @@ public static final long RawTypeReference = ASTNode.Bit46L; public static final long UnusedLabel = ASTNode.Bit47L; public static final long ParameterAssignment = ASTNode.Bit48L; - + // Default severity level for handlers public long errorThreshold = 0; @@ -296,9 +299,12 @@ // treat optional error as fatal or just like warning? public boolean treatOptionalErrorAsFatal = true; + // parser perform statements recovery + public boolean performStatementsRecovery = true; + // store annotations public boolean storeAnnotations = false; - + /** * Initializing the compiler options with defaults */ @@ -726,6 +732,13 @@ this.reportMissingJavadocCommentsOverriding = false; } } + if ((optionValue = optionsMap.get(OPTION_StatementsRecovery)) != null) { + if (ENABLED.equals(optionValue)) { + this.performStatementsRecovery = true; + } else if (DISABLED.equals(optionValue)) { + this.performStatementsRecovery = false; + } + } } public String toString() { Index: model/org/eclipse/jdt/internal/core/JavaCorePreferenceInitializer.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaCorePreferenceInitializer.java,v retrieving revision 1.26 diff -u -r1.26 JavaCorePreferenceInitializer.java --- model/org/eclipse/jdt/internal/core/JavaCorePreferenceInitializer.java 13 Jan 2006 09:36:49 -0000 1.26 +++ model/org/eclipse/jdt/internal/core/JavaCorePreferenceInitializer.java 17 Jan 2006 17:44:48 -0000 @@ -43,7 +43,8 @@ defaultOptionsMap.put(JavaCore.COMPILER_TASK_CASE_SENSITIVE, JavaCore.ENABLED); defaultOptionsMap.put(JavaCore.COMPILER_DOC_COMMENT_SUPPORT, JavaCore.ENABLED); defaultOptionsMap.put(JavaCore.COMPILER_PB_FORBIDDEN_REFERENCE, JavaCore.ERROR); - + defaultOptionsMap.put(JavaCore.COMPILER_STATEMENTS_RECOVERY, JavaCore.DISABLED); //TODO It is a temporary option + // Builder settings defaultOptionsMap.put(JavaCore.CORE_JAVA_BUILD_RESOURCE_COPY_FILTER, ""); //$NON-NLS-1$ defaultOptionsMap.put(JavaCore.CORE_JAVA_BUILD_INVALID_CLASSPATH, JavaCore.ABORT); Index: compiler/org/eclipse/jdt/internal/compiler/CompilationResult.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/CompilationResult.java,v retrieving revision 1.48 diff -u -r1.48 CompilationResult.java --- compiler/org/eclipse/jdt/internal/compiler/CompilationResult.java 10 Jan 2006 21:01:06 -0000 1.48 +++ compiler/org/eclipse/jdt/internal/compiler/CompilationResult.java 17 Jan 2006 17:44:19 -0000 @@ -42,6 +42,7 @@ import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; +import org.eclipse.jdt.internal.compiler.parser.RecoveryScannerData; import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; public class CompilationResult { @@ -60,6 +61,7 @@ public boolean hasAnnotations = false; public int lineSeparatorPositions[]; + public RecoveryScannerData recoveryScannerData; public Map compiledTypes = new Hashtable(11); public int unitIndex, totalUnitsKnown; public boolean hasBeenAccepted = false; Index: compiler/org/eclipse/jdt/internal/compiler/parser/RecoveryScannerData.java =================================================================== RCS file: compiler/org/eclipse/jdt/internal/compiler/parser/RecoveryScannerData.java diff -N compiler/org/eclipse/jdt/internal/compiler/parser/RecoveryScannerData.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ compiler/org/eclipse/jdt/internal/compiler/parser/RecoveryScannerData.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,65 @@ +/** + * + */ +package org.eclipse.jdt.internal.compiler.parser; + +public class RecoveryScannerData { + public int insertedTokensPtr = -1; + public int[][] insertedTokens; + public int[] insertedTokensPosition; + public boolean[] insertedTokenUsed; + + public int replacedTokensPtr = -1; + public int[][] replacedTokens; + public int[] replacedTokensStart; + public int[] replacedTokensEnd; + public boolean[] replacedTokenUsed; + + public int removedTokensPtr = -1; + public int[] removedTokensStart; + public int[] removedTokensEnd; + public boolean[] removedTokenUsed; + + public RecoveryScannerData removeUnused() { + if(this.insertedTokens != null) { + int newInsertedTokensPtr = -1; + for (int i = 0; i <= this.insertedTokensPtr; i++) { + if(this.insertedTokenUsed[i]) { + newInsertedTokensPtr++; + this.insertedTokens[newInsertedTokensPtr] = this.insertedTokens[i]; + this.insertedTokensPosition[newInsertedTokensPtr] = this.insertedTokensPosition[i]; + this.insertedTokenUsed[newInsertedTokensPtr] = this.insertedTokenUsed[i]; + } + } + this.insertedTokensPtr = newInsertedTokensPtr; + } + + if(this.replacedTokens != null) { + int newReplacedTokensPtr = -1; + for (int i = 0; i <= this.replacedTokensPtr; i++) { + if(this.replacedTokenUsed[i]) { + newReplacedTokensPtr++; + this.replacedTokens[newReplacedTokensPtr] = this.replacedTokens[i]; + this.replacedTokensStart[newReplacedTokensPtr] = this.replacedTokensStart[i]; + this.replacedTokensEnd[newReplacedTokensPtr] = this.replacedTokensEnd[i]; + this.replacedTokenUsed[newReplacedTokensPtr] = this.replacedTokenUsed[i]; + } + } + this.replacedTokensPtr = newReplacedTokensPtr; + } + if(this.removedTokensStart != null) { + int newRemovedTokensPtr = -1; + for (int i = 0; i <= this.removedTokensPtr; i++) { + if(this.removedTokenUsed[i]) { + newRemovedTokensPtr++; + this.removedTokensStart[newRemovedTokensPtr] = this.removedTokensStart[i]; + this.removedTokensEnd[newRemovedTokensPtr] = this.removedTokensEnd[i]; + this.removedTokenUsed[newRemovedTokensPtr] = this.removedTokenUsed[i]; + } + } + this.removedTokensPtr = newRemovedTokensPtr; + } + + return this; + } +} Index: compiler/org/eclipse/jdt/internal/compiler/parser/RecoveryScanner.java =================================================================== RCS file: compiler/org/eclipse/jdt/internal/compiler/parser/RecoveryScanner.java diff -N compiler/org/eclipse/jdt/internal/compiler/parser/RecoveryScanner.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ compiler/org/eclipse/jdt/internal/compiler/parser/RecoveryScanner.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,186 @@ +package org.eclipse.jdt.internal.compiler.parser; + +import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.core.compiler.InvalidInputException; + +public class RecoveryScanner extends Scanner { + private RecoveryScannerData data; + + private int[] pendingTokens; + private int pendingTokensPtr = -1; + private char[] fakeTokenSource = null; + private int skipNextInsertedTokens = -1; + + public boolean record = true; + + public RecoveryScanner(Scanner scanner, RecoveryScannerData data) { + super(false, + scanner.tokenizeWhiteSpace, + scanner.checkNonExternalizedStringLiterals, + scanner.sourceLevel, + scanner.complianceLevel, + scanner.taskTags, + scanner.taskPriorities, + scanner.isTaskCaseSensitive); + this.setData(data); + } + + public void insertToken(int token, int completedToken, int position) { + insertTokens(new int []{token}, completedToken, position); + } + + private int[] reverse(int[] tokens) { + int length = tokens.length; + for(int i = 0, max = length / 2; i < max; i++) { + int tmp = tokens[i]; + tokens[i] = tokens[length - i - 1]; + tokens[length - i - 1] = tmp; + } + return tokens; + } + public void insertTokens(int[] tokens, int completedToken, int position) { + if(!this.record) return; + + if(completedToken > -1 && Parser.statements_recovery_filter[completedToken] != 0) return; + + this.data.insertedTokensPtr++; + if(this.data.insertedTokens == null) { + this.data.insertedTokens = new int[10][]; + this.data.insertedTokensPosition = new int[10]; + this.data.insertedTokenUsed = new boolean[10]; + } else if(this.data.insertedTokens.length == this.data.insertedTokensPtr) { + int length = this.data.insertedTokens.length; + System.arraycopy(this.data.insertedTokens, 0, this.data.insertedTokens = new int[length * 2][], 0, length); + System.arraycopy(this.data.insertedTokensPosition, 0, this.data.insertedTokensPosition = new int[length * 2], 0, length); + System.arraycopy(this.data.insertedTokenUsed, 0, this.data.insertedTokenUsed = new boolean[length * 2], 0, length); + } + this.data.insertedTokens[this.data.insertedTokensPtr] = reverse(tokens); + this.data.insertedTokensPosition[this.data.insertedTokensPtr] = position; + this.data.insertedTokenUsed[this.data.insertedTokensPtr] = false; + } + + public void replaceTokens(int token, int start, int end) { + replaceTokens(new int []{token}, start, end); + } + + public void replaceTokens(int[] tokens, int start, int end) { + if(!this.record) return; + this.data.replacedTokensPtr++; + if(this.data.replacedTokensStart == null) { + this.data.replacedTokens = new int[10][]; + this.data.replacedTokensStart = new int[10]; + this.data.replacedTokensEnd = new int[10]; + this.data.replacedTokenUsed= new boolean[10]; + } else if(this.data.replacedTokensStart.length == this.data.replacedTokensPtr) { + int length = this.data.replacedTokensStart.length; + System.arraycopy(this.data.replacedTokens, 0, this.data.replacedTokens = new int[length * 2][], 0, length); + System.arraycopy(this.data.replacedTokensStart, 0, this.data.replacedTokensStart = new int[length * 2], 0, length); + System.arraycopy(this.data.replacedTokensEnd, 0, this.data.replacedTokensEnd = new int[length * 2], 0, length); + System.arraycopy(this.data.replacedTokenUsed, 0, this.data.replacedTokenUsed = new boolean[length * 2], 0, length); + } + this.data.replacedTokens[this.data.replacedTokensPtr] = reverse(tokens); + this.data.replacedTokensStart[this.data.replacedTokensPtr] = start; + this.data.replacedTokensEnd[this.data.replacedTokensPtr] = end; + this.data.replacedTokenUsed[this.data.replacedTokensPtr] = false; + } + + public void removeTokens(int start, int end) { + if(!this.record) return; + this.data.removedTokensPtr++; + if(this.data.removedTokensStart == null) { + this.data.removedTokensStart = new int[10]; + this.data.removedTokensEnd = new int[10]; + this.data.removedTokenUsed = new boolean[10]; + } else if(this.data.removedTokensStart.length == this.data.removedTokensPtr) { + int length = this.data.removedTokensStart.length; + System.arraycopy(this.data.removedTokensStart, 0, this.data.removedTokensStart = new int[length * 2], 0, length); + System.arraycopy(this.data.removedTokensEnd, 0, this.data.removedTokensEnd = new int[length * 2], 0, length); + System.arraycopy(this.data.removedTokenUsed, 0, this.data.removedTokenUsed = new boolean[length * 2], 0, length); + } + this.data.removedTokensStart[this.data.removedTokensPtr] = start; + this.data.removedTokensEnd[this.data.removedTokensPtr] = end; + this.data.removedTokenUsed[this.data.removedTokensPtr] = false; + } + + public int getNextToken() throws InvalidInputException { + if(this.pendingTokensPtr > -1) { + return this.pendingTokens[this.pendingTokensPtr--]; + } + + this.fakeTokenSource = null; + + if(this.data.insertedTokens != null) { +// if(!skipNextInsertedTokens) { + for (int i = 0; i <= this.data.insertedTokensPtr; i++) { + if(this.data.insertedTokensPosition[i] == this.currentPosition - 1 && i > skipNextInsertedTokens) { + this.data.insertedTokenUsed[i] = true; + this.pendingTokens = this.data.insertedTokens[i]; + this.pendingTokensPtr = this.data.insertedTokens[i].length - 1; + this.fakeTokenSource = CharOperation.NO_CHAR; + this.startPosition = this.currentPosition - 1; + this.skipNextInsertedTokens = i; + return this.pendingTokens[this.pendingTokensPtr--]; + } + } +// } + this.skipNextInsertedTokens = -1; + } + + int previousLocation = this.currentPosition; + int currentToken = super.getNextToken(); + + if(this.data.replacedTokens != null) { + for (int i = 0; i <= this.data.replacedTokensPtr; i++) { + if(this.data.replacedTokensStart[i] >= previousLocation && + this.data.replacedTokensStart[i] <= this.startPosition && + this.data.replacedTokensEnd[i] >= this.currentPosition - 1) { + this.data.replacedTokenUsed[i] = true; + this.pendingTokens = this.data.replacedTokens[i]; + this.pendingTokensPtr = this.data.replacedTokens[i].length - 1; + this.fakeTokenSource = CharOperation.NO_CHAR; + this.currentPosition = this.data.replacedTokensEnd[i] + 1; + return this.pendingTokens[this.pendingTokensPtr--]; + } + } + } + if(this.data.removedTokensStart != null) { + for (int i = 0; i <= this.data.removedTokensPtr; i++) { + if(this.data.removedTokensStart[i] >= previousLocation && + this.data.removedTokensStart[i] <= this.startPosition && + this.data.removedTokensEnd[i] >= this.currentPosition - 1) { + this.data.removedTokenUsed[i] = true; + this.currentPosition = this.data.removedTokensEnd[i] + 1; + return getNextToken(); + } + } + } + return currentToken; + } + + public char[] getCurrentIdentifierSource() { + if(this.fakeTokenSource != null) return this.fakeTokenSource; + return super.getCurrentIdentifierSource(); + } + + public char[] getCurrentTokenSourceString() { + if(this.fakeTokenSource != null) return this.fakeTokenSource; + return super.getCurrentTokenSourceString(); + } + + public char[] getCurrentTokenSource() { + if(this.fakeTokenSource != null) return this.fakeTokenSource; + return super.getCurrentTokenSource(); + } + + public RecoveryScannerData getData() { + return this.data; + } + + public void setData(RecoveryScannerData data) { + if(data == null) { + this.data = new RecoveryScannerData(); + } else { + this.data = data; + } + } +} Index: dom/org/eclipse/jdt/core/dom/ASTRecoveryPropagator.java =================================================================== RCS file: dom/org/eclipse/jdt/core/dom/ASTRecoveryPropagator.java diff -N dom/org/eclipse/jdt/core/dom/ASTRecoveryPropagator.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ dom/org/eclipse/jdt/core/dom/ASTRecoveryPropagator.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2006 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 org.eclipse.jdt.core.compiler.IProblem; + +/** + * Internal AST visitor for propagating syntax errors. + */ +class ASTRecoveryPropagator extends DefaultASTVisitor { + private IProblem[] problems; + + ASTRecoveryPropagator(IProblem[] problems) { + // visit Javadoc.tags() as well + this.problems = problems; + } + + protected boolean visitNode(ASTNode node) { + return checkAndTagAsMalformed(node); + } + + private boolean checkAndTagAsMalformed(ASTNode node) { + boolean tagWithErrors = false; + search: for (int i = 0, max = this.problems.length; i < max; i++) { + IProblem problem = this.problems[i]; + 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: + continue search; + } + int problemStart = problem.getSourceStart(); + int problemEnd = problem.getSourceEnd(); + int start = node.getStartPosition(); + int end = start + node.getLength(); + if ((start <= problemStart) && (problemStart <= end) || + (start <= problemEnd) && (problemEnd <= end)) { + node.setFlags(node.getFlags() | ASTNode.RECOVERED); + tagWithErrors = true; + } + } + return tagWithErrors; + } +}