blob: 3b4b78b422a39a6e4f9fb5663dc54620ee494c9a [file] [log] [blame]
Artem Dergachevba816322016-07-26 18:13:12 +00001//===--- 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
23using namespace clang;
24using namespace ento;
25
26namespace {
27class CloneChecker
28 : public Checker<check::ASTCodeBody, check::EndOfTranslationUnit> {
Artem Dergachev96034ca2016-07-26 19:05:22 +000029 mutable CloneDetector Detector;
Artem Dergachevba816322016-07-26 18:13:12 +000030
31public:
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;
Artem Dergachev2fc19852016-08-18 12:29:41 +000037
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;
Artem Dergachevba816322016-07-26 18:13:12 +000046};
47} // end anonymous namespace
48
49void 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.
Artem Dergachev96034ca2016-07-26 19:05:22 +000053 Detector.analyzeCodeBody(D);
Artem Dergachevba816322016-07-26 18:13:12 +000054}
55
56void 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);
Artem Dergachevba816322016-07-26 18:13:12 +000064 assert(MinComplexity >= 0);
65
Artem Dergachev2fc19852016-08-18 12:29:41 +000066 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
79void CloneChecker::reportClones(SourceManager &SM, AnalysisManager &Mgr,
80 int MinComplexity) const {
Artem Dergachevba816322016-07-26 18:13:12 +000081
82 std::vector<CloneDetector::CloneGroup> CloneGroups;
Artem Dergachev96034ca2016-07-26 19:05:22 +000083 Detector.findClones(CloneGroups, MinComplexity);
Artem Dergachevba816322016-07-26 18:13:12 +000084
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
Artem Dergachev2fc19852016-08-18 12:29:41 +0000112void 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
Artem Dergachevba816322016-07-26 18:13:12 +0000158//===----------------------------------------------------------------------===//
159// Register CloneChecker
160//===----------------------------------------------------------------------===//
161
162void ento::registerCloneChecker(CheckerManager &Mgr) {
163 Mgr.registerChecker<CloneChecker>();
164}