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