void CloneChecker::reportClones( BugReporter &BR, AnalysisManager &Mgr, std::vector<CloneDetector::CloneGroup> &CloneGroups) const { if (!BT_Exact) BT_Exact.reset(new BugType(this, "Exact code clone", "Code clone")); for (const CloneDetector::CloneGroup &Group : CloneGroups) { // We group the clones by printing the first as a warning and all others // as a note. auto R = llvm::make_unique<BugReport>(*BT_Exact, "Duplicate code detected", makeLocation(Group.front(), Mgr)); R->addRange(Group.front().getSourceRange()); for (unsigned i = 1; i < Group.size(); ++i) R->addNote("Similar code here", makeLocation(Group[i], Mgr), Group[i].getSourceRange()); BR.emitReport(std::move(R)); } }
void IdempotentOperationChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, ExprEngine &Eng) const { if (!BT) BT.reset(new BugType("Idempotent operation", "Dead code")); // Iterate over the hash to see if we have any paths with definite // idempotent operations. for (AssumptionMap::const_iterator i = hash.begin(); i != hash.end(); ++i) { // Unpack the hash contents const BinaryOperatorData &Data = i->second; const Assumption &A = Data.assumption; const ExplodedNodeSet &ES = Data.explodedNodes; // If there are no nodes accosted with the expression, nothing to report. // FIXME: This is possible because the checker does part of processing in // checkPreStmt and part in checkPostStmt. if (ES.begin() == ES.end()) continue; const BinaryOperator *B = i->first; if (A == Impossible) continue; // If the analyzer did not finish, check to see if we can still emit this // warning if (Eng.hasWorkRemaining()) { // If we can trace back AnalysisDeclContext *AC = (*ES.begin())->getLocationContext() ->getAnalysisDeclContext(); if (!pathWasCompletelyAnalyzed(AC, AC->getCFGStmtMap()->getBlock(B), Eng.getCoreEngine())) continue; } // Select the error message and SourceRanges to report. SmallString<128> buf; llvm::raw_svector_ostream os(buf); bool LHSRelevant = false, RHSRelevant = false; switch (A) { case Equal: LHSRelevant = true; RHSRelevant = true; if (B->getOpcode() == BO_Assign) os << "Assigned value is always the same as the existing value"; else os << "Both operands to '" << B->getOpcodeStr() << "' always have the same value"; break; case LHSis1: LHSRelevant = true; os << "The left operand to '" << B->getOpcodeStr() << "' is always 1"; break; case RHSis1: RHSRelevant = true; os << "The right operand to '" << B->getOpcodeStr() << "' is always 1"; break; case LHSis0: LHSRelevant = true; os << "The left operand to '" << B->getOpcodeStr() << "' is always 0"; break; case RHSis0: RHSRelevant = true; os << "The right operand to '" << B->getOpcodeStr() << "' is always 0"; break; case Possible: llvm_unreachable("Operation was never marked with an assumption"); case Impossible: llvm_unreachable(0); } // Add a report for each ExplodedNode for (ExplodedNodeSet::iterator I = ES.begin(), E = ES.end(); I != E; ++I) { BugReport *report = new BugReport(*BT, os.str(), *I); // Add source ranges and visitor hooks if (LHSRelevant) { const Expr *LHS = i->first->getLHS(); report->addRange(LHS->getSourceRange()); FindLastStoreBRVisitor::registerStatementVarDecls(*report, LHS, false); } if (RHSRelevant) { const Expr *RHS = i->first->getRHS(); report->addRange(i->first->getRHS()->getSourceRange()); FindLastStoreBRVisitor::registerStatementVarDecls(*report, RHS, false); } BR.emitReport(report); } } hash.clear(); }
void CloneChecker::reportSuspiciousClones( BugReporter &BR, AnalysisManager &Mgr, std::vector<CloneDetector::CloneGroup> &CloneGroups) const { std::vector<VariablePattern::SuspiciousClonePair> Pairs; for (const CloneDetector::CloneGroup &Group : CloneGroups) { for (unsigned i = 0; i < Group.size(); ++i) { VariablePattern PatternA(Group[i]); for (unsigned j = i + 1; j < Group.size(); ++j) { VariablePattern PatternB(Group[j]); VariablePattern::SuspiciousClonePair ClonePair; // For now, we only report clones which break the variable pattern just // once because multiple differences in a pattern are an indicator that // those differences are maybe intended (e.g. because it's actually a // different algorithm). // FIXME: In very big clones even multiple variables can be unintended, // so replacing this number with a percentage could better handle such // cases. On the other hand it could increase the false-positive rate // for all clones if the percentage is too high. if (PatternA.countPatternDifferences(PatternB, &ClonePair) == 1) { Pairs.push_back(ClonePair); break; } } } } if (!BT_Suspicious) BT_Suspicious.reset( new BugType(this, "Suspicious code clone", "Code clone")); ASTContext &ACtx = BR.getContext(); SourceManager &SM = ACtx.getSourceManager(); AnalysisDeclContext *ADC = Mgr.getAnalysisDeclContext(ACtx.getTranslationUnitDecl()); for (VariablePattern::SuspiciousClonePair &Pair : Pairs) { // FIXME: We are ignoring the suggestions currently, because they are // only 50% accurate (even if the second suggestion is unavailable), // which may confuse the user. // Think how to perform more accurate suggestions? auto R = llvm::make_unique<BugReport>( *BT_Suspicious, "Potential copy-paste error; did you really mean to use '" + Pair.FirstCloneInfo.Variable->getNameAsString() + "' here?", PathDiagnosticLocation::createBegin(Pair.FirstCloneInfo.Mention, SM, ADC)); R->addRange(Pair.FirstCloneInfo.Mention->getSourceRange()); R->addNote("Similar code using '" + Pair.SecondCloneInfo.Variable->getNameAsString() + "' here", PathDiagnosticLocation::createBegin(Pair.SecondCloneInfo.Mention, SM, ADC), Pair.SecondCloneInfo.Mention->getSourceRange()); BR.emitReport(std::move(R)); } }