summaryrefslogtreecommitdiff
path: root/unittests/Analysis
diff options
context:
space:
mode:
authorArtem Dergachev <artem.dergachev@gmail.com>2017-04-06 14:34:07 +0000
committerArtem Dergachev <artem.dergachev@gmail.com>2017-04-06 14:34:07 +0000
commit59d4b1de1aa934a4bc0216fdf8b5749c93bd462d (patch)
tree9778a9aa114fe177fb0668b4fff74038afebcfe9 /unittests/Analysis
parent3a677b92ae3d038b3a9b1d16fd63faec87c5b901 (diff)
downloadclang-59d4b1de1aa934a4bc0216fdf8b5749c93bd462d.tar.gz
[analyzer] Reland r299544 "Add a modular constraint system to the CloneDetector"
Hopefully fix crashes by unshadowing the variable. Original commit message: A big part of the clone detection code is functionality for filtering clones and clone groups based on different criteria. So far this filtering process was hardcoded into the CloneDetector class, which made it hard to understand and, ultimately, to extend. This patch splits the CloneDetector's logic into a sequence of reusable constraints that are used for filtering clone groups. These constraints can be turned on and off and reodreder at will, and new constraints are easy to implement if necessary. Unit tests are added for the new constraint interface. This is a refactoring patch - no functional change intended. Patch by Raphael Isemann! Differential Revision: https://reviews.llvm.org/D23418 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@299653 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'unittests/Analysis')
-rw-r--r--unittests/Analysis/CMakeLists.txt5
-rw-r--r--unittests/Analysis/CloneDetectionTest.cpp110
2 files changed, 113 insertions, 2 deletions
diff --git a/unittests/Analysis/CMakeLists.txt b/unittests/Analysis/CMakeLists.txt
index 926f586be3..62db8f652e 100644
--- a/unittests/Analysis/CMakeLists.txt
+++ b/unittests/Analysis/CMakeLists.txt
@@ -2,11 +2,12 @@ set(LLVM_LINK_COMPONENTS
Support
)
-add_clang_unittest(CFGTests
+add_clang_unittest(ClangAnalysisTests
CFGTest.cpp
+ CloneDetectionTest.cpp
)
-target_link_libraries(CFGTests
+target_link_libraries(ClangAnalysisTests
clangAnalysis
clangAST
clangASTMatchers
diff --git a/unittests/Analysis/CloneDetectionTest.cpp b/unittests/Analysis/CloneDetectionTest.cpp
new file mode 100644
index 0000000000..6d8ce3495f
--- /dev/null
+++ b/unittests/Analysis/CloneDetectionTest.cpp
@@ -0,0 +1,110 @@
+//===- unittests/Analysis/CloneDetectionTest.cpp - Clone detection tests --===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Analysis/CloneDetection.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace analysis {
+namespace {
+
+class CloneDetectionVisitor
+ : public RecursiveASTVisitor<CloneDetectionVisitor> {
+
+ CloneDetector &Detector;
+
+public:
+ explicit CloneDetectionVisitor(CloneDetector &D) : Detector(D) {}
+
+ bool VisitFunctionDecl(FunctionDecl *D) {
+ Detector.analyzeCodeBody(D);
+ return true;
+ }
+};
+
+/// Example constraint for testing purposes.
+/// Filters out all statements that are in a function which name starts with
+/// "bar".
+class NoBarFunctionConstraint {
+public:
+ void constrain(std::vector<CloneDetector::CloneGroup> &CloneGroups) {
+ CloneConstraint::splitCloneGroups(
+ CloneGroups, [](const StmtSequence &A, const StmtSequence &B) {
+ // Check if one of the sequences is in a function which name starts
+ // with "bar".
+ for (const StmtSequence &Arg : {A, B}) {
+ if (const auto *D =
+ dyn_cast<const FunctionDecl>(Arg.getContainingDecl())) {
+ if (D->getNameAsString().find("bar") == 0)
+ return false;
+ }
+ }
+ return true;
+ });
+ }
+};
+
+TEST(CloneDetector, FilterFunctionsByName) {
+ auto ASTUnit =
+ clang::tooling::buildASTFromCode("void foo1(int &a1) { a1++; }\n"
+ "void foo2(int &a2) { a2++; }\n"
+ "void bar1(int &a3) { a3++; }\n"
+ "void bar2(int &a4) { a4++; }\n");
+ auto TU = ASTUnit->getASTContext().getTranslationUnitDecl();
+
+ CloneDetector Detector;
+ // Push all the function bodies into the detector.
+ CloneDetectionVisitor Visitor(Detector);
+ Visitor.TraverseTranslationUnitDecl(TU);
+
+ // Find clones with the usual settings, but but we want to filter out
+ // all statements from functions which names start with "bar".
+ std::vector<CloneDetector::CloneGroup> CloneGroups;
+ Detector.findClones(CloneGroups, NoBarFunctionConstraint(),
+ RecursiveCloneTypeIIConstraint(),
+ MinComplexityConstraint(2), MinGroupSizeConstraint(2),
+ OnlyLargestCloneConstraint());
+
+ ASSERT_EQ(CloneGroups.size(), 1u);
+ ASSERT_EQ(CloneGroups.front().size(), 2u);
+
+ for (auto &Clone : CloneGroups.front()) {
+ const auto ND = dyn_cast<const FunctionDecl>(Clone.getContainingDecl());
+ ASSERT_TRUE(ND != nullptr);
+ // Check that no function name starting with "bar" is in the results...
+ ASSERT_TRUE(ND->getNameAsString().find("bar") != 0);
+ }
+
+ // Retry above's example without the filter...
+ CloneGroups.clear();
+
+ Detector.findClones(CloneGroups, RecursiveCloneTypeIIConstraint(),
+ MinComplexityConstraint(2), MinGroupSizeConstraint(2),
+ OnlyLargestCloneConstraint());
+ ASSERT_EQ(CloneGroups.size(), 1u);
+ ASSERT_EQ(CloneGroups.front().size(), 4u);
+
+ // Count how many functions with the bar prefix we have in the results.
+ int FoundFunctionsWithBarPrefix = 0;
+ for (auto &Clone : CloneGroups.front()) {
+ const auto ND = dyn_cast<const FunctionDecl>(Clone.getContainingDecl());
+ ASSERT_TRUE(ND != nullptr);
+ // This time check that we picked up the bar functions from above
+ if (ND->getNameAsString().find("bar") == 0) {
+ FoundFunctionsWithBarPrefix++;
+ }
+ }
+ // We should have found the two functions bar1 and bar2.
+ ASSERT_EQ(FoundFunctionsWithBarPrefix, 2);
+}
+} // namespace
+} // namespace analysis
+} // namespace clang