[clang] [clang][analyzer] Support `fputs` in the StreamChecker (PR #73335)

Balázs Kéri via cfe-commits cfe-commits at lists.llvm.org
Fri Nov 24 07:40:08 PST 2023


================
@@ -774,26 +780,45 @@ void StreamChecker::evalFgetcFputc(const FnDescription *Desc,
 
   // `fgetc` returns the read character on success, otherwise returns EOF.
   // `fputc` returns the written character on success, otherwise returns EOF.
+  // `fputs` returns a non negative value on sucecess, otherwise returns EOF.
 
-  // Generate a transition for the success state of fputc.
+  SValBuilder &SVB = C.getSValBuilder();
+  auto &ASTC = C.getASTContext();
   if (!IsRead) {
-    std::optional<NonLoc> PutVal = Call.getArgSVal(0).getAs<NonLoc>();
-    if (!PutVal)
-      return;
-    ProgramStateRef StateNotFailed =
-        State->BindExpr(CE, C.getLocationContext(), *PutVal);
-    StateNotFailed =
-        StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
-    C.addTransition(StateNotFailed);
+    // Generddate a transition for the success state of `fputc`.
+    if (SingleChar) {
+      std::optional<NonLoc> PutVal = Call.getArgSVal(0).getAs<NonLoc>();
+      if (!PutVal)
+        return;
+      ProgramStateRef StateNotFailed =
+          State->BindExpr(CE, C.getLocationContext(), *PutVal);
+      StateNotFailed = StateNotFailed->set<StreamMap>(
+          StreamSym, StreamState::getOpened(Desc));
+      C.addTransition(StateNotFailed);
+    }
+    // Generddate a transition for the success state of `fputs`.
+    else {
+      NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
+      ProgramStateRef StateNotFailed =
+          State->BindExpr(CE, C.getLocationContext(), RetVal);
+      auto Cond =
+          SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy),
+                        SVB.getConditionType())
+              .getAs<DefinedOrUnknownSVal>();
+      if (!Cond)
+        return;
+      StateNotFailed = StateNotFailed->assume(*Cond, true);
+      if (!StateNotFailed)
+        return;
+      C.addTransition(StateNotFailed);
+    }
----------------
balazske wrote:

This function is becoming too large. Here almost all code for `fputs` is in a new branch and really there are 4 condition branches for all 4 functions (if `fgets` is added) with not much common code. It looks better to make an `evalFputX` and `evalFgetX` function (but `fgets` can be too much different than the other). Functions for the common code at begin and end of these `eval` functions (that can be reused by the others too) could be good too (and separate eval functions for all get and put cases), but this belongs into a separate PR. Probably I can improve the code, my next plans contain anyway to work with `StreamChecker`.

https://github.com/llvm/llvm-project/pull/73335


More information about the cfe-commits mailing list