[analyzer] Clarify 'uninitialized function argument' messages

Differential Revision: https://reviews.llvm.org/D30341

llvm-svn: 297283
diff --git a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
index f474857..07285d2 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
@@ -21,6 +21,7 @@
 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
 #include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/raw_ostream.h"
 
 using namespace clang;
@@ -71,7 +72,7 @@
 
 private:
   bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange ArgRange,
-                          const Expr *ArgEx, bool IsFirstArgument,
+                          const Expr *ArgEx, int ArgumentNumber,
                           bool CheckUninitFields, const CallEvent &Call,
                           std::unique_ptr<BugType> &BT,
                           const ParmVarDecl *ParamDecl) const;
@@ -89,9 +90,10 @@
       BT.reset(new BuiltinBug(this, desc));
   }
   bool uninitRefOrPointer(CheckerContext &C, const SVal &V,
-                          SourceRange ArgRange,
-                          const Expr *ArgEx, std::unique_ptr<BugType> &BT,
-                          const ParmVarDecl *ParamDecl, const char *BD) const;
+                          SourceRange ArgRange, const Expr *ArgEx,
+                          std::unique_ptr<BugType> &BT,
+                          const ParmVarDecl *ParamDecl, const char *BD,
+                          int ArgumentNumber) const;
 };
 } // end anonymous namespace
 
@@ -111,38 +113,45 @@
   C.emitReport(std::move(R));
 }
 
-static StringRef describeUninitializedArgumentInCall(const CallEvent &Call,
-                                                     bool IsFirstArgument) {
+static void describeUninitializedArgumentInCall(const CallEvent &Call,
+                                                int ArgumentNumber,
+                                                llvm::raw_svector_ostream &Os) {
   switch (Call.getKind()) {
   case CE_ObjCMessage: {
     const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Call);
     switch (Msg.getMessageKind()) {
     case OCM_Message:
-      return "Argument in message expression is an uninitialized value";
+      Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
+         << " argument in message expression is an uninitialized value";
+      return;
     case OCM_PropertyAccess:
       assert(Msg.isSetter() && "Getters have no args");
-      return "Argument for property setter is an uninitialized value";
+      Os << "Argument for property setter is an uninitialized value";
+      return;
     case OCM_Subscript:
-      if (Msg.isSetter() && IsFirstArgument)
-        return "Argument for subscript setter is an uninitialized value";
-      return "Subscript index is an uninitialized value";
+      if (Msg.isSetter() && (ArgumentNumber == 0))
+        Os << "Argument for subscript setter is an uninitialized value";
+      else
+        Os << "Subscript index is an uninitialized value";
+      return;
     }
     llvm_unreachable("Unknown message kind.");
   }
   case CE_Block:
-    return "Block call argument is an uninitialized value";
+    Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
+       << " block call argument is an uninitialized value";
+    return;
   default:
-    return "Function call argument is an uninitialized value";
+    Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
+       << " function call argument is an uninitialized value";
+    return;
   }
 }
 
-bool CallAndMessageChecker::uninitRefOrPointer(CheckerContext &C,
-                                               const SVal &V,
-                                               SourceRange ArgRange,
-                                               const Expr *ArgEx,
-                                               std::unique_ptr<BugType> &BT,
-                                               const ParmVarDecl *ParamDecl,
-                                               const char *BD) const {
+bool CallAndMessageChecker::uninitRefOrPointer(
+    CheckerContext &C, const SVal &V, SourceRange ArgRange, const Expr *ArgEx,
+    std::unique_ptr<BugType> &BT, const ParmVarDecl *ParamDecl, const char *BD,
+    int ArgumentNumber) const {
   if (!Filter.Check_CallAndMessageUnInitRefArg)
     return false;
 
@@ -153,12 +162,15 @@
   // If parameter is declared as pointer to const in function declaration,
   // then check if corresponding argument in function call is
   // pointing to undefined symbol value (uninitialized memory).
-  StringRef Message;
+  SmallString<200> Buf;
+  llvm::raw_svector_ostream Os(Buf);
 
   if (ParamDecl->getType()->isPointerType()) {
-    Message = "Function call argument is a pointer to uninitialized value";
+    Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
+       << " function call argument is a pointer to uninitialized value";
   } else if (ParamDecl->getType()->isReferenceType()) {
-    Message = "Function call argument is an uninitialized value";
+    Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
+       << " function call argument is an uninitialized value";
   } else
     return false;
 
@@ -171,7 +183,7 @@
     if (PSV.isUndef()) {
       if (ExplodedNode *N = C.generateErrorNode()) {
         LazyInit_BT(BD, BT);
-        auto R = llvm::make_unique<BugReport>(*BT, Message, N);
+        auto R = llvm::make_unique<BugReport>(*BT, Os.str(), N);
         R->addRange(ArgRange);
         if (ArgEx) {
           bugreporter::trackNullOrUndefValue(N, ArgEx, *R);
@@ -188,7 +200,7 @@
                                                SVal V,
                                                SourceRange ArgRange,
                                                const Expr *ArgEx,
-                                               bool IsFirstArgument,
+                                               int ArgumentNumber,
                                                bool CheckUninitFields,
                                                const CallEvent &Call,
                                                std::unique_ptr<BugType> &BT,
@@ -196,17 +208,19 @@
                                                ) const {
   const char *BD = "Uninitialized argument value";
 
-  if (uninitRefOrPointer(C, V, ArgRange, ArgEx, BT, ParamDecl, BD))
+  if (uninitRefOrPointer(C, V, ArgRange, ArgEx, BT, ParamDecl, BD,
+                         ArgumentNumber))
     return true;
 
   if (V.isUndef()) {
     if (ExplodedNode *N = C.generateErrorNode()) {
       LazyInit_BT(BD, BT);
-
       // Generate a report for this bug.
-      StringRef Desc =
-          describeUninitializedArgumentInCall(Call, IsFirstArgument);
-      auto R = llvm::make_unique<BugReport>(*BT, Desc, N);
+      SmallString<200> Buf;
+      llvm::raw_svector_ostream Os(Buf);
+      describeUninitializedArgumentInCall(Call, ArgumentNumber, Os);
+      auto R = llvm::make_unique<BugReport>(*BT, Os.str(), N);
+
       R->addRange(ArgRange);
       if (ArgEx)
         bugreporter::trackNullOrUndefValue(N, ArgEx, *R);
@@ -435,7 +449,7 @@
     if(FD && i < FD->getNumParams())
       ParamDecl = FD->getParamDecl(i);
     if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i),
-                           Call.getArgExpr(i), /*IsFirstArgument=*/i == 0,
+                           Call.getArgExpr(i), i,
                            checkUninitFields, Call, *BT, ParamDecl))
       return;
   }