1 //===- unittests/Analysis/CloneDetectionTest.cpp - Clone detection tests --===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "clang/AST/RecursiveASTVisitor.h" 11 #include "clang/Analysis/CloneDetection.h" 12 #include "clang/Tooling/Tooling.h" 13 #include "gtest/gtest.h" 14 15 namespace clang { 16 namespace analysis { 17 namespace { 18 19 class CloneDetectionVisitor 20 : public RecursiveASTVisitor<CloneDetectionVisitor> { 21 22 CloneDetector &Detector; 23 24 public: 25 explicit CloneDetectionVisitor(CloneDetector &D) : Detector(D) {} 26 27 bool VisitFunctionDecl(FunctionDecl *D) { 28 Detector.analyzeCodeBody(D); 29 return true; 30 } 31 }; 32 33 /// Example constraint for testing purposes. 34 /// Filters out all statements that are in a function which name starts with 35 /// "bar". 36 class NoBarFunctionConstraint { 37 public: 38 void constrain(std::vector<CloneDetector::CloneGroup> &CloneGroups) { 39 CloneConstraint::splitCloneGroups( 40 CloneGroups, [](const StmtSequence &A, const StmtSequence &B) { 41 // Check if one of the sequences is in a function which name starts 42 // with "bar". 43 for (const StmtSequence &Arg : {A, B}) { 44 if (const auto *D = 45 dyn_cast<const FunctionDecl>(Arg.getContainingDecl())) { 46 if (D->getNameAsString().find("bar") == 0) 47 return false; 48 } 49 } 50 return true; 51 }); 52 } 53 }; 54 55 TEST(CloneDetector, FilterFunctionsByName) { 56 auto ASTUnit = 57 clang::tooling::buildASTFromCode("void foo1(int &a1) { a1++; }\n" 58 "void foo2(int &a2) { a2++; }\n" 59 "void bar1(int &a3) { a3++; }\n" 60 "void bar2(int &a4) { a4++; }\n"); 61 auto TU = ASTUnit->getASTContext().getTranslationUnitDecl(); 62 63 CloneDetector Detector; 64 // Push all the function bodies into the detector. 65 CloneDetectionVisitor Visitor(Detector); 66 Visitor.TraverseTranslationUnitDecl(TU); 67 68 // Find clones with the usual settings, but but we want to filter out 69 // all statements from functions which names start with "bar". 70 std::vector<CloneDetector::CloneGroup> CloneGroups; 71 Detector.findClones(CloneGroups, NoBarFunctionConstraint(), 72 RecursiveCloneTypeIIConstraint(), 73 MinComplexityConstraint(2), MinGroupSizeConstraint(2), 74 OnlyLargestCloneConstraint()); 75 76 ASSERT_EQ(CloneGroups.size(), 1u); 77 ASSERT_EQ(CloneGroups.front().size(), 2u); 78 79 for (auto &Clone : CloneGroups.front()) { 80 const auto ND = dyn_cast<const FunctionDecl>(Clone.getContainingDecl()); 81 ASSERT_TRUE(ND != nullptr); 82 // Check that no function name starting with "bar" is in the results... 83 ASSERT_TRUE(ND->getNameAsString().find("bar") != 0); 84 } 85 86 // Retry above's example without the filter... 87 CloneGroups.clear(); 88 89 Detector.findClones(CloneGroups, RecursiveCloneTypeIIConstraint(), 90 MinComplexityConstraint(2), MinGroupSizeConstraint(2), 91 OnlyLargestCloneConstraint()); 92 ASSERT_EQ(CloneGroups.size(), 1u); 93 ASSERT_EQ(CloneGroups.front().size(), 4u); 94 95 // Count how many functions with the bar prefix we have in the results. 96 int FoundFunctionsWithBarPrefix = 0; 97 for (auto &Clone : CloneGroups.front()) { 98 const auto ND = dyn_cast<const FunctionDecl>(Clone.getContainingDecl()); 99 ASSERT_TRUE(ND != nullptr); 100 // This time check that we picked up the bar functions from above 101 if (ND->getNameAsString().find("bar") == 0) { 102 FoundFunctionsWithBarPrefix++; 103 } 104 } 105 // We should have found the two functions bar1 and bar2. 106 ASSERT_EQ(FoundFunctionsWithBarPrefix, 2); 107 } 108 } // namespace 109 } // namespace analysis 110 } // namespace clang 111