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