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