From 83ea511e247e130e3a449ab3be005f3e8133dcc6 Mon Sep 17 00:00:00 2001 From: Alena Laskavaia Date: Wed, 1 Dec 2010 02:36:00 +0000 Subject: [PATCH] Bug 329497 - checker for no break at the end of case --- .../OSGI-INF/l10n/bundle.properties | 5 +- .../org.eclipse.cdt.codan.checkers/plugin.xml | 14 + .../internal/checkers/CaseBreakChecker.java | 306 +++++++++++++ .../internal/checkers/CheckersMessages.java | 3 + .../internal/checkers/messages.properties | 3 + .../cdt/codan/core/cxx/CxxAstUtils.java | 28 ++ .../checkers/CaseBreakCheckerTest.java | 429 ++++++++++++++++++ .../core/test/AutomatedIntegrationSuite.java | 2 + .../cdt/codan/core/test/CheckerTestCase.java | 15 +- 9 files changed, 800 insertions(+), 5 deletions(-) create mode 100644 codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CaseBreakChecker.java create mode 100644 codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/internal/checkers/CaseBreakCheckerTest.java diff --git a/codan/org.eclipse.cdt.codan.checkers/OSGI-INF/l10n/bundle.properties b/codan/org.eclipse.cdt.codan.checkers/OSGI-INF/l10n/bundle.properties index f81a2b13c43..d7bf563eb14 100644 --- a/codan/org.eclipse.cdt.codan.checkers/OSGI-INF/l10n/bundle.properties +++ b/codan/org.eclipse.cdt.codan.checkers/OSGI-INF/l10n/bundle.properties @@ -56,4 +56,7 @@ problem.description.ReturnStyle = Checks for return statements that do no return checker.name.SuspiciousSemicolon = Suspicious semicolon problem.name.SuspiciousSemicolon = Suspicious semicolon problem.messagePattern.SuspiciousSemicolon = Suspicious semicolon -problem.description.SuspiciousSemicolon = A semicolon is used as a null statement in a condition. For example, 'if(expression);' \ No newline at end of file +problem.description.SuspiciousSemicolon = A semicolon is used as a null statement in a condition. For example, 'if(expression);' +checker.name.CaseBreak = No break at end of case +problem.description.CaseBreak = Looks for "case" statements which end without a "break" statement statement statement +problem.messagePattern.CaseBreak = No break at the end of this case \ No newline at end of file diff --git a/codan/org.eclipse.cdt.codan.checkers/plugin.xml b/codan/org.eclipse.cdt.codan.checkers/plugin.xml index ab87727a4e6..cbcc6dd3241 100644 --- a/codan/org.eclipse.cdt.codan.checkers/plugin.xml +++ b/codan/org.eclipse.cdt.codan.checkers/plugin.xml @@ -306,5 +306,19 @@ name="%problem.name.SuspiciousSemicolon"> + + + + diff --git a/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CaseBreakChecker.java b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CaseBreakChecker.java new file mode 100644 index 00000000000..7f89ca9f1fc --- /dev/null +++ b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CaseBreakChecker.java @@ -0,0 +1,306 @@ +/******************************************************************************* + * Copyright (c) 2010 Gil Barash + * 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: + * Gil Barash - Initial implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.internal.checkers; + +import org.eclipse.cdt.codan.core.cxx.CxxAstUtils; +import org.eclipse.cdt.codan.core.cxx.model.AbstractIndexAstChecker; +import org.eclipse.cdt.codan.core.model.ICheckerWithPreferences; +import org.eclipse.cdt.codan.core.model.IProblemWorkingCopy; +import org.eclipse.cdt.core.dom.ast.ASTVisitor; +import org.eclipse.cdt.core.dom.ast.IASTBreakStatement; +import org.eclipse.cdt.core.dom.ast.IASTCaseStatement; +import org.eclipse.cdt.core.dom.ast.IASTComment; +import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement; +import org.eclipse.cdt.core.dom.ast.IASTContinueStatement; +import org.eclipse.cdt.core.dom.ast.IASTDefaultStatement; +import org.eclipse.cdt.core.dom.ast.IASTGotoStatement; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTReturnStatement; +import org.eclipse.cdt.core.dom.ast.IASTStatement; +import org.eclipse.cdt.core.dom.ast.IASTSwitchStatement; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; + +public class CaseBreakChecker extends AbstractIndexAstChecker implements + ICheckerWithPreferences { + public static final String ER_ID = "org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem"; //$NON-NLS-1$ + public static final String PARAM_LAST_CASE = "last_case_param"; //$NON-NLS-1$ + public static final String PARAM_EMPTY_CASE = "empty_case_param"; //$NON-NLS-1$ + public static final String PARAM_NO_BREAK_COMMENT = "no_break_comment"; //$NON-NLS-1$ + public static final String DEFAULT_NO_BREAK_COMMENT = "no break"; //$NON-NLS-1$ + private CommentsIterator _commentsIt; // Iterator over comments + private Boolean _checkLastCase; // Should we check the last case in the switch? + private Boolean _checkEmptyCase; // Should we check an empty case (a case without any statements within it) + private String _noBreakComment; // The comment suppressing this warning + + /** + * This class receives the comments of the AST and iterates over them + */ + class CommentsIterator { + private int _next; // The next comment's index + private IASTComment[] _comments; + + CommentsIterator(IASTComment[] comments) { + _comments = comments; + _next = 0; + } + + /** + * @return Is there an unvisited comment? + */ + public boolean hasNext() { + return (_next < _comments.length); + } + + /** + * @return The next comment (doesn't automatically advance to the next + * comment. + * i.e. Calling this function twice may return the same value). + * See {@link#advance} + */ + public IASTComment getNext() { + return (_comments[_next]); + } + + /** + * @param node The node to compare the comment's location to + * @return Is the next comment located after 'node' + */ + public boolean isNextAfterThis(IASTNode node) { + return (_comments[_next].getFileLocation().getNodeOffset() > node + .getFileLocation().getNodeOffset()); + } + + /** + * @param node The node to compare the comment's location to + * @return Is the next comment located after 'node' ends + */ + public boolean isNextAfterThisEnds(IASTNode node) { + return (_comments[_next].getFileLocation().getNodeOffset() > node + .getFileLocation().getNodeOffset() + + node.getFileLocation().getNodeLength()); + } + + /** + * Advance to the next comment + */ + public void advance() { + _next++; + } + } + + /** + * This visitor looks for "switch" statements and invokes "SwitchVisitor" on + * them + */ + class SwitchFindingVisitor extends ASTVisitor { + protected IASTStatement _switchStatement; // The "switch" we're visiting (used by inheriting classes to avoid re-visiting the same "switch" again) + + SwitchFindingVisitor() { + shouldVisitStatements = true; + _switchStatement = null; + } + + @Override + public int visit(IASTStatement statement) { + if (statement instanceof IASTSwitchStatement) { + // Are we already visiting this statement? + if (_switchStatement == null + || !statement.equals(_switchStatement)) { + SwitchVisitor switch_visitor = new SwitchVisitor(statement); + statement.accept(switch_visitor); + return PROCESS_SKIP; + } + } + return PROCESS_CONTINUE; + } + } + + /** + * This visitor visits a switch statement and checks the end of each + * "case" statement (to see that it ends with a "break"). + * Because it extends SwitchFindingVisitor is would also check nested + * "switch"s + */ + class SwitchVisitor extends SwitchFindingVisitor { + private IASTStatement _last_case_stmnt; + private boolean _first_case_statement; + private int _last_break_stmnt_offset; + private int _last_normal_stmnt_offset; + private int _last_case_stmnt_offset; + + SwitchVisitor(IASTStatement switch_statement) { + shouldVisitStatements = true; + _first_case_statement = true; + _switchStatement = switch_statement; + _last_break_stmnt_offset = 0; + _last_normal_stmnt_offset = 0; + _last_case_stmnt_offset = 0; + _last_case_stmnt = null; + } + + /** + * @return Is this an "empty" case (i.e. with no statements in it) + */ + private boolean isEmptyCase() { + return _last_case_stmnt_offset > _last_normal_stmnt_offset; + } + + /** + * @return Was a "break" statement the last statement in this case + */ + private boolean breakFoundLast() { + return _last_normal_stmnt_offset < _last_break_stmnt_offset + && _last_case_stmnt_offset < _last_break_stmnt_offset; + } + + /** + * Check the last case we've visited + * + * @param comment The comment ending this case (may be NULL) + */ + private void checkLastCase(IASTComment comment) { + if (comment != null) { + String str = new String(comment.getComment()); + if (comment.isBlockComment()) + str = str.substring(2, str.length() - 2); + else + str = str.substring(2); + str = str.trim(); + if (str.equalsIgnoreCase(_noBreakComment)) + return; + } + if (_last_case_stmnt == null) + return; // This is an empty switch + if (breakFoundLast()) + return; // There was a "break" before the current statement + if (!isEmptyCase() || _checkEmptyCase) { + reportProblem(ER_ID, _last_case_stmnt, (Object) null); + } + } + + @Override + public int visit(IASTStatement statement) { + if (statement instanceof IASTCaseStatement + || statement instanceof IASTDefaultStatement) { + if (_first_case_statement) { + /* + * This is the first "case", i.e. the beginning of the + * "switch" + */ + _first_case_statement = false; + } else { + /* + * This is not the 1st "case", meaning that a previous case + * has just ended, + * Let's check that case and see how it ended... + */ + IASTComment comment = null; + // Do we have a comment which is before this "case" statement (but after the previous statement)? + while (_commentsIt.hasNext() + && !_commentsIt.isNextAfterThis(statement)) { + comment = _commentsIt.getNext(); + _commentsIt.advance(); + } + /* + * 'comment' is the last comment found in this case (after + * the last statement in this "case" + */ + checkLastCase(comment); + } + /* Update variables with the new opened "case" */ + _last_case_stmnt_offset = statement.getFileLocation() + .getNodeOffset(); + _last_case_stmnt = statement; + } else if (isBreakOrExitStatement(statement)) { // A "break" statement + _last_break_stmnt_offset = statement.getFileLocation() + .getNodeOffset(); + } else { // a non-switch related statement + _last_normal_stmnt_offset = statement.getFileLocation() + .getNodeOffset(); + } + /* advance comments we already passed */ + while (_commentsIt.hasNext() + && !_commentsIt.isNextAfterThis(statement)) + _commentsIt.advance(); + return super.visit(statement); // This would handle nested "switch"s + } + + @Override + public int leave(IASTStatement statement) { + /* + * Are we leaving the "switch" altogether? (we need to see how the + * last "case" ended) + */ + if (_checkLastCase && statement instanceof IASTCompoundStatement + && statement.getParent() == _switchStatement) { + IASTComment comment = null; + // is "Next" still in the switch's scope? if it is it was after the last statement + while (_commentsIt.hasNext() + && !_commentsIt.isNextAfterThisEnds(statement)) { + comment = _commentsIt.getNext(); + _commentsIt.advance(); + } + /* + * 'comment' is the last comment found in this case (after the + * last statement in this "case" + */ + checkLastCase(comment); + } + return super.leave(statement); + } + } + + /************************************************ + * This class's functions... + ************************************************/ + public CaseBreakChecker() { + } + + /** + * @param statement + * @return + */ + public boolean isBreakOrExitStatement(IASTStatement statement) { + CxxAstUtils utils = CxxAstUtils.getInstance(); + return statement instanceof IASTBreakStatement + || statement instanceof IASTReturnStatement + || statement instanceof IASTContinueStatement + || statement instanceof IASTGotoStatement + || utils.isThrowStatement(statement) || utils.isExitStatement(statement); + } + + public void initPreferences(IProblemWorkingCopy problem) { + super.initPreferences(problem); + addPreference( + problem, + PARAM_NO_BREAK_COMMENT, + CheckersMessages.CaseBreakChecker_DefaultNoBreakCommentDescription, + DEFAULT_NO_BREAK_COMMENT); + addPreference(problem, PARAM_EMPTY_CASE, + CheckersMessages.CaseBreakChecker_EmptyCaseDescription, + Boolean.TRUE); + addPreference(problem, PARAM_LAST_CASE, + CheckersMessages.CaseBreakChecker_LastCaseDescription, + Boolean.TRUE); + } + + public void processAst(IASTTranslationUnit ast) { + _checkLastCase = (Boolean) getPreference( + getProblemById(ER_ID, getFile()), PARAM_LAST_CASE); + _checkEmptyCase = (Boolean) getPreference( + getProblemById(ER_ID, getFile()), PARAM_EMPTY_CASE); + _noBreakComment = (String) getPreference( + getProblemById(ER_ID, getFile()), PARAM_NO_BREAK_COMMENT); + SwitchFindingVisitor visitor = new SwitchFindingVisitor(); + _commentsIt = new CommentsIterator(ast.getComments()); + ast.accept(visitor); + } +} diff --git a/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CheckersMessages.java b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CheckersMessages.java index 0ea327df271..1990b4f024c 100644 --- a/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CheckersMessages.java +++ b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CheckersMessages.java @@ -17,6 +17,9 @@ import org.eclipse.osgi.util.NLS; */ public class CheckersMessages extends NLS { private static final String BUNDLE_NAME = "org.eclipse.cdt.codan.internal.checkers.messages"; //$NON-NLS-1$ + public static String CaseBreakChecker_DefaultNoBreakCommentDescription; + public static String CaseBreakChecker_EmptyCaseDescription; + public static String CaseBreakChecker_LastCaseDescription; public static String CatchByReference_ReportForUnknownType; public static String NamingConventionFunctionChecker_LabelNamePattern; public static String ReturnChecker_Param0; diff --git a/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/messages.properties b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/messages.properties index 3562b8fc74f..849783659f1 100644 --- a/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/messages.properties +++ b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/messages.properties @@ -8,6 +8,9 @@ # Contributors: # Alena Laskavaia - initial API and implementation ############################################################################### +CaseBreakChecker_DefaultNoBreakCommentDescription=Comment text to suppress the problem (regular expression): +CaseBreakChecker_EmptyCaseDescription=Check also empty case statement +CaseBreakChecker_LastCaseDescription=Check also the last case statement CatchByReference_ReportForUnknownType=Report a problem if type cannot be resolved NamingConventionFunctionChecker_LabelNamePattern=Name Pattern ReturnChecker_Param0=Also check functions with implicit return value diff --git a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/CxxAstUtils.java b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/CxxAstUtils.java index f890b27d587..c615e92c2ae 100644 --- a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/CxxAstUtils.java +++ b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/CxxAstUtils.java @@ -22,6 +22,7 @@ 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.IASTExpression; +import org.eclipse.cdt.core.dom.ast.IASTExpressionStatement; import org.eclipse.cdt.core.dom.ast.IASTFileLocation; import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression; import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator; @@ -35,6 +36,7 @@ import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroExpansion; import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclSpecifier; import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; import org.eclipse.cdt.core.dom.ast.IASTStatement; +import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression; import org.eclipse.cdt.core.dom.ast.IBasicType.Kind; import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.IFunction; @@ -408,4 +410,30 @@ public final class CxxAstUtils { }); return returnSpecifier[0]; } + + /** + * @param body + * @return + */ + public boolean isThrowStatement(IASTNode body) { + if (!(body instanceof IASTExpressionStatement)) + return false; + IASTExpression expression = ((IASTExpressionStatement) body) + .getExpression(); + if (!(expression instanceof IASTUnaryExpression)) + return false; + return ((IASTUnaryExpression) expression).getOperator() == IASTUnaryExpression.op_throw; + } + + public boolean isExitStatement(IASTNode body) { + if (!(body instanceof IASTExpressionStatement)) + return false; + IASTExpression expression = ((IASTExpressionStatement) body) + .getExpression(); + if (!(expression instanceof IASTFunctionCallExpression)) + return false; + IASTExpression functionNameExpression = ((IASTFunctionCallExpression) expression) + .getFunctionNameExpression(); + return functionNameExpression.getRawSignature().equals("exit"); //$NON-NLS-1$ + } } diff --git a/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/internal/checkers/CaseBreakCheckerTest.java b/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/internal/checkers/CaseBreakCheckerTest.java new file mode 100644 index 00000000000..e106710abe2 --- /dev/null +++ b/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/internal/checkers/CaseBreakCheckerTest.java @@ -0,0 +1,429 @@ +/******************************************************************************* + * Copyright (c) 2010 Gil Barash + * 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: + * Gil Barash - Initial implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.core.internal.checkers; + +import org.eclipse.cdt.codan.core.param.IProblemPreference; +import org.eclipse.cdt.codan.core.test.CheckerTestCase; +import org.eclipse.cdt.codan.internal.checkers.CaseBreakChecker; + +/** + * Test for {@link#CaseBreakChecker} class + */ +public class CaseBreakCheckerTest extends CheckerTestCase { + // void foo(void) { + // int a; + // switch( a ) { + // case 1: + // } + // } + public void testEmptyLastCaseBad() { + loadCodeAndRun(getAboveComment()); + checkErrorLines(4); + } + + // void foo(void) { + // int a; + // switch( a ) { + // default: + // } + // } + public void testEmptyLastCaseDefaultBad() { + loadCodeAndRun(getAboveComment()); + checkErrorLines(4); + } + + // void foo(void) { + // int a, b; + // switch( a ) { + // case 1: + // b = 2; + // } + // } + public void testLastCaseBad() { + loadCodeAndRun(getAboveComment()); + checkErrorLines(4); + } + + // void foo(void) { + // int a, b; + // switch( a ) { + // case 1: + // case 2: + // b = 2; + // break; + // } + // } + public void testEmptyCaseBad() { + loadCodeAndRun(getAboveComment()); + checkErrorLines(4); + } + + // void foo(void) { + // int a; + // switch( a ) { + // case 1: + // break; + // } + // } + public void testEmptyLastCaseOKbreak() { + loadCodeAndRun(getAboveComment()); + checkNoErrors(); + } + + // void foo(void) { + // int a; + // switch( a ) { + // case 1: + // return; + // } + // } + public void testEmptyLastCaseWithReturn() { + loadCodeAndRun(getAboveComment()); + checkNoErrors(); + } + + // void foo(int a) { + // while (a--) + // switch( a ) { + // case 1: + // continue; + // } + // } + public void testEmptyLastCaseWithContinue() { + loadCodeAndRun(getAboveComment()); + checkNoErrors(); + } + + // void foo(int a) { + // + // switch( a ) { + // case 1: + // throw 1; + // } + // } + public void testEmptyLastCaseWithThrow() { + loadCodeAndRunCpp(getAboveComment()); + checkNoErrors(); + } + + // void foo(void) { + // int a, b; + // switch( a ) { + // case 1: + // b = 2; + // break; + // } + // } + public void testLastCaseOKbreak() { + loadCodeAndRun(getAboveComment()); + checkNoErrors(); + } + + // void foo(void) { + // int a, b; + // switch( a ) { + // case 1: + // break; + // case 2: + // b = 2; + // break; + // } + // } + public void testEmptyCaseOKbreak() { + loadCodeAndRun(getAboveComment()); + checkNoErrors(); + } + + // void foo(void) { + // int a; + // switch( a ) { + // case 1: + // /* no break */ + // } + // } + public void testEmptyLastCaseOKcomment() { + loadCodeAndRun(getAboveComment()); + checkNoErrors(); + } + + // void foo(int a, int b) { + // switch( a ) { + // case 1: + // switch (b) { + // case 2: + // break; + // } + // case 2: + // break; + // } + // } + public void testEmptyLastCaseTwoSwitches() { + loadCodeAndRun(getAboveComment()); + checkErrorLines(3); + } + + // void foo(void) { + // int a, b; + // switch( a ) { + // case 1: + // b = 2; + // /* no break */ + // } + // } + public void testLastCaseOKcomment() { + loadCodeAndRun(getAboveComment()); + checkNoErrors(); + } + + // void foo(void) { + // int a, b; + // switch( a ) { + // case 1: + // /* no break */ + // case 2: + // b = 2; + // break; + // } + public void testEmptyCaseOKcomment() { + loadCodeAndRun(getAboveComment()); + checkNoErrors(); + } + + // void foo(void) { + // int a, b; + // switch( a ) { + // case 1: + // b = 2; + // /* no break */ + // bye(); + // } + // } + public void testLastCaseBadCommentNotLast() { + loadCodeAndRun(getAboveComment()); + checkErrorLines(4); + } + + // void foo(void) { + // int a, b; + // switch( a ) { + // case 1: + // b = 2; + // /* no break */ + // case 2: + // b = 2; + // /*no break*/ + // case 3: + // b = 2; + // //no break + // case 4: + // b = 2; + // // no break + // case 5: + // b = 2; + // /* no brea */ + // case 6: + // b = 2; + // /* no break1 */ + // } + // } + public void testDifferentComments() { + loadCodeAndRun(getAboveComment()); + checkErrorLines(16, 19); + } + + // void foo(void) { + // int a, b; + // switch( a ) { + // case 1: + // // lolo + // case 2: + // case 3: + // } + // + // switch( a ) { + // case 1: + // b = 2; + // // lolo + // case 2: + // b = 2; + // case 3: + // case 4: + // break; + // case 5: + // case 6: + // } + // + // switch( a ) { + // case 1: + // b = 2; + // // lolo + // case 2: + // b = 2; + // case 3: + // b = 2; + // /* no break */ + // case 4: + // b = 2; + // case 5: + // b = 2; + // break; + // case 6: + // b = 2; + // /* no break */ + // b = 2; + // case 7: + // b = 2; + // } + // + // switch( a ) { + // case 1: + // b = 2; + // // lolo + // case 2: + // b = 2; + // default: + // } + // } + public void testGeneral1() { + loadCodeAndRun(getAboveComment()); + checkErrorLines(4, 6, 7, 11, 14, 16, 19, 20, 24, 27, 32, 37, 41, 46, + 49, 51); + } + + // void foo(void) { + // int a, b; + // switch( a ) { + // case 1: + // b = 2; + // // lolo + // /* no break */ + // case 2: + // b = 2; + // /* no break */ + // // lolo + // case 3: + // /* no break */ + // b = 2; + // // loo + // case 4: + // b = 2; + // // lolo + // /* no break */ + // case 5: + // // lolo + // b = 2; + // /* no break */ + // } + // } + public void testGeneralComments1() { + loadCodeAndRun(getAboveComment()); + checkErrorLines(8, 12); + } + + // void foo(void) { + // int a, b; + // switch( a ) { + // case 0: + // switch( b ) { + // case 2: + // } + // + // case 1: + // switch( b ) { + // case 2: + // break; + // } + // case 3: + // switch( b ) { + // case 2: + // break; + // } + // break; + // case 4: + // switch( b ) { + // case 2: + // /* no break */ + // } + // case 5: + // switch( b ) { + // case 2: + // } + // /* no break */ + // } + // } + public void testNestedSwitches() { + loadCodeAndRun(getAboveComment()); + checkErrorLines(4, 6, 9, 20, 27); + } + + // void foo(void) { + // int a, b; + // switch( a ) { + // case 1: + // b = 2; + // } + // } + public void testLastCaseIgnore() { + setLast(false); + loadCodeAndRun(getAboveComment()); + checkNoErrors(); + setLast(true); + } + + // void foo(void) { + // int a, b; + // switch( a ) { + // case 1: + // case 2: + // b = 2; + // break; + // case 3: case 4: + // b = 2; + // break; + // } + // } + public void testEmptyCaseIgnore() { + setEmpty(false); + loadCodeAndRun(getAboveComment()); + checkNoErrors(); + setEmpty(true); + } + + // void foo(void) { + // int a; + // switch( a ) { + // case 1: + // } + // } + public void testEmptyLastCaseIgnore() { + String code = getAboveComment(); + setLast(false); + loadCodeAndRun(code); + checkNoErrors(); + setLast(true); + setEmpty(false); + loadCodeAndRun(code); + checkNoErrors(); + setEmpty(true); + } + + private void setLast(boolean val) { + IProblemPreference pref = getPreference(CaseBreakChecker.ER_ID, + CaseBreakChecker.PARAM_LAST_CASE); + pref.setValue(val); + } + + private void setEmpty(boolean val) { + IProblemPreference pref = getPreference(CaseBreakChecker.ER_ID, + CaseBreakChecker.PARAM_EMPTY_CASE); + pref.setValue(val); + } +} diff --git a/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/test/AutomatedIntegrationSuite.java b/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/test/AutomatedIntegrationSuite.java index 119eece8c9c..87be458f380 100644 --- a/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/test/AutomatedIntegrationSuite.java +++ b/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/test/AutomatedIntegrationSuite.java @@ -16,6 +16,7 @@ import junit.framework.TestSuite; import org.eclipse.cdt.codan.core.internal.checkers.AssignmentInConditionCheckerTest; import org.eclipse.cdt.codan.core.internal.checkers.AssignmentToItselfCheckerTest; +import org.eclipse.cdt.codan.core.internal.checkers.CaseBreakCheckerTest; import org.eclipse.cdt.codan.core.internal.checkers.CatchByReferenceTest; import org.eclipse.cdt.codan.core.internal.checkers.ReturnCheckerTest; import org.eclipse.cdt.codan.core.internal.checkers.ReturnStyleCheckerTest; @@ -51,6 +52,7 @@ public class AutomatedIntegrationSuite extends TestSuite { suite.addTestSuite(AssignmentToItselfCheckerTest.class); suite.addTestSuite(ReturnStyleCheckerTest.class); suite.addTestSuite(SuspiciousSemicolonCheckerTest.class); + suite.addTestSuite(CaseBreakCheckerTest.class); // framework suite.addTest(CodanFastTestSuite.suite()); // quick fixes diff --git a/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/test/CheckerTestCase.java b/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/test/CheckerTestCase.java index 5c4bca22a13..dca61786276 100644 --- a/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/test/CheckerTestCase.java +++ b/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/test/CheckerTestCase.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2010 Alena Laskavaia + * Copyright (c) 2009, 2010 Alena Laskavaia * 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 @@ -27,7 +27,7 @@ import org.eclipse.core.runtime.NullProgressMonitor; /** * @author Alena - * + * */ public class CheckerTestCase extends CodanTestCase { protected IMarker[] markers; @@ -36,6 +36,13 @@ public class CheckerTestCase extends CodanTestCase { return checkErrorLine(currentFile, i); } + public void checkErrorLines( Object... args ) { + for( Object i : args ) { + checkErrorLine((Integer) i); + } + assertEquals(args.length, markers.length); + } + public IMarker checkErrorLine(int i, String problemId) { return checkErrorLine(currentFile, i, problemId); } @@ -95,7 +102,7 @@ public class CheckerTestCase extends CodanTestCase { } /** - * + * */ public void runOnProject() { try { @@ -117,7 +124,7 @@ public class CheckerTestCase extends CodanTestCase { } /** - * + * */ protected void runCodan() { CodanRuntime