Skip to main content
aboutsummaryrefslogtreecommitdiffstats
path: root/codan
diff options
context:
space:
mode:
authorRolf Bislin2017-05-02 07:49:27 +0000
committerThomas Corbat2017-05-08 17:25:15 +0000
commita583190f52c69400dc25d455b170dc23d8314105 (patch)
treef40f7258e77bb0af59921d5d4704df3d4eeb1920 /codan
parent0f27b20848e526bfddea0de20693696885cb214d (diff)
downloadorg.eclipse.cdt-a583190f52c69400dc25d455b170dc23d8314105.tar.gz
org.eclipse.cdt-a583190f52c69400dc25d455b170dc23d8314105.tar.xz
org.eclipse.cdt-a583190f52c69400dc25d455b170dc23d8314105.zip
Bug 514685 - codan: handle fallthrough attribute
and provide quickfix for adding fallthrough attribute and add JUnit Tests and add StandardAttributes class Change-Id: I8cf0238771dc92bd1784b9dfb35a680d078b1db6 Depends-On: Ic09aa96f896b0a5dd998156e05930704775f695b Signed-off-by: Rolf Bislin <romibi@bluewin.ch> Signed-off-by: Thomas Corbat <tcorbat@hsr.ch>
Diffstat (limited to 'codan')
-rw-r--r--codan/org.eclipse.cdt.codan.checkers.ui/plugin.xml4
-rw-r--r--codan/org.eclipse.cdt.codan.checkers.ui/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/AbstractCaseBreakQuickFix.java102
-rw-r--r--codan/org.eclipse.cdt.codan.checkers.ui/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/CaseBreakQuickFixBreak.java79
-rw-r--r--codan/org.eclipse.cdt.codan.checkers.ui/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/CaseBreakQuickFixFallthroughAttribute.java74
-rw-r--r--codan/org.eclipse.cdt.codan.checkers.ui/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/QuickFixMessages.java1
-rw-r--r--codan/org.eclipse.cdt.codan.checkers.ui/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/QuickFixMessages.properties1
-rw-r--r--codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CaseBreakChecker.java31
-rw-r--r--codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CheckersMessages.java1
-rw-r--r--codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CheckersMessages.properties1
-rw-r--r--codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/internal/checkers/CaseBreakCheckerTest.java198
-rw-r--r--codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/tests/AutomatedIntegrationSuite.java14
-rw-r--r--codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/CaseBreakQuickFixBreakTest.java (renamed from codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/CaseBreakQuickFixTest.java)2
-rw-r--r--codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/CaseBreakQuickFixFallthroughAttributeTest.java90
13 files changed, 510 insertions, 88 deletions
diff --git a/codan/org.eclipse.cdt.codan.checkers.ui/plugin.xml b/codan/org.eclipse.cdt.codan.checkers.ui/plugin.xml
index e8e8104b71..9a20fafef4 100644
--- a/codan/org.eclipse.cdt.codan.checkers.ui/plugin.xml
+++ b/codan/org.eclipse.cdt.codan.checkers.ui/plugin.xml
@@ -75,5 +75,9 @@
class="org.eclipse.cdt.codan.internal.checkers.ui.quickfix.CaseBreakQuickFixComment"
problemId="org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem">
</resolution>
+ <resolution
+ class="org.eclipse.cdt.codan.internal.checkers.ui.quickfix.CaseBreakQuickFixFallthroughAttribute"
+ problemId="org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem">
+ </resolution>
</extension>
</plugin>
diff --git a/codan/org.eclipse.cdt.codan.checkers.ui/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/AbstractCaseBreakQuickFix.java b/codan/org.eclipse.cdt.codan.checkers.ui/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/AbstractCaseBreakQuickFix.java
new file mode 100644
index 0000000000..158545758c
--- /dev/null
+++ b/codan/org.eclipse.cdt.codan.checkers.ui/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/AbstractCaseBreakQuickFix.java
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Institute for Software, HSR Hochschule fuer Technik
+ * 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:
+ * Rolf Bislin - Initial implementation
+ * Gil Barash - getStmtBeforeBreak, getStatementAfter
+ *******************************************************************************/
+package org.eclipse.cdt.codan.internal.checkers.ui.quickfix;
+
+import java.util.Arrays;
+
+import org.eclipse.cdt.codan.core.cxx.CxxAstUtils;
+import org.eclipse.cdt.codan.internal.checkers.ui.CheckersUiActivator;
+import org.eclipse.cdt.codan.ui.AbstractAstRewriteQuickFix;
+import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement;
+import org.eclipse.cdt.core.dom.ast.IASTNode;
+import org.eclipse.cdt.core.dom.ast.IASTNodeSelector;
+import org.eclipse.cdt.core.dom.ast.IASTStatement;
+import org.eclipse.cdt.core.dom.ast.IASTSwitchStatement;
+import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
+import org.eclipse.cdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.ltk.core.refactoring.Change;
+
+abstract public class AbstractCaseBreakQuickFix extends AbstractAstRewriteQuickFix {
+ protected void addNewNodeAtMarkedCaseEnd(IASTNode newnode, IASTTranslationUnit ast, IMarker marker) {
+ try {
+ IASTStatement beforeCaseEnd = getStmtBeforeCaseEnd(marker, ast);
+ if (beforeCaseEnd != null && beforeCaseEnd.getParent() instanceof IASTCompoundStatement) {
+ IASTCompoundStatement enclosingStatement;
+ IASTStatement after;
+ if (beforeCaseEnd instanceof IASTCompoundStatement) {
+ // Case body is enclosed in braces. Add 'break' as last statement inside braces.
+ enclosingStatement = (IASTCompoundStatement) beforeCaseEnd;
+ after = null;
+ } else {
+ enclosingStatement = (IASTCompoundStatement) beforeCaseEnd.getParent();
+ after = getNextStatement(beforeCaseEnd);
+ }
+ ASTRewrite r = ASTRewrite.create(enclosingStatement.getTranslationUnit());
+ r.insertBefore(enclosingStatement, after, newnode, null);
+ Change c = r.rewriteAST();
+ c.perform(new NullProgressMonitor());
+ }
+ } catch (CoreException | BadLocationException e) {
+ CheckersUiActivator.log(e);
+ }
+ }
+
+ protected IASTStatement getStmtBeforeCaseEnd(IMarker marker, IASTTranslationUnit ast) throws BadLocationException {
+ int line = marker.getAttribute(IMarker.LINE_NUMBER, 0) - 1;
+ if (line < 0)
+ return null;
+ IDocument doc = getDocument();
+ if (doc == null)
+ doc = openDocument(marker);
+ IRegion lineInformation = doc.getLineInformation(line);
+ IASTNodeSelector nodeSelector = ast.getNodeSelector(null);
+ IASTNode containedNode = nodeSelector.findFirstContainedNode(lineInformation.getOffset(), lineInformation.getLength());
+ IASTNode beforeCaseEndNode = null;
+ if (containedNode != null) {
+ beforeCaseEndNode = CxxAstUtils.getEnclosingStatement(containedNode);
+ } else {
+ beforeCaseEndNode = nodeSelector.findEnclosingNode(lineInformation.getOffset(), lineInformation.getLength());
+ }
+ if (beforeCaseEndNode instanceof IASTCompoundStatement) {
+ while (beforeCaseEndNode != null) {
+ if (beforeCaseEndNode.getParent() instanceof IASTCompoundStatement
+ && beforeCaseEndNode.getParent().getParent() instanceof IASTSwitchStatement) {
+ return (IASTStatement) beforeCaseEndNode;
+ }
+ beforeCaseEndNode = beforeCaseEndNode.getParent();
+ }
+ }
+ if (beforeCaseEndNode instanceof IASTStatement)
+ return (IASTStatement) beforeCaseEndNode;
+ return null;
+ }
+
+ protected IASTStatement getNextStatement(IASTStatement beforeStatement) {
+ assert(beforeStatement != null);
+ IASTNode parent = beforeStatement.getParent();
+ if (parent instanceof IASTCompoundStatement) {
+ IASTCompoundStatement enclosingStatement = (IASTCompoundStatement) parent;
+ IASTStatement[] statements = enclosingStatement.getStatements();
+ int indexOfNext = Arrays.asList(statements).indexOf(beforeStatement) + 1;
+ if (indexOfNext < statements.length) {
+ return statements[indexOfNext];
+ }
+ }
+ return null;
+ }
+}
diff --git a/codan/org.eclipse.cdt.codan.checkers.ui/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/CaseBreakQuickFixBreak.java b/codan/org.eclipse.cdt.codan.checkers.ui/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/CaseBreakQuickFixBreak.java
index 5eecd182c2..d71fcb1571 100644
--- a/codan/org.eclipse.cdt.codan.checkers.ui/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/CaseBreakQuickFixBreak.java
+++ b/codan/org.eclipse.cdt.codan.checkers.ui/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/CaseBreakQuickFixBreak.java
@@ -10,27 +10,15 @@
*******************************************************************************/
package org.eclipse.cdt.codan.internal.checkers.ui.quickfix;
-import org.eclipse.cdt.codan.core.cxx.CxxAstUtils;
import org.eclipse.cdt.codan.internal.checkers.ui.CheckersUiActivator;
-import org.eclipse.cdt.codan.ui.AbstractAstRewriteQuickFix;
import org.eclipse.cdt.core.dom.ast.IASTBreakStatement;
-import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement;
-import org.eclipse.cdt.core.dom.ast.IASTNode;
-import org.eclipse.cdt.core.dom.ast.IASTNodeSelector;
-import org.eclipse.cdt.core.dom.ast.IASTStatement;
-import org.eclipse.cdt.core.dom.ast.IASTSwitchStatement;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
-import org.eclipse.cdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.NullProgressMonitor;
-import org.eclipse.jface.text.BadLocationException;
-import org.eclipse.jface.text.IRegion;
-import org.eclipse.ltk.core.refactoring.Change;
-public class CaseBreakQuickFixBreak extends AbstractAstRewriteQuickFix {
+public class CaseBreakQuickFixBreak extends AbstractCaseBreakQuickFix {
@Override
public boolean isApplicable(IMarker marker) {
int line = marker.getAttribute(IMarker.LINE_NUMBER, 0) - 1;
@@ -44,75 +32,14 @@ public class CaseBreakQuickFixBreak extends AbstractAstRewriteQuickFix {
return QuickFixMessages.CaseBreakQuickFixBreak_Label;
}
- protected IASTStatement getStmtBeforeBreak(IMarker marker, IASTTranslationUnit ast) throws BadLocationException {
- int line = marker.getAttribute(IMarker.LINE_NUMBER, 0) - 1;
- if (line < 0)
- return null;
- IRegion lineInformation = getDocument().getLineInformation(line);
- IASTNodeSelector nodeSelector = ast.getNodeSelector(null);
- IASTNode containedNode = nodeSelector.findFirstContainedNode(lineInformation.getOffset(), lineInformation.getLength());
- IASTNode beforeBreakNode = null;
- if (containedNode != null) {
- beforeBreakNode = CxxAstUtils.getEnclosingStatement(containedNode);
- } else {
- beforeBreakNode = nodeSelector.findEnclosingNode(lineInformation.getOffset(), lineInformation.getLength());
- }
- if (beforeBreakNode instanceof IASTCompoundStatement) {
- while (beforeBreakNode != null) {
- if (beforeBreakNode.getParent() instanceof IASTCompoundStatement
- && beforeBreakNode.getParent().getParent() instanceof IASTSwitchStatement) {
- return (IASTStatement) beforeBreakNode;
- }
- beforeBreakNode = beforeBreakNode.getParent();
- }
- }
- if (beforeBreakNode instanceof IASTStatement)
- return (IASTStatement) beforeBreakNode;
- return null;
- }
-
@Override
public void modifyAST(IIndex index, IMarker marker) {
try {
IASTTranslationUnit ast = getTranslationUnitViaEditor(marker).getAST(index, ITranslationUnit.AST_SKIP_INDEXED_HEADERS);
- IASTStatement beforeBreak = getStmtBeforeBreak(marker, ast);
- if (beforeBreak != null && beforeBreak.getParent() instanceof IASTCompoundStatement) {
- IASTCompoundStatement enclosingStatement;
- IASTStatement after;
- if (beforeBreak instanceof IASTCompoundStatement) {
- // Case body is enclosed in braces. Add 'break' as last statement inside braces.
- enclosingStatement = (IASTCompoundStatement) beforeBreak;
- after = null;
- } else {
- enclosingStatement = (IASTCompoundStatement) beforeBreak.getParent();
- after = getStatementAfter(beforeBreak);
- }
- ASTRewrite r = ASTRewrite.create(enclosingStatement.getTranslationUnit());
- IASTBreakStatement breakStatement = ast.getASTNodeFactory().newBreakStatement();
- r.insertBefore(enclosingStatement, after, breakStatement, null);
- Change c = r.rewriteAST();
- c.perform(new NullProgressMonitor());
- }
+ IASTBreakStatement breakStatement = ast.getASTNodeFactory().newBreakStatement();
+ addNewNodeAtMarkedCaseEnd(breakStatement, ast, marker);
} catch (CoreException e) {
CheckersUiActivator.log(e);
- } catch (BadLocationException e) {
- CheckersUiActivator.log(e);
- }
- }
-
- private IASTStatement getStatementAfter(IASTStatement beforeBreak) {
- IASTCompoundStatement enclosingStatement = (IASTCompoundStatement) beforeBreak.getParent();
- IASTStatement after = null;
- IASTStatement[] statements = enclosingStatement.getStatements();
- for (int i = 0; i < statements.length; i++) {
- IASTStatement st = statements[i];
- if (st == beforeBreak) {
- if (i < statements.length - 1) {
- after = statements[i + 1];
- break;
- }
- }
}
- return after;
}
}
diff --git a/codan/org.eclipse.cdt.codan.checkers.ui/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/CaseBreakQuickFixFallthroughAttribute.java b/codan/org.eclipse.cdt.codan.checkers.ui/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/CaseBreakQuickFixFallthroughAttribute.java
new file mode 100644
index 0000000000..84e373fc9d
--- /dev/null
+++ b/codan/org.eclipse.cdt.codan.checkers.ui/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/CaseBreakQuickFixFallthroughAttribute.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Institute for Software, HSR Hochschule fuer Technik
+ * 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
+ *******************************************************************************/
+package org.eclipse.cdt.codan.internal.checkers.ui.quickfix;
+
+import org.eclipse.cdt.codan.core.model.IProblem;
+import org.eclipse.cdt.codan.core.param.RootProblemPreference;
+import org.eclipse.cdt.codan.internal.checkers.CaseBreakChecker;
+import org.eclipse.cdt.codan.internal.checkers.ui.CheckersUiActivator;
+import org.eclipse.cdt.core.dom.ast.IASTAttribute;
+import org.eclipse.cdt.core.dom.ast.IASTAttributeList;
+import org.eclipse.cdt.core.dom.ast.IASTNullStatement;
+import org.eclipse.cdt.core.dom.ast.IASTStatement;
+import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPNodeFactory;
+import org.eclipse.cdt.core.index.IIndex;
+import org.eclipse.cdt.core.model.ITranslationUnit;
+import org.eclipse.cdt.core.parser.StandardAttributes;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.text.BadLocationException;
+
+public class CaseBreakQuickFixFallthroughAttribute extends AbstractCaseBreakQuickFix {
+ @Override
+ public boolean isApplicable(IMarker marker) {
+ IProblem problem = getProblem(marker);
+ RootProblemPreference map = (RootProblemPreference) problem.getPreference();
+ boolean enabled = (boolean) map.getChildValue(CaseBreakChecker.PARAM_ENABLE_FALLTHROUGH_QUICKFIX);
+ boolean last_case_enabled = (boolean) map.getChildValue(CaseBreakChecker.PARAM_LAST_CASE);
+ ITranslationUnit tu = getTranslationUnitViaEditor(marker);
+ return enabled && tu.isCXXLanguage() && (!last_case_enabled || validPositionForFallthrough(marker));
+ }
+
+ @Override
+ public String getLabel() {
+ return QuickFixMessages.CaseBreakQuickFixFallthroughAttribute_Label;
+ }
+
+ @Override
+ public void modifyAST(IIndex index, IMarker marker) {
+ try {
+ IASTTranslationUnit ast = getTranslationUnitViaEditor(marker).getAST(index, ITranslationUnit.AST_SKIP_INDEXED_HEADERS);
+ ICPPNodeFactory factory = (ICPPNodeFactory) ast.getASTNodeFactory();
+ IASTNullStatement nullStatement = factory.newNullStatement();
+ nullStatement.addAttributeSpecifier(getFallthroughAttributeList(factory));
+ addNewNodeAtMarkedCaseEnd(nullStatement, ast, marker);
+ } catch (CoreException e) {
+ CheckersUiActivator.log(e);
+ }
+ }
+
+ private boolean validPositionForFallthrough(IMarker marker) {
+ try {
+ IASTTranslationUnit ast = getTranslationUnitViaEditor(marker).getAST(null, ITranslationUnit.AST_SKIP_INDEXED_HEADERS);
+ IASTStatement beforeCaseEnd = getStmtBeforeCaseEnd(marker, ast);
+ if (getNextStatement(beforeCaseEnd) == null)
+ return false;
+ } catch (CoreException | BadLocationException e) {
+ e.printStackTrace();
+ }
+ return true;
+ }
+
+ private IASTAttributeList getFallthroughAttributeList(ICPPNodeFactory factory) {
+ IASTAttribute attribute = factory.newAttribute(StandardAttributes.cFALLTHROUGH, null);
+ IASTAttributeList attributeList = factory.newAttributeList();
+ attributeList.addAttribute(attribute);
+ return attributeList;
+ }
+}
diff --git a/codan/org.eclipse.cdt.codan.checkers.ui/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/QuickFixMessages.java b/codan/org.eclipse.cdt.codan.checkers.ui/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/QuickFixMessages.java
index b0fb468cf8..9daaa13933 100644
--- a/codan/org.eclipse.cdt.codan.checkers.ui/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/QuickFixMessages.java
+++ b/codan/org.eclipse.cdt.codan.checkers.ui/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/QuickFixMessages.java
@@ -15,6 +15,7 @@ import org.eclipse.osgi.util.NLS;
public class QuickFixMessages extends NLS {
public static String CaseBreakQuickFixBreak_Label;
public static String CaseBreakQuickFixComment_Label;
+ public static String CaseBreakQuickFixFallthroughAttribute_Label;
public static String QuickFixCreateClass_CreateNewClass;
public static String QuickFixCreateField_create_field;
public static String QuickFixCreateLocalVariable_create_local_variable;
diff --git a/codan/org.eclipse.cdt.codan.checkers.ui/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/QuickFixMessages.properties b/codan/org.eclipse.cdt.codan.checkers.ui/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/QuickFixMessages.properties
index 421fbd9cdc..7ca12d59bf 100644
--- a/codan/org.eclipse.cdt.codan.checkers.ui/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/QuickFixMessages.properties
+++ b/codan/org.eclipse.cdt.codan.checkers.ui/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/QuickFixMessages.properties
@@ -10,6 +10,7 @@
###############################################################################
CaseBreakQuickFixBreak_Label=Add break statement
CaseBreakQuickFixComment_Label=Add suppressing comment
+CaseBreakQuickFixFallthroughAttribute_Label=Add [[fallthrough]] attribute
QuickFixCreateClass_CreateNewClass=Create new class
QuickFixCreateField_create_field=Create field
QuickFixCreateLocalVariable_create_local_variable=Create local variable
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
index ddaecb40e9..c87a33737d 100644
--- 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
@@ -37,12 +37,15 @@ 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;
+import org.eclipse.cdt.core.parser.StandardAttributes;
+import org.eclipse.cdt.core.parser.util.AttributeUtil;
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 PARAM_ENABLE_FALLTHROUGH_QUICKFIX = "enable_fallthrough_quickfix_param"; //$NON-NLS-1$
public static final String DEFAULT_NO_BREAK_COMMENT = "no break"; //$NON-NLS-1$
private boolean fCheckLastCase; // Should we check the last case in the switch?
private boolean fCheckEmptyCase; // Should we check an empty case (a case without any statements within it)
@@ -106,6 +109,9 @@ public class CaseBreakChecker extends AbstractIndexAstChecker implements IChecke
if (!fCheckLastCase && next == null) {
continue; // Last case and we don't care
}
+ if (next != null && hasValidFallthroughAttribute(curr)) {
+ continue; // Explicit fallthrough, do not analyse case further (not valid in last case)
+ }
// If this is the null statement, base the decision on the previous statement
// instead (if there is one). Null statements can sneak in via macros in cases
// where the macro expansion ends in a semicolon, and the macro use is followed
@@ -168,6 +174,30 @@ public class CaseBreakChecker extends AbstractIndexAstChecker implements IChecke
}
return true; // TODO
}
+
+ /**
+ * Checks whether {@code statement} (or its last inner statement if it
+ * is a compound statement) is a {@code IASTNullStatement} and has the
+ * C++ standard [[fallthrough]] attribute
+ *
+ * @param statement The {@code IASTStatement} to check
+ * @return {@code true} if the {@code statement} has the [[fallthrough]]
+ * attribute,
+ * {@code false} otherwise.
+ */
+ public boolean hasValidFallthroughAttribute(IASTStatement statement) {
+ IASTStatement lastStatement = statement;
+ while (lastStatement instanceof IASTCompoundStatement && lastStatement.getChildren().length > 0) {
+ IASTCompoundStatement compoundStatement = (IASTCompoundStatement) lastStatement;
+ IASTStatement[] nestedStatements = compoundStatement.getStatements();
+ lastStatement = nestedStatements[nestedStatements.length - 1];
+ }
+ if (lastStatement instanceof IASTNullStatement) {
+ if (AttributeUtil.hasAttribute(lastStatement, new String[] { StandardAttributes.FALLTHROUGH }))
+ return true;
+ }
+ return false;
+ }
}
private void reportProblem(IASTStatement curr) {
@@ -231,6 +261,7 @@ public class CaseBreakChecker extends AbstractIndexAstChecker implements IChecke
DEFAULT_NO_BREAK_COMMENT);
addPreference(problem, PARAM_LAST_CASE, CheckersMessages.CaseBreakChecker_LastCaseDescription, Boolean.FALSE);
addPreference(problem, PARAM_EMPTY_CASE, CheckersMessages.CaseBreakChecker_EmptyCaseDescription, Boolean.FALSE);
+ addPreference(problem, PARAM_ENABLE_FALLTHROUGH_QUICKFIX, CheckersMessages.CaseBreakChecker_EnableFallthroughQuickfixDescription, Boolean.FALSE);
}
@Override
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 8a4ad7e687..6768898c97 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
@@ -19,6 +19,7 @@ public class CheckersMessages extends NLS {
public static String CaseBreakChecker_DefaultNoBreakCommentDescription;
public static String CaseBreakChecker_EmptyCaseDescription;
public static String CaseBreakChecker_LastCaseDescription;
+ public static String CaseBreakChecker_EnableFallthroughQuickfixDescription;
public static String CatchByReference_ReportForUnknownType;
public static String ClassMembersInitializationChecker_SkipConstructorsWithFCalls;
public static String NamingConventionFunctionChecker_LabelNamePattern;
diff --git a/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CheckersMessages.properties b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CheckersMessages.properties
index bc5a2fe5ce..d76c6c75a2 100644
--- a/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CheckersMessages.properties
+++ b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CheckersMessages.properties
@@ -11,6 +11,7 @@
CaseBreakChecker_DefaultNoBreakCommentDescription=Comment text to suppress the problem:
CaseBreakChecker_EmptyCaseDescription=Check also empty 'case' statement (except if last)
CaseBreakChecker_LastCaseDescription=Check also the last 'case' statement
+CaseBreakChecker_EnableFallthroughQuickfixDescription=Enable quick fix to add fallthrough attribute (C++17)
ClassMembersInitializationChecker_SkipConstructorsWithFCalls=Skip constructors with initialization function calls
CatchByReference_ReportForUnknownType=Report a problem if type cannot be resolved
NamingConventionFunctionChecker_LabelNamePattern=Name Pattern
diff --git a/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/internal/checkers/CaseBreakCheckerTest.java b/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/internal/checkers/CaseBreakCheckerTest.java
index 3f5f808b8f..b8c5e878d6 100644
--- a/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/internal/checkers/CaseBreakCheckerTest.java
+++ b/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/internal/checkers/CaseBreakCheckerTest.java
@@ -166,6 +166,18 @@ public class CaseBreakCheckerTest extends CheckerTestCase {
checkNoErrorsOfKind(ER_ID);
}
+ // void foo(void) {
+ // int a;
+ // switch (a) {
+ // case 1:
+ // [[fallthrough]]; // invalid in last case
+ // }
+ // }
+ public void testEmptyLastCaseBadFallthrough_514685() throws Exception {
+ loadCodeAndRunCpp(getAboveComment());
+ checkErrorLine(5);
+ }
+
// void foo(int a, int b) {
// switch (a) {
// case 1:
@@ -199,11 +211,25 @@ public class CaseBreakCheckerTest extends CheckerTestCase {
// int a, b;
// switch (a) {
// case 1:
+ // b = 2;
+ // [[fallthrough]];
+ // }
+ // }
+ public void testLastCaseBadFallthrough_514685() throws Exception {
+ loadCodeAndRunCpp(getAboveComment());
+ checkErrorLine(5);
+ }
+
+ // void foo(void) {
+ // int a, b;
+ // switch (a) {
+ // case 1:
// /* no break */
// case 2:
// b = 2;
// break;
// }
+ // }
public void testEmptyCaseOKcomment() throws Exception {
loadCodeAndRun(getAboveComment());
checkNoErrorsOfKind(ER_ID);
@@ -213,6 +239,21 @@ public class CaseBreakCheckerTest extends CheckerTestCase {
// int a, b;
// switch (a) {
// case 1:
+ // [[fallthrough]];
+ // case 2:
+ // b = 2;
+ // break;
+ // }
+ // }
+ public void testEmptyCaseOKFallthrough_514685() throws Exception {
+ loadCodeAndRunCpp(getAboveComment());
+ checkNoErrorsOfKind(ER_ID);
+ }
+
+ // void foo(void) {
+ // int a, b;
+ // switch (a) {
+ // case 1:
// b = 2;
// /* no break */
// bye();
@@ -223,6 +264,21 @@ public class CaseBreakCheckerTest extends CheckerTestCase {
checkErrorLines(7);
}
+ // void bye() {}
+ // void foo(void) {
+ // int a, b;
+ // switch (a) {
+ // case 1:
+ // b = 2;
+ // [[fallthrough]];
+ // bye();
+ // }
+ // }
+ public void testLastCaseBadFallthroughNotLast_514685() throws Exception {
+ loadCodeAndRunCpp(getAboveComment());
+ checkErrorLines(8);
+ }
+
// void foo(void) {
// int a, b;
// switch (a) {
@@ -257,6 +313,36 @@ public class CaseBreakCheckerTest extends CheckerTestCase {
// void foo(void) {
// int a, b;
// switch (a) {
+ // case 1:
+ // b = 2;
+ // [[fallthrough]];
+ // case 2:
+ // b = 2;
+ // [[ fallthrough ]];
+ // case 3:
+ // b = 2;
+ // [ [ fallthrough ] ] ;
+ // case 4:
+ // b = 2;[[fallthrough]];
+ // case 5:
+ // b = 2;
+ // [[fall]];
+ // case 6:
+ // b = 2;
+ // [[FALLTHROUGH]];
+ // case 7:
+ // b = 2;
+ // [[fallthrough]]
+ // }
+ // }
+ public void testDifferentFallthroughs_514685() throws Exception {
+ loadCodeAndRunCpp(getAboveComment());
+ checkErrorLines(16, 19, 24);
+ }
+
+ // void foo(void) {
+ // int a, b;
+ // switch (a) {
// case 1: //err
// // lolo
// case 2: //err
@@ -286,15 +372,22 @@ public class CaseBreakCheckerTest extends CheckerTestCase {
// b = 2;
// /* no break */
// case 4:
- // b = 2; // err
+ // b = 2;
+ // [[fallthrough]];
// case 5:
+ // b = 2; // err
+ // case 6:
// b = 2;
// break;
- // case 6:
+ // case 7:
// b = 2;
// /* no break */
// b = 2; //err
- // case 7:
+ // case 8:
+ // b = 2;
+ // [[fallthrough]];
+ // b = 2; //err
+ // case 9:
// b = 2;//err
// }
//
@@ -310,7 +403,7 @@ public class CaseBreakCheckerTest extends CheckerTestCase {
public void testGeneral1() throws Exception {
setEmpty(true);
setLast(true);
- loadCodeAndRun(getAboveComment());
+ loadCodeAndRunCpp(getAboveComment());
checkErrorComments();
}
@@ -347,6 +440,36 @@ public class CaseBreakCheckerTest extends CheckerTestCase {
// void foo(void) {
// int a, b;
// switch (a) {
+ // case 1:
+ // b = 2;
+ // // lolo
+ // [[fallthrough]];
+ // case 2:
+ // b = 2;
+ // [[fallthrough]];
+ // // lolo
+ // case 3:
+ // [[fallthrough]]; // not valid, not last statement
+ // b = 2;
+ // // loo
+ // case 4:
+ // b = 2;
+ // // lolo
+ // [[fallthrough]];
+ // case 5:
+ // // lolo
+ // b = 2;
+ // [[fallthrough]]; // not valid in last case
+ // }
+ // }
+ public void testGeneralFallthroughs1_514685() throws Exception {
+ loadCodeAndRunCpp(getAboveComment());
+ checkErrorLines(14, 22);
+ }
+
+ // void foo(void) {
+ // int a, b;
+ // switch (a) {
// case 0:
// switch( b ) {
// case 2: // err
@@ -370,13 +493,23 @@ public class CaseBreakCheckerTest extends CheckerTestCase {
// } // err
// case 5:
// switch( b ) {
+ // case 2:
+ // [[fallthrough]]; // err
+ // } // err
+ // case 6:
+ // switch( b ) {
// case 2: // err
// }
// /* no break */
+ // case 7:
+ // switch( b ) {
+ // case 2: // err
+ // } // err
+ // [[fallthrough]];
// }
// }
public void testNestedSwitches() throws Exception {
- loadCodeAndRun(getAboveComment());
+ loadCodeAndRunCpp(getAboveComment());
checkErrorComments();
}
@@ -429,6 +562,61 @@ public class CaseBreakCheckerTest extends CheckerTestCase {
checkErrorLine(4, ER_ID);
}
+ // void foo() {
+ // int a, b;
+ // switch (a) {
+ // case 1: {
+ // b = 2;
+ // [[fallthrough]];
+ // }
+ // case 2: {
+ // b = 2;
+ // }
+ // [[fallthrough]];
+ // case 3: {
+ // {
+ // b = 2;
+ // [[fallthrough]];
+ // }
+ // }
+ // case 4: {
+ // b = 2;
+ // break;
+ // }
+ // }
+ //}
+ public void testFallthroughAndCompoundStatementCombinations_514685() throws Exception {
+ String code = getAboveComment();
+ loadCodeAndRunCpp(code);
+ checkNoErrorsOfKind(ER_ID);
+ }
+
+ // void foo() {
+ // int a, b;
+ // switch (a) {
+ // case 1:
+ // b = 2; // err
+ // [[fallthrough]];
+ // }
+ // switch (a) {
+ // case 1: {
+ // b = 2;
+ // [[fallthrough]];
+ // } // err
+ // }
+ // switch (a) {
+ // case 1: {
+ // b = 2;
+ // } // err
+ // [[fallthrough]];
+ // }
+ // }
+ public void testBadFallthroughInLastStatement_514685() throws Exception {
+ String code = getAboveComment();
+ loadCodeAndRunCpp(code);
+ checkErrorComments();
+ }
+
private void setLast(boolean val) {
IProblemPreference pref = getPreference(CaseBreakChecker.ER_ID, CaseBreakChecker.PARAM_LAST_CASE);
pref.setValue(val);
diff --git a/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/tests/AutomatedIntegrationSuite.java b/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/tests/AutomatedIntegrationSuite.java
index 7233b65c95..70089d5a62 100644
--- a/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/tests/AutomatedIntegrationSuite.java
+++ b/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/tests/AutomatedIntegrationSuite.java
@@ -11,10 +11,6 @@
*******************************************************************************/
package org.eclipse.cdt.codan.core.tests;
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-
import org.eclipse.cdt.codan.core.internal.checkers.AbstractClassInstantiationCheckerTest;
import org.eclipse.cdt.codan.core.internal.checkers.AssignmentInConditionCheckerTest;
import org.eclipse.cdt.codan.core.internal.checkers.AssignmentToItselfCheckerTest;
@@ -33,12 +29,17 @@ import org.eclipse.cdt.codan.core.internal.checkers.SuggestedParenthesisCheckerT
import org.eclipse.cdt.codan.core.internal.checkers.SuspiciousSemicolonCheckerTest;
import org.eclipse.cdt.codan.core.internal.checkers.UnusedSymbolInFileScopeCheckerTest;
import org.eclipse.cdt.codan.internal.checkers.ui.quickfix.AssignmentInConditionQuickFixTest;
-import org.eclipse.cdt.codan.internal.checkers.ui.quickfix.CaseBreakQuickFixTest;
+import org.eclipse.cdt.codan.internal.checkers.ui.quickfix.CaseBreakQuickFixBreakTest;
import org.eclipse.cdt.codan.internal.checkers.ui.quickfix.CaseBreakQuickFixCommentTest;
+import org.eclipse.cdt.codan.internal.checkers.ui.quickfix.CaseBreakQuickFixFallthroughAttributeTest;
import org.eclipse.cdt.codan.internal.checkers.ui.quickfix.CatchByReferenceQuickFixTest;
import org.eclipse.cdt.codan.internal.checkers.ui.quickfix.CreateLocalVariableQuickFixTest;
import org.eclipse.cdt.codan.internal.checkers.ui.quickfix.SuggestedParenthesisQuickFixTest;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
public class AutomatedIntegrationSuite extends TestSuite {
public AutomatedIntegrationSuite() {
}
@@ -81,8 +82,9 @@ public class AutomatedIntegrationSuite extends TestSuite {
suite.addTestSuite(CreateLocalVariableQuickFixTest.class);
suite.addTestSuite(SuggestedParenthesisQuickFixTest.class);
suite.addTestSuite(CatchByReferenceQuickFixTest.class);
- suite.addTestSuite(CaseBreakQuickFixTest.class);
+ suite.addTestSuite(CaseBreakQuickFixBreakTest.class);
suite.addTestSuite(CaseBreakQuickFixCommentTest.class);
+ suite.addTestSuite(CaseBreakQuickFixFallthroughAttributeTest.class);
suite.addTestSuite(AssignmentInConditionQuickFixTest.class);
return suite;
}
diff --git a/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/CaseBreakQuickFixTest.java b/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/CaseBreakQuickFixBreakTest.java
index bafe92bc2a..b14884a6e0 100644
--- a/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/CaseBreakQuickFixTest.java
+++ b/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/CaseBreakQuickFixBreakTest.java
@@ -12,7 +12,7 @@ package org.eclipse.cdt.codan.internal.checkers.ui.quickfix;
import org.eclipse.cdt.codan.ui.AbstractCodanCMarkerResolution;
-public class CaseBreakQuickFixTest extends QuickFixTestCase {
+public class CaseBreakQuickFixBreakTest extends QuickFixTestCase {
@SuppressWarnings("restriction")
@Override
protected AbstractCodanCMarkerResolution createQuickFix() {
diff --git a/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/CaseBreakQuickFixFallthroughAttributeTest.java b/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/CaseBreakQuickFixFallthroughAttributeTest.java
new file mode 100644
index 0000000000..23b63dd755
--- /dev/null
+++ b/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/internal/checkers/ui/quickfix/CaseBreakQuickFixFallthroughAttributeTest.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Institute for Software, HSR Hochschule fuer Technik
+ * 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
+ *******************************************************************************/
+package org.eclipse.cdt.codan.internal.checkers.ui.quickfix;
+
+import org.eclipse.cdt.codan.ui.AbstractCodanCMarkerResolution;
+
+public class CaseBreakQuickFixFallthroughAttributeTest extends QuickFixTestCase {
+ @SuppressWarnings("restriction")
+ @Override
+ protected AbstractCodanCMarkerResolution createQuickFix() {
+ return new CaseBreakQuickFixFallthroughAttribute();
+ }
+
+ //void hello() {}
+ //void func() {
+ // int a;
+ // switch(a) {
+ // case 1:
+ // hello();
+ // case 2:
+ // break;
+ // }
+ //}
+ public void testSimpleCase_514685() throws Exception {
+ loadcode(getAboveComment(), true);
+ String result = runQuickFixOneFile();
+ assertContainedIn("[[fallthrough]];\tcase 2:", result);
+ }
+
+ //void hello() {}
+ //void func() {
+ // int a;
+ // switch(a) {
+ // case 1:
+ // hello();
+ // hello();
+ // case 2:
+ // break;
+ // }
+ //}
+ public void testMultipleStatementsCase_514685() throws Exception {
+ loadcode(getAboveComment(), true);
+ String result = runQuickFixOneFile();
+ assertContainedIn("[[fallthrough]];\tcase 2:", result);
+ }
+
+ //void hello() {}
+ //void func() {
+ // int a;
+ // switch(a) {
+ // case 1: {
+ // hello();
+ // }
+ // case 2:
+ // break;
+ // }
+ //}
+ public void testCompositeCase_514685() throws Exception {
+ loadcode(getAboveComment(), true);
+ String result = runQuickFixOneFile();
+ assertContainedIn("[[fallthrough]];\t}\tcase 2:", result);
+ }
+
+ //void hello() {}
+ //void func() {
+ // int a;
+ // switch(a) {
+ // case 1: {
+ // {
+ // hello();
+ // {
+ // hello();
+ // }
+ // }
+ // }
+ // case 2:
+ // break;
+ // }
+ //}
+ public void testNestedCompositeCase_514685() throws Exception {
+ loadcode(getAboveComment(), true);
+ String result = runQuickFixOneFile();
+ assertContainedIn("[[fallthrough]];\t}\tcase 2:", result);
+ }
+}

Back to the top