blob: 6d8ce3495fa8ae2d1841405758fafb7d43dfcd0e [file] [log] [blame]
Artem Dergachevda9e7182017-04-06 14:34:07 +00001//===- 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
15namespace clang {
16namespace analysis {
17namespace {
18
19class CloneDetectionVisitor
20 : public RecursiveASTVisitor<CloneDetectionVisitor> {
21
22 CloneDetector &Detector;
23
24public:
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".
36class NoBarFunctionConstraint {
37public:
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
55TEST(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 RecursiveCloneTypeIIConstraint(),
73 MinComplexityConstraint(2), MinGroupSizeConstraint(2),
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, RecursiveCloneTypeIIConstraint(),
90 MinComplexityConstraint(2), MinGroupSizeConstraint(2),
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