1 //===--- CloneDetection.cpp - Finds code clones in an AST -------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 /// 10 /// This file implements classes for searching and anlyzing source code clones. 11 /// 12 //===----------------------------------------------------------------------===// 13 14 #include "clang/Analysis/CloneDetection.h" 15 16 #include "clang/AST/DataCollection.h" 17 #include "clang/AST/DeclTemplate.h" 18 #include "llvm/Support/MD5.h" 19 #include "llvm/Support/Path.h" 20 21 using namespace clang; 22 23 StmtSequence::StmtSequence(const CompoundStmt *Stmt, const Decl *D, 24 unsigned StartIndex, unsigned EndIndex) 25 : S(Stmt), D(D), StartIndex(StartIndex), EndIndex(EndIndex) { 26 assert(Stmt && "Stmt must not be a nullptr"); 27 assert(StartIndex < EndIndex && "Given array should not be empty"); 28 assert(EndIndex <= Stmt->size() && "Given array too big for this Stmt"); 29 } 30 31 StmtSequence::StmtSequence(const Stmt *Stmt, const Decl *D) 32 : S(Stmt), D(D), StartIndex(0), EndIndex(0) {} 33 34 StmtSequence::StmtSequence() 35 : S(nullptr), D(nullptr), StartIndex(0), EndIndex(0) {} 36 37 bool StmtSequence::contains(const StmtSequence &Other) const { 38 // If both sequences reside in different declarations, they can never contain 39 // each other. 40 if (D != Other.D) 41 return false; 42 43 const SourceManager &SM = getASTContext().getSourceManager(); 44 45 // Otherwise check if the start and end locations of the current sequence 46 // surround the other sequence. 47 bool StartIsInBounds = 48 SM.isBeforeInTranslationUnit(getStartLoc(), Other.getStartLoc()) || 49 getStartLoc() == Other.getStartLoc(); 50 if (!StartIsInBounds) 51 return false; 52 53 bool EndIsInBounds = 54 SM.isBeforeInTranslationUnit(Other.getEndLoc(), getEndLoc()) || 55 Other.getEndLoc() == getEndLoc(); 56 return EndIsInBounds; 57 } 58 59 StmtSequence::iterator StmtSequence::begin() const { 60 if (!holdsSequence()) { 61 return &S; 62 } 63 auto CS = cast<CompoundStmt>(S); 64 return CS->body_begin() + StartIndex; 65 } 66 67 StmtSequence::iterator StmtSequence::end() const { 68 if (!holdsSequence()) { 69 return reinterpret_cast<StmtSequence::iterator>(&S) + 1; 70 } 71 auto CS = cast<CompoundStmt>(S); 72 return CS->body_begin() + EndIndex; 73 } 74 75 ASTContext &StmtSequence::getASTContext() const { 76 assert(D); 77 return D->getASTContext(); 78 } 79 80 SourceLocation StmtSequence::getStartLoc() const { 81 return front()->getLocStart(); 82 } 83 84 SourceLocation StmtSequence::getEndLoc() const { return back()->getLocEnd(); } 85 86 SourceRange StmtSequence::getSourceRange() const { 87 return SourceRange(getStartLoc(), getEndLoc()); 88 } 89 90 void CloneDetector::analyzeCodeBody(const Decl *D) { 91 assert(D); 92 assert(D->hasBody()); 93 94 Sequences.push_back(StmtSequence(D->getBody(), D)); 95 } 96 97 /// Returns true if and only if \p Stmt contains at least one other 98 /// sequence in the \p Group. 99 static bool containsAnyInGroup(StmtSequence &Seq, 100 CloneDetector::CloneGroup &Group) { 101 for (StmtSequence &GroupSeq : Group) { 102 if (Seq.contains(GroupSeq)) 103 return true; 104 } 105 return false; 106 } 107 108 /// Returns true if and only if all sequences in \p OtherGroup are 109 /// contained by a sequence in \p Group. 110 static bool containsGroup(CloneDetector::CloneGroup &Group, 111 CloneDetector::CloneGroup &OtherGroup) { 112 // We have less sequences in the current group than we have in the other, 113 // so we will never fulfill the requirement for returning true. This is only 114 // possible because we know that a sequence in Group can contain at most 115 // one sequence in OtherGroup. 116 if (Group.size() < OtherGroup.size()) 117 return false; 118 119 for (StmtSequence &Stmt : Group) { 120 if (!containsAnyInGroup(Stmt, OtherGroup)) 121 return false; 122 } 123 return true; 124 } 125 126 void OnlyLargestCloneConstraint::constrain( 127 std::vector<CloneDetector::CloneGroup> &Result) { 128 std::vector<unsigned> IndexesToRemove; 129 130 // Compare every group in the result with the rest. If one groups contains 131 // another group, we only need to return the bigger group. 132 // Note: This doesn't scale well, so if possible avoid calling any heavy 133 // function from this loop to minimize the performance impact. 134 for (unsigned i = 0; i < Result.size(); ++i) { 135 for (unsigned j = 0; j < Result.size(); ++j) { 136 // Don't compare a group with itself. 137 if (i == j) 138 continue; 139 140 if (containsGroup(Result[j], Result[i])) { 141 IndexesToRemove.push_back(i); 142 break; 143 } 144 } 145 } 146 147 // Erasing a list of indexes from the vector should be done with decreasing 148 // indexes. As IndexesToRemove is constructed with increasing values, we just 149 // reverse iterate over it to get the desired order. 150 for (auto I = IndexesToRemove.rbegin(); I != IndexesToRemove.rend(); ++I) { 151 Result.erase(Result.begin() + *I); 152 } 153 } 154 155 bool FilenamePatternConstraint::isAutoGenerated( 156 const CloneDetector::CloneGroup &Group) { 157 std::string Error; 158 if (IgnoredFilesPattern.empty() || Group.empty() || 159 !IgnoredFilesRegex->isValid(Error)) 160 return false; 161 162 for (const StmtSequence &S : Group) { 163 const SourceManager &SM = S.getASTContext().getSourceManager(); 164 StringRef Filename = llvm::sys::path::filename( 165 SM.getFilename(S.getContainingDecl()->getLocation())); 166 if (IgnoredFilesRegex->match(Filename)) 167 return true; 168 } 169 170 return false; 171 } 172 173 /// This class defines what a type II code clone is: If it collects for two 174 /// statements the same data, then those two statements are considered to be 175 /// clones of each other. 176 /// 177 /// All collected data is forwarded to the given data consumer of the type T. 178 /// The data consumer class needs to provide a member method with the signature: 179 /// update(StringRef Str) 180 namespace { 181 template <class T> 182 class CloneTypeIIStmtDataCollector 183 : public ConstStmtVisitor<CloneTypeIIStmtDataCollector<T>> { 184 ASTContext &Context; 185 /// The data sink to which all data is forwarded. 186 T &DataConsumer; 187 188 template <class Ty> void addData(const Ty &Data) { 189 data_collection::addDataToConsumer(DataConsumer, Data); 190 } 191 192 public: 193 CloneTypeIIStmtDataCollector(const Stmt *S, ASTContext &Context, 194 T &DataConsumer) 195 : Context(Context), DataConsumer(DataConsumer) { 196 this->Visit(S); 197 } 198 199 // Define a visit method for each class to collect data and subsequently visit 200 // all parent classes. This uses a template so that custom visit methods by us 201 // take precedence. 202 #define DEF_ADD_DATA(CLASS, CODE) \ 203 template <class = void> void Visit##CLASS(const CLASS *S) { \ 204 CODE; \ 205 ConstStmtVisitor<CloneTypeIIStmtDataCollector<T>>::Visit##CLASS(S); \ 206 } 207 208 #include "../AST/StmtDataCollectors.inc" 209 210 // Type II clones ignore variable names and literals, so let's skip them. 211 #define SKIP(CLASS) \ 212 void Visit##CLASS(const CLASS *S) { \ 213 ConstStmtVisitor<CloneTypeIIStmtDataCollector<T>>::Visit##CLASS(S); \ 214 } 215 SKIP(DeclRefExpr) 216 SKIP(MemberExpr) 217 SKIP(IntegerLiteral) 218 SKIP(FloatingLiteral) 219 SKIP(StringLiteral) 220 SKIP(CXXBoolLiteralExpr) 221 SKIP(CharacterLiteral) 222 #undef SKIP 223 }; 224 } // end anonymous namespace 225 226 static size_t createHash(llvm::MD5 &Hash) { 227 size_t HashCode; 228 229 // Create the final hash code for the current Stmt. 230 llvm::MD5::MD5Result HashResult; 231 Hash.final(HashResult); 232 233 // Copy as much as possible of the generated hash code to the Stmt's hash 234 // code. 235 std::memcpy(&HashCode, &HashResult, 236 std::min(sizeof(HashCode), sizeof(HashResult))); 237 238 return HashCode; 239 } 240 241 size_t RecursiveCloneTypeIIConstraint::saveHash( 242 const Stmt *S, const Decl *D, 243 std::vector<std::pair<size_t, StmtSequence>> &StmtsByHash) { 244 llvm::MD5 Hash; 245 ASTContext &Context = D->getASTContext(); 246 247 CloneTypeIIStmtDataCollector<llvm::MD5>(S, Context, Hash); 248 249 auto CS = dyn_cast<CompoundStmt>(S); 250 SmallVector<size_t, 8> ChildHashes; 251 252 for (const Stmt *Child : S->children()) { 253 if (Child == nullptr) { 254 ChildHashes.push_back(0); 255 continue; 256 } 257 size_t ChildHash = saveHash(Child, D, StmtsByHash); 258 Hash.update( 259 StringRef(reinterpret_cast<char *>(&ChildHash), sizeof(ChildHash))); 260 ChildHashes.push_back(ChildHash); 261 } 262 263 if (CS) { 264 // If we're in a CompoundStmt, we hash all possible combinations of child 265 // statements to find clones in those subsequences. 266 // We first go through every possible starting position of a subsequence. 267 for (unsigned Pos = 0; Pos < CS->size(); ++Pos) { 268 // Then we try all possible lengths this subsequence could have and 269 // reuse the same hash object to make sure we only hash every child 270 // hash exactly once. 271 llvm::MD5 Hash; 272 for (unsigned Length = 1; Length <= CS->size() - Pos; ++Length) { 273 // Grab the current child hash and put it into our hash. We do 274 // -1 on the index because we start counting the length at 1. 275 size_t ChildHash = ChildHashes[Pos + Length - 1]; 276 Hash.update( 277 StringRef(reinterpret_cast<char *>(&ChildHash), sizeof(ChildHash))); 278 // If we have at least two elements in our subsequence, we can start 279 // saving it. 280 if (Length > 1) { 281 llvm::MD5 SubHash = Hash; 282 StmtsByHash.push_back(std::make_pair( 283 createHash(SubHash), StmtSequence(CS, D, Pos, Pos + Length))); 284 } 285 } 286 } 287 } 288 289 size_t HashCode = createHash(Hash); 290 StmtsByHash.push_back(std::make_pair(HashCode, StmtSequence(S, D))); 291 return HashCode; 292 } 293 294 namespace { 295 /// Wrapper around FoldingSetNodeID that it can be used as the template 296 /// argument of the StmtDataCollector. 297 class FoldingSetNodeIDWrapper { 298 299 llvm::FoldingSetNodeID &FS; 300 301 public: 302 FoldingSetNodeIDWrapper(llvm::FoldingSetNodeID &FS) : FS(FS) {} 303 304 void update(StringRef Str) { FS.AddString(Str); } 305 }; 306 } // end anonymous namespace 307 308 /// Writes the relevant data from all statements and child statements 309 /// in the given StmtSequence into the given FoldingSetNodeID. 310 static void CollectStmtSequenceData(const StmtSequence &Sequence, 311 FoldingSetNodeIDWrapper &OutputData) { 312 for (const Stmt *S : Sequence) { 313 CloneTypeIIStmtDataCollector<FoldingSetNodeIDWrapper>( 314 S, Sequence.getASTContext(), OutputData); 315 316 for (const Stmt *Child : S->children()) { 317 if (!Child) 318 continue; 319 320 CollectStmtSequenceData(StmtSequence(Child, Sequence.getContainingDecl()), 321 OutputData); 322 } 323 } 324 } 325 326 /// Returns true if both sequences are clones of each other. 327 static bool areSequencesClones(const StmtSequence &LHS, 328 const StmtSequence &RHS) { 329 // We collect the data from all statements in the sequence as we did before 330 // when generating a hash value for each sequence. But this time we don't 331 // hash the collected data and compare the whole data set instead. This 332 // prevents any false-positives due to hash code collisions. 333 llvm::FoldingSetNodeID DataLHS, DataRHS; 334 FoldingSetNodeIDWrapper LHSWrapper(DataLHS); 335 FoldingSetNodeIDWrapper RHSWrapper(DataRHS); 336 337 CollectStmtSequenceData(LHS, LHSWrapper); 338 CollectStmtSequenceData(RHS, RHSWrapper); 339 340 return DataLHS == DataRHS; 341 } 342 343 void RecursiveCloneTypeIIConstraint::constrain( 344 std::vector<CloneDetector::CloneGroup> &Sequences) { 345 // FIXME: Maybe we can do this in-place and don't need this additional vector. 346 std::vector<CloneDetector::CloneGroup> Result; 347 348 for (CloneDetector::CloneGroup &Group : Sequences) { 349 // We assume in the following code that the Group is non-empty, so we 350 // skip all empty groups. 351 if (Group.empty()) 352 continue; 353 354 std::vector<std::pair<size_t, StmtSequence>> StmtsByHash; 355 356 // Generate hash codes for all children of S and save them in StmtsByHash. 357 for (const StmtSequence &S : Group) { 358 saveHash(S.front(), S.getContainingDecl(), StmtsByHash); 359 } 360 361 // Sort hash_codes in StmtsByHash. 362 std::stable_sort(StmtsByHash.begin(), StmtsByHash.end(), 363 [](std::pair<size_t, StmtSequence> LHS, 364 std::pair<size_t, StmtSequence> RHS) { 365 return LHS.first < RHS.first; 366 }); 367 368 // Check for each StmtSequence if its successor has the same hash value. 369 // We don't check the last StmtSequence as it has no successor. 370 // Note: The 'size - 1 ' in the condition is safe because we check for an 371 // empty Group vector at the beginning of this function. 372 for (unsigned i = 0; i < StmtsByHash.size() - 1; ++i) { 373 const auto Current = StmtsByHash[i]; 374 375 // It's likely that we just found an sequence of StmtSequences that 376 // represent a CloneGroup, so we create a new group and start checking and 377 // adding the StmtSequences in this sequence. 378 CloneDetector::CloneGroup NewGroup; 379 380 size_t PrototypeHash = Current.first; 381 382 for (; i < StmtsByHash.size(); ++i) { 383 // A different hash value means we have reached the end of the sequence. 384 if (PrototypeHash != StmtsByHash[i].first || 385 !areSequencesClones(StmtsByHash[i].second, Current.second)) { 386 // The current sequence could be the start of a new CloneGroup. So we 387 // decrement i so that we visit it again in the outer loop. 388 // Note: i can never be 0 at this point because we are just comparing 389 // the hash of the Current StmtSequence with itself in the 'if' above. 390 assert(i != 0); 391 --i; 392 break; 393 } 394 // Same hash value means we should add the StmtSequence to the current 395 // group. 396 NewGroup.push_back(StmtsByHash[i].second); 397 } 398 399 // We created a new clone group with matching hash codes and move it to 400 // the result vector. 401 Result.push_back(NewGroup); 402 } 403 } 404 // Sequences is the output parameter, so we copy our result into it. 405 Sequences = Result; 406 } 407 408 size_t MinComplexityConstraint::calculateStmtComplexity( 409 const StmtSequence &Seq, const std::string &ParentMacroStack) { 410 if (Seq.empty()) 411 return 0; 412 413 size_t Complexity = 1; 414 415 ASTContext &Context = Seq.getASTContext(); 416 417 // Look up what macros expanded into the current statement. 418 std::string StartMacroStack = 419 data_collection::getMacroStack(Seq.getStartLoc(), Context); 420 std::string EndMacroStack = 421 data_collection::getMacroStack(Seq.getEndLoc(), Context); 422 423 // First, check if ParentMacroStack is not empty which means we are currently 424 // dealing with a parent statement which was expanded from a macro. 425 // If this parent statement was expanded from the same macros as this 426 // statement, we reduce the initial complexity of this statement to zero. 427 // This causes that a group of statements that were generated by a single 428 // macro expansion will only increase the total complexity by one. 429 // Note: This is not the final complexity of this statement as we still 430 // add the complexity of the child statements to the complexity value. 431 if (!ParentMacroStack.empty() && (StartMacroStack == ParentMacroStack && 432 EndMacroStack == ParentMacroStack)) { 433 Complexity = 0; 434 } 435 436 // Iterate over the Stmts in the StmtSequence and add their complexity values 437 // to the current complexity value. 438 if (Seq.holdsSequence()) { 439 for (const Stmt *S : Seq) { 440 Complexity += calculateStmtComplexity( 441 StmtSequence(S, Seq.getContainingDecl()), StartMacroStack); 442 } 443 } else { 444 for (const Stmt *S : Seq.front()->children()) { 445 Complexity += calculateStmtComplexity( 446 StmtSequence(S, Seq.getContainingDecl()), StartMacroStack); 447 } 448 } 449 return Complexity; 450 } 451 452 void MatchingVariablePatternConstraint::constrain( 453 std::vector<CloneDetector::CloneGroup> &CloneGroups) { 454 CloneConstraint::splitCloneGroups( 455 CloneGroups, [](const StmtSequence &A, const StmtSequence &B) { 456 VariablePattern PatternA(A); 457 VariablePattern PatternB(B); 458 return PatternA.countPatternDifferences(PatternB) == 0; 459 }); 460 } 461 462 void CloneConstraint::splitCloneGroups( 463 std::vector<CloneDetector::CloneGroup> &CloneGroups, 464 std::function<bool(const StmtSequence &, const StmtSequence &)> Compare) { 465 std::vector<CloneDetector::CloneGroup> Result; 466 for (auto &HashGroup : CloneGroups) { 467 // Contains all indexes in HashGroup that were already added to a 468 // CloneGroup. 469 std::vector<char> Indexes; 470 Indexes.resize(HashGroup.size()); 471 472 for (unsigned i = 0; i < HashGroup.size(); ++i) { 473 // Skip indexes that are already part of a CloneGroup. 474 if (Indexes[i]) 475 continue; 476 477 // Pick the first unhandled StmtSequence and consider it as the 478 // beginning 479 // of a new CloneGroup for now. 480 // We don't add i to Indexes because we never iterate back. 481 StmtSequence Prototype = HashGroup[i]; 482 CloneDetector::CloneGroup PotentialGroup = {Prototype}; 483 ++Indexes[i]; 484 485 // Check all following StmtSequences for clones. 486 for (unsigned j = i + 1; j < HashGroup.size(); ++j) { 487 // Skip indexes that are already part of a CloneGroup. 488 if (Indexes[j]) 489 continue; 490 491 // If a following StmtSequence belongs to our CloneGroup, we add it. 492 const StmtSequence &Candidate = HashGroup[j]; 493 494 if (!Compare(Prototype, Candidate)) 495 continue; 496 497 PotentialGroup.push_back(Candidate); 498 // Make sure we never visit this StmtSequence again. 499 ++Indexes[j]; 500 } 501 502 // Otherwise, add it to the result and continue searching for more 503 // groups. 504 Result.push_back(PotentialGroup); 505 } 506 507 assert(std::all_of(Indexes.begin(), Indexes.end(), 508 [](char c) { return c == 1; })); 509 } 510 CloneGroups = Result; 511 } 512 513 void VariablePattern::addVariableOccurence(const VarDecl *VarDecl, 514 const Stmt *Mention) { 515 // First check if we already reference this variable 516 for (size_t KindIndex = 0; KindIndex < Variables.size(); ++KindIndex) { 517 if (Variables[KindIndex] == VarDecl) { 518 // If yes, add a new occurence that points to the existing entry in 519 // the Variables vector. 520 Occurences.emplace_back(KindIndex, Mention); 521 return; 522 } 523 } 524 // If this variable wasn't already referenced, add it to the list of 525 // referenced variables and add a occurence that points to this new entry. 526 Occurences.emplace_back(Variables.size(), Mention); 527 Variables.push_back(VarDecl); 528 } 529 530 void VariablePattern::addVariables(const Stmt *S) { 531 // Sometimes we get a nullptr (such as from IfStmts which often have nullptr 532 // children). We skip such statements as they don't reference any 533 // variables. 534 if (!S) 535 return; 536 537 // Check if S is a reference to a variable. If yes, add it to the pattern. 538 if (auto D = dyn_cast<DeclRefExpr>(S)) { 539 if (auto VD = dyn_cast<VarDecl>(D->getDecl()->getCanonicalDecl())) 540 addVariableOccurence(VD, D); 541 } 542 543 // Recursively check all children of the given statement. 544 for (const Stmt *Child : S->children()) { 545 addVariables(Child); 546 } 547 } 548 549 unsigned VariablePattern::countPatternDifferences( 550 const VariablePattern &Other, 551 VariablePattern::SuspiciousClonePair *FirstMismatch) { 552 unsigned NumberOfDifferences = 0; 553 554 assert(Other.Occurences.size() == Occurences.size()); 555 for (unsigned i = 0; i < Occurences.size(); ++i) { 556 auto ThisOccurence = Occurences[i]; 557 auto OtherOccurence = Other.Occurences[i]; 558 if (ThisOccurence.KindID == OtherOccurence.KindID) 559 continue; 560 561 ++NumberOfDifferences; 562 563 // If FirstMismatch is not a nullptr, we need to store information about 564 // the first difference between the two patterns. 565 if (FirstMismatch == nullptr) 566 continue; 567 568 // Only proceed if we just found the first difference as we only store 569 // information about the first difference. 570 if (NumberOfDifferences != 1) 571 continue; 572 573 const VarDecl *FirstSuggestion = nullptr; 574 // If there is a variable available in the list of referenced variables 575 // which wouldn't break the pattern if it is used in place of the 576 // current variable, we provide this variable as the suggested fix. 577 if (OtherOccurence.KindID < Variables.size()) 578 FirstSuggestion = Variables[OtherOccurence.KindID]; 579 580 // Store information about the first clone. 581 FirstMismatch->FirstCloneInfo = 582 VariablePattern::SuspiciousClonePair::SuspiciousCloneInfo( 583 Variables[ThisOccurence.KindID], ThisOccurence.Mention, 584 FirstSuggestion); 585 586 // Same as above but with the other clone. We do this for both clones as 587 // we don't know which clone is the one containing the unintended 588 // pattern error. 589 const VarDecl *SecondSuggestion = nullptr; 590 if (ThisOccurence.KindID < Other.Variables.size()) 591 SecondSuggestion = Other.Variables[ThisOccurence.KindID]; 592 593 // Store information about the second clone. 594 FirstMismatch->SecondCloneInfo = 595 VariablePattern::SuspiciousClonePair::SuspiciousCloneInfo( 596 Other.Variables[OtherOccurence.KindID], OtherOccurence.Mention, 597 SecondSuggestion); 598 599 // SuspiciousClonePair guarantees that the first clone always has a 600 // suggested variable associated with it. As we know that one of the two 601 // clones in the pair always has suggestion, we swap the two clones 602 // in case the first clone has no suggested variable which means that 603 // the second clone has a suggested variable and should be first. 604 if (!FirstMismatch->FirstCloneInfo.Suggestion) 605 std::swap(FirstMismatch->FirstCloneInfo, FirstMismatch->SecondCloneInfo); 606 607 // This ensures that we always have at least one suggestion in a pair. 608 assert(FirstMismatch->FirstCloneInfo.Suggestion); 609 } 610 611 return NumberOfDifferences; 612 } 613