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 RecursiveCloneTypeIIHashConstraint(), 73 MinComplexityConstraint(2), MinGroupSizeConstraint(2), 74 RecursiveCloneTypeIIVerifyConstraint(), 75 OnlyLargestCloneConstraint()); 76 77 ASSERT_EQ(CloneGroups.size(), 1u); 78 ASSERT_EQ(CloneGroups.front().size(), 2u); 79 80 for (auto &Clone : CloneGroups.front()) { 81 const auto ND = dyn_cast<const FunctionDecl>(Clone.getContainingDecl()); 82 ASSERT_TRUE(ND != nullptr); 83 // Check that no function name starting with "bar" is in the results... 84 ASSERT_TRUE(ND->getNameAsString().find("bar") != 0); 85 } 86 87 // Retry above's example without the filter... 88 CloneGroups.clear(); 89 90 Detector.findClones(CloneGroups, RecursiveCloneTypeIIHashConstraint(), 91 MinComplexityConstraint(2), MinGroupSizeConstraint(2), 92 RecursiveCloneTypeIIVerifyConstraint(), 93 OnlyLargestCloneConstraint()); 94 ASSERT_EQ(CloneGroups.size(), 1u); 95 ASSERT_EQ(CloneGroups.front().size(), 4u); 96 97 // Count how many functions with the bar prefix we have in the results. 98 int FoundFunctionsWithBarPrefix = 0; 99 for (auto &Clone : CloneGroups.front()) { 100 const auto ND = dyn_cast<const FunctionDecl>(Clone.getContainingDecl()); 101 ASSERT_TRUE(ND != nullptr); 102 // This time check that we picked up the bar functions from above 103 if (ND->getNameAsString().find("bar") == 0) { 104 FoundFunctionsWithBarPrefix++; 105 } 106 } 107 // We should have found the two functions bar1 and bar2. 108 ASSERT_EQ(FoundFunctionsWithBarPrefix, 2); 109 } 110 } // namespace 111 } // namespace analysis 112 } // namespace clang 113