================ @@ -1191,6 +1199,82 @@ void StreamChecker::evalSetFeofFerror(const FnDescription *Desc, C.addTransition(State); } +void StreamChecker::preFflush(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SVal StreamVal = getStreamArg(Desc, Call); + std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>(); + if (!Stream) + return; + + ConstraintManager::ProgramStatePair SP = + C.getConstraintManager().assumeDual(State, *Stream); + if (SP.first) + ensureStreamOpened(StreamVal, C, SP.first); +} + +void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SVal StreamVal = getStreamArg(Desc, Call); + std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>(); + if (!Stream) + return; + + // Skip if the stream can be both NULL and non-NULL. + ProgramStateRef StateNotNull, StateNull; + std::tie(StateNotNull, StateNull) = + C.getConstraintManager().assumeDual(State, *Stream); + if (StateNotNull && StateNull) + return; + if (StateNotNull && !StateNull) + State = StateNotNull; + else + State = StateNull; + + const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return; + + // `fflush` returns EOF on failure, otherwise returns 0. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + ProgramStateRef StateNotFailed = bindInt(0, State, C, CE); + + // Clear error states if `fflush` returns 0, but retain their EOF flags. + auto ClearError = [&StateNotFailed, Desc](SymbolRef Sym, + const StreamState *SS) { + if (SS->ErrorState & ErrorFError) { + StreamErrorState NewES = + (SS->ErrorState & ErrorFEof) ? ErrorFEof : ErrorNone; + StreamState NewSS = StreamState::getOpened(Desc, NewES, false); + StateNotFailed = StateNotFailed->set<StreamMap>(Sym, NewSS); + } + }; + + if (StateNotNull && !StateNull) { + // Skip if the input stream's state is unknown, open-failed or closed. + if (SymbolRef StreamSym = StreamVal.getAsSymbol()) { + const StreamState *SS = State->get<StreamMap>(StreamSym); + if (SS && SS->isOpened()) { + ClearError(StreamSym, SS); + C.addTransition(StateNotFailed); + C.addTransition(StateFailed); + } + } + } else { + // Clear error states for all streams. + const StreamMapTy &Map = StateNotFailed->get<StreamMap>(); + for (const auto &I : Map) { + SymbolRef Sym = I.first; + const StreamState &SS = I.second; + if (SS.isOpened()) + ClearError(Sym, &SS); + } + C.addTransition(StateNotFailed); + C.addTransition(StateFailed); ---------------- balazske wrote:
These `addTransition` calls (and at line 1260) can be moved to after the `if` statement. https://github.com/llvm/llvm-project/pull/74296 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits