@@ -245,11 +245,14 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
245
245
{{{" fclose" }, 1 },
246
246
{&StreamChecker::preDefault, &StreamChecker::evalFclose, 0 }},
247
247
{{{" fread" }, 4 },
248
- {std::bind (&StreamChecker::preFreadFwrite , _1, _2, _3, _4, true ),
248
+ {std::bind (&StreamChecker::preReadWrite , _1, _2, _3, _4, true ),
249
249
std::bind (&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true ), 3 }},
250
250
{{{" fwrite" }, 4 },
251
- {std::bind (&StreamChecker::preFreadFwrite , _1, _2, _3, _4, false ),
251
+ {std::bind (&StreamChecker::preReadWrite , _1, _2, _3, _4, false ),
252
252
std::bind (&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false ), 3 }},
253
+ {{{" fputc" }, 2 },
254
+ {std::bind (&StreamChecker::preReadWrite, _1, _2, _3, _4, false ),
255
+ &StreamChecker::evalFputc, 1 }},
253
256
{{{" fseek" }, 3 },
254
257
{&StreamChecker::preFseek, &StreamChecker::evalFseek, 0 }},
255
258
{{{" ftell" }, 1 },
@@ -305,12 +308,15 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
305
308
void evalFclose (const FnDescription *Desc, const CallEvent &Call,
306
309
CheckerContext &C) const ;
307
310
308
- void preFreadFwrite (const FnDescription *Desc, const CallEvent &Call,
309
- CheckerContext &C, bool IsFread ) const ;
311
+ void preReadWrite (const FnDescription *Desc, const CallEvent &Call,
312
+ CheckerContext &C, bool IsRead ) const ;
310
313
311
314
void evalFreadFwrite (const FnDescription *Desc, const CallEvent &Call,
312
315
CheckerContext &C, bool IsFread) const ;
313
316
317
+ void evalFputc (const FnDescription *Desc, const CallEvent &Call,
318
+ CheckerContext &C) const ;
319
+
314
320
void preFseek (const FnDescription *Desc, const CallEvent &Call,
315
321
CheckerContext &C) const ;
316
322
void evalFseek (const FnDescription *Desc, const CallEvent &Call,
@@ -634,9 +640,9 @@ void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
634
640
C.addTransition (StateFailure);
635
641
}
636
642
637
- void StreamChecker::preFreadFwrite (const FnDescription *Desc,
638
- const CallEvent &Call, CheckerContext &C,
639
- bool IsFread ) const {
643
+ void StreamChecker::preReadWrite (const FnDescription *Desc,
644
+ const CallEvent &Call, CheckerContext &C,
645
+ bool IsRead ) const {
640
646
ProgramStateRef State = C.getState ();
641
647
SVal StreamVal = getStreamArg (Desc, Call);
642
648
State = ensureStreamNonNull (StreamVal, Call.getArgExpr (Desc->StreamArgNo ), C,
@@ -650,7 +656,7 @@ void StreamChecker::preFreadFwrite(const FnDescription *Desc,
650
656
if (!State)
651
657
return ;
652
658
653
- if (!IsFread ) {
659
+ if (!IsRead ) {
654
660
C.addTransition (State);
655
661
return ;
656
662
}
@@ -745,6 +751,45 @@ void StreamChecker::evalFreadFwrite(const FnDescription *Desc,
745
751
C.addTransition (StateFailed);
746
752
}
747
753
754
+ void StreamChecker::evalFputc (const FnDescription *Desc, const CallEvent &Call,
755
+ CheckerContext &C) const {
756
+ ProgramStateRef State = C.getState ();
757
+ SymbolRef StreamSym = getStreamArg (Desc, Call).getAsSymbol ();
758
+ if (!StreamSym)
759
+ return ;
760
+
761
+ const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr ());
762
+ if (!CE)
763
+ return ;
764
+
765
+ const StreamState *OldSS = State->get <StreamMap>(StreamSym);
766
+ if (!OldSS)
767
+ return ;
768
+
769
+ assertStreamStateOpened (OldSS);
770
+
771
+ // `fputc` returns the written character on success, otherwise returns EOF.
772
+
773
+ // Generate a transition for the success state.
774
+ std::optional<NonLoc> PutVal = Call.getArgSVal (0 ).getAs <NonLoc>();
775
+ if (!PutVal)
776
+ return ;
777
+ ProgramStateRef StateNotFailed =
778
+ State->BindExpr (CE, C.getLocationContext (), *PutVal);
779
+ StateNotFailed =
780
+ StateNotFailed->set <StreamMap>(StreamSym, StreamState::getOpened (Desc));
781
+ C.addTransition (StateNotFailed);
782
+
783
+ // Add transition for the failed state.
784
+ // If a (non-EOF) error occurs, the resulting value of the file position
785
+ // indicator for the stream is indeterminate.
786
+ ProgramStateRef StateFailed = bindInt (*EofVal, State, C, CE);
787
+ StreamState NewSS = StreamState::getOpened (
788
+ Desc, ErrorFError, /* IsFilePositionIndeterminate*/ true );
789
+ StateFailed = StateFailed->set <StreamMap>(StreamSym, NewSS);
790
+ C.addTransition (StateFailed);
791
+ }
792
+
748
793
void StreamChecker::preFseek (const FnDescription *Desc, const CallEvent &Call,
749
794
CheckerContext &C) const {
750
795
ProgramStateRef State = C.getState ();
0 commit comments