111 lines · cpp
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-exception6//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 14namespace clang {15namespace analysis {16namespace {17 18class CloneDetectionVisitor : public DynamicRecursiveASTVisitor {19 20 CloneDetector &Detector;21 22public: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 with33/// "bar".34class NoBarFunctionConstraint {35public: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 starts40 // 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 53TEST(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 out67 // 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 above101 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} // namespace109} // namespace analysis110} // namespace clang111