xref: /llvm-project/clang/lib/Analysis/CloneDetection.cpp (revision eb1c7c13390105f8b84c7ec85bd222a56e57a480)
1ba816326SArtem Dergachev //===--- CloneDetection.cpp - Finds code clones in an AST -------*- C++ -*-===//
2ba816326SArtem 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
6ba816326SArtem Dergachev //
7ba816326SArtem Dergachev //===----------------------------------------------------------------------===//
8ba816326SArtem Dergachev ///
9b23ccecbSRaphael Isemann /// This file implements classes for searching and analyzing source code clones.
10ba816326SArtem Dergachev ///
11ba816326SArtem Dergachev //===----------------------------------------------------------------------===//
12ba816326SArtem Dergachev 
13ba816326SArtem Dergachev #include "clang/Analysis/CloneDetection.h"
1460573ae6SReid Kleckner #include "clang/AST/Attr.h"
151a267692SJohannes Altmanninger #include "clang/AST/DataCollection.h"
161a267692SJohannes Altmanninger #include "clang/AST/DeclTemplate.h"
1786565c13SReid Kleckner #include "clang/Basic/SourceManager.h"
1856574868SArtem Dergachev #include "llvm/Support/MD5.h"
19d91d19e6SLeslie Zhai #include "llvm/Support/Path.h"
20ba816326SArtem Dergachev 
21ba816326SArtem Dergachev using namespace clang;
22ba816326SArtem Dergachev 
StmtSequence(const CompoundStmt * Stmt,const Decl * D,unsigned StartIndex,unsigned EndIndex)23da9e718fSArtem Dergachev StmtSequence::StmtSequence(const CompoundStmt *Stmt, const Decl *D,
24ba816326SArtem Dergachev                            unsigned StartIndex, unsigned EndIndex)
25da9e718fSArtem Dergachev     : S(Stmt), D(D), StartIndex(StartIndex), EndIndex(EndIndex) {
26ba816326SArtem Dergachev   assert(Stmt && "Stmt must not be a nullptr");
27ba816326SArtem Dergachev   assert(StartIndex < EndIndex && "Given array should not be empty");
28ba816326SArtem Dergachev   assert(EndIndex <= Stmt->size() && "Given array too big for this Stmt");
29ba816326SArtem Dergachev }
30ba816326SArtem Dergachev 
StmtSequence(const Stmt * Stmt,const Decl * D)31da9e718fSArtem Dergachev StmtSequence::StmtSequence(const Stmt *Stmt, const Decl *D)
32da9e718fSArtem Dergachev     : S(Stmt), D(D), StartIndex(0), EndIndex(0) {}
33ba816326SArtem Dergachev 
StmtSequence()34ba816326SArtem Dergachev StmtSequence::StmtSequence()
35da9e718fSArtem Dergachev     : S(nullptr), D(nullptr), StartIndex(0), EndIndex(0) {}
36ba816326SArtem Dergachev 
contains(const StmtSequence & Other) const37ba816326SArtem Dergachev bool StmtSequence::contains(const StmtSequence &Other) const {
38da9e718fSArtem Dergachev   // If both sequences reside in different declarations, they can never contain
39da9e718fSArtem Dergachev   // each other.
40da9e718fSArtem Dergachev   if (D != Other.D)
41ba816326SArtem Dergachev     return false;
42ba816326SArtem Dergachev 
43da9e718fSArtem Dergachev   const SourceManager &SM = getASTContext().getSourceManager();
44ba816326SArtem Dergachev 
45ba816326SArtem Dergachev   // Otherwise check if the start and end locations of the current sequence
46ba816326SArtem Dergachev   // surround the other sequence.
47ba816326SArtem Dergachev   bool StartIsInBounds =
48a6e4358fSStephen Kelly       SM.isBeforeInTranslationUnit(getBeginLoc(), Other.getBeginLoc()) ||
49a6e4358fSStephen Kelly       getBeginLoc() == Other.getBeginLoc();
50ba816326SArtem Dergachev   if (!StartIsInBounds)
51ba816326SArtem Dergachev     return false;
52ba816326SArtem Dergachev 
53ba816326SArtem Dergachev   bool EndIsInBounds =
54ba816326SArtem Dergachev       SM.isBeforeInTranslationUnit(Other.getEndLoc(), getEndLoc()) ||
55ba816326SArtem Dergachev       Other.getEndLoc() == getEndLoc();
56ba816326SArtem Dergachev   return EndIsInBounds;
57ba816326SArtem Dergachev }
58ba816326SArtem Dergachev 
begin() const59ba816326SArtem Dergachev StmtSequence::iterator StmtSequence::begin() const {
60ba816326SArtem Dergachev   if (!holdsSequence()) {
61ba816326SArtem Dergachev     return &S;
62ba816326SArtem Dergachev   }
63ba816326SArtem Dergachev   auto CS = cast<CompoundStmt>(S);
64ba816326SArtem Dergachev   return CS->body_begin() + StartIndex;
65ba816326SArtem Dergachev }
66ba816326SArtem Dergachev 
end() const67ba816326SArtem Dergachev StmtSequence::iterator StmtSequence::end() const {
68ba816326SArtem Dergachev   if (!holdsSequence()) {
695721e0f3SVassil Vassilev     return reinterpret_cast<StmtSequence::iterator>(&S) + 1;
70ba816326SArtem Dergachev   }
71ba816326SArtem Dergachev   auto CS = cast<CompoundStmt>(S);
72ba816326SArtem Dergachev   return CS->body_begin() + EndIndex;
73ba816326SArtem Dergachev }
74ba816326SArtem Dergachev 
getASTContext() const75da9e718fSArtem Dergachev ASTContext &StmtSequence::getASTContext() const {
76da9e718fSArtem Dergachev   assert(D);
77da9e718fSArtem Dergachev   return D->getASTContext();
78da9e718fSArtem Dergachev }
79da9e718fSArtem Dergachev 
getBeginLoc() const8094d33c04SStephen Kelly SourceLocation StmtSequence::getBeginLoc() const {
81f2ceec48SStephen Kelly   return front()->getBeginLoc();
82ba816326SArtem Dergachev }
83ba816326SArtem Dergachev 
getEndLoc() const841c301dcbSStephen Kelly SourceLocation StmtSequence::getEndLoc() const { return back()->getEndLoc(); }
85ba816326SArtem Dergachev 
getSourceRange() const864eca0de7SArtem Dergachev SourceRange StmtSequence::getSourceRange() const {
87a6e4358fSStephen Kelly   return SourceRange(getBeginLoc(), getEndLoc());
884eca0de7SArtem Dergachev }
894eca0de7SArtem Dergachev 
analyzeCodeBody(const Decl * D)90ba816326SArtem Dergachev void CloneDetector::analyzeCodeBody(const Decl *D) {
91ba816326SArtem Dergachev   assert(D);
92ba816326SArtem Dergachev   assert(D->hasBody());
93da9e718fSArtem Dergachev 
94da9e718fSArtem Dergachev   Sequences.push_back(StmtSequence(D->getBody(), D));
95ba816326SArtem Dergachev }
96ba816326SArtem Dergachev 
97da9e718fSArtem Dergachev /// Returns true if and only if \p Stmt contains at least one other
98ba816326SArtem Dergachev /// sequence in the \p Group.
containsAnyInGroup(StmtSequence & Seq,CloneDetector::CloneGroup & Group)99da9e718fSArtem Dergachev static bool containsAnyInGroup(StmtSequence &Seq,
100da9e718fSArtem Dergachev                                CloneDetector::CloneGroup &Group) {
101da9e718fSArtem Dergachev   for (StmtSequence &GroupSeq : Group) {
102da9e718fSArtem Dergachev     if (Seq.contains(GroupSeq))
103ba816326SArtem Dergachev       return true;
104ba816326SArtem Dergachev   }
105ba816326SArtem Dergachev   return false;
106ba816326SArtem Dergachev }
107ba816326SArtem Dergachev 
108da9e718fSArtem Dergachev /// Returns true if and only if all sequences in \p OtherGroup are
109ba816326SArtem Dergachev /// contained by a sequence in \p Group.
containsGroup(CloneDetector::CloneGroup & Group,CloneDetector::CloneGroup & OtherGroup)110da9e718fSArtem Dergachev static bool containsGroup(CloneDetector::CloneGroup &Group,
111ba816326SArtem Dergachev                           CloneDetector::CloneGroup &OtherGroup) {
112ba816326SArtem Dergachev   // We have less sequences in the current group than we have in the other,
113ba816326SArtem Dergachev   // so we will never fulfill the requirement for returning true. This is only
114ba816326SArtem Dergachev   // possible because we know that a sequence in Group can contain at most
115ba816326SArtem Dergachev   // one sequence in OtherGroup.
116da9e718fSArtem Dergachev   if (Group.size() < OtherGroup.size())
117ba816326SArtem Dergachev     return false;
118ba816326SArtem Dergachev 
119da9e718fSArtem Dergachev   for (StmtSequence &Stmt : Group) {
120ba816326SArtem Dergachev     if (!containsAnyInGroup(Stmt, OtherGroup))
121ba816326SArtem Dergachev       return false;
122ba816326SArtem Dergachev   }
123ba816326SArtem Dergachev   return true;
124ba816326SArtem Dergachev }
125ba816326SArtem Dergachev 
constrain(std::vector<CloneDetector::CloneGroup> & Result)126da9e718fSArtem Dergachev void OnlyLargestCloneConstraint::constrain(
127da9e718fSArtem Dergachev     std::vector<CloneDetector::CloneGroup> &Result) {
128ba816326SArtem Dergachev   std::vector<unsigned> IndexesToRemove;
129ba816326SArtem Dergachev 
130ba816326SArtem Dergachev   // Compare every group in the result with the rest. If one groups contains
131ba816326SArtem Dergachev   // another group, we only need to return the bigger group.
132ba816326SArtem Dergachev   // Note: This doesn't scale well, so if possible avoid calling any heavy
133ba816326SArtem Dergachev   // function from this loop to minimize the performance impact.
134ba816326SArtem Dergachev   for (unsigned i = 0; i < Result.size(); ++i) {
135ba816326SArtem Dergachev     for (unsigned j = 0; j < Result.size(); ++j) {
136ba816326SArtem Dergachev       // Don't compare a group with itself.
137ba816326SArtem Dergachev       if (i == j)
138ba816326SArtem Dergachev         continue;
139ba816326SArtem Dergachev 
140ba816326SArtem Dergachev       if (containsGroup(Result[j], Result[i])) {
141ba816326SArtem Dergachev         IndexesToRemove.push_back(i);
142ba816326SArtem Dergachev         break;
143ba816326SArtem Dergachev       }
144ba816326SArtem Dergachev     }
145ba816326SArtem Dergachev   }
146ba816326SArtem Dergachev 
147ba816326SArtem Dergachev   // Erasing a list of indexes from the vector should be done with decreasing
148ba816326SArtem Dergachev   // indexes. As IndexesToRemove is constructed with increasing values, we just
149ba816326SArtem Dergachev   // reverse iterate over it to get the desired order.
150*eb1c7c13SKazu Hirata   for (unsigned I : llvm::reverse(IndexesToRemove))
151*eb1c7c13SKazu Hirata     Result.erase(Result.begin() + I);
152ba816326SArtem Dergachev }
1532fc1985dSArtem Dergachev 
isAutoGenerated(const CloneDetector::CloneGroup & Group)1541a267692SJohannes Altmanninger bool FilenamePatternConstraint::isAutoGenerated(
1551a267692SJohannes Altmanninger     const CloneDetector::CloneGroup &Group) {
156d91d19e6SLeslie Zhai   if (IgnoredFilesPattern.empty() || Group.empty() ||
15795b5f42dSJan Kratochvil       !IgnoredFilesRegex->isValid())
158d91d19e6SLeslie Zhai     return false;
159d91d19e6SLeslie Zhai 
160d91d19e6SLeslie Zhai   for (const StmtSequence &S : Group) {
161d91d19e6SLeslie Zhai     const SourceManager &SM = S.getASTContext().getSourceManager();
1621a267692SJohannes Altmanninger     StringRef Filename = llvm::sys::path::filename(
1631a267692SJohannes Altmanninger         SM.getFilename(S.getContainingDecl()->getLocation()));
164d91d19e6SLeslie Zhai     if (IgnoredFilesRegex->match(Filename))
165d91d19e6SLeslie Zhai       return true;
166d91d19e6SLeslie Zhai   }
167d91d19e6SLeslie Zhai 
168d91d19e6SLeslie Zhai   return false;
169d91d19e6SLeslie Zhai }
170d91d19e6SLeslie Zhai 
1711a267692SJohannes Altmanninger /// This class defines what a type II code clone is: If it collects for two
1721a267692SJohannes Altmanninger /// statements the same data, then those two statements are considered to be
1731a267692SJohannes Altmanninger /// clones of each other.
1741a267692SJohannes Altmanninger ///
1751a267692SJohannes Altmanninger /// All collected data is forwarded to the given data consumer of the type T.
1761a267692SJohannes Altmanninger /// The data consumer class needs to provide a member method with the signature:
1771a267692SJohannes Altmanninger ///   update(StringRef Str)
1781a267692SJohannes Altmanninger namespace {
1791a267692SJohannes Altmanninger template <class T>
1801a267692SJohannes Altmanninger class CloneTypeIIStmtDataCollector
1811a267692SJohannes Altmanninger     : public ConstStmtVisitor<CloneTypeIIStmtDataCollector<T>> {
1821a267692SJohannes Altmanninger   ASTContext &Context;
1831a267692SJohannes Altmanninger   /// The data sink to which all data is forwarded.
1841a267692SJohannes Altmanninger   T &DataConsumer;
1851a267692SJohannes Altmanninger 
addData(const Ty & Data)1861a267692SJohannes Altmanninger   template <class Ty> void addData(const Ty &Data) {
1871a267692SJohannes Altmanninger     data_collection::addDataToConsumer(DataConsumer, Data);
1881a267692SJohannes Altmanninger   }
1891a267692SJohannes Altmanninger 
1901a267692SJohannes Altmanninger public:
CloneTypeIIStmtDataCollector(const Stmt * S,ASTContext & Context,T & DataConsumer)1911a267692SJohannes Altmanninger   CloneTypeIIStmtDataCollector(const Stmt *S, ASTContext &Context,
1921a267692SJohannes Altmanninger                                T &DataConsumer)
1931a267692SJohannes Altmanninger       : Context(Context), DataConsumer(DataConsumer) {
1941a267692SJohannes Altmanninger     this->Visit(S);
1951a267692SJohannes Altmanninger   }
1961a267692SJohannes Altmanninger 
1971a267692SJohannes Altmanninger // Define a visit method for each class to collect data and subsequently visit
1981a267692SJohannes Altmanninger // all parent classes. This uses a template so that custom visit methods by us
1991a267692SJohannes Altmanninger // take precedence.
2001a267692SJohannes Altmanninger #define DEF_ADD_DATA(CLASS, CODE)                                              \
2011a267692SJohannes Altmanninger   template <class = void> void Visit##CLASS(const CLASS *S) {                  \
2021a267692SJohannes Altmanninger     CODE;                                                                      \
2031a267692SJohannes Altmanninger     ConstStmtVisitor<CloneTypeIIStmtDataCollector<T>>::Visit##CLASS(S);        \
2041a267692SJohannes Altmanninger   }
2051a267692SJohannes Altmanninger 
2061509da08SJohannes Altmanninger #include "clang/AST/StmtDataCollectors.inc"
2071a267692SJohannes Altmanninger 
2081a267692SJohannes Altmanninger // Type II clones ignore variable names and literals, so let's skip them.
2091a267692SJohannes Altmanninger #define SKIP(CLASS)                                                            \
2101a267692SJohannes Altmanninger   void Visit##CLASS(const CLASS *S) {                                          \
2111a267692SJohannes Altmanninger     ConstStmtVisitor<CloneTypeIIStmtDataCollector<T>>::Visit##CLASS(S);        \
2121a267692SJohannes Altmanninger   }
2131a267692SJohannes Altmanninger   SKIP(DeclRefExpr)
2141a267692SJohannes Altmanninger   SKIP(MemberExpr)
2151a267692SJohannes Altmanninger   SKIP(IntegerLiteral)
2161a267692SJohannes Altmanninger   SKIP(FloatingLiteral)
2171a267692SJohannes Altmanninger   SKIP(StringLiteral)
2181a267692SJohannes Altmanninger   SKIP(CXXBoolLiteralExpr)
2191a267692SJohannes Altmanninger   SKIP(CharacterLiteral)
2201a267692SJohannes Altmanninger #undef SKIP
2211a267692SJohannes Altmanninger };
2221a267692SJohannes Altmanninger } // end anonymous namespace
2231a267692SJohannes Altmanninger 
createHash(llvm::MD5 & Hash)224da9e718fSArtem Dergachev static size_t createHash(llvm::MD5 &Hash) {
225da9e718fSArtem Dergachev   size_t HashCode;
2262fc1985dSArtem Dergachev 
227da9e718fSArtem Dergachev   // Create the final hash code for the current Stmt.
228da9e718fSArtem Dergachev   llvm::MD5::MD5Result HashResult;
229da9e718fSArtem Dergachev   Hash.final(HashResult);
2302fc1985dSArtem Dergachev 
231da9e718fSArtem Dergachev   // Copy as much as possible of the generated hash code to the Stmt's hash
232da9e718fSArtem Dergachev   // code.
233da9e718fSArtem Dergachev   std::memcpy(&HashCode, &HashResult,
234da9e718fSArtem Dergachev               std::min(sizeof(HashCode), sizeof(HashResult)));
2352fc1985dSArtem Dergachev 
236da9e718fSArtem Dergachev   return HashCode;
237da9e718fSArtem Dergachev }
238da9e718fSArtem Dergachev 
23970686a15SRaphael Isemann /// Generates and saves a hash code for the given Stmt.
24070686a15SRaphael Isemann /// \param S The given Stmt.
24170686a15SRaphael Isemann /// \param D The Decl containing S.
24270686a15SRaphael Isemann /// \param StmtsByHash Output parameter that will contain the hash codes for
24370686a15SRaphael Isemann ///                    each StmtSequence in the given Stmt.
24470686a15SRaphael Isemann /// \return The hash code of the given Stmt.
24570686a15SRaphael Isemann ///
24670686a15SRaphael Isemann /// If the given Stmt is a CompoundStmt, this method will also generate
24770686a15SRaphael Isemann /// hashes for all possible StmtSequences in the children of this Stmt.
24870686a15SRaphael Isemann static size_t
saveHash(const Stmt * S,const Decl * D,std::vector<std::pair<size_t,StmtSequence>> & StmtsByHash)24970686a15SRaphael Isemann saveHash(const Stmt *S, const Decl *D,
250da9e718fSArtem Dergachev          std::vector<std::pair<size_t, StmtSequence>> &StmtsByHash) {
251da9e718fSArtem Dergachev   llvm::MD5 Hash;
252da9e718fSArtem Dergachev   ASTContext &Context = D->getASTContext();
253da9e718fSArtem Dergachev 
2541a267692SJohannes Altmanninger   CloneTypeIIStmtDataCollector<llvm::MD5>(S, Context, Hash);
255da9e718fSArtem Dergachev 
256da9e718fSArtem Dergachev   auto CS = dyn_cast<CompoundStmt>(S);
257da9e718fSArtem Dergachev   SmallVector<size_t, 8> ChildHashes;
258da9e718fSArtem Dergachev 
259da9e718fSArtem Dergachev   for (const Stmt *Child : S->children()) {
260da9e718fSArtem Dergachev     if (Child == nullptr) {
261da9e718fSArtem Dergachev       ChildHashes.push_back(0);
262da9e718fSArtem Dergachev       continue;
263da9e718fSArtem Dergachev     }
264da9e718fSArtem Dergachev     size_t ChildHash = saveHash(Child, D, StmtsByHash);
265da9e718fSArtem Dergachev     Hash.update(
266da9e718fSArtem Dergachev         StringRef(reinterpret_cast<char *>(&ChildHash), sizeof(ChildHash)));
267da9e718fSArtem Dergachev     ChildHashes.push_back(ChildHash);
268da9e718fSArtem Dergachev   }
269da9e718fSArtem Dergachev 
270da9e718fSArtem Dergachev   if (CS) {
2714eac9f05SRaphael Isemann     // If we're in a CompoundStmt, we hash all possible combinations of child
2724eac9f05SRaphael Isemann     // statements to find clones in those subsequences.
2734eac9f05SRaphael Isemann     // We first go through every possible starting position of a subsequence.
2744eac9f05SRaphael Isemann     for (unsigned Pos = 0; Pos < CS->size(); ++Pos) {
2754eac9f05SRaphael Isemann       // Then we try all possible lengths this subsequence could have and
2764eac9f05SRaphael Isemann       // reuse the same hash object to make sure we only hash every child
2774eac9f05SRaphael Isemann       // hash exactly once.
278da9e718fSArtem Dergachev       llvm::MD5 Hash;
2794eac9f05SRaphael Isemann       for (unsigned Length = 1; Length <= CS->size() - Pos; ++Length) {
2804eac9f05SRaphael Isemann         // Grab the current child hash and put it into our hash. We do
2814eac9f05SRaphael Isemann         // -1 on the index because we start counting the length at 1.
2824eac9f05SRaphael Isemann         size_t ChildHash = ChildHashes[Pos + Length - 1];
2834eac9f05SRaphael Isemann         Hash.update(
2844eac9f05SRaphael Isemann             StringRef(reinterpret_cast<char *>(&ChildHash), sizeof(ChildHash)));
2854eac9f05SRaphael Isemann         // If we have at least two elements in our subsequence, we can start
2864eac9f05SRaphael Isemann         // saving it.
2874eac9f05SRaphael Isemann         if (Length > 1) {
2884eac9f05SRaphael Isemann           llvm::MD5 SubHash = Hash;
289da9e718fSArtem Dergachev           StmtsByHash.push_back(std::make_pair(
2904eac9f05SRaphael Isemann               createHash(SubHash), StmtSequence(CS, D, Pos, Pos + Length)));
2914eac9f05SRaphael Isemann         }
292da9e718fSArtem Dergachev       }
293da9e718fSArtem Dergachev     }
294da9e718fSArtem Dergachev   }
295da9e718fSArtem Dergachev 
296da9e718fSArtem Dergachev   size_t HashCode = createHash(Hash);
297da9e718fSArtem Dergachev   StmtsByHash.push_back(std::make_pair(HashCode, StmtSequence(S, D)));
298da9e718fSArtem Dergachev   return HashCode;
299da9e718fSArtem Dergachev }
300da9e718fSArtem Dergachev 
301da9e718fSArtem Dergachev namespace {
302da9e718fSArtem Dergachev /// Wrapper around FoldingSetNodeID that it can be used as the template
303da9e718fSArtem Dergachev /// argument of the StmtDataCollector.
304da9e718fSArtem Dergachev class FoldingSetNodeIDWrapper {
305da9e718fSArtem Dergachev 
306da9e718fSArtem Dergachev   llvm::FoldingSetNodeID &FS;
307da9e718fSArtem Dergachev 
308da9e718fSArtem Dergachev public:
FoldingSetNodeIDWrapper(llvm::FoldingSetNodeID & FS)309da9e718fSArtem Dergachev   FoldingSetNodeIDWrapper(llvm::FoldingSetNodeID &FS) : FS(FS) {}
310da9e718fSArtem Dergachev 
update(StringRef Str)311da9e718fSArtem Dergachev   void update(StringRef Str) { FS.AddString(Str); }
312da9e718fSArtem Dergachev };
313da9e718fSArtem Dergachev } // end anonymous namespace
314da9e718fSArtem Dergachev 
315da9e718fSArtem Dergachev /// Writes the relevant data from all statements and child statements
316da9e718fSArtem Dergachev /// in the given StmtSequence into the given FoldingSetNodeID.
CollectStmtSequenceData(const StmtSequence & Sequence,FoldingSetNodeIDWrapper & OutputData)317da9e718fSArtem Dergachev static void CollectStmtSequenceData(const StmtSequence &Sequence,
318da9e718fSArtem Dergachev                                     FoldingSetNodeIDWrapper &OutputData) {
319da9e718fSArtem Dergachev   for (const Stmt *S : Sequence) {
3201a267692SJohannes Altmanninger     CloneTypeIIStmtDataCollector<FoldingSetNodeIDWrapper>(
3211a267692SJohannes Altmanninger         S, Sequence.getASTContext(), OutputData);
322da9e718fSArtem Dergachev 
323da9e718fSArtem Dergachev     for (const Stmt *Child : S->children()) {
324da9e718fSArtem Dergachev       if (!Child)
325da9e718fSArtem Dergachev         continue;
326da9e718fSArtem Dergachev 
327da9e718fSArtem Dergachev       CollectStmtSequenceData(StmtSequence(Child, Sequence.getContainingDecl()),
328da9e718fSArtem Dergachev                               OutputData);
329da9e718fSArtem Dergachev     }
330da9e718fSArtem Dergachev   }
331da9e718fSArtem Dergachev }
332da9e718fSArtem Dergachev 
333da9e718fSArtem Dergachev /// Returns true if both sequences are clones of each other.
areSequencesClones(const StmtSequence & LHS,const StmtSequence & RHS)334da9e718fSArtem Dergachev static bool areSequencesClones(const StmtSequence &LHS,
335da9e718fSArtem Dergachev                                const StmtSequence &RHS) {
336da9e718fSArtem Dergachev   // We collect the data from all statements in the sequence as we did before
337da9e718fSArtem Dergachev   // when generating a hash value for each sequence. But this time we don't
338da9e718fSArtem Dergachev   // hash the collected data and compare the whole data set instead. This
339da9e718fSArtem Dergachev   // prevents any false-positives due to hash code collisions.
340da9e718fSArtem Dergachev   llvm::FoldingSetNodeID DataLHS, DataRHS;
341da9e718fSArtem Dergachev   FoldingSetNodeIDWrapper LHSWrapper(DataLHS);
342da9e718fSArtem Dergachev   FoldingSetNodeIDWrapper RHSWrapper(DataRHS);
343da9e718fSArtem Dergachev 
344da9e718fSArtem Dergachev   CollectStmtSequenceData(LHS, LHSWrapper);
345da9e718fSArtem Dergachev   CollectStmtSequenceData(RHS, RHSWrapper);
346da9e718fSArtem Dergachev 
347da9e718fSArtem Dergachev   return DataLHS == DataRHS;
348da9e718fSArtem Dergachev }
349da9e718fSArtem Dergachev 
constrain(std::vector<CloneDetector::CloneGroup> & Sequences)35070686a15SRaphael Isemann void RecursiveCloneTypeIIHashConstraint::constrain(
351da9e718fSArtem Dergachev     std::vector<CloneDetector::CloneGroup> &Sequences) {
352da9e718fSArtem Dergachev   // FIXME: Maybe we can do this in-place and don't need this additional vector.
353da9e718fSArtem Dergachev   std::vector<CloneDetector::CloneGroup> Result;
354da9e718fSArtem Dergachev 
355da9e718fSArtem Dergachev   for (CloneDetector::CloneGroup &Group : Sequences) {
356da9e718fSArtem Dergachev     // We assume in the following code that the Group is non-empty, so we
357da9e718fSArtem Dergachev     // skip all empty groups.
358da9e718fSArtem Dergachev     if (Group.empty())
359da9e718fSArtem Dergachev       continue;
360da9e718fSArtem Dergachev 
361da9e718fSArtem Dergachev     std::vector<std::pair<size_t, StmtSequence>> StmtsByHash;
362da9e718fSArtem Dergachev 
363da9e718fSArtem Dergachev     // Generate hash codes for all children of S and save them in StmtsByHash.
364da9e718fSArtem Dergachev     for (const StmtSequence &S : Group) {
365da9e718fSArtem Dergachev       saveHash(S.front(), S.getContainingDecl(), StmtsByHash);
366da9e718fSArtem Dergachev     }
367da9e718fSArtem Dergachev 
368da9e718fSArtem Dergachev     // Sort hash_codes in StmtsByHash.
369899d1392SFangrui Song     llvm::stable_sort(StmtsByHash, llvm::less_first());
370da9e718fSArtem Dergachev 
371da9e718fSArtem Dergachev     // Check for each StmtSequence if its successor has the same hash value.
372da9e718fSArtem Dergachev     // We don't check the last StmtSequence as it has no successor.
373da9e718fSArtem Dergachev     // Note: The 'size - 1 ' in the condition is safe because we check for an
374da9e718fSArtem Dergachev     // empty Group vector at the beginning of this function.
375da9e718fSArtem Dergachev     for (unsigned i = 0; i < StmtsByHash.size() - 1; ++i) {
376da9e718fSArtem Dergachev       const auto Current = StmtsByHash[i];
377da9e718fSArtem Dergachev 
3782a8c18d9SAlexander Kornienko       // It's likely that we just found a sequence of StmtSequences that
379da9e718fSArtem Dergachev       // represent a CloneGroup, so we create a new group and start checking and
380da9e718fSArtem Dergachev       // adding the StmtSequences in this sequence.
381da9e718fSArtem Dergachev       CloneDetector::CloneGroup NewGroup;
382da9e718fSArtem Dergachev 
383da9e718fSArtem Dergachev       size_t PrototypeHash = Current.first;
384da9e718fSArtem Dergachev 
385da9e718fSArtem Dergachev       for (; i < StmtsByHash.size(); ++i) {
386da9e718fSArtem Dergachev         // A different hash value means we have reached the end of the sequence.
38770686a15SRaphael Isemann         if (PrototypeHash != StmtsByHash[i].first) {
388da9e718fSArtem Dergachev           // The current sequence could be the start of a new CloneGroup. So we
389da9e718fSArtem Dergachev           // decrement i so that we visit it again in the outer loop.
390da9e718fSArtem Dergachev           // Note: i can never be 0 at this point because we are just comparing
391da9e718fSArtem Dergachev           // the hash of the Current StmtSequence with itself in the 'if' above.
392da9e718fSArtem Dergachev           assert(i != 0);
393da9e718fSArtem Dergachev           --i;
3942fc1985dSArtem Dergachev           break;
3952fc1985dSArtem Dergachev         }
396da9e718fSArtem Dergachev         // Same hash value means we should add the StmtSequence to the current
397da9e718fSArtem Dergachev         // group.
398da9e718fSArtem Dergachev         NewGroup.push_back(StmtsByHash[i].second);
399da9e718fSArtem Dergachev       }
400da9e718fSArtem Dergachev 
401da9e718fSArtem Dergachev       // We created a new clone group with matching hash codes and move it to
402da9e718fSArtem Dergachev       // the result vector.
403da9e718fSArtem Dergachev       Result.push_back(NewGroup);
4042fc1985dSArtem Dergachev     }
4052fc1985dSArtem Dergachev   }
406da9e718fSArtem Dergachev   // Sequences is the output parameter, so we copy our result into it.
407da9e718fSArtem Dergachev   Sequences = Result;
4082fc1985dSArtem Dergachev }
409da9e718fSArtem Dergachev 
constrain(std::vector<CloneDetector::CloneGroup> & Sequences)41070686a15SRaphael Isemann void RecursiveCloneTypeIIVerifyConstraint::constrain(
41170686a15SRaphael Isemann     std::vector<CloneDetector::CloneGroup> &Sequences) {
41270686a15SRaphael Isemann   CloneConstraint::splitCloneGroups(
41370686a15SRaphael Isemann       Sequences, [](const StmtSequence &A, const StmtSequence &B) {
41470686a15SRaphael Isemann         return areSequencesClones(A, B);
41570686a15SRaphael Isemann       });
41670686a15SRaphael Isemann }
41770686a15SRaphael Isemann 
calculateStmtComplexity(const StmtSequence & Seq,std::size_t Limit,const std::string & ParentMacroStack)418da9e718fSArtem Dergachev size_t MinComplexityConstraint::calculateStmtComplexity(
419785e8161SRaphael Isemann     const StmtSequence &Seq, std::size_t Limit,
420785e8161SRaphael Isemann     const std::string &ParentMacroStack) {
421da9e718fSArtem Dergachev   if (Seq.empty())
422da9e718fSArtem Dergachev     return 0;
423da9e718fSArtem Dergachev 
424da9e718fSArtem Dergachev   size_t Complexity = 1;
425da9e718fSArtem Dergachev 
426da9e718fSArtem Dergachev   ASTContext &Context = Seq.getASTContext();
427da9e718fSArtem Dergachev 
428da9e718fSArtem Dergachev   // Look up what macros expanded into the current statement.
429785e8161SRaphael Isemann   std::string MacroStack =
430a6e4358fSStephen Kelly       data_collection::getMacroStack(Seq.getBeginLoc(), Context);
431da9e718fSArtem Dergachev 
432da9e718fSArtem Dergachev   // First, check if ParentMacroStack is not empty which means we are currently
433da9e718fSArtem Dergachev   // dealing with a parent statement which was expanded from a macro.
434da9e718fSArtem Dergachev   // If this parent statement was expanded from the same macros as this
435da9e718fSArtem Dergachev   // statement, we reduce the initial complexity of this statement to zero.
436da9e718fSArtem Dergachev   // This causes that a group of statements that were generated by a single
437da9e718fSArtem Dergachev   // macro expansion will only increase the total complexity by one.
438da9e718fSArtem Dergachev   // Note: This is not the final complexity of this statement as we still
439da9e718fSArtem Dergachev   // add the complexity of the child statements to the complexity value.
440785e8161SRaphael Isemann   if (!ParentMacroStack.empty() && MacroStack == ParentMacroStack) {
441da9e718fSArtem Dergachev     Complexity = 0;
442da9e718fSArtem Dergachev   }
443da9e718fSArtem Dergachev 
444da9e718fSArtem Dergachev   // Iterate over the Stmts in the StmtSequence and add their complexity values
445da9e718fSArtem Dergachev   // to the current complexity value.
446da9e718fSArtem Dergachev   if (Seq.holdsSequence()) {
447da9e718fSArtem Dergachev     for (const Stmt *S : Seq) {
448da9e718fSArtem Dergachev       Complexity += calculateStmtComplexity(
449785e8161SRaphael Isemann           StmtSequence(S, Seq.getContainingDecl()), Limit, MacroStack);
450785e8161SRaphael Isemann       if (Complexity >= Limit)
451785e8161SRaphael Isemann         return Limit;
452da9e718fSArtem Dergachev     }
453da9e718fSArtem Dergachev   } else {
454da9e718fSArtem Dergachev     for (const Stmt *S : Seq.front()->children()) {
455da9e718fSArtem Dergachev       Complexity += calculateStmtComplexity(
456785e8161SRaphael Isemann           StmtSequence(S, Seq.getContainingDecl()), Limit, MacroStack);
457785e8161SRaphael Isemann       if (Complexity >= Limit)
458785e8161SRaphael Isemann         return Limit;
459da9e718fSArtem Dergachev     }
460da9e718fSArtem Dergachev   }
461da9e718fSArtem Dergachev   return Complexity;
462da9e718fSArtem Dergachev }
463da9e718fSArtem Dergachev 
constrain(std::vector<CloneDetector::CloneGroup> & CloneGroups)464da9e718fSArtem Dergachev void MatchingVariablePatternConstraint::constrain(
465da9e718fSArtem Dergachev     std::vector<CloneDetector::CloneGroup> &CloneGroups) {
466da9e718fSArtem Dergachev   CloneConstraint::splitCloneGroups(
467da9e718fSArtem Dergachev       CloneGroups, [](const StmtSequence &A, const StmtSequence &B) {
468da9e718fSArtem Dergachev         VariablePattern PatternA(A);
469da9e718fSArtem Dergachev         VariablePattern PatternB(B);
470da9e718fSArtem Dergachev         return PatternA.countPatternDifferences(PatternB) == 0;
471da9e718fSArtem Dergachev       });
472da9e718fSArtem Dergachev }
473da9e718fSArtem Dergachev 
splitCloneGroups(std::vector<CloneDetector::CloneGroup> & CloneGroups,llvm::function_ref<bool (const StmtSequence &,const StmtSequence &)> Compare)474da9e718fSArtem Dergachev void CloneConstraint::splitCloneGroups(
475da9e718fSArtem Dergachev     std::vector<CloneDetector::CloneGroup> &CloneGroups,
4760b94bfc7SBenjamin Kramer     llvm::function_ref<bool(const StmtSequence &, const StmtSequence &)>
4770b94bfc7SBenjamin Kramer         Compare) {
478da9e718fSArtem Dergachev   std::vector<CloneDetector::CloneGroup> Result;
479da9e718fSArtem Dergachev   for (auto &HashGroup : CloneGroups) {
480da9e718fSArtem Dergachev     // Contains all indexes in HashGroup that were already added to a
481da9e718fSArtem Dergachev     // CloneGroup.
482da9e718fSArtem Dergachev     std::vector<char> Indexes;
483da9e718fSArtem Dergachev     Indexes.resize(HashGroup.size());
484da9e718fSArtem Dergachev 
485da9e718fSArtem Dergachev     for (unsigned i = 0; i < HashGroup.size(); ++i) {
486da9e718fSArtem Dergachev       // Skip indexes that are already part of a CloneGroup.
487da9e718fSArtem Dergachev       if (Indexes[i])
488da9e718fSArtem Dergachev         continue;
489da9e718fSArtem Dergachev 
490da9e718fSArtem Dergachev       // Pick the first unhandled StmtSequence and consider it as the
491da9e718fSArtem Dergachev       // beginning
492da9e718fSArtem Dergachev       // of a new CloneGroup for now.
493da9e718fSArtem Dergachev       // We don't add i to Indexes because we never iterate back.
494da9e718fSArtem Dergachev       StmtSequence Prototype = HashGroup[i];
495da9e718fSArtem Dergachev       CloneDetector::CloneGroup PotentialGroup = {Prototype};
496da9e718fSArtem Dergachev       ++Indexes[i];
497da9e718fSArtem Dergachev 
498da9e718fSArtem Dergachev       // Check all following StmtSequences for clones.
499da9e718fSArtem Dergachev       for (unsigned j = i + 1; j < HashGroup.size(); ++j) {
500da9e718fSArtem Dergachev         // Skip indexes that are already part of a CloneGroup.
501da9e718fSArtem Dergachev         if (Indexes[j])
502da9e718fSArtem Dergachev           continue;
503da9e718fSArtem Dergachev 
504676b457bSRaphael Isemann         // If a following StmtSequence belongs to our CloneGroup, we add it.
505da9e718fSArtem Dergachev         const StmtSequence &Candidate = HashGroup[j];
506da9e718fSArtem Dergachev 
507da9e718fSArtem Dergachev         if (!Compare(Prototype, Candidate))
508da9e718fSArtem Dergachev           continue;
509da9e718fSArtem Dergachev 
510da9e718fSArtem Dergachev         PotentialGroup.push_back(Candidate);
511da9e718fSArtem Dergachev         // Make sure we never visit this StmtSequence again.
512da9e718fSArtem Dergachev         ++Indexes[j];
513da9e718fSArtem Dergachev       }
514da9e718fSArtem Dergachev 
515da9e718fSArtem Dergachev       // Otherwise, add it to the result and continue searching for more
516da9e718fSArtem Dergachev       // groups.
517da9e718fSArtem Dergachev       Result.push_back(PotentialGroup);
518da9e718fSArtem Dergachev     }
519da9e718fSArtem Dergachev 
5203117b17bSFangrui Song     assert(llvm::all_of(Indexes, [](char c) { return c == 1; }));
521da9e718fSArtem Dergachev   }
522da9e718fSArtem Dergachev   CloneGroups = Result;
523da9e718fSArtem Dergachev }
524da9e718fSArtem Dergachev 
addVariableOccurence(const VarDecl * VarDecl,const Stmt * Mention)525da9e718fSArtem Dergachev void VariablePattern::addVariableOccurence(const VarDecl *VarDecl,
526da9e718fSArtem Dergachev                                            const Stmt *Mention) {
527da9e718fSArtem Dergachev   // First check if we already reference this variable
528da9e718fSArtem Dergachev   for (size_t KindIndex = 0; KindIndex < Variables.size(); ++KindIndex) {
529da9e718fSArtem Dergachev     if (Variables[KindIndex] == VarDecl) {
53051d3fb04SMalcolm Parsons       // If yes, add a new occurrence that points to the existing entry in
531da9e718fSArtem Dergachev       // the Variables vector.
532da9e718fSArtem Dergachev       Occurences.emplace_back(KindIndex, Mention);
533da9e718fSArtem Dergachev       return;
534da9e718fSArtem Dergachev     }
535da9e718fSArtem Dergachev   }
536da9e718fSArtem Dergachev   // If this variable wasn't already referenced, add it to the list of
53751d3fb04SMalcolm Parsons   // referenced variables and add a occurrence that points to this new entry.
538da9e718fSArtem Dergachev   Occurences.emplace_back(Variables.size(), Mention);
539da9e718fSArtem Dergachev   Variables.push_back(VarDecl);
540da9e718fSArtem Dergachev }
541da9e718fSArtem Dergachev 
addVariables(const Stmt * S)542da9e718fSArtem Dergachev void VariablePattern::addVariables(const Stmt *S) {
543da9e718fSArtem Dergachev   // Sometimes we get a nullptr (such as from IfStmts which often have nullptr
544da9e718fSArtem Dergachev   // children). We skip such statements as they don't reference any
545da9e718fSArtem Dergachev   // variables.
546da9e718fSArtem Dergachev   if (!S)
547da9e718fSArtem Dergachev     return;
548da9e718fSArtem Dergachev 
549da9e718fSArtem Dergachev   // Check if S is a reference to a variable. If yes, add it to the pattern.
550da9e718fSArtem Dergachev   if (auto D = dyn_cast<DeclRefExpr>(S)) {
551da9e718fSArtem Dergachev     if (auto VD = dyn_cast<VarDecl>(D->getDecl()->getCanonicalDecl()))
552da9e718fSArtem Dergachev       addVariableOccurence(VD, D);
553da9e718fSArtem Dergachev   }
554da9e718fSArtem Dergachev 
555da9e718fSArtem Dergachev   // Recursively check all children of the given statement.
556da9e718fSArtem Dergachev   for (const Stmt *Child : S->children()) {
557da9e718fSArtem Dergachev     addVariables(Child);
558da9e718fSArtem Dergachev   }
559da9e718fSArtem Dergachev }
560da9e718fSArtem Dergachev 
countPatternDifferences(const VariablePattern & Other,VariablePattern::SuspiciousClonePair * FirstMismatch)561da9e718fSArtem Dergachev unsigned VariablePattern::countPatternDifferences(
562da9e718fSArtem Dergachev     const VariablePattern &Other,
563da9e718fSArtem Dergachev     VariablePattern::SuspiciousClonePair *FirstMismatch) {
564da9e718fSArtem Dergachev   unsigned NumberOfDifferences = 0;
565da9e718fSArtem Dergachev 
566da9e718fSArtem Dergachev   assert(Other.Occurences.size() == Occurences.size());
567da9e718fSArtem Dergachev   for (unsigned i = 0; i < Occurences.size(); ++i) {
568da9e718fSArtem Dergachev     auto ThisOccurence = Occurences[i];
569da9e718fSArtem Dergachev     auto OtherOccurence = Other.Occurences[i];
570da9e718fSArtem Dergachev     if (ThisOccurence.KindID == OtherOccurence.KindID)
571da9e718fSArtem Dergachev       continue;
572da9e718fSArtem Dergachev 
573da9e718fSArtem Dergachev     ++NumberOfDifferences;
574da9e718fSArtem Dergachev 
575da9e718fSArtem Dergachev     // If FirstMismatch is not a nullptr, we need to store information about
576da9e718fSArtem Dergachev     // the first difference between the two patterns.
577da9e718fSArtem Dergachev     if (FirstMismatch == nullptr)
578da9e718fSArtem Dergachev       continue;
579da9e718fSArtem Dergachev 
580da9e718fSArtem Dergachev     // Only proceed if we just found the first difference as we only store
581da9e718fSArtem Dergachev     // information about the first difference.
582da9e718fSArtem Dergachev     if (NumberOfDifferences != 1)
583da9e718fSArtem Dergachev       continue;
584da9e718fSArtem Dergachev 
585da9e718fSArtem Dergachev     const VarDecl *FirstSuggestion = nullptr;
586da9e718fSArtem Dergachev     // If there is a variable available in the list of referenced variables
587da9e718fSArtem Dergachev     // which wouldn't break the pattern if it is used in place of the
588da9e718fSArtem Dergachev     // current variable, we provide this variable as the suggested fix.
589da9e718fSArtem Dergachev     if (OtherOccurence.KindID < Variables.size())
590da9e718fSArtem Dergachev       FirstSuggestion = Variables[OtherOccurence.KindID];
591da9e718fSArtem Dergachev 
592da9e718fSArtem Dergachev     // Store information about the first clone.
593da9e718fSArtem Dergachev     FirstMismatch->FirstCloneInfo =
594da9e718fSArtem Dergachev         VariablePattern::SuspiciousClonePair::SuspiciousCloneInfo(
595da9e718fSArtem Dergachev             Variables[ThisOccurence.KindID], ThisOccurence.Mention,
596da9e718fSArtem Dergachev             FirstSuggestion);
597da9e718fSArtem Dergachev 
598da9e718fSArtem Dergachev     // Same as above but with the other clone. We do this for both clones as
599da9e718fSArtem Dergachev     // we don't know which clone is the one containing the unintended
600da9e718fSArtem Dergachev     // pattern error.
601da9e718fSArtem Dergachev     const VarDecl *SecondSuggestion = nullptr;
602da9e718fSArtem Dergachev     if (ThisOccurence.KindID < Other.Variables.size())
603da9e718fSArtem Dergachev       SecondSuggestion = Other.Variables[ThisOccurence.KindID];
604da9e718fSArtem Dergachev 
605da9e718fSArtem Dergachev     // Store information about the second clone.
606da9e718fSArtem Dergachev     FirstMismatch->SecondCloneInfo =
607da9e718fSArtem Dergachev         VariablePattern::SuspiciousClonePair::SuspiciousCloneInfo(
608da9e718fSArtem Dergachev             Other.Variables[OtherOccurence.KindID], OtherOccurence.Mention,
609da9e718fSArtem Dergachev             SecondSuggestion);
610da9e718fSArtem Dergachev 
611da9e718fSArtem Dergachev     // SuspiciousClonePair guarantees that the first clone always has a
612da9e718fSArtem Dergachev     // suggested variable associated with it. As we know that one of the two
613da9e718fSArtem Dergachev     // clones in the pair always has suggestion, we swap the two clones
614da9e718fSArtem Dergachev     // in case the first clone has no suggested variable which means that
615da9e718fSArtem Dergachev     // the second clone has a suggested variable and should be first.
616da9e718fSArtem Dergachev     if (!FirstMismatch->FirstCloneInfo.Suggestion)
617da9e718fSArtem Dergachev       std::swap(FirstMismatch->FirstCloneInfo, FirstMismatch->SecondCloneInfo);
618da9e718fSArtem Dergachev 
619da9e718fSArtem Dergachev     // This ensures that we always have at least one suggestion in a pair.
620da9e718fSArtem Dergachev     assert(FirstMismatch->FirstCloneInfo.Suggestion);
621da9e718fSArtem Dergachev   }
622da9e718fSArtem Dergachev 
623da9e718fSArtem Dergachev   return NumberOfDifferences;
6242fc1985dSArtem Dergachev }
625