25 using namespace clang;
27 using namespace std::placeholders;
43 struct StreamErrorState {
51 bool isNoError()
const {
return NoError && !FEof && !FError; }
52 bool isFEof()
const {
return !NoError && FEof && !FError; }
53 bool isFError()
const {
return !NoError && !FEof && FError; }
55 bool operator==(
const StreamErrorState &ES)
const {
56 return NoError == ES.NoError && FEof == ES.FEof && FError == ES.FError;
59 bool operator!=(
const StreamErrorState &ES)
const {
return !(*
this == ES); }
61 StreamErrorState
operator|(
const StreamErrorState &E)
const {
62 return {NoError || E.NoError, FEof || E.FEof, FError || E.FError};
65 StreamErrorState
operator&(
const StreamErrorState &E)
const {
66 return {NoError && E.NoError, FEof && E.FEof, FError && E.FError};
69 StreamErrorState
operator~()
const {
return {!NoError, !FEof, !FError}; }
72 operator bool()
const {
return NoError || FEof || FError; }
74 void Profile(llvm::FoldingSetNodeID &
ID)
const {
75 ID.AddBoolean(NoError);
77 ID.AddBoolean(FError);
81 const StreamErrorState ErrorNone{
true,
false,
false};
82 const StreamErrorState ErrorFEof{
false,
true,
false};
83 const StreamErrorState ErrorFError{
false,
false,
true};
88 const FnDescription *LastOperation;
100 StreamErrorState
const ErrorState;
110 bool const FilePositionIndeterminate =
false;
112 StreamState(
const FnDescription *L, KindTy S,
const StreamErrorState &ES,
113 bool IsFilePositionIndeterminate)
114 : LastOperation(L),
State(S), ErrorState(ES),
115 FilePositionIndeterminate(IsFilePositionIndeterminate) {
116 assert((!ES.isFEof() || !IsFilePositionIndeterminate) &&
117 "FilePositionIndeterminate should be false in FEof case.");
118 assert((
State == Opened || ErrorState.isNoError()) &&
119 "ErrorState should be None in non-opened stream state.");
122 bool isOpened()
const {
return State == Opened; }
124 bool isOpenFailed()
const {
return State == OpenFailed; }
129 return LastOperation ==
X.LastOperation &&
State ==
X.State &&
130 ErrorState ==
X.ErrorState &&
131 FilePositionIndeterminate ==
X.FilePositionIndeterminate;
134 static StreamState getOpened(
const FnDescription *L,
135 const StreamErrorState &ES = ErrorNone,
136 bool IsFilePositionIndeterminate =
false) {
137 return StreamState{L, Opened, ES, IsFilePositionIndeterminate};
139 static StreamState getClosed(
const FnDescription *L) {
140 return StreamState{L,
Closed, {},
false};
142 static StreamState getOpenFailed(
const FnDescription *L) {
143 return StreamState{L, OpenFailed, {},
false};
146 void Profile(llvm::FoldingSetNodeID &
ID)
const {
147 ID.AddPointer(LastOperation);
149 ErrorState.Profile(
ID);
150 ID.AddBoolean(FilePositionIndeterminate);
163 using FnCheck = std::function<void(
const StreamChecker *,
const FnDescription *,
164 const CallEvent &, CheckerContext &)>;
166 using ArgNoTy =
unsigned int;
169 struct FnDescription {
177 SVal getStreamArg(
const FnDescription *Desc,
const CallEvent &Call) {
178 assert(Desc && Desc->StreamArgNo != ArgNone &&
179 "Try to get a non-existing stream argument.");
180 return Call.getArgSVal(Desc->StreamArgNo);
184 DefinedSVal makeRetVal(CheckerContext &C,
const CallExpr *CE) {
185 assert(CE &&
"Expecting a call expression.");
188 return C.getSValBuilder()
189 .conjureSymbolVal(
nullptr, CE, LCtx,
C.blockCount())
190 .castAs<DefinedSVal>();
195 DefinedSVal RetVal = makeRetVal(C, CE);
196 State =
State->BindExpr(CE,
C.getLocationContext(), RetVal);
198 assert(
State &&
"Assumption on new value should not fail.");
203 CheckerContext &C,
const CallExpr *CE) {
204 State =
State->BindExpr(CE,
C.getLocationContext(),
205 C.getSValBuilder().makeIntVal(
Value,
false));
209 class StreamChecker :
public Checker<check::PreCall, eval::Call,
210 check::DeadSymbols, check::PointerEscape> {
211 BugType BT_FileNull{
this,
"NULL stream pointer",
"Stream handling error"};
212 BugType BT_UseAfterClose{
this,
"Closed stream",
"Stream handling error"};
213 BugType BT_UseAfterOpenFailed{
this,
"Invalid stream",
214 "Stream handling error"};
215 BugType BT_IndeterminatePosition{
this,
"Invalid stream state",
216 "Stream handling error"};
217 BugType BT_IllegalWhence{
this,
"Illegal whence argument",
218 "Stream handling error"};
219 BugType BT_StreamEof{
this,
"Stream already in EOF",
"Stream handling error"};
220 BugType BT_ResourceLeak{
this,
"Resource leak",
"Stream handling error",
224 void checkPreCall(
const CallEvent &Call, CheckerContext &C)
const;
225 bool evalCall(
const CallEvent &Call, CheckerContext &C)
const;
226 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C)
const;
229 const CallEvent *Call,
233 bool TestMode =
false;
235 const BugType *getBT_StreamEof()
const {
return &BT_StreamEof; }
238 CallDescriptionMap<FnDescription> FnDescriptions = {
239 {{
"fopen"}, {
nullptr, &StreamChecker::evalFopen, ArgNone}},
241 {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}},
242 {{
"tmpfile"}, {
nullptr, &StreamChecker::evalFopen, ArgNone}},
244 {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}},
246 {&StreamChecker::preFread,
247 std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4,
true), 3}},
249 {&StreamChecker::preFwrite,
250 std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4,
false), 3}},
251 {{
"fseek", 3}, {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
252 {{
"ftell", 1}, {&StreamChecker::preDefault,
nullptr, 0}},
253 {{
"rewind", 1}, {&StreamChecker::preDefault,
nullptr, 0}},
254 {{
"fgetpos", 2}, {&StreamChecker::preDefault,
nullptr, 0}},
255 {{
"fsetpos", 2}, {&StreamChecker::preDefault,
nullptr, 0}},
257 {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}},
259 {&StreamChecker::preDefault,
260 std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof),
263 {&StreamChecker::preDefault,
264 std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError),
266 {{
"fileno", 1}, {&StreamChecker::preDefault,
nullptr, 0}},
269 CallDescriptionMap<FnDescription> FnTestDescriptions = {
270 {{
"StreamTesterChecker_make_feof_stream", 1},
272 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof),
274 {{
"StreamTesterChecker_make_ferror_stream", 1},
276 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
281 void evalFopen(
const FnDescription *Desc,
const CallEvent &Call,
282 CheckerContext &C)
const;
284 void preFreopen(
const FnDescription *Desc,
const CallEvent &Call,
285 CheckerContext &C)
const;
286 void evalFreopen(
const FnDescription *Desc,
const CallEvent &Call,
287 CheckerContext &C)
const;
289 void evalFclose(
const FnDescription *Desc,
const CallEvent &Call,
290 CheckerContext &C)
const;
292 void preFread(
const FnDescription *Desc,
const CallEvent &Call,
293 CheckerContext &C)
const;
295 void preFwrite(
const FnDescription *Desc,
const CallEvent &Call,
296 CheckerContext &C)
const;
298 void evalFreadFwrite(
const FnDescription *Desc,
const CallEvent &Call,
299 CheckerContext &C,
bool IsFread)
const;
301 void preFseek(
const FnDescription *Desc,
const CallEvent &Call,
302 CheckerContext &C)
const;
303 void evalFseek(
const FnDescription *Desc,
const CallEvent &Call,
304 CheckerContext &C)
const;
306 void preDefault(
const FnDescription *Desc,
const CallEvent &Call,
307 CheckerContext &C)
const;
309 void evalClearerr(
const FnDescription *Desc,
const CallEvent &Call,
310 CheckerContext &C)
const;
312 void evalFeofFerror(
const FnDescription *Desc,
const CallEvent &Call,
314 const StreamErrorState &ErrorKind)
const;
316 void evalSetFeofFerror(
const FnDescription *Desc,
const CallEvent &Call,
318 const StreamErrorState &ErrorKind)
const;
341 ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C,
348 ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
355 void reportFEofWarning(
SymbolRef StreamSym, CheckerContext &C,
362 CheckerContext &C, ExplodedNode *Pred)
const;
366 const FnDescription *lookupFn(
const CallEvent &Call)
const {
369 if (!
Call.isGlobalCFunction())
371 for (
auto P :
Call.parameters()) {
377 return FnDescriptions.lookup(Call);
384 const BugType *BT_ResourceLeak;
388 std::string operator()(PathSensitiveBugReport &BR)
const {
389 if (BR.isInteresting(StreamSym) && &BR.getBugType() == BT_ResourceLeak)
396 const NoteTag *constructNoteTag(CheckerContext &C,
SymbolRef StreamSym,
398 return C.getNoteTag(NoteFn{&BT_ResourceLeak, StreamSym,
Message});
401 const NoteTag *constructSetEofNoteTag(CheckerContext &C,
403 return C.getNoteTag([
this, StreamSym](PathSensitiveBugReport &BR) {
404 if (!BR.isInteresting(StreamSym) ||
405 &BR.getBugType() != this->getBT_StreamEof())
408 BR.markNotInteresting(StreamSym);
410 return "Assuming stream reaches end-of-file here";
416 static const ExplodedNode *getAcquisitionSite(
const ExplodedNode *N,
429 assert(SS->isOpened() &&
430 "Previous create of error node for non-opened stream failed?");
433 const ExplodedNode *StreamChecker::getAcquisitionSite(
const ExplodedNode *N,
439 if (!
State->get<StreamMap>(StreamSym))
440 N = N->getFirstPred();
442 const ExplodedNode *Pred = N;
444 State = N->getState();
445 if (!
State->get<StreamMap>(StreamSym))
448 N = N->getFirstPred();
458 void StreamChecker::checkPreCall(
const CallEvent &Call,
459 CheckerContext &C)
const {
460 const FnDescription *Desc = lookupFn(Call);
461 if (!Desc || !Desc->PreFn)
464 Desc->PreFn(
this, Desc, Call, C);
467 bool StreamChecker::evalCall(
const CallEvent &Call, CheckerContext &C)
const {
468 const FnDescription *Desc = lookupFn(Call);
469 if (!Desc && TestMode)
470 Desc = FnTestDescriptions.lookup(Call);
471 if (!Desc || !Desc->EvalFn)
474 Desc->EvalFn(
this, Desc, Call, C);
476 return C.isDifferent();
479 void StreamChecker::evalFopen(
const FnDescription *Desc,
const CallEvent &Call,
480 CheckerContext &C)
const {
482 const CallExpr *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
486 DefinedSVal RetVal = makeRetVal(C, CE);
488 assert(RetSym &&
"RetVal must be a symbol here.");
490 State =
State->BindExpr(CE,
C.getLocationContext(), RetVal);
495 std::tie(StateNotNull, StateNull) =
496 C.getConstraintManager().assumeDual(
State, RetVal);
499 StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc));
501 StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc));
503 C.addTransition(StateNotNull,
504 constructNoteTag(C, RetSym,
"Stream opened here"));
505 C.addTransition(StateNull);
508 void StreamChecker::preFreopen(
const FnDescription *Desc,
const CallEvent &Call,
509 CheckerContext &C)
const {
512 State = ensureStreamNonNull(getStreamArg(Desc, Call),
513 Call.getArgExpr(Desc->StreamArgNo), C,
State);
520 void StreamChecker::evalFreopen(
const FnDescription *Desc,
521 const CallEvent &Call,
522 CheckerContext &C)
const {
525 auto *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
530 getStreamArg(Desc, Call).getAs<DefinedSVal>();
534 SymbolRef StreamSym = StreamVal->getAsSymbol();
541 if (!
State->get<StreamMap>(StreamSym))
549 State->BindExpr(CE,
C.getLocationContext(), *StreamVal);
553 State->BindExpr(CE,
C.getLocationContext(),
554 C.getSValBuilder().makeNullWithType(CE->
getType()));
557 StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
559 StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc));
561 C.addTransition(StateRetNotNull,
562 constructNoteTag(C, StreamSym,
"Stream reopened here"));
563 C.addTransition(StateRetNull);
566 void StreamChecker::evalFclose(
const FnDescription *Desc,
const CallEvent &Call,
567 CheckerContext &C)
const {
569 SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
573 const StreamState *SS =
State->get<StreamMap>(Sym);
582 State =
State->set<StreamMap>(Sym, StreamState::getClosed(Desc));
587 void StreamChecker::preFread(
const FnDescription *Desc,
const CallEvent &Call,
588 CheckerContext &C)
const {
590 SVal StreamVal = getStreamArg(Desc, Call);
591 State = ensureStreamNonNull(StreamVal,
Call.getArgExpr(Desc->StreamArgNo), C,
595 State = ensureStreamOpened(StreamVal, C,
State);
598 State = ensureNoFilePositionIndeterminate(StreamVal, C,
State);
603 if (Sym &&
State->get<StreamMap>(Sym)) {
604 const StreamState *SS =
State->get<StreamMap>(Sym);
605 if (SS->ErrorState & ErrorFEof)
606 reportFEofWarning(Sym, C,
State);
612 void StreamChecker::preFwrite(
const FnDescription *Desc,
const CallEvent &Call,
613 CheckerContext &C)
const {
615 SVal StreamVal = getStreamArg(Desc, Call);
616 State = ensureStreamNonNull(StreamVal,
Call.getArgExpr(Desc->StreamArgNo), C,
620 State = ensureStreamOpened(StreamVal, C,
State);
623 State = ensureNoFilePositionIndeterminate(StreamVal, C,
State);
630 void StreamChecker::evalFreadFwrite(
const FnDescription *Desc,
631 const CallEvent &Call, CheckerContext &C,
632 bool IsFread)
const {
634 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
638 const CallExpr *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
649 const StreamState *OldSS =
State->get<StreamMap>(StreamSym);
661 if (
State->isNull(*SizeVal).isConstrainedTrue() ||
662 State->isNull(*NMembVal).isConstrainedTrue()) {
672 if (!IsFread || (OldSS->ErrorState != ErrorFEof)) {
674 State->BindExpr(CE,
C.getLocationContext(), *NMembVal);
676 StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
677 C.addTransition(StateNotFailed);
681 NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
683 State->BindExpr(CE,
C.getLocationContext(), RetVal);
686 .evalBinOpNN(
State, BO_LT, RetVal, *NMembVal,
C.getASTContext().IntTy)
687 .getAs<DefinedOrUnknownSVal>();
690 StateFailed = StateFailed->assume(*Cond,
true);
694 StreamErrorState NewES;
697 (OldSS->ErrorState == ErrorFEof) ? ErrorFEof : ErrorFEof | ErrorFError;
702 StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof());
703 StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS);
704 if (IsFread && OldSS->ErrorState != ErrorFEof)
705 C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
707 C.addTransition(StateFailed);
710 void StreamChecker::preFseek(
const FnDescription *Desc,
const CallEvent &Call,
711 CheckerContext &C)
const {
713 SVal StreamVal = getStreamArg(Desc, Call);
714 State = ensureStreamNonNull(StreamVal,
Call.getArgExpr(Desc->StreamArgNo), C,
718 State = ensureStreamOpened(StreamVal, C,
State);
728 void StreamChecker::evalFseek(
const FnDescription *Desc,
const CallEvent &Call,
729 CheckerContext &C)
const {
731 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
735 const CallExpr *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
740 if (!
State->get<StreamMap>(StreamSym))
743 DefinedSVal RetVal = makeRetVal(C, CE);
746 State =
State->BindExpr(CE,
C.getLocationContext(), RetVal);
751 std::tie(StateFailed, StateNotFailed) =
752 C.getConstraintManager().assumeDual(
State, RetVal);
756 StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
761 StateFailed = StateFailed->set<StreamMap>(
763 StreamState::getOpened(Desc, ErrorNone | ErrorFEof | ErrorFError,
true));
765 C.addTransition(StateNotFailed);
766 C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
769 void StreamChecker::evalClearerr(
const FnDescription *Desc,
770 const CallEvent &Call,
771 CheckerContext &C)
const {
773 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
777 const StreamState *SS =
State->get<StreamMap>(StreamSym);
786 StreamState::getOpened(Desc, ErrorNone, SS->FilePositionIndeterminate));
790 void StreamChecker::evalFeofFerror(
const FnDescription *Desc,
791 const CallEvent &Call, CheckerContext &C,
792 const StreamErrorState &ErrorKind)
const {
794 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
798 const CallExpr *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
802 const StreamState *SS =
State->get<StreamMap>(StreamSym);
808 if (SS->ErrorState & ErrorKind) {
813 C.addTransition(TrueState->set<StreamMap>(
814 StreamSym, StreamState::getOpened(Desc, ErrorKind,
815 SS->FilePositionIndeterminate &&
816 !ErrorKind.isFEof())));
818 if (StreamErrorState NewES = SS->ErrorState & (~ErrorKind)) {
823 C.addTransition(FalseState->set<StreamMap>(
825 StreamState::getOpened(
826 Desc, NewES, SS->FilePositionIndeterminate && !NewES.isFEof())));
830 void StreamChecker::preDefault(
const FnDescription *Desc,
const CallEvent &Call,
831 CheckerContext &C)
const {
833 SVal StreamVal = getStreamArg(Desc, Call);
834 State = ensureStreamNonNull(StreamVal,
Call.getArgExpr(Desc->StreamArgNo), C,
838 State = ensureStreamOpened(StreamVal, C,
State);
845 void StreamChecker::evalSetFeofFerror(
const FnDescription *Desc,
846 const CallEvent &Call, CheckerContext &C,
847 const StreamErrorState &ErrorKind)
const {
849 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
850 assert(StreamSym &&
"Operation not permitted on non-symbolic stream value.");
851 const StreamState *SS =
State->get<StreamMap>(StreamSym);
852 assert(SS &&
"Stream should be tracked by the checker.");
854 StreamSym, StreamState::getOpened(SS->LastOperation, ErrorKind));
859 StreamChecker::ensureStreamNonNull(SVal StreamVal,
const Expr *StreamE,
862 auto Stream = StreamVal.getAs<DefinedSVal>();
866 ConstraintManager &CM =
C.getConstraintManager();
869 std::tie(StateNotNull, StateNull) = CM.assumeDual(
C.getState(), *Stream);
871 if (!StateNotNull && StateNull) {
872 if (ExplodedNode *N =
C.generateErrorNode(StateNull)) {
873 auto R = std::make_unique<PathSensitiveBugReport>(
874 BT_FileNull,
"Stream pointer might be NULL.", N);
877 C.emitReport(std::move(R));
892 const StreamState *SS =
State->get<StreamMap>(Sym);
896 if (SS->isClosed()) {
899 ExplodedNode *N =
C.generateErrorNode();
901 C.emitReport(std::make_unique<PathSensitiveBugReport>(
903 "Stream might be already closed. Causes undefined behaviour.", N));
910 if (SS->isOpenFailed()) {
915 ExplodedNode *N =
C.generateErrorNode();
917 C.emitReport(std::make_unique<PathSensitiveBugReport>(
918 BT_UseAfterOpenFailed,
919 "Stream might be invalid after "
920 "(re-)opening it has failed. "
921 "Can cause undefined behaviour.",
933 static const char *BugMessage =
934 "File position of the stream might be 'indeterminate' "
935 "after a failed operation. "
936 "Can cause undefined behavior.";
942 const StreamState *SS =
State->get<StreamMap>(Sym);
946 assert(SS->isOpened() &&
"First ensure that stream is opened.");
948 if (SS->FilePositionIndeterminate) {
949 if (SS->ErrorState & ErrorFEof) {
953 ExplodedNode *N =
C.generateNonFatalErrorNode(
State);
957 C.emitReport(std::make_unique<PathSensitiveBugReport>(
958 BT_IndeterminatePosition, BugMessage, N));
959 return State->set<StreamMap>(
960 Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof,
false));
965 ExplodedNode *N =
C.generateErrorNode(
State);
967 C.emitReport(std::make_unique<PathSensitiveBugReport>(
968 BT_IndeterminatePosition, BugMessage, N));
977 StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
983 int64_t X = CI->getValue().getSExtValue();
984 if (
X >= 0 &&
X <= 2)
987 if (ExplodedNode *N =
C.generateNonFatalErrorNode(
State)) {
988 C.emitReport(std::make_unique<PathSensitiveBugReport>(
990 "The whence argument to fseek() should be "
991 "SEEK_SET, SEEK_END, or SEEK_CUR.",
999 void StreamChecker::reportFEofWarning(
SymbolRef StreamSym, CheckerContext &C,
1001 if (ExplodedNode *N =
C.generateNonFatalErrorNode(
State)) {
1002 auto R = std::make_unique<PathSensitiveBugReport>(
1004 "Read function called when stream is in EOF state. "
1005 "Function has no effect.",
1007 R->markInteresting(StreamSym);
1008 C.emitReport(std::move(R));
1016 CheckerContext &C, ExplodedNode *Pred)
const {
1017 ExplodedNode *Err =
C.generateNonFatalErrorNode(
C.getState(), Pred);
1033 const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C);
1034 assert(StreamOpenNode &&
"Could not find place of stream opening.");
1035 PathDiagnosticLocation LocUsedForUniqueing =
1037 StreamOpenNode->getStmtForDiagnostics(),
C.getSourceManager(),
1038 StreamOpenNode->getLocationContext());
1040 std::unique_ptr<PathSensitiveBugReport> R =
1041 std::make_unique<PathSensitiveBugReport>(
1043 "Opened stream never closed. Potential resource leak.", Err,
1044 LocUsedForUniqueing,
1045 StreamOpenNode->getLocationContext()->getDecl());
1046 R->markInteresting(LeakSym);
1047 C.emitReport(std::move(R));
1053 void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
1054 CheckerContext &C)
const {
1059 const StreamMapTy &Map =
State->get<StreamMap>();
1060 for (
const auto &I : Map) {
1062 const StreamState &SS = I.second;
1063 if (!SymReaper.isDead(Sym))
1066 LeakedSyms.push_back(Sym);
1070 ExplodedNode *N =
C.getPredecessor();
1071 if (!LeakedSyms.empty())
1072 N = reportLeaks(LeakedSyms, C, N);
1074 C.addTransition(
State, N);
1103 void ento::registerStreamChecker(CheckerManager &Mgr) {
1104 Mgr.registerChecker<StreamChecker>();
1107 bool ento::shouldRegisterStreamChecker(
const CheckerManager &Mgr) {
1111 void ento::registerStreamTesterChecker(CheckerManager &Mgr) {
1112 auto *Checker = Mgr.getChecker<StreamChecker>();
1113 Checker->TestMode =
true;
1116 bool ento::shouldRegisterStreamTesterChecker(
const CheckerManager &Mgr) {