From 2ec6a92a98d534a5cde5723ff48ffae9b3d562e7 Mon Sep 17 00:00:00 2001 From: Markus Schorn Date: Wed, 23 Jun 2010 09:24:39 +0000 Subject: [PATCH] Bug 314593: Handling of missing semicolon after statement. --- .../tests/ast2/AST2SelectionParseTest.java | 9 --- .../tests/ast2/FaultToleranceTests.java | 23 +++++- .../parser/AbstractGNUSourceCodeParser.java | 74 +++++++++++++------ .../core/dom/parser/c/GNUCSourceParser.java | 13 +++- .../dom/parser/cpp/GNUCPPSourceParser.java | 11 ++- .../DOMAST/CPPPopulateASTViewAction.java | 22 +++--- 6 files changed, 98 insertions(+), 54 deletions(-) diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2SelectionParseTest.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2SelectionParseTest.java index f71c1f37682..9ec90bf1263 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2SelectionParseTest.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2SelectionParseTest.java @@ -101,15 +101,6 @@ public class AST2SelectionParseTest extends AST2SelectionParseBaseTest { assertEquals(((IFunction)name.resolveBinding()).getName(), "x"); //$NON-NLS-1$ } - public void testBaseCase_Error() throws Exception - { - String code = "int x() { y( ) }"; //$NON-NLS-1$ - int offset1 = code.indexOf( "y( " ); //$NON-NLS-1$ - int length = "y".length(); //$NON-NLS-1$ - assertNull( parse( code, ParserLanguage.C, offset1, length, false )); - assertNull( parse( code, ParserLanguage.CPP, offset1, length, false )); - } - public void testBaseCase_FunctionDeclaration() throws Exception { String code = "int x(); x( );"; //$NON-NLS-1$ diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/FaultToleranceTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/FaultToleranceTests.java index 46af1ad9e0c..ef2e04cccab 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/FaultToleranceTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/FaultToleranceTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2009 Wind River Systems, Inc. and others. + * Copyright (c) 2008, 2010 Wind River Systems, Inc. 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 @@ -21,6 +21,7 @@ import org.eclipse.cdt.core.dom.ast.IASTProblemExpression; import org.eclipse.cdt.core.dom.ast.IASTProblemStatement; import org.eclipse.cdt.core.dom.ast.IASTReturnStatement; import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTStatement; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTLinkageSpecification; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition; @@ -102,7 +103,25 @@ public class FaultToleranceTests extends AST2BaseTest { sdecl= getDeclaration(tu, 3); } } - + + // void f() { + // int a= 1 + // f() + // } + public void testExpressionWithoutSemi_314593() throws Exception { + final String comment= getAboveComment(); + for (ParserLanguage lang : ParserLanguage.values()) { + IASTTranslationUnit tu= parse(comment, lang, false, false); + IASTFunctionDefinition fdef= getDeclaration(tu, 0); + IASTStatement stmt= getStatement(fdef, 0); + assertEquals("int a= 1", stmt.getRawSignature()); + IASTProblemStatement pstmt= getStatement(fdef, 1); + stmt= getStatement(fdef, 2); + assertEquals("f()", stmt.getRawSignature()); + pstmt= getStatement(fdef, 3); + } + } + // struct X { // int a; public void testIncompleteCompositeType() throws Exception { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/AbstractGNUSourceCodeParser.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/AbstractGNUSourceCodeParser.java index 1d12c106982..02fa552fa6a 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/AbstractGNUSourceCodeParser.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/AbstractGNUSourceCodeParser.java @@ -852,9 +852,17 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser { result.addStatement(stmt); endOffset= calculateEndOffset(stmt); } catch (BacktrackException bt) { - IASTStatement stmt= skipProblemStatement(stmtOffset); - result.addStatement(stmt); - endOffset= calculateEndOffset(stmt); + final IASTNode beforeProblem = bt.getNodeBeforeProblem(); + final IASTProblem problem = bt.getProblem(); + if (problem != null && beforeProblem instanceof IASTStatement) { + result.addStatement((IASTStatement) beforeProblem); + result.addStatement(buildProblemStatement(problem)); + endOffset= calculateEndOffset(beforeProblem); + } else { + IASTStatement stmt= skipProblemStatement(stmtOffset); + result.addStatement(stmt); + endOffset= calculateEndOffset(stmt); + } } catch (EndOfFileException e) { IASTStatement stmt= skipProblemStatement(stmtOffset); result.addStatement(stmt); @@ -1809,21 +1817,24 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser { * This method will attempt to parse a statement as both an expression and a declaration, * if both parses succeed then an ambiguity node is returned. */ - protected IASTStatement parseDeclarationOrExpressionStatement(DeclarationOptions option) throws EndOfFileException, BacktrackException { + protected IASTStatement parseDeclarationOrExpressionStatement() throws EndOfFileException, BacktrackException { // First attempt to parse an expressionStatement // Note: the function style cast ambiguity is handled in expression // Since it only happens when we are in a statement IToken mark = mark(); IASTExpressionStatement expressionStatement = null; - IToken lastTokenOfExpression = null; + IToken afterExpression = null; + boolean foundSemicolon= false; try { IASTExpression expression = expression(); - if (LT(1) == IToken.tEOC) - lastTokenOfExpression = consume(); - else - lastTokenOfExpression = consume(IToken.tSEMI); expressionStatement = nodeFactory.newExpressionStatement(expression); - ((ASTNode) expressionStatement).setOffsetAndLength(mark.getOffset(), lastTokenOfExpression.getEndOffset() - mark.getOffset()); + setRange(expressionStatement, expression); + afterExpression= LA(); + + IToken semi= consumeOrEOC(IToken.tSEMI); + foundSemicolon= true; + adjustEndOffset(expressionStatement, semi.getEndOffset()); + afterExpression= LA(); } catch (BacktrackException b) { } @@ -1832,22 +1843,31 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser { // Now attempt to parse a declarationStatement IASTDeclarationStatement ds = null; try { - IASTDeclaration d = declaration(option); + IASTDeclaration d = declaration(DeclarationOptions.LOCAL); ds = nodeFactory.newDeclarationStatement(d); - ((ASTNode) ds).setOffsetAndLength(((ASTNode) d).getOffset(), ((ASTNode) d).getLength()); + setRange(ds, d); } catch (BacktrackException b) { if (expressionStatement == null) { - backup(mark); + IASTNode node = b.getNodeBeforeProblem(); + if (node instanceof IASTDeclaration) { + ds= nodeFactory.newDeclarationStatement((IASTDeclaration) node); + b.initialize(b.getProblem(), setRange(ds, node)); + } throw b; } } - if (expressionStatement == null) { - return ds; - } if (ds == null) { - backup(lastTokenOfExpression); consume(); - return expressionStatement; + backup(afterExpression); + if (foundSemicolon) + return expressionStatement; + + throwBacktrack(createProblem(IProblem.SYNTAX_ERROR, calculateEndOffset(expressionStatement), 0), expressionStatement); + return null; // Hint for java-compiler + } + + if (expressionStatement == null || !foundSemicolon) { + return ds; } // At this point we know we have an ambiguity. @@ -1879,7 +1899,7 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser { // x; // can be parsed as a named declaration specifier without a declarator if (declarators.length == 0) { - backup(lastTokenOfExpression); consume(); + backup(afterExpression); return expressionStatement; } } @@ -1889,8 +1909,7 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser { IASTAmbiguousStatement statement = createAmbiguousStatement(); statement.addStatement(expressionStatement); statement.addStatement(ds); - ((ASTNode) statement).setOffsetAndLength((ASTNode) ds); - return statement; + return setRange(statement, ds); } @@ -2259,10 +2278,19 @@ public abstract class AbstractGNUSourceCodeParser implements ISourceCodeParser { protected abstract IASTAmbiguousExpression createAmbiguousBinaryVsCastExpression(IASTBinaryExpression binary, IASTCastExpression castExpr); protected abstract IASTAmbiguousExpression createAmbiguousCastVsFunctionCallExpression(IASTCastExpression castExpr, IASTFunctionCallExpression funcCall); - protected IASTStatement forInitStatement(DeclarationOptions option) throws BacktrackException, EndOfFileException { + protected IASTStatement forInitStatement() throws BacktrackException, EndOfFileException { if( LT(1) == IToken.tSEMI ) return parseNullStatement(); - return parseDeclarationOrExpressionStatement(option); + try { + return parseDeclarationOrExpressionStatement(); + } catch (BacktrackException e) { + // Missing semicolon within for loop does not make a complete for-statement + IASTNode before = e.getNodeBeforeProblem(); + if (before != null) { + e.initialize(e.getProblem()); + } + throw e; + } } /** diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/c/GNUCSourceParser.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/c/GNUCSourceParser.java index d7a750bc41b..dd2434feac1 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/c/GNUCSourceParser.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/c/GNUCSourceParser.java @@ -357,8 +357,13 @@ public class GNUCSourceParser extends AbstractGNUSourceCodeParser { return functionDefinition(firstOffset, declSpec, declarators); default: - if (declOption != DeclarationOptions.LOCAL) { - insertSemi= true; + insertSemi= true; + if (declOption == DeclarationOptions.LOCAL) { + endOffset= figureEndOffset(declSpec, declarators); + if (firstOffset != endOffset) { + break; + } + } else { if (markBeforDtor != null) { endOffset= calculateEndOffset(declSpec); if (firstOffset != endOffset && !isOnSameLine(endOffset, markBeforDtor.getOffset())) { @@ -1830,7 +1835,7 @@ public class GNUCSourceParser extends AbstractGNUSourceCodeParser { return parseLabelStatement(); } - return parseDeclarationOrExpressionStatement(DeclarationOptions.LOCAL); + return parseDeclarationOrExpressionStatement(); } } @@ -2075,7 +2080,7 @@ public class GNUCSourceParser extends AbstractGNUSourceCodeParser { int startOffset; startOffset = consume().getOffset(); consume(IToken.tLPAREN); - IASTStatement init = forInitStatement(DeclarationOptions.LOCAL); + IASTStatement init = forInitStatement(); IASTExpression for_condition = null; switch (LT(1)) { case IToken.tSEMI: diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java index 9cca528d573..7b8aba2bfaa 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java @@ -1972,8 +1972,11 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { return functionDefinition(firstOffset, declSpec, dtor); default: - if (declOption != DeclarationOptions.LOCAL) { - insertSemi= true; + insertSemi= true; + if (declOption == DeclarationOptions.LOCAL) { + endOffset= figureEndOffset(declSpec, declarators); + break; + } else { if (isLegalWithoutDtor(declSpec) && markBeforDtor != null && !isOnSameLine(calculateEndOffset(declSpec), markBeforDtor.getOffset())) { backup(markBeforDtor); declarators= IASTDeclarator.EMPTY_DECLARATOR_ARRAY; @@ -3851,7 +3854,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { return parseLabelStatement(); } - return parseDeclarationOrExpressionStatement(DeclarationOptions.LOCAL); + return parseDeclarationOrExpressionStatement(); } } @@ -4104,7 +4107,7 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { int startOffset; startOffset = consume().getOffset(); consume(IToken.tLPAREN); - IASTStatement init = forInitStatement(DeclarationOptions.LOCAL); + IASTStatement init = forInitStatement(); IASTNode for_condition = null; switch (LT(1)) { case IToken.tSEMI: diff --git a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/CPPPopulateASTViewAction.java b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/CPPPopulateASTViewAction.java index 4e84faa877d..7be0018be2f 100644 --- a/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/CPPPopulateASTViewAction.java +++ b/core/org.eclipse.cdt.ui.tests/src/org/eclipse/cdt/ui/tests/DOMAST/CPPPopulateASTViewAction.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2008 IBM Corporation and others. + * Copyright (c) 2005, 2010 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 @@ -15,6 +15,7 @@ import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; import org.eclipse.cdt.core.dom.ast.IASTDeclaration; import org.eclipse.cdt.core.dom.ast.IASTDeclarator; +import org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier.IASTEnumerator; import org.eclipse.cdt.core.dom.ast.IASTExpression; import org.eclipse.cdt.core.dom.ast.IASTInitializer; import org.eclipse.cdt.core.dom.ast.IASTName; @@ -29,11 +30,10 @@ import org.eclipse.cdt.core.dom.ast.IASTProblemHolder; import org.eclipse.cdt.core.dom.ast.IASTStatement; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.IASTTypeId; -import org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier.IASTEnumerator; import org.eclipse.cdt.core.dom.ast.cpp.CPPASTVisitor; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier.ICPPASTBaseSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateParameter; -import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier.ICPPASTBaseSpecifier; import org.eclipse.cdt.core.parser.util.ArrayUtil; import org.eclipse.cdt.internal.core.dom.parser.ASTNode; @@ -87,7 +87,7 @@ public class CPPPopulateASTViewAction extends CPPASTVisitor implements IPopulate if (node == null) return new DOMASTNodeLeafContinue(null); // only do length check for ASTNode (getNodeLocations on PreprocessorStatements is very expensive) - if (node instanceof ASTNode && ((ASTNode)node).getLength() <= 0) + if (node instanceof ASTNode && ((ASTNode)node).getLength() <= 0 && !(node instanceof IASTProblemHolder)) return new DOMASTNodeLeafContinue(null); DOMASTNodeParent parent = null; @@ -153,8 +153,8 @@ public class CPPPopulateASTViewAction extends CPPASTVisitor implements IPopulate DOMASTNodeLeaf temp = addRoot(declarator); IASTPointerOperator[] ops = declarator.getPointerOperators(); - for(int i=0; i