clang  9.0.0svn
CloneChecker.cpp
Go to the documentation of this file.
1 //===--- CloneChecker.cpp - Clone detection checker -------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// CloneChecker is a checker that reports clones in the current translation
11 /// unit.
12 ///
13 //===----------------------------------------------------------------------===//
14 
17 #include "clang/Basic/Diagnostic.h"
23 
24 using namespace clang;
25 using namespace ento;
26 
27 namespace {
28 class CloneChecker
29  : public Checker<check::ASTCodeBody, check::EndOfTranslationUnit> {
30  mutable CloneDetector Detector;
31  mutable std::unique_ptr<BugType> BT_Exact, BT_Suspicious;
32 
33 public:
34  void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
35  BugReporter &BR) const;
36 
37  void checkEndOfTranslationUnit(const TranslationUnitDecl *TU,
38  AnalysisManager &Mgr, BugReporter &BR) const;
39 
40  /// Reports all clones to the user.
41  void reportClones(BugReporter &BR, AnalysisManager &Mgr,
42  std::vector<CloneDetector::CloneGroup> &CloneGroups) const;
43 
44  /// Reports only suspicious clones to the user along with information
45  /// that explain why they are suspicious.
46  void reportSuspiciousClones(
47  BugReporter &BR, AnalysisManager &Mgr,
48  std::vector<CloneDetector::CloneGroup> &CloneGroups) const;
49 };
50 } // end anonymous namespace
51 
52 void 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.
56  Detector.analyzeCodeBody(D);
57 }
58 
59 void 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().getCheckerIntegerOption(
66  "MinimumCloneComplexity", 50, this);
67  assert(MinComplexity >= 0);
68 
69  bool ReportSuspiciousClones = Mgr.getAnalyzerOptions()
70  .getCheckerBooleanOption("ReportSuspiciousClones", true, this);
71 
72  bool ReportNormalClones = Mgr.getAnalyzerOptions().getCheckerBooleanOption(
73  "ReportNormalClones", true, this);
74 
75  StringRef IgnoredFilesPattern = Mgr.getAnalyzerOptions()
76  .getCheckerStringOption("IgnoredFilesPattern", "", this);
77 
78  // Let the CloneDetector create a list of clones from all the analyzed
79  // statements. We don't filter for matching variable patterns at this point
80  // because reportSuspiciousClones() wants to search them for errors.
81  std::vector<CloneDetector::CloneGroup> AllCloneGroups;
82 
83  Detector.findClones(
84  AllCloneGroups, FilenamePatternConstraint(IgnoredFilesPattern),
86  MinComplexityConstraint(MinComplexity),
88 
89  if (ReportSuspiciousClones)
90  reportSuspiciousClones(BR, Mgr, AllCloneGroups);
91 
92  // We are done for this translation unit unless we also need to report normal
93  // clones.
94  if (!ReportNormalClones)
95  return;
96 
97  // Now that the suspicious clone detector has checked for pattern errors,
98  // we also filter all clones who don't have matching patterns
99  CloneDetector::constrainClones(AllCloneGroups,
102 
103  reportClones(BR, Mgr, AllCloneGroups);
104 }
105 
106 static PathDiagnosticLocation makeLocation(const StmtSequence &S,
107  AnalysisManager &Mgr) {
108  ASTContext &ACtx = Mgr.getASTContext();
110  S.front(), ACtx.getSourceManager(),
111  Mgr.getAnalysisDeclContext(ACtx.getTranslationUnitDecl()));
112 }
113 
114 void CloneChecker::reportClones(
115  BugReporter &BR, AnalysisManager &Mgr,
116  std::vector<CloneDetector::CloneGroup> &CloneGroups) const {
117 
118  if (!BT_Exact)
119  BT_Exact.reset(new BugType(this, "Exact code clone", "Code clone"));
120 
121  for (const CloneDetector::CloneGroup &Group : CloneGroups) {
122  // We group the clones by printing the first as a warning and all others
123  // as a note.
124  auto R = llvm::make_unique<BugReport>(*BT_Exact, "Duplicate code detected",
125  makeLocation(Group.front(), Mgr));
126  R->addRange(Group.front().getSourceRange());
127 
128  for (unsigned i = 1; i < Group.size(); ++i)
129  R->addNote("Similar code here", makeLocation(Group[i], Mgr),
130  Group[i].getSourceRange());
131  BR.emitReport(std::move(R));
132  }
133 }
134 
135 void CloneChecker::reportSuspiciousClones(
136  BugReporter &BR, AnalysisManager &Mgr,
137  std::vector<CloneDetector::CloneGroup> &CloneGroups) const {
138  std::vector<VariablePattern::SuspiciousClonePair> Pairs;
139 
140  for (const CloneDetector::CloneGroup &Group : CloneGroups) {
141  for (unsigned i = 0; i < Group.size(); ++i) {
142  VariablePattern PatternA(Group[i]);
143 
144  for (unsigned j = i + 1; j < Group.size(); ++j) {
145  VariablePattern PatternB(Group[j]);
146 
148  // For now, we only report clones which break the variable pattern just
149  // once because multiple differences in a pattern are an indicator that
150  // those differences are maybe intended (e.g. because it's actually a
151  // different algorithm).
152  // FIXME: In very big clones even multiple variables can be unintended,
153  // so replacing this number with a percentage could better handle such
154  // cases. On the other hand it could increase the false-positive rate
155  // for all clones if the percentage is too high.
156  if (PatternA.countPatternDifferences(PatternB, &ClonePair) == 1) {
157  Pairs.push_back(ClonePair);
158  break;
159  }
160  }
161  }
162  }
163 
164  if (!BT_Suspicious)
165  BT_Suspicious.reset(
166  new BugType(this, "Suspicious code clone", "Code clone"));
167 
168  ASTContext &ACtx = BR.getContext();
170  AnalysisDeclContext *ADC =
171  Mgr.getAnalysisDeclContext(ACtx.getTranslationUnitDecl());
172 
173  for (VariablePattern::SuspiciousClonePair &Pair : Pairs) {
174  // FIXME: We are ignoring the suggestions currently, because they are
175  // only 50% accurate (even if the second suggestion is unavailable),
176  // which may confuse the user.
177  // Think how to perform more accurate suggestions?
178 
179  auto R = llvm::make_unique<BugReport>(
180  *BT_Suspicious,
181  "Potential copy-paste error; did you really mean to use '" +
182  Pair.FirstCloneInfo.Variable->getNameAsString() + "' here?",
183  PathDiagnosticLocation::createBegin(Pair.FirstCloneInfo.Mention, SM,
184  ADC));
185  R->addRange(Pair.FirstCloneInfo.Mention->getSourceRange());
186 
187  R->addNote("Similar code using '" +
188  Pair.SecondCloneInfo.Variable->getNameAsString() + "' here",
189  PathDiagnosticLocation::createBegin(Pair.SecondCloneInfo.Mention,
190  SM, ADC),
191  Pair.SecondCloneInfo.Mention->getSourceRange());
192 
193  BR.emitReport(std::move(R));
194  }
195 }
196 
197 //===----------------------------------------------------------------------===//
198 // Register CloneChecker
199 //===----------------------------------------------------------------------===//
200 
201 void ento::registerCloneChecker(CheckerManager &Mgr) {
202  Mgr.registerChecker<CloneChecker>();
203 }
204 
205 bool ento::shouldRegisterCloneChecker(const LangOptions &LO) {
206  return true;
207 }
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:154
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.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:49
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.
Defines the Diagnostic-related interfaces.
Searches for similar subtrees in the AST.
Describes two clones that reference their variables in a different pattern which could indicate a pro...
This constraint moves clones into clone groups of type II via hashing.
This constraint moves clones into clone groups of type II by comparing them.
Ensures that every clone has at least the given complexity.
const SourceManager & SM
Definition: Format.cpp:1489
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
Dataflow Directional Tag Classes.
SourceRange getSourceRange(const SourceRange &Range)
Returns the SourceRange of a SourceRange.
Definition: FixIt.h:33
static PathDiagnosticLocation makeLocation(const StmtSequence &S, AnalysisManager &Mgr)
SourceManager & getSourceManager()
Definition: ASTContext.h:661
TranslationUnitDecl * getTranslationUnitDecl() const
Definition: ASTContext.h:1008
The top declaration context.
Definition: Decl.h:107
This class handles loading and caching of source files into memory.
Ensures that no clone group fully contains another clone group.