1 //===--- CloneChecker.cpp - Clone detection checker -------------*- 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 /// \file 11 /// CloneChecker is a checker that reports clones in the current translation 12 /// unit. 13 /// 14 //===----------------------------------------------------------------------===// 15 16 #include "ClangSACheckers.h" 17 #include "clang/Analysis/CloneDetection.h" 18 #include "clang/Basic/Diagnostic.h" 19 #include "clang/StaticAnalyzer/Core/Checker.h" 20 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 21 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 22 23 using namespace clang; 24 using namespace ento; 25 26 namespace { 27 class CloneChecker 28 : public Checker<check::ASTCodeBody, check::EndOfTranslationUnit> { 29 mutable CloneDetector Detector; 30 31 public: 32 void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr, 33 BugReporter &BR) const; 34 35 void checkEndOfTranslationUnit(const TranslationUnitDecl *TU, 36 AnalysisManager &Mgr, BugReporter &BR) const; 37 38 /// \brief Reports all clones to the user. 39 void reportClones(SourceManager &SM, AnalysisManager &Mgr, 40 int MinComplexity) const; 41 42 /// \brief Reports only suspicious clones to the user along with informaton 43 /// that explain why they are suspicious. 44 void reportSuspiciousClones(SourceManager &SM, AnalysisManager &Mgr, 45 int MinComplexity) const; 46 }; 47 } // end anonymous namespace 48 49 void CloneChecker::checkASTCodeBody(const Decl *D, AnalysisManager &Mgr, 50 BugReporter &BR) const { 51 // Every statement that should be included in the search for clones needs to 52 // be passed to the CloneDetector. 53 Detector.analyzeCodeBody(D); 54 } 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 62 int MinComplexity = Mgr.getAnalyzerOptions().getOptionAsInteger( 63 "MinimumCloneComplexity", 10, this); 64 assert(MinComplexity >= 0); 65 66 bool ReportSuspiciousClones = Mgr.getAnalyzerOptions().getBooleanOption( 67 "ReportSuspiciousClones", true, this); 68 69 bool ReportNormalClones = Mgr.getAnalyzerOptions().getBooleanOption( 70 "ReportNormalClones", true, this); 71 72 if (ReportSuspiciousClones) 73 reportSuspiciousClones(BR.getSourceManager(), Mgr, MinComplexity); 74 75 if (ReportNormalClones) 76 reportClones(BR.getSourceManager(), Mgr, MinComplexity); 77 } 78 79 void CloneChecker::reportClones(SourceManager &SM, AnalysisManager &Mgr, 80 int MinComplexity) const { 81 82 std::vector<CloneDetector::CloneGroup> CloneGroups; 83 Detector.findClones(CloneGroups, MinComplexity); 84 85 DiagnosticsEngine &DiagEngine = Mgr.getDiagnostic(); 86 87 unsigned WarnID = DiagEngine.getCustomDiagID(DiagnosticsEngine::Warning, 88 "Detected code clone."); 89 90 unsigned NoteID = DiagEngine.getCustomDiagID(DiagnosticsEngine::Note, 91 "Related code clone is here."); 92 93 for (CloneDetector::CloneGroup &Group : CloneGroups) { 94 // For readability reasons we sort the clones by line numbers. 95 std::sort(Group.Sequences.begin(), Group.Sequences.end(), 96 [&SM](const StmtSequence &LHS, const StmtSequence &RHS) { 97 return SM.isBeforeInTranslationUnit(LHS.getStartLoc(), 98 RHS.getStartLoc()) && 99 SM.isBeforeInTranslationUnit(LHS.getEndLoc(), 100 RHS.getEndLoc()); 101 }); 102 103 // We group the clones by printing the first as a warning and all others 104 // as a note. 105 DiagEngine.Report(Group.Sequences.front().getStartLoc(), WarnID); 106 for (unsigned i = 1; i < Group.Sequences.size(); ++i) { 107 DiagEngine.Report(Group.Sequences[i].getStartLoc(), NoteID); 108 } 109 } 110 } 111 112 void CloneChecker::reportSuspiciousClones(SourceManager &SM, 113 AnalysisManager &Mgr, 114 int MinComplexity) const { 115 116 std::vector<CloneDetector::SuspiciousClonePair> Clones; 117 Detector.findSuspiciousClones(Clones, MinComplexity); 118 119 DiagnosticsEngine &DiagEngine = Mgr.getDiagnostic(); 120 121 auto SuspiciousCloneWarning = DiagEngine.getCustomDiagID( 122 DiagnosticsEngine::Warning, "suspicious code clone detected; did you " 123 "mean to use %0?"); 124 125 auto RelatedCloneNote = DiagEngine.getCustomDiagID( 126 DiagnosticsEngine::Note, "suggestion is based on the usage of this " 127 "variable in a similar piece of code"); 128 129 auto RelatedSuspiciousCloneNote = DiagEngine.getCustomDiagID( 130 DiagnosticsEngine::Note, "suggestion is based on the usage of this " 131 "variable in a similar piece of code; did you " 132 "mean to use %0?"); 133 134 for (CloneDetector::SuspiciousClonePair &Pair : Clones) { 135 // The first clone always has a suggestion and we report it to the user 136 // along with the place where the suggestion should be used. 137 DiagEngine.Report(Pair.FirstCloneInfo.VarRange.getBegin(), 138 SuspiciousCloneWarning) 139 << Pair.FirstCloneInfo.VarRange << Pair.FirstCloneInfo.Suggestion; 140 141 // The second clone can have a suggestion and if there is one, we report 142 // that suggestion to the user. 143 if (Pair.SecondCloneInfo.Suggestion) { 144 DiagEngine.Report(Pair.SecondCloneInfo.VarRange.getBegin(), 145 RelatedSuspiciousCloneNote) 146 << Pair.SecondCloneInfo.VarRange << Pair.SecondCloneInfo.Suggestion; 147 continue; 148 } 149 150 // If there isn't a suggestion in the second clone, we only inform the 151 // user where we got the idea that his code could contain an error. 152 DiagEngine.Report(Pair.SecondCloneInfo.VarRange.getBegin(), 153 RelatedCloneNote) 154 << Pair.SecondCloneInfo.VarRange; 155 } 156 } 157 158 //===----------------------------------------------------------------------===// 159 // Register CloneChecker 160 //===----------------------------------------------------------------------===// 161 162 void ento::registerCloneChecker(CheckerManager &Mgr) { 163 Mgr.registerChecker<CloneChecker>(); 164 } 165