<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
</head>
<body bgcolor="#FFFFFF" text="#000000">
Yep, sorry, should be fixed in r276791.<br>
<br>
<div class="moz-cite-prefix">On 26.07.2016 22:13, Mike Aizatsky
wrote:<br>
</div>
<blockquote
cite="mid:CAGq4m1-2w3TQx4AQc7n+uppHtZXbftp=cyhjy1vm1Hg-nJTwFw@mail.gmail.com"
type="cite">
<div dir="ltr">Artem,
<div><br>
</div>
<div>I think you broke the build. Could you take a look please?</div>
<div><br>
</div>
<div><a moz-do-not-send="true"
href="http://lab.llvm.org:8011/builders/sanitizer-x86_64-linux-fuzzer/builds/11051">http://lab.llvm.org:8011/builders/sanitizer-x86_64-linux-fuzzer/builds/11051</a><br>
</div>
<div><a moz-do-not-send="true"
href="http://lab.llvm.org:8011/builders/sanitizer-x86_64-linux-fuzzer/builds/11051/steps/build%20clang/logs/stdio">http://lab.llvm.org:8011/builders/sanitizer-x86_64-linux-fuzzer/builds/11051/steps/build%20clang/logs/stdio</a><br>
</div>
<div><br>
</div>
<div>
<pre style="font-family:"courier new",courier,monotype,monospace;font-size:medium;line-height:normal"><span class="inbox-inbox-stdout">[80/140] Building CXX object tools/clang/lib/StaticAnalyzer/Checkers/CMakeFiles/clangStaticAnalyzerCheckers.dir/CloneChecker.cpp.o
FAILED: tools/clang/lib/StaticAnalyzer/Checkers/CMakeFiles/clangStaticAnalyzerCheckers.dir/CloneChecker.cpp.o
/usr/bin/c++ -DCLANG_ENABLE_ARCMT -DCLANG_ENABLE_OBJC_REWRITER -DCLANG_ENABLE_STATIC_ANALYZER -DGTEST_HAS_RTTI=0 -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -Itools/clang/lib/StaticAnalyzer/Checkers -I/mnt/b/sanitizer-buildbo</span><span class="inbox-inbox-stdout">t5/sanitizer-x86_64-linux-fuzzer/build/llvm/tools/clang/lib/StaticAnalyzer/Checkers -I/mnt/b/sanitizer-buildbot5/sanitizer-x86_64-linux-fuzzer/build/llvm/tools/clang/include -Itools/clang/include -Iinclude -I/mnt/b/sanitizer-buildbot5/sanitizer-x86_64-linux-fuzzer/build/llvm/include -fPIC -fvisibility-inlines-hidden -Wall -W -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wno-maybe-uninitialized -Wdelete-non-virtual-dtor -Wno-comment -Werror=date-time -std=c++11 -ffunction-sections -fdata-sections -fno-common -Woverloaded-virtual -fno-strict-aliasing -O3 -DNDEBUG -fno-exceptions -fno-rtti -MMD -MT tools/clang/lib/StaticAn
alyzer/Checkers/CMakeFiles/clangStaticAnalyzerCheckers.dir/CloneChecker.cpp.o -MF tools/clang/lib/StaticAnalyzer/Checkers/CMakeFiles/clangStaticAnalyzerCheckers.dir/CloneChecker.cpp.o.d -o tools/clang/lib/StaticAnalyzer/Checkers/CMakeFiles/clangStaticAnalyzerCheckers.dir/CloneChecker.cpp.o -c /mnt/b/sanitizer-buildbot5/sanitizer-x86_64-linux-fuzzer/build/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp
</span><span class="inbox-inbox-stdout">/mnt/b/sanitizer-buildbot5/sanitizer-x86_64-linux-fuzzer/build/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp:29:25: error: declaration of ‘clang::CloneDetector {anonymous}::CloneChecker::CloneDetector’ [-fpermissive]
mutable CloneDetector CloneDetector;
^
In file included from /mnt/b/sanitizer-buildbot5/sanitizer-x86_64-linux-fuzzer/build/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp:17:0:
/mnt/b/sanitizer-buildbot5/sanitizer-x86_64-linux-fuzzer/build/llvm/tools/clang/include/clang/Analysis/CloneDetection.h:158:7: error: changes meaning of ‘CloneDetector’ from ‘class clang::CloneDetector’ [-fpermissive]
class CloneDetector {
^
</span></pre>
<br class="inbox-inbox-Apple-interchange-newline">
</div>
</div>
<br>
<div class="gmail_quote">
<div dir="ltr">On Tue, Jul 26, 2016 at 11:28 AM Artem Dergachev
via cfe-commits <<a moz-do-not-send="true"
href="mailto:cfe-commits@lists.llvm.org">cfe-commits@lists.llvm.org</a>>
wrote:<br>
</div>
<blockquote class="gmail_quote" style="margin:0 0 0
.8ex;border-left:1px #ccc solid;padding-left:1ex">Author:
dergachev<br>
Date: Tue Jul 26 13:13:12 2016<br>
New Revision: 276782<br>
<br>
URL: <a moz-do-not-send="true"
href="http://llvm.org/viewvc/llvm-project?rev=276782&view=rev"
rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=276782&view=rev</a><br>
Log:<br>
[analyzer] Add basic capabilities to detect source code
clones.<br>
<br>
This patch adds the CloneDetector class which allows searching
source code<br>
for clones.<br>
<br>
For every statement or group of statements within a compound
statement,<br>
CloneDetector computes a hash value, and finds clones by
detecting<br>
identical hash values.<br>
<br>
This initial patch only provides a simple hashing mechanism<br>
that hashes the kind of each sub-statement.<br>
<br>
This patch also adds CloneChecker - a simple static analyzer
checker<br>
that uses CloneDetector to report copy-pasted code.<br>
<br>
Patch by Raphael Isemann!<br>
<br>
Differential Revision: <a moz-do-not-send="true"
href="https://reviews.llvm.org/D20795" rel="noreferrer"
target="_blank">https://reviews.llvm.org/D20795</a><br>
<br>
Added:<br>
cfe/trunk/include/clang/Analysis/CloneDetection.h<br>
cfe/trunk/lib/Analysis/CloneDetection.cpp<br>
cfe/trunk/lib/StaticAnalyzer/Checkers/CloneChecker.cpp<br>
cfe/trunk/test/Analysis/copypaste/<br>
cfe/trunk/test/Analysis/copypaste/blocks.cpp<br>
cfe/trunk/test/Analysis/copypaste/false-positives.cpp<br>
cfe/trunk/test/Analysis/copypaste/functions.cpp<br>
cfe/trunk/test/Analysis/copypaste/objc-methods.m<br>
cfe/trunk/test/Analysis/copypaste/sub-sequences.cpp<br>
Modified:<br>
cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td<br>
cfe/trunk/lib/Analysis/CMakeLists.txt<br>
cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt<br>
<br>
Added: cfe/trunk/include/clang/Analysis/CloneDetection.h<br>
URL: <a moz-do-not-send="true"
href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/CloneDetection.h?rev=276782&view=auto"
rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/CloneDetection.h?rev=276782&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/Analysis/CloneDetection.h (added)<br>
+++ cfe/trunk/include/clang/Analysis/CloneDetection.h Tue Jul
26 13:13:12 2016<br>
@@ -0,0 +1,235 @@<br>
+//===--- CloneDetection.h - Finds code clones in an AST
---------*- C++ -*-===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois
Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+///<br>
+/// /file<br>
+/// This file defines classes for searching and anlyzing
source code clones.<br>
+///<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#ifndef LLVM_CLANG_AST_CLONEDETECTION_H<br>
+#define LLVM_CLANG_AST_CLONEDETECTION_H<br>
+<br>
+#include "clang/Basic/SourceLocation.h"<br>
+#include "llvm/ADT/StringMap.h"<br>
+<br>
+#include <vector><br>
+<br>
+namespace clang {<br>
+<br>
+class Stmt;<br>
+class Decl;<br>
+class ASTContext;<br>
+class CompoundStmt;<br>
+<br>
+/// \brief Identifies a list of statements.<br>
+///<br>
+/// Can either identify a single arbitrary Stmt object, a
continuous sequence of<br>
+/// child statements inside a CompoundStmt or no statements
at all.<br>
+class StmtSequence {<br>
+ /// If this object identifies a sequence of statements
inside a CompoundStmt,<br>
+ /// S points to this CompoundStmt. If this object only
identifies a single<br>
+ /// Stmt, then S is a pointer to this Stmt.<br>
+ const Stmt *S;<br>
+<br>
+ /// The related ASTContext for S.<br>
+ ASTContext *Context;<br>
+<br>
+ /// If EndIndex is non-zero, then S is a CompoundStmt and
this StmtSequence<br>
+ /// instance is representing the CompoundStmt children
inside the array<br>
+ /// [StartIndex, EndIndex).<br>
+ unsigned StartIndex;<br>
+ unsigned EndIndex;<br>
+<br>
+public:<br>
+ /// \brief Constructs a StmtSequence holding multiple
statements.<br>
+ ///<br>
+ /// The resulting StmtSequence identifies a continuous
sequence of statements<br>
+ /// in the body of the given CompoundStmt. Which statements
of the body should<br>
+ /// be identified needs to be specified by providing a
start and end index<br>
+ /// that describe a non-empty sub-array in the body of the
given CompoundStmt.<br>
+ ///<br>
+ /// \param Stmt A CompoundStmt that contains all statements
in its body.<br>
+ /// \param Context The ASTContext for the given
CompoundStmt.<br>
+ /// \param StartIndex The inclusive start index in the
children array of<br>
+ /// \p Stmt<br>
+ /// \param EndIndex The exclusive end index in the children
array of \p Stmt.<br>
+ StmtSequence(const CompoundStmt *Stmt, ASTContext
&Context,<br>
+ unsigned StartIndex, unsigned EndIndex);<br>
+<br>
+ /// \brief Constructs a StmtSequence holding a single
statement.<br>
+ ///<br>
+ /// \param Stmt An arbitrary Stmt.<br>
+ /// \param Context The ASTContext for the given Stmt.<br>
+ StmtSequence(const Stmt *Stmt, ASTContext &Context);<br>
+<br>
+ /// \brief Constructs an empty StmtSequence.<br>
+ StmtSequence();<br>
+<br>
+ typedef const Stmt *const *iterator;<br>
+<br>
+ /// Returns an iterator pointing to the first statement in
this sequence.<br>
+ iterator begin() const;<br>
+<br>
+ /// Returns an iterator pointing behind the last statement
in this sequence.<br>
+ iterator end() const;<br>
+<br>
+ /// Returns the first statement in this sequence.<br>
+ ///<br>
+ /// This method should only be called on a non-empty
StmtSequence object.<br>
+ const Stmt *front() const {<br>
+ assert(!empty());<br>
+ return begin()[0];<br>
+ }<br>
+<br>
+ /// Returns the last statement in this sequence.<br>
+ ///<br>
+ /// This method should only be called on a non-empty
StmtSequence object.<br>
+ const Stmt *back() const {<br>
+ assert(!empty());<br>
+ return begin()[size() - 1];<br>
+ }<br>
+<br>
+ /// Returns the number of statements this object holds.<br>
+ unsigned size() const {<br>
+ if (holdsSequence())<br>
+ return EndIndex - StartIndex;<br>
+ if (S == nullptr)<br>
+ return 0;<br>
+ return 1;<br>
+ }<br>
+<br>
+ /// Returns true if and only if this StmtSequence contains
no statements.<br>
+ bool empty() const { return size() == 0; }<br>
+<br>
+ /// Returns the related ASTContext for the stored Stmts.<br>
+ ASTContext &getASTContext() const {<br>
+ assert(Context);<br>
+ return *Context;<br>
+ }<br>
+<br>
+ /// Returns true if this objects holds a list of
statements.<br>
+ bool holdsSequence() const { return EndIndex != 0; }<br>
+<br>
+ /// Returns the start sourcelocation of the first statement
in this sequence.<br>
+ ///<br>
+ /// This method should only be called on a non-empty
StmtSequence object.<br>
+ SourceLocation getStartLoc() const;<br>
+<br>
+ /// Returns the end sourcelocation of the last statement in
this sequence.<br>
+ ///<br>
+ /// This method should only be called on a non-empty
StmtSequence object.<br>
+ SourceLocation getEndLoc() const;<br>
+<br>
+ bool operator==(const StmtSequence &Other) const {<br>
+ return std::tie(S, StartIndex, EndIndex) ==<br>
+ std::tie(Other.S, Other.StartIndex,
Other.EndIndex);<br>
+ }<br>
+<br>
+ bool operator!=(const StmtSequence &Other) const {<br>
+ return std::tie(S, StartIndex, EndIndex) !=<br>
+ std::tie(Other.S, Other.StartIndex,
Other.EndIndex);<br>
+ }<br>
+<br>
+ /// Returns true if and only if this sequence covers a
source range that<br>
+ /// contains the source range of the given sequence \p
Other.<br>
+ ///<br>
+ /// This method should only be called on a non-empty
StmtSequence object<br>
+ /// and passed a non-empty StmtSequence object.<br>
+ bool contains(const StmtSequence &Other) const;<br>
+};<br>
+<br>
+/// \brief Searches for clones in source code.<br>
+///<br>
+/// First, this class needs a translation unit which is
passed via<br>
+/// \p analyzeTranslationUnit . It will then generate and
store search data<br>
+/// for all statements inside the given translation unit.<br>
+/// Afterwards the generated data can be used to find code
clones by calling<br>
+/// \p findClones .<br>
+///<br>
+/// This class only searches for clones in exectuable source
code<br>
+/// (e.g. function bodies). Other clones (e.g. cloned
comments or declarations)<br>
+/// are not supported.<br>
+class CloneDetector {<br>
+public:<br>
+ /// Holds the data about a StmtSequence that is needed
during the search for<br>
+ /// code clones.<br>
+ struct CloneSignature {<br>
+ /// \brief Holds all relevant data of a StmtSequence.<br>
+ ///<br>
+ /// If this variable is equal for two different
StmtSequences, then they can<br>
+ /// be considered clones of each other.<br>
+ std::vector<unsigned> Data;<br>
+<br>
+ /// \brief The complexity of the StmtSequence.<br>
+ ///<br>
+ /// This scalar value serves as a simple way of filtering
clones that are<br>
+ /// too small to be reported. A greater value indicates
that the related<br>
+ /// StmtSequence is probably more interesting to the
user.<br>
+ unsigned Complexity;<br>
+<br>
+ /// \brief Creates an empty CloneSignature without any
data.<br>
+ CloneSignature() : Complexity(1) {}<br>
+<br>
+ CloneSignature(const std::vector<unsigned>
&Data, unsigned Complexity)<br>
+ : Data(Data), Complexity(Complexity) {}<br>
+<br>
+ /// \brief Adds the data from the given CloneSignature to
this one.<br>
+ void add(const CloneSignature &Other) {<br>
+ Data.insert(Data.end(), Other.Data.begin(),
Other.Data.end());<br>
+ Complexity += Other.Complexity;<br>
+ }<br>
+ };<br>
+<br>
+ /// Holds group of StmtSequences that are clones of each
other and the<br>
+ /// complexity value (see CloneSignature::Complexity) that
all stored<br>
+ /// StmtSequences have in common.<br>
+ struct CloneGroup {<br>
+ std::vector<StmtSequence> Sequences;<br>
+ unsigned Complexity;<br>
+<br>
+ CloneGroup(const StmtSequence &Seq, unsigned
Complexity)<br>
+ : Complexity(Complexity) {<br>
+ Sequences.push_back(Seq);<br>
+ }<br>
+<br>
+ /// \brief Returns false if and only if this group should
be skipped when<br>
+ /// searching for clones.<br>
+ bool isValid() const {<br>
+ // A clone group with only one member makes no sense,
so we skip them.<br>
+ return Sequences.size() > 1;<br>
+ }<br>
+ };<br>
+<br>
+ /// \brief Generates and stores search data for all
statements in the body of<br>
+ /// the given Decl.<br>
+ void analyzeCodeBody(const Decl *D);<br>
+<br>
+ /// \brief Stores the CloneSignature to allow future
querying.<br>
+ void add(const StmtSequence &S, const CloneSignature
&Signature);<br>
+<br>
+ /// \brief Searches the provided statements for clones.<br>
+ ///<br>
+ /// \param Result Output parameter that is filled with a
list of found<br>
+ /// clone groups. Each group contains
multiple StmtSequences<br>
+ /// that were identified to be clones of each
other.<br>
+ /// \param MinGroupComplexity Only return clones which have
at least this<br>
+ /// complexity value.<br>
+ void findClones(std::vector<CloneGroup> &Result,
unsigned MinGroupComplexity);<br>
+<br>
+private:<br>
+ /// Stores all found clone groups including invalid groups
with only a single<br>
+ /// statement.<br>
+ std::vector<CloneGroup> CloneGroups;<br>
+ /// Maps search data to its related index in the \p
CloneGroups vector.<br>
+ llvm::StringMap<std::size_t> CloneGroupIndexes;<br>
+};<br>
+<br>
+} // end namespace clang<br>
+<br>
+#endif // LLVM_CLANG_AST_CLONEDETECTION_H<br>
<br>
Modified:
cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td<br>
URL: <a moz-do-not-send="true"
href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td?rev=276782&r1=276781&r2=276782&view=diff"
rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td?rev=276782&r1=276781&r2=276782&view=diff</a><br>
==============================================================================<br>
---
cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td
(original)<br>
+++
cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td
Tue Jul 26 13:13:12 2016<br>
@@ -77,6 +77,8 @@ def MPI : Package<"mpi">,
InPackage<OptI<br>
def LLVM : Package<"llvm">;<br>
def Debug : Package<"debug">;<br>
<br>
+def CloneDetectionAlpha : Package<"clone">,
InPackage<Alpha>, Hidden;<br>
+<br>
//===----------------------------------------------------------------------===//<br>
// Core Checkers.<br>
//===----------------------------------------------------------------------===//<br>
@@ -661,3 +663,17 @@ def BugHashDumper :
Checker<"DumpBugHash<br>
DescFile<"DebugCheckers.cpp">;<br>
<br>
} // end "debug"<br>
+<br>
+<br>
+//===----------------------------------------------------------------------===//<br>
+// Clone Detection<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+let ParentPackage = CloneDetectionAlpha in {<br>
+<br>
+def CloneChecker : Checker<"CloneChecker">,<br>
+ HelpText<"Reports similar pieces of code.">,<br>
+ DescFile<"CloneChecker.cpp">;<br>
+<br>
+} // end "clone"<br>
+<br>
<br>
Modified: cfe/trunk/lib/Analysis/CMakeLists.txt<br>
URL: <a moz-do-not-send="true"
href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CMakeLists.txt?rev=276782&r1=276781&r2=276782&view=diff"
rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CMakeLists.txt?rev=276782&r1=276781&r2=276782&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Analysis/CMakeLists.txt (original)<br>
+++ cfe/trunk/lib/Analysis/CMakeLists.txt Tue Jul 26 13:13:12
2016<br>
@@ -9,6 +9,7 @@ add_clang_library(clangAnalysis<br>
CFGReachabilityAnalysis.cpp<br>
CFGStmtMap.cpp<br>
CallGraph.cpp<br>
+ CloneDetection.cpp<br>
CocoaConventions.cpp<br>
Consumed.cpp<br>
CodeInjector.cpp<br>
<br>
Added: cfe/trunk/lib/Analysis/CloneDetection.cpp<br>
URL: <a moz-do-not-send="true"
href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CloneDetection.cpp?rev=276782&view=auto"
rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CloneDetection.cpp?rev=276782&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Analysis/CloneDetection.cpp (added)<br>
+++ cfe/trunk/lib/Analysis/CloneDetection.cpp Tue Jul 26
13:13:12 2016<br>
@@ -0,0 +1,277 @@<br>
+//===--- CloneDetection.cpp - Finds code clones in an AST
-------*- C++ -*-===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois
Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+///<br>
+/// This file implements classes for searching and anlyzing
source code clones.<br>
+///<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "clang/Analysis/CloneDetection.h"<br>
+<br>
+#include "clang/AST/ASTContext.h"<br>
+#include "clang/AST/RecursiveASTVisitor.h"<br>
+#include "clang/AST/Stmt.h"<br>
+#include "llvm/ADT/StringRef.h"<br>
+<br>
+using namespace clang;<br>
+<br>
+StmtSequence::StmtSequence(const CompoundStmt *Stmt,
ASTContext &Context,<br>
+ unsigned StartIndex, unsigned
EndIndex)<br>
+ : S(Stmt), Context(&Context), StartIndex(StartIndex),
EndIndex(EndIndex) {<br>
+ assert(Stmt && "Stmt must not be a nullptr");<br>
+ assert(StartIndex < EndIndex && "Given array
should not be empty");<br>
+ assert(EndIndex <= Stmt->size() && "Given
array too big for this Stmt");<br>
+}<br>
+<br>
+StmtSequence::StmtSequence(const Stmt *Stmt, ASTContext
&Context)<br>
+ : S(Stmt), Context(&Context), StartIndex(0),
EndIndex(0) {}<br>
+<br>
+StmtSequence::StmtSequence()<br>
+ : S(nullptr), Context(nullptr), StartIndex(0),
EndIndex(0) {}<br>
+<br>
+bool StmtSequence::contains(const StmtSequence &Other)
const {<br>
+ // If both sequences reside in different translation units,
they can never<br>
+ // contain each other.<br>
+ if (Context != Other.Context)<br>
+ return false;<br>
+<br>
+ const SourceManager &SM =
Context->getSourceManager();<br>
+<br>
+ // Otherwise check if the start and end locations of the
current sequence<br>
+ // surround the other sequence.<br>
+ bool StartIsInBounds =<br>
+ SM.isBeforeInTranslationUnit(getStartLoc(),
Other.getStartLoc()) ||<br>
+ getStartLoc() == Other.getStartLoc();<br>
+ if (!StartIsInBounds)<br>
+ return false;<br>
+<br>
+ bool EndIsInBounds =<br>
+ SM.isBeforeInTranslationUnit(Other.getEndLoc(),
getEndLoc()) ||<br>
+ Other.getEndLoc() == getEndLoc();<br>
+ return EndIsInBounds;<br>
+}<br>
+<br>
+StmtSequence::iterator StmtSequence::begin() const {<br>
+ if (!holdsSequence()) {<br>
+ return &S;<br>
+ }<br>
+ auto CS = cast<CompoundStmt>(S);<br>
+ return CS->body_begin() + StartIndex;<br>
+}<br>
+<br>
+StmtSequence::iterator StmtSequence::end() const {<br>
+ if (!holdsSequence()) {<br>
+ return &S + 1;<br>
+ }<br>
+ auto CS = cast<CompoundStmt>(S);<br>
+ return CS->body_begin() + EndIndex;<br>
+}<br>
+<br>
+SourceLocation StmtSequence::getStartLoc() const {<br>
+ return front()->getLocStart();<br>
+}<br>
+<br>
+SourceLocation StmtSequence::getEndLoc() const { return
back()->getLocEnd(); }<br>
+<br>
+namespace {<br>
+/// Generates CloneSignatures for a set of statements and
stores the results in<br>
+/// a CloneDetector object.<br>
+class CloneSignatureGenerator {<br>
+<br>
+ CloneDetector &CD;<br>
+ ASTContext &Context;<br>
+<br>
+ /// \brief Generates CloneSignatures for all statements in
the given statement<br>
+ /// tree and stores them in the CloneDetector.<br>
+ ///<br>
+ /// \param S The root of the given statement tree.<br>
+ /// \return The CloneSignature of the root statement.<br>
+ CloneDetector::CloneSignature generateSignatures(const Stmt
*S) {<br>
+ // Create an empty signature that will be filled in this
method.<br>
+ CloneDetector::CloneSignature Signature;<br>
+<br>
+ // The only relevant data for now is the class of the
statement.<br>
+ // TODO: Collect statement class specific data.<br>
+ Signature.Data.push_back(S->getStmtClass());<br>
+<br>
+ // Storage for the signatures of the direct child
statements. This is only<br>
+ // needed if the current statement is a CompoundStmt.<br>
+ std::vector<CloneDetector::CloneSignature>
ChildSignatures;<br>
+ const CompoundStmt *CS = dyn_cast<const
CompoundStmt>(S);<br>
+<br>
+ // The signature of a statement includes the signatures
of its children.<br>
+ // Therefore we create the signatures for every child and
add them to the<br>
+ // current signature.<br>
+ for (const Stmt *Child : S->children()) {<br>
+ // Some statements like 'if' can have nullptr children
that we will skip.<br>
+ if (!Child)<br>
+ continue;<br>
+<br>
+ // Recursive call to create the signature of the child
statement. This<br>
+ // will also create and store all clone groups in this
child statement.<br>
+ auto ChildSignature = generateSignatures(Child);<br>
+<br>
+ // Add the collected data to the signature of the
current statement.<br>
+ Signature.add(ChildSignature);<br>
+<br>
+ // If the current statement is a CompoundStatement, we
need to store the<br>
+ // signature for the generation of the sub-sequences.<br>
+ if (CS)<br>
+ ChildSignatures.push_back(ChildSignature);<br>
+ }<br>
+<br>
+ // If the current statement is a CompoundStmt, we also
need to create the<br>
+ // clone groups from the sub-sequences inside the
children.<br>
+ if (CS)<br>
+ handleSubSequences(CS, ChildSignatures);<br>
+<br>
+ // Save the signature for the current statement in the
CloneDetector object.<br>
+ CD.add(StmtSequence(S, Context), Signature);<br>
+<br>
+ return Signature;<br>
+ }<br>
+<br>
+ /// \brief Adds all possible sub-sequences in the child
array of the given<br>
+ /// CompoundStmt to the CloneDetector.<br>
+ /// \param CS The given CompoundStmt.<br>
+ /// \param ChildSignatures A list of calculated signatures
for each child in<br>
+ /// the given CompoundStmt.<br>
+ void handleSubSequences(<br>
+ const CompoundStmt *CS,<br>
+ const std::vector<CloneDetector::CloneSignature>
&ChildSignatures) {<br>
+<br>
+ // FIXME: This function has quadratic runtime right now.
Check if skipping<br>
+ // this function for too long CompoundStmts is an option.<br>
+<br>
+ // The length of the sub-sequence. We don't need to
handle sequences with<br>
+ // the length 1 as they are already handled in
CollectData().<br>
+ for (unsigned Length = 2; Length <= CS->size();
++Length) {<br>
+ // The start index in the body of the CompoundStmt. We
increase the<br>
+ // position until the end of the sub-sequence reaches
the end of the<br>
+ // CompoundStmt body.<br>
+ for (unsigned Pos = 0; Pos <= CS->size() -
Length; ++Pos) {<br>
+ // Create an empty signature and add the signatures
of all selected<br>
+ // child statements to it.<br>
+ CloneDetector::CloneSignature SubSignature;<br>
+<br>
+ for (unsigned i = Pos; i < Pos + Length; ++i) {<br>
+ SubSignature.add(ChildSignatures[i]);<br>
+ }<br>
+<br>
+ // Save the signature together with the information
about what children<br>
+ // sequence we selected.<br>
+ CD.add(StmtSequence(CS, Context, Pos, Pos + Length),
SubSignature);<br>
+ }<br>
+ }<br>
+ }<br>
+<br>
+public:<br>
+ explicit CloneSignatureGenerator(CloneDetector &CD,
ASTContext &Context)<br>
+ : CD(CD), Context(Context) {}<br>
+<br>
+ /// \brief Generates signatures for all statements in the
given function body.<br>
+ void consumeCodeBody(const Stmt *S) {
generateSignatures(S); }<br>
+};<br>
+} // end anonymous namespace<br>
+<br>
+void CloneDetector::analyzeCodeBody(const Decl *D) {<br>
+ assert(D);<br>
+ assert(D->hasBody());<br>
+ CloneSignatureGenerator Generator(*this,
D->getASTContext());<br>
+ Generator.consumeCodeBody(D->getBody());<br>
+}<br>
+<br>
+void CloneDetector::add(const StmtSequence &S,<br>
+ const CloneSignature &Signature)
{<br>
+ // StringMap only works with StringRefs, so we create one
for our data vector.<br>
+ auto &Data = Signature.Data;<br>
+ StringRef DataRef = StringRef(reinterpret_cast<const
char *>(Data.data()),<br>
+ Data.size() *
sizeof(unsigned));<br>
+<br>
+ // Search with the help of the signature if we already have
encountered a<br>
+ // clone of the given StmtSequence.<br>
+ auto I = CloneGroupIndexes.find(DataRef);<br>
+ if (I == CloneGroupIndexes.end()) {<br>
+ // We haven't found an existing clone group, so we create
a new clone group<br>
+ // for this StmtSequence and store the index of it in our
search map.<br>
+ CloneGroupIndexes[DataRef] = CloneGroups.size();<br>
+ CloneGroups.emplace_back(S, Signature.Complexity);<br>
+ return;<br>
+ }<br>
+<br>
+ // We have found an existing clone group and can expand it
with the given<br>
+ // StmtSequence.<br>
+ CloneGroups[I->getValue()].Sequences.push_back(S);<br>
+}<br>
+<br>
+namespace {<br>
+/// \brief Returns true if and only if \p Stmt contains at
least one other<br>
+/// sequence in the \p Group.<br>
+bool containsAnyInGroup(StmtSequence &Stmt,<br>
+ CloneDetector::CloneGroup
&Group) {<br>
+ for (StmtSequence &GroupStmt : Group.Sequences) {<br>
+ if (Stmt.contains(GroupStmt))<br>
+ return true;<br>
+ }<br>
+ return false;<br>
+}<br>
+<br>
+/// \brief Returns true if and only if all sequences in \p
OtherGroup are<br>
+/// contained by a sequence in \p Group.<br>
+bool containsGroup(CloneDetector::CloneGroup &Group,<br>
+ CloneDetector::CloneGroup &OtherGroup)
{<br>
+ // We have less sequences in the current group than we have
in the other,<br>
+ // so we will never fulfill the requirement for returning
true. This is only<br>
+ // possible because we know that a sequence in Group can
contain at most<br>
+ // one sequence in OtherGroup.<br>
+ if (Group.Sequences.size() <
OtherGroup.Sequences.size())<br>
+ return false;<br>
+<br>
+ for (StmtSequence &Stmt : Group.Sequences) {<br>
+ if (!containsAnyInGroup(Stmt, OtherGroup))<br>
+ return false;<br>
+ }<br>
+ return true;<br>
+}<br>
+} // end anonymous namespace<br>
+<br>
+void CloneDetector::findClones(std::vector<CloneGroup>
&Result,<br>
+ unsigned MinGroupComplexity) {<br>
+ // Add every valid clone group that fulfills the complexity
requirement.<br>
+ for (const CloneGroup &Group : CloneGroups) {<br>
+ if (Group.isValid() && Group.Complexity >=
MinGroupComplexity) {<br>
+ Result.push_back(Group);<br>
+ }<br>
+ }<br>
+<br>
+ std::vector<unsigned> IndexesToRemove;<br>
+<br>
+ // Compare every group in the result with the rest. If one
groups contains<br>
+ // another group, we only need to return the bigger group.<br>
+ // Note: This doesn't scale well, so if possible avoid
calling any heavy<br>
+ // function from this loop to minimize the performance
impact.<br>
+ for (unsigned i = 0; i < Result.size(); ++i) {<br>
+ for (unsigned j = 0; j < Result.size(); ++j) {<br>
+ // Don't compare a group with itself.<br>
+ if (i == j)<br>
+ continue;<br>
+<br>
+ if (containsGroup(Result[j], Result[i])) {<br>
+ IndexesToRemove.push_back(i);<br>
+ break;<br>
+ }<br>
+ }<br>
+ }<br>
+<br>
+ // Erasing a list of indexes from the vector should be done
with decreasing<br>
+ // indexes. As IndexesToRemove is constructed with
increasing values, we just<br>
+ // reverse iterate over it to get the desired order.<br>
+ for (auto I = IndexesToRemove.rbegin(); I !=
IndexesToRemove.rend(); ++I) {<br>
+ Result.erase(Result.begin() + *I);<br>
+ }<br>
+}<br>
<br>
Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt<br>
URL: <a moz-do-not-send="true"
href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt?rev=276782&r1=276781&r2=276782&view=diff"
rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt?rev=276782&r1=276781&r2=276782&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt
(original)<br>
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt Tue
Jul 26 13:13:12 2016<br>
@@ -22,6 +22,7 @@ add_clang_library(clangStaticAnalyzerChe<br>
CheckerDocumentation.cpp<br>
ChrootChecker.cpp<br>
ClangCheckers.cpp<br>
+ CloneChecker.cpp<br>
CXXSelfAssignmentChecker.cpp<br>
DeadStoresChecker.cpp<br>
DebugCheckers.cpp<br>
<br>
Added: cfe/trunk/lib/StaticAnalyzer/Checkers/CloneChecker.cpp<br>
URL: <a moz-do-not-send="true"
href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/CloneChecker.cpp?rev=276782&view=auto"
rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/CloneChecker.cpp?rev=276782&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/lib/StaticAnalyzer/Checkers/CloneChecker.cpp
(added)<br>
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/CloneChecker.cpp Tue
Jul 26 13:13:12 2016<br>
@@ -0,0 +1,96 @@<br>
+//===--- CloneChecker.cpp - Clone detection checker
-------------*- C++ -*-===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois
Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+///<br>
+/// \file<br>
+/// CloneChecker is a checker that reports clones in the
current translation<br>
+/// unit.<br>
+///<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "ClangSACheckers.h"<br>
+#include "clang/Analysis/CloneDetection.h"<br>
+#include "clang/Basic/Diagnostic.h"<br>
+#include "clang/StaticAnalyzer/Core/Checker.h"<br>
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"<br>
+#include
"clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"<br>
+<br>
+using namespace clang;<br>
+using namespace ento;<br>
+<br>
+namespace {<br>
+class CloneChecker<br>
+ : public Checker<check::ASTCodeBody,
check::EndOfTranslationUnit> {<br>
+ mutable CloneDetector CloneDetector;<br>
+<br>
+public:<br>
+ void checkASTCodeBody(const Decl *D, AnalysisManager
&Mgr,<br>
+ BugReporter &BR) const;<br>
+<br>
+ void checkEndOfTranslationUnit(const TranslationUnitDecl
*TU,<br>
+ AnalysisManager &Mgr,
BugReporter &BR) const;<br>
+};<br>
+} // end anonymous namespace<br>
+<br>
+void CloneChecker::checkASTCodeBody(const Decl *D,
AnalysisManager &Mgr,<br>
+ BugReporter &BR)
const {<br>
+ // Every statement that should be included in the search
for clones needs to<br>
+ // be passed to the CloneDetector.<br>
+ CloneDetector.analyzeCodeBody(D);<br>
+}<br>
+<br>
+void CloneChecker::checkEndOfTranslationUnit(const
TranslationUnitDecl *TU,<br>
+ AnalysisManager
&Mgr,<br>
+ BugReporter
&BR) const {<br>
+ // At this point, every statement in the translation unit
has been analyzed by<br>
+ // the CloneDetector. The only thing left to do is to
report the found clones.<br>
+<br>
+ int MinComplexity =
Mgr.getAnalyzerOptions().getOptionAsInteger(<br>
+ "MinimumCloneComplexity", 10, this);<br>
+<br>
+ assert(MinComplexity >= 0);<br>
+<br>
+ SourceManager &SM = BR.getSourceManager();<br>
+<br>
+ std::vector<CloneDetector::CloneGroup> CloneGroups;<br>
+ CloneDetector.findClones(CloneGroups, MinComplexity);<br>
+<br>
+ DiagnosticsEngine &DiagEngine = Mgr.getDiagnostic();<br>
+<br>
+ unsigned WarnID =
DiagEngine.getCustomDiagID(DiagnosticsEngine::Warning,<br>
+ "Detected code
clone.");<br>
+<br>
+ unsigned NoteID =
DiagEngine.getCustomDiagID(DiagnosticsEngine::Note,<br>
+ "Related code
clone is here.");<br>
+<br>
+ for (CloneDetector::CloneGroup &Group : CloneGroups) {<br>
+ // For readability reasons we sort the clones by line
numbers.<br>
+ std::sort(Group.Sequences.begin(), Group.Sequences.end(),<br>
+ [&SM](const StmtSequence &LHS, const
StmtSequence &RHS) {<br>
+ return
SM.isBeforeInTranslationUnit(LHS.getStartLoc(),<br>
+
RHS.getStartLoc()) &&<br>
+
SM.isBeforeInTranslationUnit(LHS.getEndLoc(),<br>
+
RHS.getEndLoc());<br>
+ });<br>
+<br>
+ // We group the clones by printing the first as a warning
and all others<br>
+ // as a note.<br>
+ DiagEngine.Report(Group.Sequences.front().getStartLoc(),
WarnID);<br>
+ for (unsigned i = 1; i < Group.Sequences.size(); ++i)
{<br>
+ DiagEngine.Report(Group.Sequences[i].getStartLoc(),
NoteID);<br>
+ }<br>
+ }<br>
+}<br>
+<br>
+//===----------------------------------------------------------------------===//<br>
+// Register CloneChecker<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+void ento::registerCloneChecker(CheckerManager &Mgr) {<br>
+ Mgr.registerChecker<CloneChecker>();<br>
+}<br>
<br>
Added: cfe/trunk/test/Analysis/copypaste/blocks.cpp<br>
URL: <a moz-do-not-send="true"
href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/copypaste/blocks.cpp?rev=276782&view=auto"
rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/copypaste/blocks.cpp?rev=276782&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/Analysis/copypaste/blocks.cpp (added)<br>
+++ cfe/trunk/test/Analysis/copypaste/blocks.cpp Tue Jul 26
13:13:12 2016<br>
@@ -0,0 +1,19 @@<br>
+// RUN: %clang_cc1 -analyze -fblocks -std=c++11
-analyzer-checker=alpha.clone.CloneChecker -verify %s<br>
+<br>
+// This tests if we search for clones in blocks.<br>
+<br>
+void log();<br>
+<br>
+auto BlockA = ^(int a, int b){ // expected-warning{{Detected
code clone.}}<br>
+ log();<br>
+ if (a > b)<br>
+ return a;<br>
+ return b;<br>
+};<br>
+<br>
+auto BlockB = ^(int a, int b){ // expected-note{{Related code
clone is here.}}<br>
+ log();<br>
+ if (a > b)<br>
+ return a;<br>
+ return b;<br>
+};<br>
<br>
Added: cfe/trunk/test/Analysis/copypaste/false-positives.cpp<br>
URL: <a moz-do-not-send="true"
href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/copypaste/false-positives.cpp?rev=276782&view=auto"
rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/copypaste/false-positives.cpp?rev=276782&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/Analysis/copypaste/false-positives.cpp
(added)<br>
+++ cfe/trunk/test/Analysis/copypaste/false-positives.cpp Tue
Jul 26 13:13:12 2016<br>
@@ -0,0 +1,29 @@<br>
+// RUN: %clang_cc1 -analyze -std=c++11
-analyzer-checker=alpha.clone.CloneChecker -verify %s<br>
+<br>
+// This test contains false-positive reports from the
CloneChecker that need to<br>
+// be fixed.<br>
+<br>
+void log();<br>
+<br>
+int max(int a, int b) { // expected-warning{{Detected code
clone.}}<br>
+ log();<br>
+ if (a > b)<br>
+ return a;<br>
+ return b;<br>
+}<br>
+<br>
+// FIXME: Detect different binary operator kinds.<br>
+int min1(int a, int b) { // expected-note{{Related code clone
is here.}}<br>
+ log();<br>
+ if (a < b)<br>
+ return a;<br>
+ return b;<br>
+}<br>
+<br>
+// FIXME: Detect different variable patterns.<br>
+int min2(int a, int b) { // expected-note{{Related code clone
is here.}}<br>
+ log();<br>
+ if (b > a)<br>
+ return a;<br>
+ return b;<br>
+}<br>
<br>
Added: cfe/trunk/test/Analysis/copypaste/functions.cpp<br>
URL: <a moz-do-not-send="true"
href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/copypaste/functions.cpp?rev=276782&view=auto"
rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/copypaste/functions.cpp?rev=276782&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/Analysis/copypaste/functions.cpp (added)<br>
+++ cfe/trunk/test/Analysis/copypaste/functions.cpp Tue Jul 26
13:13:12 2016<br>
@@ -0,0 +1,25 @@<br>
+// RUN: %clang_cc1 -analyze -std=c++11
-analyzer-checker=alpha.clone.CloneChecker -verify %s<br>
+<br>
+// This tests if we search for clones in functions.<br>
+<br>
+void log();<br>
+<br>
+int max(int a, int b) { // expected-warning{{Detected code
clone.}}<br>
+ log();<br>
+ if (a > b)<br>
+ return a;<br>
+ return b;<br>
+}<br>
+<br>
+int maxClone(int x, int y) { // expected-note{{Related code
clone is here.}}<br>
+ log();<br>
+ if (x > y)<br>
+ return x;<br>
+ return y;<br>
+}<br>
+<br>
+// Functions below are not clones and should not be reported.<br>
+<br>
+int foo(int a, int b) { // no-warning<br>
+ return a + b;<br>
+}<br>
<br>
Added: cfe/trunk/test/Analysis/copypaste/objc-methods.m<br>
URL: <a moz-do-not-send="true"
href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/copypaste/objc-methods.m?rev=276782&view=auto"
rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/copypaste/objc-methods.m?rev=276782&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/Analysis/copypaste/objc-methods.m (added)<br>
+++ cfe/trunk/test/Analysis/copypaste/objc-methods.m Tue Jul
26 13:13:12 2016<br>
@@ -0,0 +1,27 @@<br>
+// RUN: %clang_cc1 -analyze -Wno-objc-root-class
-analyzer-checker=alpha.clone.CloneChecker -verify %s<br>
+<br>
+// This tests if we search for clones in Objective-C methods.<br>
+<br>
+@interface A<br>
+- (int) setOk : (int) a : (int) b;<br>
+@end<br>
+<br>
+@implementation A<br>
+- (int) setOk : (int) a : (int) b { //
expected-warning{{Detected code clone.}}<br>
+ if (a > b)<br>
+ return a;<br>
+ return b;<br>
+}<br>
+@end<br>
+<br>
+@interface B<br>
+- (int) setOk : (int) a : (int) b;<br>
+@end<br>
+<br>
+@implementation B<br>
+- (int) setOk : (int) a : (int) b { // expected-note{{Related
code clone is here.}}<br>
+ if (a > b)<br>
+ return a;<br>
+ return b;<br>
+}<br>
+@end<br>
<br>
Added: cfe/trunk/test/Analysis/copypaste/sub-sequences.cpp<br>
URL: <a moz-do-not-send="true"
href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/copypaste/sub-sequences.cpp?rev=276782&view=auto"
rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/copypaste/sub-sequences.cpp?rev=276782&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/Analysis/copypaste/sub-sequences.cpp
(added)<br>
+++ cfe/trunk/test/Analysis/copypaste/sub-sequences.cpp Tue
Jul 26 13:13:12 2016<br>
@@ -0,0 +1,27 @@<br>
+// RUN: %clang_cc1 -analyze -std=c++11
-analyzer-checker=alpha.clone.CloneChecker -verify %s<br>
+<br>
+// This tests if sub-sequences can match with normal
sequences.<br>
+<br>
+void log2(int a);<br>
+void log();<br>
+<br>
+int max(int a, int b) {<br>
+ log2(a);<br>
+ log(); // expected-warning{{Detected code clone.}}<br>
+ if (a > b)<br>
+ return a;<br>
+ return b;<br>
+}<br>
+<br>
+int maxClone(int a, int b) {<br>
+ log(); // expected-note{{Related code clone is here.}}<br>
+ if (a > b)<br>
+ return a;<br>
+ return b;<br>
+}<br>
+<br>
+// Functions below are not clones and should not be reported.<br>
+<br>
+int foo(int a, int b) { // no-warning<br>
+ return a + b;<br>
+}<br>
<br>
<br>
_______________________________________________<br>
cfe-commits mailing list<br>
<a moz-do-not-send="true"
href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br>
<a moz-do-not-send="true"
href="http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits"
rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br>
</blockquote>
</div>
<div dir="ltr">-- <br>
</div>
<div data-smartmail="gmail_signature">Mike<br>
Sent from phone</div>
</blockquote>
<br>
</body>
</html>