xref: /llvm-project/clang/unittests/Analysis/CloneDetectionTest.cpp (revision 4e600751d2f7e8e7b85a71b7128b68444bdde91b)
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