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