std::shared_ptr<PathDiagnosticPiece> SuperDeallocBRVisitor::VisitNode(const ExplodedNode *Succ, const ExplodedNode *Pred, BugReporterContext &BRC, BugReport &BR) { if (Satisfied) return nullptr; ProgramStateRef State = Succ->getState(); bool CalledNow = Succ->getState()->contains<CalledSuperDealloc>(ReceiverSymbol); bool CalledBefore = Pred->getState()->contains<CalledSuperDealloc>(ReceiverSymbol); // Is Succ the node on which the analyzer noted that [super dealloc] was // called on ReceiverSymbol? if (CalledNow && !CalledBefore) { Satisfied = true; ProgramPoint P = Succ->getLocation(); PathDiagnosticLocation L = PathDiagnosticLocation::create(P, BRC.getSourceManager()); if (!L.isValid() || !L.asLocation().isValid()) return nullptr; return std::make_shared<PathDiagnosticEventPiece>( L, "[super dealloc] called here"); } return nullptr; }
/// Add an extra note piece describing a declaration that is important /// for understanding the bug report. void ObjCDeallocChecker::addNoteForDecl(std::unique_ptr<BugReport> &BR, StringRef Msg, const Decl *D) const { ASTContext &ACtx = D->getASTContext(); SourceManager &SM = ACtx.getSourceManager(); PathDiagnosticLocation Pos = PathDiagnosticLocation::createBegin(D, SM); if (Pos.isValid() && Pos.asLocation().isValid()) BR->addNote(Msg, Pos, D->getSourceRange()); }
PathDiagnosticPiece * TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) { if (isSatisfied) return NULL; // Check if in the previous state it was feasible for this constraint // to *not* be true. if (PrevN->getState()->assume(Constraint, !Assumption)) { isSatisfied = true; // As a sanity check, make sure that the negation of the constraint // was infeasible in the current state. If it is feasible, we somehow // missed the transition point. if (N->getState()->assume(Constraint, !Assumption)) return NULL; // We found the transition point for the constraint. We now need to // pretty-print the constraint. (work-in-progress) std::string sbuf; llvm::raw_string_ostream os(sbuf); if (Constraint.getAs<Loc>()) { os << "Assuming pointer value is "; os << (Assumption ? "non-null" : "null"); } if (os.str().empty()) return NULL; // Construct a new PathDiagnosticPiece. ProgramPoint P = N->getLocation(); PathDiagnosticLocation L = PathDiagnosticLocation::create(P, BRC.getSourceManager()); if (!L.isValid()) return NULL; PathDiagnosticEventPiece *X = new PathDiagnosticEventPiece(L, os.str()); X->setTag(getTag()); return X; } return NULL; }
std::shared_ptr<PathDiagnosticPiece> GenericTaintChecker::TaintBugVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) { // Find the ExplodedNode where the taint was first introduced if (!N->getState()->isTainted(V) || PrevN->getState()->isTainted(V)) return nullptr; const Stmt *S = PathDiagnosticLocation::getStmt(N); if (!S) return nullptr; const LocationContext *NCtx = N->getLocationContext(); PathDiagnosticLocation L = PathDiagnosticLocation::createBegin(S, BRC.getSourceManager(), NCtx); if (!L.isValid() || !L.asLocation().isValid()) return nullptr; return std::make_shared<PathDiagnosticEventPiece>( L, "Taint originated here"); }
PathDiagnosticPiece *DivisionBRVisitor::VisitNode(const ExplodedNode *Succ, const ExplodedNode *Pred, BugReporterContext &BRC, BugReport &BR) { if (Satisfied) return nullptr; const Expr *E = nullptr; if (Optional<PostStmt> P = Succ->getLocationAs<PostStmt>()) if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) { BinaryOperator::Opcode Op = BO->getOpcode(); if (Op == BO_Div || Op == BO_Rem || Op == BO_DivAssign || Op == BO_RemAssign) { E = BO->getRHS(); } } if (!E) return nullptr; ProgramStateRef State = Succ->getState(); SVal S = State->getSVal(E, Succ->getLocationContext()); if (ZeroSymbol == S.getAsSymbol() && SFC == Succ->getStackFrame()) { Satisfied = true; // Construct a new PathDiagnosticPiece. ProgramPoint P = Succ->getLocation(); PathDiagnosticLocation L = PathDiagnosticLocation::create(P, BRC.getSourceManager()); if (!L.isValid() || !L.asLocation().isValid()) return nullptr; return new PathDiagnosticEventPiece( L, "Division with compared value made here"); } return nullptr; }
PathDiagnosticPiece* BugReporterVisitor::getDefaultEndPath(BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) { const ProgramPoint &PP = EndPathNode->getLocation(); PathDiagnosticLocation L; if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&PP)) { const CFGBlock *block = BE->getBlock(); if (block->getBlockID() == 0) { L = PathDiagnosticLocation::createDeclEnd(PP.getLocationContext(), BRC.getSourceManager()); } } if (!L.isValid()) { const Stmt *S = BR.getStmt(); if (!S) return NULL; L = PathDiagnosticLocation(S, BRC.getSourceManager(), PP.getLocationContext()); } BugReport::ranges_iterator Beg, End; llvm::tie(Beg, End) = BR.getRanges(); // Only add the statement itself as a range if we didn't specify any // special ranges for this report. PathDiagnosticPiece *P = new PathDiagnosticEventPiece(L, BR.getDescription(), Beg == End); for (; Beg != End; ++Beg) P->addRange(*Beg); return P; }
void PlistDiagnostics::FlushDiagnosticsImpl( std::vector<const PathDiagnostic *> &Diags, FilesMade *filesMade) { // Build up a set of FIDs that we use by scanning the locations and // ranges of the diagnostics. FIDMap FM; SmallVector<FileID, 10> Fids; const SourceManager* SM = nullptr; if (!Diags.empty()) SM = &(*(*Diags.begin())->path.begin())->getLocation().getManager(); for (std::vector<const PathDiagnostic*>::iterator DI = Diags.begin(), DE = Diags.end(); DI != DE; ++DI) { const PathDiagnostic *D = *DI; SmallVector<const PathPieces *, 5> WorkList; WorkList.push_back(&D->path); while (!WorkList.empty()) { const PathPieces &path = *WorkList.pop_back_val(); for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E; ++I) { const PathDiagnosticPiece *piece = I->get(); AddFID(FM, Fids, *SM, piece->getLocation().asLocation()); ArrayRef<SourceRange> Ranges = piece->getRanges(); for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); I != E; ++I) { AddFID(FM, Fids, *SM, I->getBegin()); AddFID(FM, Fids, *SM, I->getEnd()); } if (const PathDiagnosticCallPiece *call = dyn_cast<PathDiagnosticCallPiece>(piece)) { IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnterWithin = call->getCallEnterWithinCallerEvent(); if (callEnterWithin) AddFID(FM, Fids, *SM, callEnterWithin->getLocation().asLocation()); WorkList.push_back(&call->path); } else if (const PathDiagnosticMacroPiece *macro = dyn_cast<PathDiagnosticMacroPiece>(piece)) { WorkList.push_back(¯o->subPieces); } } } } // Open the file. std::error_code EC; llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::F_Text); if (EC) { llvm::errs() << "warning: could not create file: " << EC.message() << '\n'; return; } EmitPlistHeader(o); // Write the root object: a <dict> containing... // - "clang_version", the string representation of clang version // - "files", an <array> mapping from FIDs to file names // - "diagnostics", an <array> containing the path diagnostics o << "<dict>\n" << " <key>clang_version</key>\n"; EmitString(o, getClangFullVersion()) << '\n'; o << " <key>files</key>\n" " <array>\n"; for (FileID FID : Fids) EmitString(o << " ", SM->getFileEntryForID(FID)->getName()) << '\n'; o << " </array>\n" " <key>diagnostics</key>\n" " <array>\n"; for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(), DE = Diags.end(); DI!=DE; ++DI) { o << " <dict>\n" " <key>path</key>\n"; const PathDiagnostic *D = *DI; o << " <array>\n"; for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end(); I != E; ++I) ReportDiag(o, **I, FM, *SM, LangOpts); o << " </array>\n"; // Output the bug type and bug category. o << " <key>description</key>"; EmitString(o, D->getShortDescription()) << '\n'; o << " <key>category</key>"; EmitString(o, D->getCategory()) << '\n'; o << " <key>type</key>"; EmitString(o, D->getBugType()) << '\n'; o << " <key>check_name</key>"; EmitString(o, D->getCheckName()) << '\n'; o << " <!-- This hash is experimental and going to change! -->\n"; o << " <key>issue_hash_content_of_line_in_context</key>"; PathDiagnosticLocation UPDLoc = D->getUniqueingLoc(); FullSourceLoc L(SM->getExpansionLoc(UPDLoc.isValid() ? UPDLoc.asLocation() : D->getLocation().asLocation()), *SM); const Decl *DeclWithIssue = D->getDeclWithIssue(); EmitString(o, GetIssueHash(*SM, L, D->getCheckName(), D->getBugType(), DeclWithIssue)) << '\n'; // Output information about the semantic context where // the issue occurred. if (const Decl *DeclWithIssue = D->getDeclWithIssue()) { // FIXME: handle blocks, which have no name. if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) { StringRef declKind; switch (ND->getKind()) { case Decl::CXXRecord: declKind = "C++ class"; break; case Decl::CXXMethod: declKind = "C++ method"; break; case Decl::ObjCMethod: declKind = "Objective-C method"; break; case Decl::Function: declKind = "function"; break; default: break; } if (!declKind.empty()) { const std::string &declName = ND->getDeclName().getAsString(); o << " <key>issue_context_kind</key>"; EmitString(o, declKind) << '\n'; o << " <key>issue_context</key>"; EmitString(o, declName) << '\n'; } // Output the bug hash for issue unique-ing. Currently, it's just an // offset from the beginning of the function. if (const Stmt *Body = DeclWithIssue->getBody()) { // If the bug uniqueing location exists, use it for the hash. // For example, this ensures that two leaks reported on the same line // will have different issue_hashes and that the hash will identify // the leak location even after code is added between the allocation // site and the end of scope (leak report location). if (UPDLoc.isValid()) { FullSourceLoc UFunL(SM->getExpansionLoc( D->getUniqueingDecl()->getBody()->getLocStart()), *SM); o << " <key>issue_hash_function_offset</key><string>" << L.getExpansionLineNumber() - UFunL.getExpansionLineNumber() << "</string>\n"; // Otherwise, use the location on which the bug is reported. } else { FullSourceLoc FunL(SM->getExpansionLoc(Body->getLocStart()), *SM); o << " <key>issue_hash_function_offset</key><string>" << L.getExpansionLineNumber() - FunL.getExpansionLineNumber() << "</string>\n"; } } } } // Output the location of the bug. o << " <key>location</key>\n"; EmitLocation(o, *SM, D->getLocation().asLocation(), FM, 2); // Output the diagnostic to the sub-diagnostic client, if any. if (!filesMade->empty()) { StringRef lastName; PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D); if (files) { for (PDFileEntry::ConsumerFiles::const_iterator CI = files->begin(), CE = files->end(); CI != CE; ++CI) { StringRef newName = CI->first; if (newName != lastName) { if (!lastName.empty()) { o << " </array>\n"; } lastName = newName; o << " <key>" << lastName << "_files</key>\n"; o << " <array>\n"; } o << " <string>" << CI->second << "</string>\n"; } o << " </array>\n"; } } // Close up the entry. o << " </dict>\n"; } o << " </array>\n"; // Finish. o << "</dict>\n</plist>"; }
PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, const ExplodedNode *Pred, BugReporterContext &BRC, BugReport &BR) { if (Satisfied) return NULL; const ExplodedNode *StoreSite = 0; const Expr *InitE = 0; bool IsParam = false; // First see if we reached the declaration of the region. if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { if (Optional<PostStmt> P = Pred->getLocationAs<PostStmt>()) { if (const DeclStmt *DS = P->getStmtAs<DeclStmt>()) { if (DS->getSingleDecl() == VR->getDecl()) { StoreSite = Pred; InitE = VR->getDecl()->getInit(); } } } } // Otherwise, see if this is the store site: // (1) Succ has this binding and Pred does not, i.e. this is // where the binding first occurred. // (2) Succ has this binding and is a PostStore node for this region, i.e. // the same binding was re-assigned here. if (!StoreSite) { if (Succ->getState()->getSVal(R) != V) return NULL; if (Pred->getState()->getSVal(R) == V) { Optional<PostStore> PS = Succ->getLocationAs<PostStore>(); if (!PS || PS->getLocationValue() != R) return NULL; } StoreSite = Succ; // If this is an assignment expression, we can track the value // being assigned. if (Optional<PostStmt> P = Succ->getLocationAs<PostStmt>()) if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) if (BO->isAssignmentOp()) InitE = BO->getRHS(); // If this is a call entry, the variable should be a parameter. // FIXME: Handle CXXThisRegion as well. (This is not a priority because // 'this' should never be NULL, but this visitor isn't just for NULL and // UndefinedVal.) if (Optional<CallEnter> CE = Succ->getLocationAs<CallEnter>()) { if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { const ParmVarDecl *Param = cast<ParmVarDecl>(VR->getDecl()); ProgramStateManager &StateMgr = BRC.getStateManager(); CallEventManager &CallMgr = StateMgr.getCallEventManager(); CallEventRef<> Call = CallMgr.getCaller(CE->getCalleeContext(), Succ->getState()); InitE = Call->getArgExpr(Param->getFunctionScopeIndex()); IsParam = true; } } } if (!StoreSite) return NULL; Satisfied = true; // If we have an expression that provided the value, try to track where it // came from. if (InitE) { if (V.isUndef() || V.getAs<loc::ConcreteInt>()) { if (!IsParam) InitE = InitE->IgnoreParenCasts(); bugreporter::trackNullOrUndefValue(StoreSite, InitE, BR, IsParam); } else { ReturnVisitor::addVisitorIfNecessary(StoreSite, InitE->IgnoreParenCasts(), BR); } } if (!R->canPrintPretty()) return 0; // Okay, we've found the binding. Emit an appropriate message. SmallString<256> sbuf; llvm::raw_svector_ostream os(sbuf); if (Optional<PostStmt> PS = StoreSite->getLocationAs<PostStmt>()) { const Stmt *S = PS->getStmt(); const char *action = 0; const DeclStmt *DS = dyn_cast<DeclStmt>(S); const VarRegion *VR = dyn_cast<VarRegion>(R); if (DS) { action = "initialized to "; } else if (isa<BlockExpr>(S)) { action = "captured by block as "; if (VR) { // See if we can get the BlockVarRegion. ProgramStateRef State = StoreSite->getState(); SVal V = State->getSVal(S, PS->getLocationContext()); if (const BlockDataRegion *BDR = dyn_cast_or_null<BlockDataRegion>(V.getAsRegion())) { if (const VarRegion *OriginalR = BDR->getOriginalRegion(VR)) { if (Optional<KnownSVal> KV = State->getSVal(OriginalR).getAs<KnownSVal>()) BR.addVisitor(new FindLastStoreBRVisitor(*KV, OriginalR)); } } } } if (action) { if (!R) return 0; os << '\''; R->printPretty(os); os << "' "; if (V.getAs<loc::ConcreteInt>()) { bool b = false; if (R->isBoundable()) { if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) { if (TR->getValueType()->isObjCObjectPointerType()) { os << action << "nil"; b = true; } } } if (!b) os << action << "a null pointer value"; } else if (Optional<nonloc::ConcreteInt> CVal = V.getAs<nonloc::ConcreteInt>()) { os << action << CVal->getValue(); } else if (DS) { if (V.isUndef()) { if (isa<VarRegion>(R)) { const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl()); if (VD->getInit()) os << "initialized to a garbage value"; else os << "declared without an initial value"; } } else { os << "initialized here"; } } } } else if (StoreSite->getLocation().getAs<CallEnter>()) { if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { const ParmVarDecl *Param = cast<ParmVarDecl>(VR->getDecl()); os << "Passing "; if (V.getAs<loc::ConcreteInt>()) { if (Param->getType()->isObjCObjectPointerType()) os << "nil object reference"; else os << "null pointer value"; } else if (V.isUndef()) { os << "uninitialized value"; } else if (Optional<nonloc::ConcreteInt> CI = V.getAs<nonloc::ConcreteInt>()) { os << "the value " << CI->getValue(); } else { os << "value"; } // Printed parameter indexes are 1-based, not 0-based. unsigned Idx = Param->getFunctionScopeIndex() + 1; os << " via " << Idx << llvm::getOrdinalSuffix(Idx) << " parameter '"; R->printPretty(os); os << '\''; } } if (os.str().empty()) { if (V.getAs<loc::ConcreteInt>()) { bool b = false; if (R->isBoundable()) { if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) { if (TR->getValueType()->isObjCObjectPointerType()) { os << "nil object reference stored to "; b = true; } } } if (!b) os << "Null pointer value stored to "; } else if (V.isUndef()) { os << "Uninitialized value stored to "; } else if (Optional<nonloc::ConcreteInt> CV = V.getAs<nonloc::ConcreteInt>()) { os << "The value " << CV->getValue() << " is assigned to "; } else os << "Value assigned to "; os << '\''; R->printPretty(os); os << '\''; } // Construct a new PathDiagnosticPiece. ProgramPoint P = StoreSite->getLocation(); PathDiagnosticLocation L; if (P.getAs<CallEnter>() && InitE) L = PathDiagnosticLocation(InitE, BRC.getSourceManager(), P.getLocationContext()); else L = PathDiagnosticLocation::create(P, BRC.getSourceManager()); if (!L.isValid()) return NULL; return new PathDiagnosticEventPiece(L, os.str()); }
void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, FilesMade *filesMade) { // Create the HTML directory if it is missing. if (!createdDir) { createdDir = true; if (std::error_code ec = llvm::sys::fs::create_directories(Directory)) { llvm::errs() << "warning: could not create directory '" << Directory << "': " << ec.message() << '\n'; noDir = true; return; } } if (noDir) return; // First flatten out the entire path to make it easier to use. PathPieces path = D.path.flatten(/*ShouldFlattenMacros=*/false); // The path as already been prechecked that all parts of the path are // from the same file and that it is non-empty. const SourceManager &SMgr = path.front()->getLocation().getManager(); assert(!path.empty()); FileID FID = path.front()->getLocation().asLocation().getExpansionLoc().getFileID(); assert(FID.isValid()); // Create a new rewriter to generate HTML. Rewriter R(const_cast<SourceManager&>(SMgr), PP.getLangOpts()); // Get the function/method name SmallString<128> declName("unknown"); int offsetDecl = 0; if (const Decl *DeclWithIssue = D.getDeclWithIssue()) { if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) { declName = ND->getDeclName().getAsString(); } if (const Stmt *Body = DeclWithIssue->getBody()) { // Retrieve the relative position of the declaration which will be used // for the file name FullSourceLoc L( SMgr.getExpansionLoc(path.back()->getLocation().asLocation()), SMgr); FullSourceLoc FunL(SMgr.getExpansionLoc(Body->getLocStart()), SMgr); offsetDecl = L.getExpansionLineNumber() - FunL.getExpansionLineNumber(); } } // Process the path. // Maintain the counts of extra note pieces separately. unsigned TotalPieces = path.size(); unsigned TotalNotePieces = std::count_if(path.begin(), path.end(), [](const std::shared_ptr<PathDiagnosticPiece> &p) { return isa<PathDiagnosticNotePiece>(*p); }); unsigned TotalRegularPieces = TotalPieces - TotalNotePieces; unsigned NumRegularPieces = TotalRegularPieces; unsigned NumNotePieces = TotalNotePieces; for (auto I = path.rbegin(), E = path.rend(); I != E; ++I) { if (isa<PathDiagnosticNotePiece>(I->get())) { // This adds diagnostic bubbles, but not navigation. // Navigation through note pieces would be added later, // as a separate pass through the piece list. HandlePiece(R, FID, **I, NumNotePieces, TotalNotePieces); --NumNotePieces; } else { HandlePiece(R, FID, **I, NumRegularPieces, TotalRegularPieces); --NumRegularPieces; } } // Add line numbers, header, footer, etc. // unsigned FID = R.getSourceMgr().getMainFileID(); html::EscapeText(R, FID); html::AddLineNumbers(R, FID); // If we have a preprocessor, relex the file and syntax highlight. // We might not have a preprocessor if we come from a deserialized AST file, // for example. html::SyntaxHighlight(R, FID, PP); html::HighlightMacros(R, FID, PP); // Get the full directory name of the analyzed file. const FileEntry* Entry = SMgr.getFileEntryForID(FID); // This is a cludge; basically we want to append either the full // working directory if we have no directory information. This is // a work in progress. llvm::SmallString<0> DirName; if (llvm::sys::path::is_relative(Entry->getName())) { llvm::sys::fs::current_path(DirName); DirName += '/'; } int LineNumber = path.back()->getLocation().asLocation().getExpansionLineNumber(); int ColumnNumber = path.back()->getLocation().asLocation().getExpansionColumnNumber(); // Add the name of the file as an <h1> tag. { std::string s; llvm::raw_string_ostream os(s); os << "<!-- REPORTHEADER -->\n" << "<h3>Bug Summary</h3>\n<table class=\"simpletable\">\n" "<tr><td class=\"rowname\">File:</td><td>" << html::EscapeText(DirName) << html::EscapeText(Entry->getName()) << "</td></tr>\n<tr><td class=\"rowname\">Warning:</td><td>" "<a href=\"#EndPath\">line " << LineNumber << ", column " << ColumnNumber << "</a><br />" << D.getVerboseDescription() << "</td></tr>\n"; // The navigation across the extra notes pieces. unsigned NumExtraPieces = 0; for (const auto &Piece : path) { if (const auto *P = dyn_cast<PathDiagnosticNotePiece>(Piece.get())) { int LineNumber = P->getLocation().asLocation().getExpansionLineNumber(); int ColumnNumber = P->getLocation().asLocation().getExpansionColumnNumber(); os << "<tr><td class=\"rowname\">Note:</td><td>" << "<a href=\"#Note" << NumExtraPieces << "\">line " << LineNumber << ", column " << ColumnNumber << "</a><br />" << P->getString() << "</td></tr>"; ++NumExtraPieces; } } // Output any other meta data. for (PathDiagnostic::meta_iterator I=D.meta_begin(), E=D.meta_end(); I!=E; ++I) { os << "<tr><td></td><td>" << html::EscapeText(*I) << "</td></tr>\n"; } os << "</table>\n<!-- REPORTSUMMARYEXTRA -->\n" "<h3>Annotated Source Code</h3>\n"; R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str()); } // Embed meta-data tags. { std::string s; llvm::raw_string_ostream os(s); StringRef BugDesc = D.getVerboseDescription(); if (!BugDesc.empty()) os << "\n<!-- BUGDESC " << BugDesc << " -->\n"; StringRef BugType = D.getBugType(); if (!BugType.empty()) os << "\n<!-- BUGTYPE " << BugType << " -->\n"; PathDiagnosticLocation UPDLoc = D.getUniqueingLoc(); FullSourceLoc L(SMgr.getExpansionLoc(UPDLoc.isValid() ? UPDLoc.asLocation() : D.getLocation().asLocation()), SMgr); const Decl *DeclWithIssue = D.getDeclWithIssue(); StringRef BugCategory = D.getCategory(); if (!BugCategory.empty()) os << "\n<!-- BUGCATEGORY " << BugCategory << " -->\n"; os << "\n<!-- BUGFILE " << DirName << Entry->getName() << " -->\n"; os << "\n<!-- FILENAME " << llvm::sys::path::filename(Entry->getName()) << " -->\n"; os << "\n<!-- FUNCTIONNAME " << declName << " -->\n"; os << "\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT " << GetIssueHash(SMgr, L, D.getCheckName(), D.getBugType(), DeclWithIssue, PP.getLangOpts()) << " -->\n"; os << "\n<!-- BUGLINE " << LineNumber << " -->\n"; os << "\n<!-- BUGCOLUMN " << ColumnNumber << " -->\n"; os << "\n<!-- BUGPATHLENGTH " << path.size() << " -->\n"; // Mark the end of the tags. os << "\n<!-- BUGMETAEND -->\n"; // Insert the text. R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str()); } // Add CSS, header, and footer. html::AddHeaderFooterInternalBuiltinCSS(R, FID, Entry->getName()); // Get the rewrite buffer. const RewriteBuffer *Buf = R.getRewriteBufferFor(FID); if (!Buf) { llvm::errs() << "warning: no diagnostics generated for main file.\n"; return; } // Create a path for the target HTML file. int FD; SmallString<128> Model, ResultPath; if (!AnalyzerOpts.shouldWriteStableReportFilename()) { llvm::sys::path::append(Model, Directory, "report-%%%%%%.html"); if (std::error_code EC = llvm::sys::fs::make_absolute(Model)) { llvm::errs() << "warning: could not make '" << Model << "' absolute: " << EC.message() << '\n'; return; } if (std::error_code EC = llvm::sys::fs::createUniqueFile(Model, FD, ResultPath)) { llvm::errs() << "warning: could not create file in '" << Directory << "': " << EC.message() << '\n'; return; } } else { int i = 1; std::error_code EC; do { // Find a filename which is not already used std::stringstream filename; Model = ""; filename << "report-" << llvm::sys::path::filename(Entry->getName()).str() << "-" << declName.c_str() << "-" << offsetDecl << "-" << i << ".html"; llvm::sys::path::append(Model, Directory, filename.str()); EC = llvm::sys::fs::openFileForWrite(Model, FD, llvm::sys::fs::F_RW | llvm::sys::fs::F_Excl); if (EC && EC != llvm::errc::file_exists) { llvm::errs() << "warning: could not create file '" << Model << "': " << EC.message() << '\n'; return; } i++; } while (EC); } llvm::raw_fd_ostream os(FD, true); if (filesMade) filesMade->addDiagnostic(D, getName(), llvm::sys::path::filename(ResultPath)); // Emit the HTML to disk. for (RewriteBuffer::iterator I = Buf->begin(), E = Buf->end(); I!=E; ++I) os << *I; }
PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) { if (satisfied) return NULL; if (!StoreSite) { const ExplodedNode *Node = N, *Last = NULL; for ( ; Node ; Last = Node, Node = Node->getFirstPred()) { if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { if (const PostStmt *P = Node->getLocationAs<PostStmt>()) if (const DeclStmt *DS = P->getStmtAs<DeclStmt>()) if (DS->getSingleDecl() == VR->getDecl()) { Last = Node; break; } } if (Node->getState()->getSVal(R) != V) break; } if (!Node || !Last) { satisfied = true; return NULL; } StoreSite = Last; } if (StoreSite != N) return NULL; satisfied = true; llvm::SmallString<256> sbuf; llvm::raw_svector_ostream os(sbuf); if (const PostStmt *PS = N->getLocationAs<PostStmt>()) { if (const DeclStmt *DS = PS->getStmtAs<DeclStmt>()) { if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { os << "Variable '" << VR->getDecl() << "' "; } else return NULL; if (isa<loc::ConcreteInt>(V)) { bool b = false; if (R->isBoundable()) { if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) { if (TR->getValueType()->isObjCObjectPointerType()) { os << "initialized to nil"; b = true; } } } if (!b) os << "initialized to a null pointer value"; } else if (isa<nonloc::ConcreteInt>(V)) { os << "initialized to " << cast<nonloc::ConcreteInt>(V).getValue(); } else if (V.isUndef()) { if (isa<VarRegion>(R)) { const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl()); if (VD->getInit()) os << "initialized to a garbage value"; else os << "declared without an initial value"; } } } } if (os.str().empty()) { if (isa<loc::ConcreteInt>(V)) { bool b = false; if (R->isBoundable()) { if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) { if (TR->getValueType()->isObjCObjectPointerType()) { os << "nil object reference stored to "; b = true; } } } if (!b) os << "Null pointer value stored to "; } else if (V.isUndef()) { os << "Uninitialized value stored to "; } else if (isa<nonloc::ConcreteInt>(V)) { os << "The value " << cast<nonloc::ConcreteInt>(V).getValue() << " is assigned to "; } else return NULL; if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { os << '\'' << VR->getDecl() << '\''; } else return NULL; } // Construct a new PathDiagnosticPiece. ProgramPoint P = N->getLocation(); PathDiagnosticLocation L = PathDiagnosticLocation::create(P, BRC.getSourceManager()); if (!L.isValid()) return NULL; return new PathDiagnosticEventPiece(L, os.str()); }
PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, const ExplodedNode *Pred, BugReporterContext &BRC, BugReport &BR) { if (satisfied) return NULL; const ExplodedNode *StoreSite = 0; const Expr *InitE = 0; // First see if we reached the declaration of the region. if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { if (const PostStmt *P = Pred->getLocationAs<PostStmt>()) { if (const DeclStmt *DS = P->getStmtAs<DeclStmt>()) { if (DS->getSingleDecl() == VR->getDecl()) { StoreSite = Pred; InitE = VR->getDecl()->getInit(); } } } } // Otherwise, check that Succ has this binding and Pred does not, i.e. this is // where the binding first occurred. if (!StoreSite) { if (Succ->getState()->getSVal(R) != V) return NULL; if (Pred->getState()->getSVal(R) == V) return NULL; StoreSite = Succ; // If this is an assignment expression, we can track the value // being assigned. if (const PostStmt *P = Succ->getLocationAs<PostStmt>()) if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) if (BO->isAssignmentOp()) InitE = BO->getRHS(); } if (!StoreSite) return NULL; satisfied = true; // If the value that was stored came from an inlined call, make sure we // step into the call. if (InitE) { InitE = InitE->IgnoreParenCasts(); ReturnVisitor::addVisitorIfNecessary(StoreSite, InitE, BR); } // Okay, we've found the binding. Emit an appropriate message. SmallString<256> sbuf; llvm::raw_svector_ostream os(sbuf); if (const PostStmt *PS = StoreSite->getLocationAs<PostStmt>()) { if (const DeclStmt *DS = PS->getStmtAs<DeclStmt>()) { if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { os << "Variable '" << *VR->getDecl() << "' "; } else return NULL; if (isa<loc::ConcreteInt>(V)) { bool b = false; if (R->isBoundable()) { if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) { if (TR->getValueType()->isObjCObjectPointerType()) { os << "initialized to nil"; b = true; } } } if (!b) os << "initialized to a null pointer value"; } else if (isa<nonloc::ConcreteInt>(V)) { os << "initialized to " << cast<nonloc::ConcreteInt>(V).getValue(); } else if (V.isUndef()) { if (isa<VarRegion>(R)) { const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl()); if (VD->getInit()) os << "initialized to a garbage value"; else os << "declared without an initial value"; } } else { os << "initialized here"; } } } if (os.str().empty()) { if (isa<loc::ConcreteInt>(V)) { bool b = false; if (R->isBoundable()) { if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) { if (TR->getValueType()->isObjCObjectPointerType()) { os << "nil object reference stored to "; b = true; } } } if (!b) os << "Null pointer value stored to "; } else if (V.isUndef()) { os << "Uninitialized value stored to "; } else if (isa<nonloc::ConcreteInt>(V)) { os << "The value " << cast<nonloc::ConcreteInt>(V).getValue() << " is assigned to "; } else os << "Value assigned to "; if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { os << '\'' << *VR->getDecl() << '\''; } else return NULL; } // Construct a new PathDiagnosticPiece. ProgramPoint P = StoreSite->getLocation(); PathDiagnosticLocation L = PathDiagnosticLocation::create(P, BRC.getSourceManager()); if (!L.isValid()) return NULL; return new PathDiagnosticEventPiece(L, os.str()); }