diff options
author | Artem Dergachev <artem.dergachev@gmail.com> | 2017-04-06 14:34:07 +0000 |
---|---|---|
committer | Artem Dergachev <artem.dergachev@gmail.com> | 2017-04-06 14:34:07 +0000 |
commit | 59d4b1de1aa934a4bc0216fdf8b5749c93bd462d (patch) | |
tree | 9778a9aa114fe177fb0668b4fff74038afebcfe9 /unittests/Analysis | |
parent | 3a677b92ae3d038b3a9b1d16fd63faec87c5b901 (diff) | |
download | clang-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.txt | 5 | ||||
-rw-r--r-- | unittests/Analysis/CloneDetectionTest.cpp | 110 |
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 |