CloneChecker.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. //===--- CloneChecker.cpp - Clone detection checker -------------*- C++ -*-===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. ///
  9. /// \file
  10. /// CloneChecker is a checker that reports clones in the current translation
  11. /// unit.
  12. ///
  13. //===----------------------------------------------------------------------===//
  14. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  15. #include "clang/Analysis/CloneDetection.h"
  16. #include "clang/Basic/Diagnostic.h"
  17. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  18. #include "clang/StaticAnalyzer/Core/Checker.h"
  19. #include "clang/StaticAnalyzer/Core/CheckerManager.h"
  20. #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
  21. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  22. using namespace clang;
  23. using namespace ento;
  24. namespace {
  25. class CloneChecker
  26. : public Checker<check::ASTCodeBody, check::EndOfTranslationUnit> {
  27. public:
  28. // Checker options.
  29. int MinComplexity;
  30. bool ReportNormalClones;
  31. StringRef IgnoredFilesPattern;
  32. private:
  33. mutable CloneDetector Detector;
  34. mutable std::unique_ptr<BugType> BT_Exact, BT_Suspicious;
  35. public:
  36. void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
  37. BugReporter &BR) const;
  38. void checkEndOfTranslationUnit(const TranslationUnitDecl *TU,
  39. AnalysisManager &Mgr, BugReporter &BR) const;
  40. /// Reports all clones to the user.
  41. void reportClones(BugReporter &BR, AnalysisManager &Mgr,
  42. std::vector<CloneDetector::CloneGroup> &CloneGroups) const;
  43. /// Reports only suspicious clones to the user along with information
  44. /// that explain why they are suspicious.
  45. void reportSuspiciousClones(
  46. BugReporter &BR, AnalysisManager &Mgr,
  47. std::vector<CloneDetector::CloneGroup> &CloneGroups) const;
  48. };
  49. } // end anonymous namespace
  50. void CloneChecker::checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
  51. BugReporter &BR) const {
  52. // Every statement that should be included in the search for clones needs to
  53. // be passed to the CloneDetector.
  54. Detector.analyzeCodeBody(D);
  55. }
  56. void CloneChecker::checkEndOfTranslationUnit(const TranslationUnitDecl *TU,
  57. AnalysisManager &Mgr,
  58. BugReporter &BR) const {
  59. // At this point, every statement in the translation unit has been analyzed by
  60. // the CloneDetector. The only thing left to do is to report the found clones.
  61. // Let the CloneDetector create a list of clones from all the analyzed
  62. // statements. We don't filter for matching variable patterns at this point
  63. // because reportSuspiciousClones() wants to search them for errors.
  64. std::vector<CloneDetector::CloneGroup> AllCloneGroups;
  65. Detector.findClones(
  66. AllCloneGroups, FilenamePatternConstraint(IgnoredFilesPattern),
  67. RecursiveCloneTypeIIHashConstraint(), MinGroupSizeConstraint(2),
  68. MinComplexityConstraint(MinComplexity),
  69. RecursiveCloneTypeIIVerifyConstraint(), OnlyLargestCloneConstraint());
  70. reportSuspiciousClones(BR, Mgr, AllCloneGroups);
  71. // We are done for this translation unit unless we also need to report normal
  72. // clones.
  73. if (!ReportNormalClones)
  74. return;
  75. // Now that the suspicious clone detector has checked for pattern errors,
  76. // we also filter all clones who don't have matching patterns
  77. CloneDetector::constrainClones(AllCloneGroups,
  78. MatchingVariablePatternConstraint(),
  79. MinGroupSizeConstraint(2));
  80. reportClones(BR, Mgr, AllCloneGroups);
  81. }
  82. static PathDiagnosticLocation makeLocation(const StmtSequence &S,
  83. AnalysisManager &Mgr) {
  84. ASTContext &ACtx = Mgr.getASTContext();
  85. return PathDiagnosticLocation::createBegin(
  86. S.front(), ACtx.getSourceManager(),
  87. Mgr.getAnalysisDeclContext(ACtx.getTranslationUnitDecl()));
  88. }
  89. void CloneChecker::reportClones(
  90. BugReporter &BR, AnalysisManager &Mgr,
  91. std::vector<CloneDetector::CloneGroup> &CloneGroups) const {
  92. if (!BT_Exact)
  93. BT_Exact.reset(new BugType(this, "Exact code clone", "Code clone"));
  94. for (const CloneDetector::CloneGroup &Group : CloneGroups) {
  95. // We group the clones by printing the first as a warning and all others
  96. // as a note.
  97. auto R = std::make_unique<BasicBugReport>(
  98. *BT_Exact, "Duplicate code detected", makeLocation(Group.front(), Mgr));
  99. R->addRange(Group.front().getSourceRange());
  100. for (unsigned i = 1; i < Group.size(); ++i)
  101. R->addNote("Similar code here", makeLocation(Group[i], Mgr),
  102. Group[i].getSourceRange());
  103. BR.emitReport(std::move(R));
  104. }
  105. }
  106. void CloneChecker::reportSuspiciousClones(
  107. BugReporter &BR, AnalysisManager &Mgr,
  108. std::vector<CloneDetector::CloneGroup> &CloneGroups) const {
  109. std::vector<VariablePattern::SuspiciousClonePair> Pairs;
  110. for (const CloneDetector::CloneGroup &Group : CloneGroups) {
  111. for (unsigned i = 0; i < Group.size(); ++i) {
  112. VariablePattern PatternA(Group[i]);
  113. for (unsigned j = i + 1; j < Group.size(); ++j) {
  114. VariablePattern PatternB(Group[j]);
  115. VariablePattern::SuspiciousClonePair ClonePair;
  116. // For now, we only report clones which break the variable pattern just
  117. // once because multiple differences in a pattern are an indicator that
  118. // those differences are maybe intended (e.g. because it's actually a
  119. // different algorithm).
  120. // FIXME: In very big clones even multiple variables can be unintended,
  121. // so replacing this number with a percentage could better handle such
  122. // cases. On the other hand it could increase the false-positive rate
  123. // for all clones if the percentage is too high.
  124. if (PatternA.countPatternDifferences(PatternB, &ClonePair) == 1) {
  125. Pairs.push_back(ClonePair);
  126. break;
  127. }
  128. }
  129. }
  130. }
  131. if (!BT_Suspicious)
  132. BT_Suspicious.reset(
  133. new BugType(this, "Suspicious code clone", "Code clone"));
  134. ASTContext &ACtx = BR.getContext();
  135. SourceManager &SM = ACtx.getSourceManager();
  136. AnalysisDeclContext *ADC =
  137. Mgr.getAnalysisDeclContext(ACtx.getTranslationUnitDecl());
  138. for (VariablePattern::SuspiciousClonePair &Pair : Pairs) {
  139. // FIXME: We are ignoring the suggestions currently, because they are
  140. // only 50% accurate (even if the second suggestion is unavailable),
  141. // which may confuse the user.
  142. // Think how to perform more accurate suggestions?
  143. auto R = std::make_unique<BasicBugReport>(
  144. *BT_Suspicious,
  145. "Potential copy-paste error; did you really mean to use '" +
  146. Pair.FirstCloneInfo.Variable->getNameAsString() + "' here?",
  147. PathDiagnosticLocation::createBegin(Pair.FirstCloneInfo.Mention, SM,
  148. ADC));
  149. R->addRange(Pair.FirstCloneInfo.Mention->getSourceRange());
  150. R->addNote("Similar code using '" +
  151. Pair.SecondCloneInfo.Variable->getNameAsString() + "' here",
  152. PathDiagnosticLocation::createBegin(Pair.SecondCloneInfo.Mention,
  153. SM, ADC),
  154. Pair.SecondCloneInfo.Mention->getSourceRange());
  155. BR.emitReport(std::move(R));
  156. }
  157. }
  158. //===----------------------------------------------------------------------===//
  159. // Register CloneChecker
  160. //===----------------------------------------------------------------------===//
  161. void ento::registerCloneChecker(CheckerManager &Mgr) {
  162. auto *Checker = Mgr.registerChecker<CloneChecker>();
  163. Checker->MinComplexity = Mgr.getAnalyzerOptions().getCheckerIntegerOption(
  164. Checker, "MinimumCloneComplexity");
  165. if (Checker->MinComplexity < 0)
  166. Mgr.reportInvalidCheckerOptionValue(
  167. Checker, "MinimumCloneComplexity", "a non-negative value");
  168. Checker->ReportNormalClones = Mgr.getAnalyzerOptions().getCheckerBooleanOption(
  169. Checker, "ReportNormalClones");
  170. Checker->IgnoredFilesPattern = Mgr.getAnalyzerOptions()
  171. .getCheckerStringOption(Checker, "IgnoredFilesPattern");
  172. }
  173. bool ento::shouldRegisterCloneChecker(const CheckerManager &mgr) {
  174. return true;
  175. }