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/Analysis/CloneDetection.h" 10 #include "clang/AST/DynamicRecursiveASTVisitor.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 : public DynamicRecursiveASTVisitor { 19 20 CloneDetector &Detector; 21 22 public: 23 explicit CloneDetectionVisitor(CloneDetector &D) : Detector(D) {} 24 25 bool VisitFunctionDecl(FunctionDecl *D) override { 26 Detector.analyzeCodeBody(D); 27 return true; 28 } 29 }; 30 31 /// Example constraint for testing purposes. 32 /// Filters out all statements that are in a function which name starts with 33 /// "bar". 34 class NoBarFunctionConstraint { 35 public: 36 void constrain(std::vector<CloneDetector::CloneGroup> &CloneGroups) { 37 CloneConstraint::splitCloneGroups( 38 CloneGroups, [](const StmtSequence &A, const StmtSequence &B) { 39 // Check if one of the sequences is in a function which name starts 40 // with "bar". 41 for (const StmtSequence &Arg : {A, B}) { 42 if (const auto *D = 43 dyn_cast<const FunctionDecl>(Arg.getContainingDecl())) { 44 if (D->getName().starts_with("bar")) 45 return false; 46 } 47 } 48 return true; 49 }); 50 } 51 }; 52 53 TEST(CloneDetector, FilterFunctionsByName) { 54 auto ASTUnit = 55 clang::tooling::buildASTFromCode("void foo1(int &a1) { a1++; }\n" 56 "void foo2(int &a2) { a2++; }\n" 57 "void bar1(int &a3) { a3++; }\n" 58 "void bar2(int &a4) { a4++; }\n"); 59 auto TU = ASTUnit->getASTContext().getTranslationUnitDecl(); 60 61 CloneDetector Detector; 62 // Push all the function bodies into the detector. 63 CloneDetectionVisitor Visitor(Detector); 64 Visitor.TraverseTranslationUnitDecl(TU); 65 66 // Find clones with the usual settings, but we want to filter out 67 // all statements from functions which names start with "bar". 68 std::vector<CloneDetector::CloneGroup> CloneGroups; 69 Detector.findClones(CloneGroups, NoBarFunctionConstraint(), 70 RecursiveCloneTypeIIHashConstraint(), 71 MinComplexityConstraint(2), MinGroupSizeConstraint(2), 72 RecursiveCloneTypeIIVerifyConstraint(), 73 OnlyLargestCloneConstraint()); 74 75 ASSERT_EQ(CloneGroups.size(), 1u); 76 ASSERT_EQ(CloneGroups.front().size(), 2u); 77 78 for (auto &Clone : CloneGroups.front()) { 79 const auto ND = dyn_cast<const FunctionDecl>(Clone.getContainingDecl()); 80 ASSERT_TRUE(ND != nullptr); 81 // Check that no function name starting with "bar" is in the results... 82 ASSERT_TRUE(ND->getNameAsString().find("bar") != 0); 83 } 84 85 // Retry above's example without the filter... 86 CloneGroups.clear(); 87 88 Detector.findClones(CloneGroups, RecursiveCloneTypeIIHashConstraint(), 89 MinComplexityConstraint(2), MinGroupSizeConstraint(2), 90 RecursiveCloneTypeIIVerifyConstraint(), 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