clang  6.0.0svn
CloneChecker.cpp
Go to the documentation of this file.
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"
18 #include "clang/Basic/Diagnostic.h"
24 
25 using namespace clang;
26 using namespace ento;
27 
28 namespace {
29 class CloneChecker
30  : public Checker<check::ASTCodeBody, check::EndOfTranslationUnit> {
31  mutable CloneDetector Detector;
32  mutable std::unique_ptr<BugType> BT_Exact, BT_Suspicious;
33 
34 public:
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;
40 
41  /// Reports all clones to the user.
42  void reportClones(BugReporter &BR, AnalysisManager &Mgr,
43  std::vector<CloneDetector::CloneGroup> &CloneGroups) const;
44 
45  /// Reports only suspicious clones to the user along with informaton
46  /// that explain why they are suspicious.
47  void reportSuspiciousClones(
48  BugReporter &BR, AnalysisManager &Mgr,
49  std::vector<CloneDetector::CloneGroup> &CloneGroups) const;
50 };
51 } // end anonymous namespace
52 
53 void CloneChecker::checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
54  BugReporter &BR) const {
55  // Every statement that should be included in the search for clones needs to
56  // be passed to the CloneDetector.
57  Detector.analyzeCodeBody(D);
58 }
59 
60 void CloneChecker::checkEndOfTranslationUnit(const TranslationUnitDecl *TU,
61  AnalysisManager &Mgr,
62  BugReporter &BR) const {
63  // At this point, every statement in the translation unit has been analyzed by
64  // the CloneDetector. The only thing left to do is to report the found clones.
65 
66  int MinComplexity = Mgr.getAnalyzerOptions().getOptionAsInteger(
67  "MinimumCloneComplexity", 50, this);
68  assert(MinComplexity >= 0);
69 
70  bool ReportSuspiciousClones = Mgr.getAnalyzerOptions().getBooleanOption(
71  "ReportSuspiciousClones", true, this);
72 
73  bool ReportNormalClones = Mgr.getAnalyzerOptions().getBooleanOption(
74  "ReportNormalClones", true, this);
75 
76  StringRef IgnoredFilesPattern = Mgr.getAnalyzerOptions().getOptionAsString(
77  "IgnoredFilesPattern", "", this);
78 
79  // Let the CloneDetector create a list of clones from all the analyzed
80  // statements. We don't filter for matching variable patterns at this point
81  // because reportSuspiciousClones() wants to search them for errors.
82  std::vector<CloneDetector::CloneGroup> AllCloneGroups;
83 
84  Detector.findClones(
85  AllCloneGroups, FilenamePatternConstraint(IgnoredFilesPattern),
87  MinComplexityConstraint(MinComplexity),
89 
90  if (ReportSuspiciousClones)
91  reportSuspiciousClones(BR, Mgr, AllCloneGroups);
92 
93  // We are done for this translation unit unless we also need to report normal
94  // clones.
95  if (!ReportNormalClones)
96  return;
97 
98  // Now that the suspicious clone detector has checked for pattern errors,
99  // we also filter all clones who don't have matching patterns
100  CloneDetector::constrainClones(AllCloneGroups,
103 
104  reportClones(BR, Mgr, AllCloneGroups);
105 }
106 
108  AnalysisManager &Mgr) {
109  ASTContext &ACtx = Mgr.getASTContext();
111  S.front(), ACtx.getSourceManager(),
113 }
114 
115 void CloneChecker::reportClones(
116  BugReporter &BR, AnalysisManager &Mgr,
117  std::vector<CloneDetector::CloneGroup> &CloneGroups) const {
118 
119  if (!BT_Exact)
120  BT_Exact.reset(new BugType(this, "Exact code clone", "Code clone"));
121 
122  for (const CloneDetector::CloneGroup &Group : CloneGroups) {
123  // We group the clones by printing the first as a warning and all others
124  // as a note.
125  auto R = llvm::make_unique<BugReport>(*BT_Exact, "Duplicate code detected",
126  makeLocation(Group.front(), Mgr));
127  R->addRange(Group.front().getSourceRange());
128 
129  for (unsigned i = 1; i < Group.size(); ++i)
130  R->addNote("Similar code here", makeLocation(Group[i], Mgr),
131  Group[i].getSourceRange());
132  BR.emitReport(std::move(R));
133  }
134 }
135 
136 void CloneChecker::reportSuspiciousClones(
137  BugReporter &BR, AnalysisManager &Mgr,
138  std::vector<CloneDetector::CloneGroup> &CloneGroups) const {
139  std::vector<VariablePattern::SuspiciousClonePair> Pairs;
140 
141  for (const CloneDetector::CloneGroup &Group : CloneGroups) {
142  for (unsigned i = 0; i < Group.size(); ++i) {
143  VariablePattern PatternA(Group[i]);
144 
145  for (unsigned j = i + 1; j < Group.size(); ++j) {
146  VariablePattern PatternB(Group[j]);
147 
149  // For now, we only report clones which break the variable pattern just
150  // once because multiple differences in a pattern are an indicator that
151  // those differences are maybe intended (e.g. because it's actually a
152  // different algorithm).
153  // FIXME: In very big clones even multiple variables can be unintended,
154  // so replacing this number with a percentage could better handle such
155  // cases. On the other hand it could increase the false-positive rate
156  // for all clones if the percentage is too high.
157  if (PatternA.countPatternDifferences(PatternB, &ClonePair) == 1) {
158  Pairs.push_back(ClonePair);
159  break;
160  }
161  }
162  }
163  }
164 
165  if (!BT_Suspicious)
166  BT_Suspicious.reset(
167  new BugType(this, "Suspicious code clone", "Code clone"));
168 
169  ASTContext &ACtx = BR.getContext();
171  AnalysisDeclContext *ADC =
173 
174  for (VariablePattern::SuspiciousClonePair &Pair : Pairs) {
175  // FIXME: We are ignoring the suggestions currently, because they are
176  // only 50% accurate (even if the second suggestion is unavailable),
177  // which may confuse the user.
178  // Think how to perform more accurate suggestions?
179 
180  auto R = llvm::make_unique<BugReport>(
181  *BT_Suspicious,
182  "Potential copy-paste error; did you really mean to use '" +
183  Pair.FirstCloneInfo.Variable->getNameAsString() + "' here?",
184  PathDiagnosticLocation::createBegin(Pair.FirstCloneInfo.Mention, SM,
185  ADC));
186  R->addRange(Pair.FirstCloneInfo.Mention->getSourceRange());
187 
188  R->addNote("Similar code using '" +
189  Pair.SecondCloneInfo.Variable->getNameAsString() + "' here",
190  PathDiagnosticLocation::createBegin(Pair.SecondCloneInfo.Mention,
191  SM, ADC),
192  Pair.SecondCloneInfo.Mention->getSourceRange());
193 
194  BR.emitReport(std::move(R));
195  }
196 }
197 
198 //===----------------------------------------------------------------------===//
199 // Register CloneChecker
200 //===----------------------------------------------------------------------===//
201 
202 void ento::registerCloneChecker(CheckerManager &Mgr) {
203  Mgr.registerChecker<CloneChecker>();
204 }
Analyzes the pattern of the referenced variables in a statement.
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
const Stmt * front() const
Returns the first statement in this sequence.
This file defines classes for searching and analyzing source code clones.
Identifies a list of statements.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:149
AnalysisDeclContext contains the context data for the function or method under analysis.
Ensures that all clones reference variables in the same pattern.
unsigned countPatternDifferences(const VariablePattern &Other, VariablePattern::SuspiciousClonePair *FirstMismatch=nullptr)
Counts the differences between this pattern and the given one.
Ensures that all clone groups contain at least the given amount of clones.
static void constrainClones(std::vector< CloneGroup > &CloneGroups, T C)
Constrains the given list of clone groups with the given constraint.
StringRef getOptionAsString(StringRef Name, StringRef DefaultVal, const ento::CheckerBase *C=nullptr, bool SearchInParents=false)
Query an option&#39;s string value.
ASTContext & getContext()
Definition: BugReporter.h:461
Defines the Diagnostic-related interfaces.
Searches for similar subtrees in the AST.
ASTContext & getASTContext() override
Describes two clones that reference their variables in a different pattern which could indicate a pro...
AnalysisDeclContext * getAnalysisDeclContext(const Decl *D)
This constraint moves clones into clone groups of type II via hashing.
AnalyzerOptions & getAnalyzerOptions() override
This constraint moves clones into clone groups of type II by comparing them.
Ensures that every clone has at least the given complexity.
bool getBooleanOption(StringRef Name, bool DefaultVal, const ento::CheckerBase *C=nullptr, bool SearchInParents=false)
Interprets an option&#39;s string value as a boolean.
const SourceManager & SM
Definition: Format.cpp:1337
BugReporter is a utility class for generating PathDiagnostics for analysis.
Definition: BugReporter.h:403
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
CHECKER * registerChecker()
Used to register checkers.
void emitReport(std::unique_ptr< BugReport > R)
Add the given report to the set of reports tracked by BugReporter.
Dataflow Directional Tag Classes.
SourceRange getSourceRange(const SourceRange &Range)
Returns the SourceRange of a SourceRange.
Definition: FixIt.h:34
static PathDiagnosticLocation makeLocation(const StmtSequence &S, AnalysisManager &Mgr)
SourceManager & getSourceManager()
Definition: ASTContext.h:643
TranslationUnitDecl * getTranslationUnitDecl() const
Definition: ASTContext.h:989
TranslationUnitDecl - The top declaration context.
Definition: Decl.h:107
int getOptionAsInteger(StringRef Name, int DefaultVal, const ento::CheckerBase *C=nullptr, bool SearchInParents=false)
Interprets an option&#39;s string value as an integer value.
This class handles loading and caching of source files into memory.
Ensures that no clone group fully contains another clone group.