Add precision/field width checking to AlternateCheckPrintfString().


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@94774 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp
index 3589aff..f34d238 100644
--- a/lib/Sema/SemaChecking.cpp
+++ b/lib/Sema/SemaChecking.cpp
@@ -1290,14 +1290,20 @@
   const unsigned NumDataArgs;
   const bool IsObjCLiteral;
   const char *Beg; // Start of format string.
+  const bool HasVAListArg;
+  const CallExpr *TheCall;
+  unsigned FormatIdx;
 public:  
   CheckPrintfHandler(Sema &s, const StringLiteral *fexpr,
                      const Expr *origFormatExpr,
                      unsigned numDataArgs, bool isObjCLiteral,
-                     const char *beg)
+                     const char *beg, bool hasVAListArg,
+                     const CallExpr *theCall, unsigned formatIdx)
     : S(s), FExpr(fexpr), OrigFormatExpr(origFormatExpr),
       NumConversions(0), NumDataArgs(numDataArgs),
-      IsObjCLiteral(isObjCLiteral), Beg(beg) {}
+      IsObjCLiteral(isObjCLiteral), Beg(beg),
+      HasVAListArg(hasVAListArg),
+      TheCall(theCall), FormatIdx(formatIdx) {}
     
   void HandleNullChar(const char *nullCharacter);
   
@@ -1307,6 +1313,11 @@
 private:
   SourceRange getFormatRange();
   SourceLocation getLocationOfByte(const char *x);
+  
+  bool HandleAmount(const analyze_printf::OptionalAmount &Amt,
+                    unsigned MissingArgDiag, unsigned BadTypeDiag);
+  
+  const Expr *getDataArg(unsigned i) const;
 };
 }
 
@@ -1325,6 +1336,43 @@
     << getFormatRange();
 }
 
+const Expr *CheckPrintfHandler::getDataArg(unsigned i) const {
+  return TheCall->getArg(FormatIdx + i);  
+}
+
+bool
+CheckPrintfHandler::HandleAmount(const analyze_printf::OptionalAmount &Amt,
+                                 unsigned MissingArgDiag,
+                                 unsigned BadTypeDiag) {
+
+  if (Amt.hasDataArgument()) {
+    ++NumConversions;
+    if (!HasVAListArg) {
+      if (NumConversions > NumDataArgs) {
+        S.Diag(getLocationOfByte(Amt.getStart()), MissingArgDiag)
+          << getFormatRange();      
+        // Don't do any more checking.  We will just emit
+        // spurious errors.
+        return false;
+      }
+      
+      // Type check the data argument.  It should be an 'int'.
+      const Expr *Arg = getDataArg(NumConversions);
+      QualType T = Arg->getType();
+      const BuiltinType *BT = T->getAs<BuiltinType>();
+      if (!BT || BT->getKind() != BuiltinType::Int) {
+        S.Diag(getLocationOfByte(Amt.getStart()), BadTypeDiag)
+          << T << getFormatRange() << Arg->getSourceRange();
+        // Don't do any more checking.  We will just emit
+        // spurious errors.
+        return false;
+      }
+    }
+  }
+  return true;
+}
+                                      
+
 bool
 CheckPrintfHandler::HandleFormatSpecifier(const analyze_printf::FormatSpecifier &FS,
                                           const char *startSpecifier,
@@ -1333,6 +1381,22 @@
   using namespace analyze_printf;
   const ConversionSpecifier &CS = FS.getConversionSpecifier();
 
+  // First check if the field width, precision, and conversion specifier
+  // have matching data arguments.
+  if (!HandleAmount(FS.getFieldWidth(),
+                    diag::warn_printf_asterisk_width_missing_arg,
+                    diag::warn_printf_asterisk_width_wrong_type)) {
+    return false;
+  }
+    
+  if (!HandleAmount(FS.getPrecision(),
+                    diag::warn_printf_asterisk_precision_missing_arg,
+                    diag::warn_printf_asterisk_precision_wrong_type)) {
+    return false;
+  }
+
+  ++NumConversions;  
+  
   // Check for using an Objective-C specific conversion specifier
   // in a non-ObjC literal.
   if (!IsObjCLiteral && CS.isObjCArg()) {
@@ -1377,7 +1441,8 @@
   
   CheckPrintfHandler H(*this, FExpr, OrigFormatExpr,
                        TheCall->getNumArgs() - firstDataArg,
-                       isa<ObjCStringLiteral>(OrigFormatExpr), Str);
+                       isa<ObjCStringLiteral>(OrigFormatExpr), Str,
+                       HasVAListArg, TheCall, format_idx);
 
   analyze_printf::ParseFormatString(H, Str, Str + StrLen);
 }