forked from OSchip/llvm-project
[analyzer] Re-apply r283094 "Improve CloneChecker diagnostics"
The parent commit (r283092) was reverted before and now finally landed. llvm-svn: 283661
This commit is contained in:
parent
46209e1dd0
commit
4eca0de7b7
|
@ -128,6 +128,10 @@ public:
|
||||||
/// This method should only be called on a non-empty StmtSequence object.
|
/// This method should only be called on a non-empty StmtSequence object.
|
||||||
SourceLocation getEndLoc() const;
|
SourceLocation getEndLoc() const;
|
||||||
|
|
||||||
|
/// Returns the source range of the whole sequence - from the beginning
|
||||||
|
/// of the first statement to the end of the last statement.
|
||||||
|
SourceRange getSourceRange() const;
|
||||||
|
|
||||||
bool operator==(const StmtSequence &Other) const {
|
bool operator==(const StmtSequence &Other) const {
|
||||||
return std::tie(S, StartIndex, EndIndex) ==
|
return std::tie(S, StartIndex, EndIndex) ==
|
||||||
std::tie(Other.S, Other.StartIndex, Other.EndIndex);
|
std::tie(Other.S, Other.StartIndex, Other.EndIndex);
|
||||||
|
@ -250,14 +254,14 @@ public:
|
||||||
/// The variable which referencing in this clone was against the pattern.
|
/// The variable which referencing in this clone was against the pattern.
|
||||||
const VarDecl *Variable;
|
const VarDecl *Variable;
|
||||||
/// Where the variable was referenced.
|
/// Where the variable was referenced.
|
||||||
SourceRange VarRange;
|
const Stmt *Mention;
|
||||||
/// The variable that should have been referenced to follow the pattern.
|
/// The variable that should have been referenced to follow the pattern.
|
||||||
/// If Suggestion is a nullptr then it's not possible to fix the pattern
|
/// If Suggestion is a nullptr then it's not possible to fix the pattern
|
||||||
/// by referencing a different variable in this clone.
|
/// by referencing a different variable in this clone.
|
||||||
const VarDecl *Suggestion;
|
const VarDecl *Suggestion;
|
||||||
SuspiciousCloneInfo(const VarDecl *Variable, SourceRange Range,
|
SuspiciousCloneInfo(const VarDecl *Variable, const Stmt *Mention,
|
||||||
const VarDecl *Suggestion)
|
const VarDecl *Suggestion)
|
||||||
: Variable(Variable), VarRange(Range), Suggestion(Suggestion) {}
|
: Variable(Variable), Mention(Mention), Suggestion(Suggestion) {}
|
||||||
SuspiciousCloneInfo() {}
|
SuspiciousCloneInfo() {}
|
||||||
};
|
};
|
||||||
/// The first clone in the pair which always has a suggested variable.
|
/// The first clone in the pair which always has a suggested variable.
|
||||||
|
|
|
@ -82,6 +82,10 @@ SourceLocation StmtSequence::getStartLoc() const {
|
||||||
|
|
||||||
SourceLocation StmtSequence::getEndLoc() const { return back()->getLocEnd(); }
|
SourceLocation StmtSequence::getEndLoc() const { return back()->getLocEnd(); }
|
||||||
|
|
||||||
|
SourceRange StmtSequence::getSourceRange() const {
|
||||||
|
return SourceRange(getStartLoc(), getEndLoc());
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
/// \brief Analyzes the pattern of the referenced variables in a statement.
|
/// \brief Analyzes the pattern of the referenced variables in a statement.
|
||||||
|
@ -91,11 +95,11 @@ class VariablePattern {
|
||||||
struct VariableOccurence {
|
struct VariableOccurence {
|
||||||
/// The index of the associated VarDecl in the Variables vector.
|
/// The index of the associated VarDecl in the Variables vector.
|
||||||
size_t KindID;
|
size_t KindID;
|
||||||
/// The source range in the code where the variable was referenced.
|
/// The statement in the code where the variable was referenced.
|
||||||
SourceRange Range;
|
const Stmt *Mention;
|
||||||
|
|
||||||
VariableOccurence(size_t KindID, SourceRange Range)
|
VariableOccurence(size_t KindID, const Stmt *Mention)
|
||||||
: KindID(KindID), Range(Range) {}
|
: KindID(KindID), Mention(Mention) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// All occurences of referenced variables in the order of appearance.
|
/// All occurences of referenced variables in the order of appearance.
|
||||||
|
@ -106,20 +110,20 @@ class VariablePattern {
|
||||||
|
|
||||||
/// \brief Adds a new variable referenced to this pattern.
|
/// \brief Adds a new variable referenced to this pattern.
|
||||||
/// \param VarDecl The declaration of the variable that is referenced.
|
/// \param VarDecl The declaration of the variable that is referenced.
|
||||||
/// \param Range The SourceRange where this variable is referenced.
|
/// \param Mention The SourceRange where this variable is referenced.
|
||||||
void addVariableOccurence(const VarDecl *VarDecl, SourceRange Range) {
|
void addVariableOccurence(const VarDecl *VarDecl, const Stmt *Mention) {
|
||||||
// First check if we already reference this variable
|
// First check if we already reference this variable
|
||||||
for (size_t KindIndex = 0; KindIndex < Variables.size(); ++KindIndex) {
|
for (size_t KindIndex = 0; KindIndex < Variables.size(); ++KindIndex) {
|
||||||
if (Variables[KindIndex] == VarDecl) {
|
if (Variables[KindIndex] == VarDecl) {
|
||||||
// If yes, add a new occurence that points to the existing entry in
|
// If yes, add a new occurence that points to the existing entry in
|
||||||
// the Variables vector.
|
// the Variables vector.
|
||||||
Occurences.emplace_back(KindIndex, Range);
|
Occurences.emplace_back(KindIndex, Mention);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If this variable wasn't already referenced, add it to the list of
|
// If this variable wasn't already referenced, add it to the list of
|
||||||
// referenced variables and add a occurence that points to this new entry.
|
// referenced variables and add a occurence that points to this new entry.
|
||||||
Occurences.emplace_back(Variables.size(), Range);
|
Occurences.emplace_back(Variables.size(), Mention);
|
||||||
Variables.push_back(VarDecl);
|
Variables.push_back(VarDecl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +138,7 @@ class VariablePattern {
|
||||||
// Check if S is a reference to a variable. If yes, add it to the pattern.
|
// Check if S is a reference to a variable. If yes, add it to the pattern.
|
||||||
if (auto D = dyn_cast<DeclRefExpr>(S)) {
|
if (auto D = dyn_cast<DeclRefExpr>(S)) {
|
||||||
if (auto VD = dyn_cast<VarDecl>(D->getDecl()->getCanonicalDecl()))
|
if (auto VD = dyn_cast<VarDecl>(D->getDecl()->getCanonicalDecl()))
|
||||||
addVariableOccurence(VD, D->getSourceRange());
|
addVariableOccurence(VD, D);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively check all children of the given statement.
|
// Recursively check all children of the given statement.
|
||||||
|
@ -208,7 +212,7 @@ public:
|
||||||
// Store information about the first clone.
|
// Store information about the first clone.
|
||||||
FirstMismatch->FirstCloneInfo =
|
FirstMismatch->FirstCloneInfo =
|
||||||
CloneDetector::SuspiciousClonePair::SuspiciousCloneInfo(
|
CloneDetector::SuspiciousClonePair::SuspiciousCloneInfo(
|
||||||
Variables[ThisOccurence.KindID], ThisOccurence.Range,
|
Variables[ThisOccurence.KindID], ThisOccurence.Mention,
|
||||||
FirstSuggestion);
|
FirstSuggestion);
|
||||||
|
|
||||||
// Same as above but with the other clone. We do this for both clones as
|
// Same as above but with the other clone. We do this for both clones as
|
||||||
|
@ -221,7 +225,7 @@ public:
|
||||||
// Store information about the second clone.
|
// Store information about the second clone.
|
||||||
FirstMismatch->SecondCloneInfo =
|
FirstMismatch->SecondCloneInfo =
|
||||||
CloneDetector::SuspiciousClonePair::SuspiciousCloneInfo(
|
CloneDetector::SuspiciousClonePair::SuspiciousCloneInfo(
|
||||||
Variables[ThisOccurence.KindID], OtherOccurence.Range,
|
Other.Variables[OtherOccurence.KindID], OtherOccurence.Mention,
|
||||||
SecondSuggestion);
|
SecondSuggestion);
|
||||||
|
|
||||||
// SuspiciousClonePair guarantees that the first clone always has a
|
// SuspiciousClonePair guarantees that the first clone always has a
|
||||||
|
|
|
@ -16,8 +16,10 @@
|
||||||
#include "ClangSACheckers.h"
|
#include "ClangSACheckers.h"
|
||||||
#include "clang/Analysis/CloneDetection.h"
|
#include "clang/Analysis/CloneDetection.h"
|
||||||
#include "clang/Basic/Diagnostic.h"
|
#include "clang/Basic/Diagnostic.h"
|
||||||
|
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
|
||||||
#include "clang/StaticAnalyzer/Core/Checker.h"
|
#include "clang/StaticAnalyzer/Core/Checker.h"
|
||||||
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
|
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
|
||||||
|
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
|
||||||
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
||||||
|
|
||||||
using namespace clang;
|
using namespace clang;
|
||||||
|
@ -27,6 +29,7 @@ namespace {
|
||||||
class CloneChecker
|
class CloneChecker
|
||||||
: public Checker<check::ASTCodeBody, check::EndOfTranslationUnit> {
|
: public Checker<check::ASTCodeBody, check::EndOfTranslationUnit> {
|
||||||
mutable CloneDetector Detector;
|
mutable CloneDetector Detector;
|
||||||
|
mutable std::unique_ptr<BugType> BT_Exact, BT_Suspicious;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
|
void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
|
||||||
|
@ -36,12 +39,12 @@ public:
|
||||||
AnalysisManager &Mgr, BugReporter &BR) const;
|
AnalysisManager &Mgr, BugReporter &BR) const;
|
||||||
|
|
||||||
/// \brief Reports all clones to the user.
|
/// \brief Reports all clones to the user.
|
||||||
void reportClones(SourceManager &SM, AnalysisManager &Mgr,
|
void reportClones(BugReporter &BR, AnalysisManager &Mgr,
|
||||||
int MinComplexity) const;
|
int MinComplexity) const;
|
||||||
|
|
||||||
/// \brief Reports only suspicious clones to the user along with informaton
|
/// \brief Reports only suspicious clones to the user along with informaton
|
||||||
/// that explain why they are suspicious.
|
/// that explain why they are suspicious.
|
||||||
void reportSuspiciousClones(SourceManager &SM, AnalysisManager &Mgr,
|
void reportSuspiciousClones(BugReporter &BR, AnalysisManager &Mgr,
|
||||||
int MinComplexity) const;
|
int MinComplexity) const;
|
||||||
};
|
};
|
||||||
} // end anonymous namespace
|
} // end anonymous namespace
|
||||||
|
@ -70,79 +73,82 @@ void CloneChecker::checkEndOfTranslationUnit(const TranslationUnitDecl *TU,
|
||||||
"ReportNormalClones", true, this);
|
"ReportNormalClones", true, this);
|
||||||
|
|
||||||
if (ReportSuspiciousClones)
|
if (ReportSuspiciousClones)
|
||||||
reportSuspiciousClones(BR.getSourceManager(), Mgr, MinComplexity);
|
reportSuspiciousClones(BR, Mgr, MinComplexity);
|
||||||
|
|
||||||
if (ReportNormalClones)
|
if (ReportNormalClones)
|
||||||
reportClones(BR.getSourceManager(), Mgr, MinComplexity);
|
reportClones(BR, Mgr, MinComplexity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CloneChecker::reportClones(SourceManager &SM, AnalysisManager &Mgr,
|
static PathDiagnosticLocation makeLocation(const StmtSequence &S,
|
||||||
|
AnalysisManager &Mgr) {
|
||||||
|
ASTContext &ACtx = Mgr.getASTContext();
|
||||||
|
return PathDiagnosticLocation::createBegin(
|
||||||
|
S.front(), ACtx.getSourceManager(),
|
||||||
|
Mgr.getAnalysisDeclContext(ACtx.getTranslationUnitDecl()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloneChecker::reportClones(BugReporter &BR, AnalysisManager &Mgr,
|
||||||
int MinComplexity) const {
|
int MinComplexity) const {
|
||||||
|
|
||||||
std::vector<CloneDetector::CloneGroup> CloneGroups;
|
std::vector<CloneDetector::CloneGroup> CloneGroups;
|
||||||
Detector.findClones(CloneGroups, MinComplexity);
|
Detector.findClones(CloneGroups, MinComplexity);
|
||||||
|
|
||||||
DiagnosticsEngine &DiagEngine = Mgr.getDiagnostic();
|
if (!BT_Exact)
|
||||||
|
BT_Exact.reset(new BugType(this, "Exact code clone", "Code clone"));
|
||||||
unsigned WarnID = DiagEngine.getCustomDiagID(DiagnosticsEngine::Warning,
|
|
||||||
"Detected code clone.");
|
|
||||||
|
|
||||||
unsigned NoteID = DiagEngine.getCustomDiagID(DiagnosticsEngine::Note,
|
|
||||||
"Related code clone is here.");
|
|
||||||
|
|
||||||
for (CloneDetector::CloneGroup &Group : CloneGroups) {
|
for (CloneDetector::CloneGroup &Group : CloneGroups) {
|
||||||
// We group the clones by printing the first as a warning and all others
|
// We group the clones by printing the first as a warning and all others
|
||||||
// as a note.
|
// as a note.
|
||||||
DiagEngine.Report(Group.Sequences.front().getStartLoc(), WarnID);
|
auto R = llvm::make_unique<BugReport>(
|
||||||
for (unsigned i = 1; i < Group.Sequences.size(); ++i) {
|
*BT_Exact, "Duplicate code detected",
|
||||||
DiagEngine.Report(Group.Sequences[i].getStartLoc(), NoteID);
|
makeLocation(Group.Sequences.front(), Mgr));
|
||||||
}
|
R->addRange(Group.Sequences.front().getSourceRange());
|
||||||
|
|
||||||
|
for (unsigned i = 1; i < Group.Sequences.size(); ++i)
|
||||||
|
R->addNote("Similar code here",
|
||||||
|
makeLocation(Group.Sequences[i], Mgr),
|
||||||
|
Group.Sequences[i].getSourceRange());
|
||||||
|
BR.emitReport(std::move(R));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CloneChecker::reportSuspiciousClones(SourceManager &SM,
|
void CloneChecker::reportSuspiciousClones(BugReporter &BR,
|
||||||
AnalysisManager &Mgr,
|
AnalysisManager &Mgr,
|
||||||
int MinComplexity) const {
|
int MinComplexity) const {
|
||||||
|
|
||||||
std::vector<CloneDetector::SuspiciousClonePair> Clones;
|
std::vector<CloneDetector::SuspiciousClonePair> Clones;
|
||||||
Detector.findSuspiciousClones(Clones, MinComplexity);
|
Detector.findSuspiciousClones(Clones, MinComplexity);
|
||||||
|
|
||||||
DiagnosticsEngine &DiagEngine = Mgr.getDiagnostic();
|
if (!BT_Suspicious)
|
||||||
|
BT_Suspicious.reset(
|
||||||
|
new BugType(this, "Suspicious code clone", "Code clone"));
|
||||||
|
|
||||||
auto SuspiciousCloneWarning = DiagEngine.getCustomDiagID(
|
ASTContext &ACtx = BR.getContext();
|
||||||
DiagnosticsEngine::Warning, "suspicious code clone detected; did you "
|
SourceManager &SM = ACtx.getSourceManager();
|
||||||
"mean to use %0?");
|
AnalysisDeclContext *ADC =
|
||||||
|
Mgr.getAnalysisDeclContext(ACtx.getTranslationUnitDecl());
|
||||||
auto RelatedCloneNote = DiagEngine.getCustomDiagID(
|
|
||||||
DiagnosticsEngine::Note, "suggestion is based on the usage of this "
|
|
||||||
"variable in a similar piece of code");
|
|
||||||
|
|
||||||
auto RelatedSuspiciousCloneNote = DiagEngine.getCustomDiagID(
|
|
||||||
DiagnosticsEngine::Note, "suggestion is based on the usage of this "
|
|
||||||
"variable in a similar piece of code; did you "
|
|
||||||
"mean to use %0?");
|
|
||||||
|
|
||||||
for (CloneDetector::SuspiciousClonePair &Pair : Clones) {
|
for (CloneDetector::SuspiciousClonePair &Pair : Clones) {
|
||||||
// The first clone always has a suggestion and we report it to the user
|
// FIXME: We are ignoring the suggestions currently, because they are
|
||||||
// along with the place where the suggestion should be used.
|
// only 50% accurate (even if the second suggestion is unavailable),
|
||||||
DiagEngine.Report(Pair.FirstCloneInfo.VarRange.getBegin(),
|
// which may confuse the user.
|
||||||
SuspiciousCloneWarning)
|
// Think how to perform more accurate suggestions?
|
||||||
<< Pair.FirstCloneInfo.VarRange << Pair.FirstCloneInfo.Suggestion;
|
|
||||||
|
|
||||||
// The second clone can have a suggestion and if there is one, we report
|
auto R = llvm::make_unique<BugReport>(
|
||||||
// that suggestion to the user.
|
*BT_Suspicious,
|
||||||
if (Pair.SecondCloneInfo.Suggestion) {
|
"Potential copy-paste error; did you really mean to use '" +
|
||||||
DiagEngine.Report(Pair.SecondCloneInfo.VarRange.getBegin(),
|
Pair.FirstCloneInfo.Variable->getNameAsString() + "' here?",
|
||||||
RelatedSuspiciousCloneNote)
|
PathDiagnosticLocation::createBegin(Pair.FirstCloneInfo.Mention, SM,
|
||||||
<< Pair.SecondCloneInfo.VarRange << Pair.SecondCloneInfo.Suggestion;
|
ADC));
|
||||||
continue;
|
R->addRange(Pair.FirstCloneInfo.Mention->getSourceRange());
|
||||||
}
|
|
||||||
|
|
||||||
// If there isn't a suggestion in the second clone, we only inform the
|
R->addNote("Similar code using '" +
|
||||||
// user where we got the idea that his code could contain an error.
|
Pair.SecondCloneInfo.Variable->getNameAsString() + "' here",
|
||||||
DiagEngine.Report(Pair.SecondCloneInfo.VarRange.getBegin(),
|
PathDiagnosticLocation::createBegin(Pair.SecondCloneInfo.Mention,
|
||||||
RelatedCloneNote)
|
SM, ADC),
|
||||||
<< Pair.SecondCloneInfo.VarRange;
|
Pair.SecondCloneInfo.Mention->getSourceRange());
|
||||||
|
|
||||||
|
BR.emitReport(std::move(R));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,14 +4,14 @@
|
||||||
|
|
||||||
void log();
|
void log();
|
||||||
|
|
||||||
auto BlockA = ^(int a, int b){ // expected-warning{{Detected code clone.}}
|
auto BlockA = ^(int a, int b){ // expected-warning{{Duplicate code detected}}
|
||||||
log();
|
log();
|
||||||
if (a > b)
|
if (a > b)
|
||||||
return a;
|
return a;
|
||||||
return b;
|
return b;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto BlockB = ^(int a, int b){ // expected-note{{Related code clone is here.}}
|
auto BlockB = ^(int a, int b){ // expected-note{{Similar code here}}
|
||||||
log();
|
log();
|
||||||
if (a > b)
|
if (a > b)
|
||||||
return a;
|
return a;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// Tests if function try blocks are correctly handled.
|
// Tests if function try blocks are correctly handled.
|
||||||
|
|
||||||
void nonCompoundStmt1(int& x)
|
void nonCompoundStmt1(int& x)
|
||||||
try { x += 1; } catch(...) { x -= 1; } // expected-warning{{Detected code clone.}}
|
try { x += 1; } catch(...) { x -= 1; } // expected-warning{{Duplicate code detected}}
|
||||||
|
|
||||||
void nonCompoundStmt2(int& x)
|
void nonCompoundStmt2(int& x)
|
||||||
try { x += 1; } catch(...) { x -= 1; } // expected-note{{Related code clone is here.}}
|
try { x += 1; } catch(...) { x -= 1; } // expected-note{{Similar code here}}
|
||||||
|
|
|
@ -4,14 +4,14 @@
|
||||||
|
|
||||||
void log();
|
void log();
|
||||||
|
|
||||||
int max(int a, int b) { // expected-warning{{Detected code clone.}}
|
int max(int a, int b) { // expected-warning{{Duplicate code detected}}
|
||||||
log();
|
log();
|
||||||
if (a > b)
|
if (a > b)
|
||||||
return a;
|
return a;
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
int maxClone(int x, int y) { // expected-note{{Related code clone is here.}}
|
int maxClone(int x, int y) { // expected-note{{Similar code here}}
|
||||||
log();
|
log();
|
||||||
if (x > y)
|
if (x > y)
|
||||||
return x;
|
return x;
|
||||||
|
|
|
@ -11,11 +11,11 @@
|
||||||
// This confirms that with the current configuration the macro body would be
|
// This confirms that with the current configuration the macro body would be
|
||||||
// considered large enough to pass the MinimumCloneComplexity constraint.
|
// considered large enough to pass the MinimumCloneComplexity constraint.
|
||||||
|
|
||||||
int manualMacro(int a, int b) { // expected-warning{{Detected code clone.}}
|
int manualMacro(int a, int b) { // expected-warning{{Duplicate code detected}}
|
||||||
return a > b ? -a * a : -b * b;
|
return a > b ? -a * a : -b * b;
|
||||||
}
|
}
|
||||||
|
|
||||||
int manualMacroClone(int a, int b) { // expected-note{{Related code clone is here.}}
|
int manualMacroClone(int a, int b) { // expected-note{{Similar code here}}
|
||||||
return a > b ? -a * a : -b * b;
|
return a > b ? -a * a : -b * b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,10 +41,10 @@ int macroClone(int a, int b) {
|
||||||
|
|
||||||
#define NEG(A) -(A)
|
#define NEG(A) -(A)
|
||||||
|
|
||||||
int nestedMacros() { // expected-warning{{Detected code clone.}}
|
int nestedMacros() { // expected-warning{{Duplicate code detected}}
|
||||||
return NEG(NEG(NEG(NEG(NEG(NEG(NEG(NEG(NEG(NEG(1))))))))));
|
return NEG(NEG(NEG(NEG(NEG(NEG(NEG(NEG(NEG(NEG(1))))))))));
|
||||||
}
|
}
|
||||||
|
|
||||||
int nestedMacrosClone() { // expected-note{{Related code clone is here.}}
|
int nestedMacrosClone() { // expected-note{{Similar code here}}
|
||||||
return NEG(NEG(NEG(NEG(NEG(NEG(NEG(NEG(NEG(NEG(1))))))))));
|
return NEG(NEG(NEG(NEG(NEG(NEG(NEG(NEG(NEG(NEG(1))))))))));
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
// to have the same complexity value. Macros have smaller complexity values
|
// to have the same complexity value. Macros have smaller complexity values
|
||||||
// and need to be in their own hash group.
|
// and need to be in their own hash group.
|
||||||
|
|
||||||
int foo(int a) { // expected-warning{{Detected code clone.}}
|
int foo(int a) { // expected-warning{{Duplicate code detected}}
|
||||||
a = a + 1;
|
a = a + 1;
|
||||||
a = a + 1 / 1;
|
a = a + 1 / 1;
|
||||||
a = a + 1 + 1 + 1;
|
a = a + 1 + 1 + 1;
|
||||||
|
@ -15,7 +15,7 @@ int foo(int a) { // expected-warning{{Detected code clone.}}
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
int fooClone(int a) { // expected-note{{Related code clone is here.}}
|
int fooClone(int a) { // expected-note{{Similar code here}}
|
||||||
a = a + 1;
|
a = a + 1;
|
||||||
a = a + 1 / 1;
|
a = a + 1 / 1;
|
||||||
a = a + 1 + 1 + 1;
|
a = a + 1 + 1 + 1;
|
||||||
|
@ -30,7 +30,7 @@ int fooClone(int a) { // expected-note{{Related code clone is here.}}
|
||||||
|
|
||||||
#define ASSIGN(T, V) T = T + V
|
#define ASSIGN(T, V) T = T + V
|
||||||
|
|
||||||
int macro(int a) { // expected-warning{{Detected code clone.}}
|
int macro(int a) { // expected-warning{{Duplicate code detected}}
|
||||||
ASSIGN(a, 1);
|
ASSIGN(a, 1);
|
||||||
ASSIGN(a, 1 / 1);
|
ASSIGN(a, 1 / 1);
|
||||||
ASSIGN(a, 1 + 1 + 1);
|
ASSIGN(a, 1 + 1 + 1);
|
||||||
|
@ -40,7 +40,7 @@ int macro(int a) { // expected-warning{{Detected code clone.}}
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
int macroClone(int a) { // expected-note{{Related code clone is here.}}
|
int macroClone(int a) { // expected-note{{Similar code here}}
|
||||||
ASSIGN(a, 1);
|
ASSIGN(a, 1);
|
||||||
ASSIGN(a, 1 / 1);
|
ASSIGN(a, 1 / 1);
|
||||||
ASSIGN(a, 1 + 1 + 1);
|
ASSIGN(a, 1 + 1 + 1);
|
||||||
|
@ -54,7 +54,7 @@ int macroClone(int a) { // expected-note{{Related code clone is here.}}
|
||||||
|
|
||||||
#define EMPTY
|
#define EMPTY
|
||||||
|
|
||||||
int fooFalsePositiveClone(int a) { // expected-note{{Related code clone is here.}}
|
int fooFalsePositiveClone(int a) { // expected-note{{Similar code here}}
|
||||||
a = EMPTY a + 1;
|
a = EMPTY a + 1;
|
||||||
a = a + 1 / 1;
|
a = a + 1 / 1;
|
||||||
a = a + 1 + 1 + 1;
|
a = a + 1 + 1 + 1;
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation A
|
@implementation A
|
||||||
- (int) setOk : (int) a : (int) b { // expected-warning{{Detected code clone.}}
|
- (int) setOk : (int) a : (int) b { // expected-warning{{Duplicate code detected}}
|
||||||
if (a > b)
|
if (a > b)
|
||||||
return a;
|
return a;
|
||||||
return b;
|
return b;
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation B
|
@implementation B
|
||||||
- (int) setOk : (int) a : (int) b { // expected-note{{Related code clone is here.}}
|
- (int) setOk : (int) a : (int) b { // expected-note{{Similar code here}}
|
||||||
if (a > b)
|
if (a > b)
|
||||||
return a;
|
return a;
|
||||||
return b;
|
return b;
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
// RUN: %clang_cc1 -analyze -analyzer-output=plist -analyzer-config notes-as-events=true -o %t.plist -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -verify %s
|
||||||
|
// RUN: FileCheck --input-file=%t.plist %s
|
||||||
|
|
||||||
|
void log();
|
||||||
|
|
||||||
|
int max(int a, int b) { // expected-warning{{Duplicate code detected}}
|
||||||
|
log();
|
||||||
|
if (a > b)
|
||||||
|
return a;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxClone(int a, int b) { // no-note (converted into event)
|
||||||
|
log();
|
||||||
|
if (a > b)
|
||||||
|
return a;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK: <key>diagnostics</key>
|
||||||
|
// CHECK-NEXT: <array>
|
||||||
|
// CHECK-NEXT: <dict>
|
||||||
|
// CHECK-NEXT: <key>path</key>
|
||||||
|
// CHECK-NEXT: <array>
|
||||||
|
// CHECK-NEXT: <dict>
|
||||||
|
// CHECK-NEXT: <key>kind</key><string>event</string>
|
||||||
|
// CHECK-NEXT: <key>location</key>
|
||||||
|
// CHECK-NEXT: <dict>
|
||||||
|
// CHECK-NEXT: <key>line</key><integer>13</integer>
|
||||||
|
// CHECK-NEXT: <key>col</key><integer>28</integer>
|
||||||
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
|
// CHECK-NEXT: </dict>
|
||||||
|
// CHECK-NEXT: <key>ranges</key>
|
||||||
|
// CHECK-NEXT: <array>
|
||||||
|
// CHECK-NEXT: <array>
|
||||||
|
// CHECK-NEXT: <dict>
|
||||||
|
// CHECK-NEXT: <key>line</key><integer>13</integer>
|
||||||
|
// CHECK-NEXT: <key>col</key><integer>28</integer>
|
||||||
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
|
// CHECK-NEXT: </dict>
|
||||||
|
// CHECK-NEXT: <dict>
|
||||||
|
// CHECK-NEXT: <key>line</key><integer>18</integer>
|
||||||
|
// CHECK-NEXT: <key>col</key><integer>1</integer>
|
||||||
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
|
// CHECK-NEXT: </dict>
|
||||||
|
// CHECK-NEXT: </array>
|
||||||
|
// CHECK-NEXT: </array>
|
||||||
|
// CHECK-NEXT: <key>depth</key><integer>0</integer>
|
||||||
|
// CHECK-NEXT: <key>extended_message</key>
|
||||||
|
// CHECK-NEXT: <string>Similar code here</string>
|
||||||
|
// CHECK-NEXT: <key>message</key>
|
||||||
|
// CHECK-NEXT: <string>Similar code here</string>
|
||||||
|
// CHECK-NEXT: </dict>
|
||||||
|
// CHECK-NEXT: <dict>
|
||||||
|
// CHECK-NEXT: <key>kind</key><string>event</string>
|
||||||
|
// CHECK-NEXT: <key>location</key>
|
||||||
|
// CHECK-NEXT: <dict>
|
||||||
|
// CHECK-NEXT: <key>line</key><integer>6</integer>
|
||||||
|
// CHECK-NEXT: <key>col</key><integer>23</integer>
|
||||||
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
|
// CHECK-NEXT: </dict>
|
||||||
|
// CHECK-NEXT: <key>ranges</key>
|
||||||
|
// CHECK-NEXT: <array>
|
||||||
|
// CHECK-NEXT: <array>
|
||||||
|
// CHECK-NEXT: <dict>
|
||||||
|
// CHECK-NEXT: <key>line</key><integer>6</integer>
|
||||||
|
// CHECK-NEXT: <key>col</key><integer>23</integer>
|
||||||
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
|
// CHECK-NEXT: </dict>
|
||||||
|
// CHECK-NEXT: <dict>
|
||||||
|
// CHECK-NEXT: <key>line</key><integer>11</integer>
|
||||||
|
// CHECK-NEXT: <key>col</key><integer>1</integer>
|
||||||
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
|
// CHECK-NEXT: </dict>
|
||||||
|
// CHECK-NEXT: </array>
|
||||||
|
// CHECK-NEXT: </array>
|
||||||
|
// CHECK-NEXT: <key>depth</key><integer>0</integer>
|
||||||
|
// CHECK-NEXT: <key>extended_message</key>
|
||||||
|
// CHECK-NEXT: <string>Duplicate code detected</string>
|
||||||
|
// CHECK-NEXT: <key>message</key>
|
||||||
|
// CHECK-NEXT: <string>Duplicate code detected</string>
|
||||||
|
// CHECK-NEXT: </dict>
|
||||||
|
// CHECK-NEXT: </array>
|
||||||
|
// CHECK-NEXT: <key>description</key><string>Duplicate code detected</string>
|
||||||
|
// CHECK-NEXT: <key>category</key><string>Code clone</string>
|
||||||
|
// CHECK-NEXT: <key>type</key><string>Exact code clone</string>
|
||||||
|
// CHECK-NEXT: <key>check_name</key><string>alpha.clone.CloneChecker</string>
|
||||||
|
// CHECK-NEXT: <!-- This hash is experimental and going to change! -->
|
||||||
|
// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>3d15184f38c5fa57e479b744fe3f5035</string>
|
||||||
|
// CHECK-NEXT: <key>location</key>
|
||||||
|
// CHECK-NEXT: <dict>
|
||||||
|
// CHECK-NEXT: <key>line</key><integer>6</integer>
|
||||||
|
// CHECK-NEXT: <key>col</key><integer>23</integer>
|
||||||
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
|
// CHECK-NEXT: </dict>
|
||||||
|
// CHECK-NEXT: </dict>
|
||||||
|
// CHECK-NEXT: </array>
|
|
@ -0,0 +1,71 @@
|
||||||
|
// RUN: %clang_cc1 -analyze -analyzer-output=plist -o %t.plist -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -verify %s
|
||||||
|
// RUN: FileCheck --input-file=%t.plist %s
|
||||||
|
|
||||||
|
void log();
|
||||||
|
|
||||||
|
int max(int a, int b) { // expected-warning{{Duplicate code detected}}
|
||||||
|
log();
|
||||||
|
if (a > b)
|
||||||
|
return a;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxClone(int a, int b) { // expected-note{{Similar code here}}
|
||||||
|
log();
|
||||||
|
if (a > b)
|
||||||
|
return a;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: This plist output doesn't include the extra note on line 13.
|
||||||
|
// It should be updated once the format for extra notes in plists is defined.
|
||||||
|
|
||||||
|
// CHECK: <key>diagnostics</key>
|
||||||
|
// CHECK-NEXT: <array>
|
||||||
|
// CHECK-NEXT: <dict>
|
||||||
|
// CHECK-NEXT: <key>path</key>
|
||||||
|
// CHECK-NEXT: <array>
|
||||||
|
// CHECK-NEXT: <dict>
|
||||||
|
// CHECK-NEXT: <key>kind</key><string>event</string>
|
||||||
|
// CHECK-NEXT: <key>location</key>
|
||||||
|
// CHECK-NEXT: <dict>
|
||||||
|
// CHECK-NEXT: <key>line</key><integer>6</integer>
|
||||||
|
// CHECK-NEXT: <key>col</key><integer>23</integer>
|
||||||
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
|
// CHECK-NEXT: </dict>
|
||||||
|
// CHECK-NEXT: <key>ranges</key>
|
||||||
|
// CHECK-NEXT: <array>
|
||||||
|
// CHECK-NEXT: <array>
|
||||||
|
// CHECK-NEXT: <dict>
|
||||||
|
// CHECK-NEXT: <key>line</key><integer>6</integer>
|
||||||
|
// CHECK-NEXT: <key>col</key><integer>23</integer>
|
||||||
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
|
// CHECK-NEXT: </dict>
|
||||||
|
// CHECK-NEXT: <dict>
|
||||||
|
// CHECK-NEXT: <key>line</key><integer>11</integer>
|
||||||
|
// CHECK-NEXT: <key>col</key><integer>1</integer>
|
||||||
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
|
// CHECK-NEXT: </dict>
|
||||||
|
// CHECK-NEXT: </array>
|
||||||
|
// CHECK-NEXT: </array>
|
||||||
|
// CHECK-NEXT: <key>depth</key><integer>0</integer>
|
||||||
|
// CHECK-NEXT: <key>extended_message</key>
|
||||||
|
// CHECK-NEXT: <string>Duplicate code detected</string>
|
||||||
|
// CHECK-NEXT: <key>message</key>
|
||||||
|
// CHECK-NEXT: <string>Duplicate code detected</string>
|
||||||
|
// CHECK-NEXT: </dict>
|
||||||
|
// CHECK-NEXT: </array>
|
||||||
|
// CHECK-NEXT: <key>description</key><string>Duplicate code detected</string>
|
||||||
|
// CHECK-NEXT: <key>category</key><string>Code clone</string>
|
||||||
|
// CHECK-NEXT: <key>type</key><string>Exact code clone</string>
|
||||||
|
// CHECK-NEXT: <key>check_name</key><string>alpha.clone.CloneChecker</string>
|
||||||
|
// CHECK-NEXT: <!-- This hash is experimental and going to change! -->
|
||||||
|
// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>3d15184f38c5fa57e479b744fe3f5035</string>
|
||||||
|
// CHECK-NEXT: <key>location</key>
|
||||||
|
// CHECK-NEXT: <dict>
|
||||||
|
// CHECK-NEXT: <key>line</key><integer>6</integer>
|
||||||
|
// CHECK-NEXT: <key>col</key><integer>23</integer>
|
||||||
|
// CHECK-NEXT: <key>file</key><integer>0</integer>
|
||||||
|
// CHECK-NEXT: </dict>
|
||||||
|
// CHECK-NEXT: </dict>
|
||||||
|
// CHECK-NEXT: </array>
|
|
@ -7,14 +7,14 @@ void log();
|
||||||
|
|
||||||
int max(int a, int b) {
|
int max(int a, int b) {
|
||||||
log2(a);
|
log2(a);
|
||||||
log(); // expected-warning{{Detected code clone.}}
|
log(); // expected-warning{{Duplicate code detected}}
|
||||||
if (a > b)
|
if (a > b)
|
||||||
return a;
|
return a;
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
int maxClone(int a, int b) {
|
int maxClone(int a, int b) {
|
||||||
log(); // expected-note{{Related code clone is here.}}
|
log(); // expected-note{{Similar code here}}
|
||||||
if (a > b)
|
if (a > b)
|
||||||
return a;
|
return a;
|
||||||
return b;
|
return b;
|
||||||
|
|
|
@ -8,14 +8,14 @@ int max(int a, int b) {
|
||||||
log();
|
log();
|
||||||
if (a > b)
|
if (a > b)
|
||||||
return a;
|
return a;
|
||||||
return b; // expected-note{{suggestion is based on the usage of this variable in a similar piece of code}}
|
return b; // expected-note{{Similar code using 'b' here}}
|
||||||
}
|
}
|
||||||
|
|
||||||
int maxClone(int x, int y, int z) {
|
int maxClone(int x, int y, int z) {
|
||||||
log();
|
log();
|
||||||
if (x > y)
|
if (x > y)
|
||||||
return x;
|
return x;
|
||||||
return z; // expected-warning{{suspicious code clone detected; did you mean to use 'y'?}}
|
return z; // expected-warning{{Potential copy-paste error; did you really mean to use 'z' here?}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests finding a suspicious clone that references global variables.
|
// Tests finding a suspicious clone that references global variables.
|
||||||
|
@ -33,7 +33,7 @@ void busyIncrement() {
|
||||||
while (true) {
|
while (true) {
|
||||||
if (m1.try_lock()) {
|
if (m1.try_lock()) {
|
||||||
++i;
|
++i;
|
||||||
m1.unlock(); // expected-note{{suggestion is based on the usage of this variable in a similar piece of code}}
|
m1.unlock(); // expected-note{{Similar code using 'm1' here}}
|
||||||
if (i > 1000) {
|
if (i > 1000) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ void faultyBusyIncrement() {
|
||||||
while (true) {
|
while (true) {
|
||||||
if (m1.try_lock()) {
|
if (m1.try_lock()) {
|
||||||
++i;
|
++i;
|
||||||
m2.unlock(); // expected-warning{{suspicious code clone detected; did you mean to use 'm1'?}}
|
m2.unlock(); // expected-warning{{Potential copy-paste error; did you really mean to use 'm2' here?}}
|
||||||
if (i > 1000) {
|
if (i > 1000) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -58,14 +58,14 @@ void faultyBusyIncrement() {
|
||||||
int foo(int a, int b, int c) {
|
int foo(int a, int b, int c) {
|
||||||
a += b + c;
|
a += b + c;
|
||||||
b /= a + b;
|
b /= a + b;
|
||||||
c -= b * a; // expected-warning{{suspicious code clone detected; did you mean to use 'a'?}}
|
c -= b * a; // expected-warning{{Potential copy-paste error; did you really mean to use 'b' here?}}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
int fooClone(int a, int b, int c) {
|
int fooClone(int a, int b, int c) {
|
||||||
a += b + c;
|
a += b + c;
|
||||||
b /= a + b;
|
b /= a + b;
|
||||||
c -= a * a; // expected-note{{suggestion is based on the usage of this variable in a similar piece of code; did you mean to use 'b'?}}
|
c -= a * a; // expected-note{{Similar code using 'a' here}}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,21 +77,21 @@ int fooClone(int a, int b, int c) {
|
||||||
long bar1(long a, long b, long c, long d) {
|
long bar1(long a, long b, long c, long d) {
|
||||||
c = a - b;
|
c = a - b;
|
||||||
c = c / d * a;
|
c = c / d * a;
|
||||||
d = b * b - c; // expected-warning{{suspicious code clone detected; did you mean to use 'c'?}}
|
d = b * b - c; // expected-warning{{Potential copy-paste error; did you really mean to use 'b' here?}}
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
long bar2(long a, long b, long c, long d) {
|
long bar2(long a, long b, long c, long d) {
|
||||||
c = a - b;
|
c = a - b;
|
||||||
c = c / d * a;
|
c = c / d * a;
|
||||||
d = c * b - c; // expected-note{{suggestion is based on the usage of this variable in a similar piece of code; did you mean to use 'b'?}} \
|
d = c * b - c; // expected-note{{Similar code using 'c' here}} \
|
||||||
// expected-warning{{suspicious code clone detected; did you mean to use 'a'?}}
|
// expected-warning{{Potential copy-paste error; did you really mean to use 'c' here?}}
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
long bar3(long a, long b, long c, long d) {
|
long bar3(long a, long b, long c, long d) {
|
||||||
c = a - b;
|
c = a - b;
|
||||||
c = c / d * a;
|
c = c / d * a;
|
||||||
d = a * b - c; // expected-note{{suggestion is based on the usage of this variable in a similar piece of code; did you mean to use 'c'?}}
|
d = a * b - c; // expected-note{{Similar code using 'a' here}}
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
// RUN: %clang_cc1 -analyze -analyzer-output=text -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -verify %s
|
||||||
|
|
||||||
|
void log();
|
||||||
|
|
||||||
|
int max(int a, int b) { // expected-warning{{Duplicate code detected}} // expected-note{{Duplicate code detected}}
|
||||||
|
log();
|
||||||
|
if (a > b)
|
||||||
|
return a;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxClone(int a, int b) { // expected-note{{Similar code here}}
|
||||||
|
log();
|
||||||
|
if (a > b)
|
||||||
|
return a;
|
||||||
|
return b;
|
||||||
|
}
|
Loading…
Reference in New Issue