#include "BranchCloneCheck.h"
#include "../utils/ASTUtils.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Analysis/CloneDetection.h"
#include "clang/Lex/Lexer.h"
/// Determines if the bodies of two branches in a switch statements are Type I
/// clones of each other. This function only examines the body of the branch
/// and ignores the `case X:` or `default:` at the start of the branch.
-static bool areSwitchBranchesIdentical(const SwitchBranch LHS,
- const SwitchBranch RHS,
+static bool areSwitchBranchesIdentical(const SwitchBranch &LHS,
+ const SwitchBranch &RHS,
const ASTContext &Context) {
if (LHS.size() != RHS.size())
return false;
return true;
}
+static bool isFallthroughSwitchBranch(const SwitchBranch &Branch) {
+ struct SwitchCaseVisitor : RecursiveASTVisitor<SwitchCaseVisitor> {
+ using RecursiveASTVisitor<SwitchCaseVisitor>::DataRecursionQueue;
+
+ bool TraverseLambdaExpr(LambdaExpr *, DataRecursionQueue * = nullptr) {
+ return true; // Ignore lambdas
+ }
+
+ bool TraverseDecl(Decl *) {
+ return true; // No need to check declarations
+ }
+
+ bool TraverseSwitchStmt(SwitchStmt *, DataRecursionQueue * = nullptr) {
+ return true; // Ignore sub-switches
+ }
+
+ bool TraverseSwitchCase(SwitchCase *, DataRecursionQueue * = nullptr) {
+ return true; // Ignore cases
+ }
+
+ bool TraverseDefaultStmt(DefaultStmt *, DataRecursionQueue * = nullptr) {
+ return true; // Ignore defaults
+ }
+
+ bool TraverseAttributedStmt(AttributedStmt *S) {
+ if (!S)
+ return true;
+
+ for (const Attr *A : S->getAttrs()) {
+ if (isa<FallThroughAttr>(A))
+ return false;
+ }
+
+ return true;
+ }
+ } Visitor;
+
+ for (const Stmt *Elem : Branch) {
+ if (!Visitor.TraverseStmt(const_cast<Stmt *>(Elem)))
+ return true;
+ }
+ return false;
+}
+
namespace clang::tidy::bugprone {
void BranchCloneCheck::registerMatchers(MatchFinder *Finder) {
auto *End = Branches.end();
auto *BeginCurrent = Branches.begin();
while (BeginCurrent < End) {
+ if (isFallthroughSwitchBranch(*BeginCurrent)) {
+ ++BeginCurrent;
+ continue;
+ }
+
auto *EndCurrent = BeginCurrent + 1;
while (EndCurrent < End &&
areSwitchBranchesIdentical(*BeginCurrent, *EndCurrent, Context)) {
}
// At this point the iterator range {BeginCurrent, EndCurrent} contains a
// complete family of consecutive identical branches.
- if (EndCurrent > BeginCurrent + 1) {
- diag(BeginCurrent->front()->getBeginLoc(),
- "switch has %0 consecutive identical branches")
- << static_cast<int>(std::distance(BeginCurrent, EndCurrent));
-
- SourceLocation EndLoc = (EndCurrent - 1)->back()->getEndLoc();
- // If the case statement is generated from a macro, it's SourceLocation
- // may be invalid, resulting in an assertion failure down the line.
- // While not optimal, try the begin location in this case, it's still
- // better then nothing.
- if (EndLoc.isInvalid())
- EndLoc = (EndCurrent - 1)->back()->getBeginLoc();
-
- if (EndLoc.isMacroID())
- EndLoc = Context.getSourceManager().getExpansionLoc(EndLoc);
- EndLoc = Lexer::getLocForEndOfToken(EndLoc, 0, *Result.SourceManager,
- getLangOpts());
-
- if (EndLoc.isValid()) {
- diag(EndLoc, "last of these clones ends here", DiagnosticIDs::Note);
- }
+
+ if (EndCurrent == (BeginCurrent + 1)) {
+ // No consecutive identical branches that start on BeginCurrent
+ BeginCurrent = EndCurrent;
+ continue;
+ }
+
+ diag(BeginCurrent->front()->getBeginLoc(),
+ "switch has %0 consecutive identical branches")
+ << static_cast<int>(std::distance(BeginCurrent, EndCurrent));
+
+ SourceLocation EndLoc = (EndCurrent - 1)->back()->getEndLoc();
+ // If the case statement is generated from a macro, it's SourceLocation
+ // may be invalid, resulting in an assertion failure down the line.
+ // While not optimal, try the begin location in this case, it's still
+ // better then nothing.
+ if (EndLoc.isInvalid())
+ EndLoc = (EndCurrent - 1)->back()->getBeginLoc();
+ if (EndLoc.isMacroID())
+ EndLoc = Context.getSourceManager().getExpansionLoc(EndLoc);
+ EndLoc = Lexer::getLocForEndOfToken(EndLoc, 0, *Result.SourceManager,
+ getLangOpts());
+ if (EndLoc.isValid()) {
+ diag(EndLoc, "last of these clones ends here", DiagnosticIDs::Note);
}
BeginCurrent = EndCurrent;
}