diff options
Diffstat (limited to 'unittests/Analysis/CloneDetectionTest.cpp')
-rw-r--r-- | unittests/Analysis/CloneDetectionTest.cpp | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/unittests/Analysis/CloneDetectionTest.cpp b/unittests/Analysis/CloneDetectionTest.cpp new file mode 100644 index 0000000000000..6d8ce3495fa8a --- /dev/null +++ b/unittests/Analysis/CloneDetectionTest.cpp @@ -0,0 +1,110 @@ +//===- unittests/Analysis/CloneDetectionTest.cpp - Clone detection tests --===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Analysis/CloneDetection.h" +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" + +namespace clang { +namespace analysis { +namespace { + +class CloneDetectionVisitor + : public RecursiveASTVisitor<CloneDetectionVisitor> { + + CloneDetector &Detector; + +public: + explicit CloneDetectionVisitor(CloneDetector &D) : Detector(D) {} + + bool VisitFunctionDecl(FunctionDecl *D) { + Detector.analyzeCodeBody(D); + return true; + } +}; + +/// Example constraint for testing purposes. +/// Filters out all statements that are in a function which name starts with +/// "bar". +class NoBarFunctionConstraint { +public: + void constrain(std::vector<CloneDetector::CloneGroup> &CloneGroups) { + CloneConstraint::splitCloneGroups( + CloneGroups, [](const StmtSequence &A, const StmtSequence &B) { + // Check if one of the sequences is in a function which name starts + // with "bar". + for (const StmtSequence &Arg : {A, B}) { + if (const auto *D = + dyn_cast<const FunctionDecl>(Arg.getContainingDecl())) { + if (D->getNameAsString().find("bar") == 0) + return false; + } + } + return true; + }); + } +}; + +TEST(CloneDetector, FilterFunctionsByName) { + auto ASTUnit = + clang::tooling::buildASTFromCode("void foo1(int &a1) { a1++; }\n" + "void foo2(int &a2) { a2++; }\n" + "void bar1(int &a3) { a3++; }\n" + "void bar2(int &a4) { a4++; }\n"); + auto TU = ASTUnit->getASTContext().getTranslationUnitDecl(); + + CloneDetector Detector; + // Push all the function bodies into the detector. + CloneDetectionVisitor Visitor(Detector); + Visitor.TraverseTranslationUnitDecl(TU); + + // Find clones with the usual settings, but but we want to filter out + // all statements from functions which names start with "bar". + std::vector<CloneDetector::CloneGroup> CloneGroups; + Detector.findClones(CloneGroups, NoBarFunctionConstraint(), + RecursiveCloneTypeIIConstraint(), + MinComplexityConstraint(2), MinGroupSizeConstraint(2), + OnlyLargestCloneConstraint()); + + ASSERT_EQ(CloneGroups.size(), 1u); + ASSERT_EQ(CloneGroups.front().size(), 2u); + + for (auto &Clone : CloneGroups.front()) { + const auto ND = dyn_cast<const FunctionDecl>(Clone.getContainingDecl()); + ASSERT_TRUE(ND != nullptr); + // Check that no function name starting with "bar" is in the results... + ASSERT_TRUE(ND->getNameAsString().find("bar") != 0); + } + + // Retry above's example without the filter... + CloneGroups.clear(); + + Detector.findClones(CloneGroups, RecursiveCloneTypeIIConstraint(), + MinComplexityConstraint(2), MinGroupSizeConstraint(2), + OnlyLargestCloneConstraint()); + ASSERT_EQ(CloneGroups.size(), 1u); + ASSERT_EQ(CloneGroups.front().size(), 4u); + + // Count how many functions with the bar prefix we have in the results. + int FoundFunctionsWithBarPrefix = 0; + for (auto &Clone : CloneGroups.front()) { + const auto ND = dyn_cast<const FunctionDecl>(Clone.getContainingDecl()); + ASSERT_TRUE(ND != nullptr); + // This time check that we picked up the bar functions from above + if (ND->getNameAsString().find("bar") == 0) { + FoundFunctionsWithBarPrefix++; + } + } + // We should have found the two functions bar1 and bar2. + ASSERT_EQ(FoundFunctionsWithBarPrefix, 2); +} +} // namespace +} // namespace analysis +} // namespace clang |