blob: 6fa5732d10cba7d6eaa0d66032f6e9d2eeb19863 [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"
Artem Dergachev4eca0de2016-10-08 10:54:30 +000019#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
Artem Dergachevba816322016-07-26 18:13:12 +000020#include "clang/StaticAnalyzer/Core/Checker.h"
21#include "clang/StaticAnalyzer/Core/CheckerManager.h"
Artem Dergachev4eca0de2016-10-08 10:54:30 +000022#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
Artem Dergachevba816322016-07-26 18:13:12 +000023#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
24
25using namespace clang;
26using namespace ento;
27
28namespace {
29class CloneChecker
30 : public Checker<check::ASTCodeBody, check::EndOfTranslationUnit> {
Artem Dergachev96034ca2016-07-26 19:05:22 +000031 mutable CloneDetector Detector;
Artem Dergachev4eca0de2016-10-08 10:54:30 +000032 mutable std::unique_ptr<BugType> BT_Exact, BT_Suspicious;
Artem Dergachevba816322016-07-26 18:13:12 +000033
34public:
35 void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
36 BugReporter &BR) const;
37
38 void checkEndOfTranslationUnit(const TranslationUnitDecl *TU,
39 AnalysisManager &Mgr, BugReporter &BR) const;
Artem Dergachev2fc19852016-08-18 12:29:41 +000040
Artem Dergachevc4aee482017-04-05 15:06:17 +000041 /// \brief Reports all clones to the user.
Artem Dergachev4eca0de2016-10-08 10:54:30 +000042 void reportClones(BugReporter &BR, AnalysisManager &Mgr,
Artem Dergachevc4aee482017-04-05 15:06:17 +000043 int MinComplexity) const;
Artem Dergachev2fc19852016-08-18 12:29:41 +000044
Artem Dergachevc4aee482017-04-05 15:06:17 +000045 /// \brief Reports only suspicious clones to the user along with informaton
46 /// that explain why they are suspicious.
47 void reportSuspiciousClones(BugReporter &BR, AnalysisManager &Mgr,
48 int MinComplexity) const;
Artem Dergachevba816322016-07-26 18:13:12 +000049};
50} // end anonymous namespace
51
52void CloneChecker::checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
53 BugReporter &BR) const {
54 // Every statement that should be included in the search for clones needs to
55 // be passed to the CloneDetector.
Artem Dergachev96034ca2016-07-26 19:05:22 +000056 Detector.analyzeCodeBody(D);
Artem Dergachevba816322016-07-26 18:13:12 +000057}
58
59void CloneChecker::checkEndOfTranslationUnit(const TranslationUnitDecl *TU,
60 AnalysisManager &Mgr,
61 BugReporter &BR) const {
62 // At this point, every statement in the translation unit has been analyzed by
63 // the CloneDetector. The only thing left to do is to report the found clones.
64
65 int MinComplexity = Mgr.getAnalyzerOptions().getOptionAsInteger(
66 "MinimumCloneComplexity", 10, this);
Artem Dergachevba816322016-07-26 18:13:12 +000067 assert(MinComplexity >= 0);
68
Artem Dergachev2fc19852016-08-18 12:29:41 +000069 bool ReportSuspiciousClones = Mgr.getAnalyzerOptions().getBooleanOption(
70 "ReportSuspiciousClones", true, this);
71
72 bool ReportNormalClones = Mgr.getAnalyzerOptions().getBooleanOption(
73 "ReportNormalClones", true, this);
74
Artem Dergachevf8b4fc32017-04-05 14:17:36 +000075 if (ReportSuspiciousClones)
Artem Dergachevc4aee482017-04-05 15:06:17 +000076 reportSuspiciousClones(BR, Mgr, MinComplexity);
Artem Dergachevf8b4fc32017-04-05 14:17:36 +000077
Artem Dergachevc4aee482017-04-05 15:06:17 +000078 if (ReportNormalClones)
79 reportClones(BR, Mgr, MinComplexity);
Artem Dergachev2fc19852016-08-18 12:29:41 +000080}
81
Artem Dergachev4eca0de2016-10-08 10:54:30 +000082static 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
Artem Dergachevc4aee482017-04-05 15:06:17 +000090void CloneChecker::reportClones(BugReporter &BR, AnalysisManager &Mgr,
91 int MinComplexity) const {
92
93 std::vector<CloneDetector::CloneGroup> CloneGroups;
94 Detector.findClones(CloneGroups, MinComplexity);
Artem Dergachevba816322016-07-26 18:13:12 +000095
Artem Dergachev4eca0de2016-10-08 10:54:30 +000096 if (!BT_Exact)
97 BT_Exact.reset(new BugType(this, "Exact code clone", "Code clone"));
Artem Dergachevba816322016-07-26 18:13:12 +000098
Artem Dergachevc4aee482017-04-05 15:06:17 +000099 for (CloneDetector::CloneGroup &Group : CloneGroups) {
Artem Dergachevba816322016-07-26 18:13:12 +0000100 // We group the clones by printing the first as a warning and all others
101 // as a note.
Artem Dergachevc4aee482017-04-05 15:06:17 +0000102 auto R = llvm::make_unique<BugReport>(
103 *BT_Exact, "Duplicate code detected",
104 makeLocation(Group.Sequences.front(), Mgr));
105 R->addRange(Group.Sequences.front().getSourceRange());
Artem Dergachev4eca0de2016-10-08 10:54:30 +0000106
Artem Dergachevc4aee482017-04-05 15:06:17 +0000107 for (unsigned i = 1; i < Group.Sequences.size(); ++i)
108 R->addNote("Similar code here",
109 makeLocation(Group.Sequences[i], Mgr),
110 Group.Sequences[i].getSourceRange());
Artem Dergachev4eca0de2016-10-08 10:54:30 +0000111 BR.emitReport(std::move(R));
Artem Dergachevba816322016-07-26 18:13:12 +0000112 }
113}
114
Artem Dergachevc4aee482017-04-05 15:06:17 +0000115void CloneChecker::reportSuspiciousClones(BugReporter &BR,
116 AnalysisManager &Mgr,
117 int MinComplexity) const {
Artem Dergachev2fc19852016-08-18 12:29:41 +0000118
Artem Dergachevc4aee482017-04-05 15:06:17 +0000119 std::vector<CloneDetector::SuspiciousClonePair> Clones;
120 Detector.findSuspiciousClones(Clones, MinComplexity);
Artem Dergachev2fc19852016-08-18 12:29:41 +0000121
Artem Dergachev4eca0de2016-10-08 10:54:30 +0000122 if (!BT_Suspicious)
123 BT_Suspicious.reset(
124 new BugType(this, "Suspicious code clone", "Code clone"));
Artem Dergachev2fc19852016-08-18 12:29:41 +0000125
Artem Dergachev4eca0de2016-10-08 10:54:30 +0000126 ASTContext &ACtx = BR.getContext();
127 SourceManager &SM = ACtx.getSourceManager();
128 AnalysisDeclContext *ADC =
129 Mgr.getAnalysisDeclContext(ACtx.getTranslationUnitDecl());
Artem Dergachev2fc19852016-08-18 12:29:41 +0000130
Artem Dergachevc4aee482017-04-05 15:06:17 +0000131 for (CloneDetector::SuspiciousClonePair &Pair : Clones) {
Artem Dergachev4eca0de2016-10-08 10:54:30 +0000132 // FIXME: We are ignoring the suggestions currently, because they are
133 // only 50% accurate (even if the second suggestion is unavailable),
134 // which may confuse the user.
135 // Think how to perform more accurate suggestions?
Artem Dergachev2fc19852016-08-18 12:29:41 +0000136
Artem Dergachev4eca0de2016-10-08 10:54:30 +0000137 auto R = llvm::make_unique<BugReport>(
138 *BT_Suspicious,
139 "Potential copy-paste error; did you really mean to use '" +
140 Pair.FirstCloneInfo.Variable->getNameAsString() + "' here?",
141 PathDiagnosticLocation::createBegin(Pair.FirstCloneInfo.Mention, SM,
142 ADC));
143 R->addRange(Pair.FirstCloneInfo.Mention->getSourceRange());
Artem Dergachev2fc19852016-08-18 12:29:41 +0000144
Artem Dergachev4eca0de2016-10-08 10:54:30 +0000145 R->addNote("Similar code using '" +
146 Pair.SecondCloneInfo.Variable->getNameAsString() + "' here",
147 PathDiagnosticLocation::createBegin(Pair.SecondCloneInfo.Mention,
148 SM, ADC),
149 Pair.SecondCloneInfo.Mention->getSourceRange());
150
151 BR.emitReport(std::move(R));
Artem Dergachev2fc19852016-08-18 12:29:41 +0000152 }
153}
154
Artem Dergachevba816322016-07-26 18:13:12 +0000155//===----------------------------------------------------------------------===//
156// Register CloneChecker
157//===----------------------------------------------------------------------===//
158
159void ento::registerCloneChecker(CheckerManager &Mgr) {
160 Mgr.registerChecker<CloneChecker>();
161}