[cfe-commits] r94864 - in /cfe/trunk: include/clang/Basic/DiagnosticSemaKinds.td lib/Sema/SemaChecking.cpp test/Sema/format-strings.c test/Sema/ucn-cstring.c

Ted Kremenek kremenek at apple.com
Fri Jan 29 16:49:52 PST 2010


Author: kremenek
Date: Fri Jan 29 18:49:51 2010
New Revision: 94864

URL: http://llvm.org/viewvc/llvm-project?rev=94864&view=rev
Log:
Add basic type checking of format string conversion specifiers and their arguments.  Thanks to Cristian Draghici for his help with this patch!

Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/Sema/SemaChecking.cpp
    cfe/trunk/test/Sema/format-strings.c
    cfe/trunk/test/Sema/ucn-cstring.c

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=94864&r1=94863&r2=94864&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Jan 29 18:49:51 2010
@@ -2449,6 +2449,9 @@
   "incomplete format specifier">, InGroup<Format>;
 def warn_printf_missing_format_string : Warning<
   "format string missing">, InGroup<Format>;
+def warn_printf_conversion_argument_type_mismatch : Warning<
+  "conversion specifies type %0 but the argument has type %1">,
+  InGroup<Format>;
 def warn_null_arg : Warning<
   "null passed to a callee which requires a non-null argument">,
   InGroup<NonNull>;
@@ -2463,10 +2466,10 @@
 def warn_printf_asterisk_precision_missing_arg : Warning<
   "'.*' specified field precision is missing a matching 'int' argument">;
 def warn_printf_asterisk_width_wrong_type : Warning<
-  "field width should have type 'int', but argument has type %0">,
+  "field width should have type %0, but argument has type %1">,
   InGroup<Format>;
 def warn_printf_asterisk_precision_wrong_type : Warning<
-  "field precision should have type 'int', but argument has type %0">,
+  "field precision should have type %0, but argument has type %1">,
   InGroup<Format>;
 
 // CHECK: returning address/reference of stack memory

Modified: cfe/trunk/lib/Sema/SemaChecking.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaChecking.cpp?rev=94864&r1=94863&r2=94864&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaChecking.cpp (original)
+++ cfe/trunk/lib/Sema/SemaChecking.cpp Fri Jan 29 18:49:51 2010
@@ -1081,6 +1081,8 @@
                     unsigned MissingArgDiag, unsigned BadTypeDiag,
 					const char *startSpecifier, unsigned specifierLen);
   
+  bool MatchType(QualType A, QualType B, bool ignoreSign);
+  
   const Expr *getDataArg(unsigned i) const;
 };
 }
@@ -1132,6 +1134,47 @@
   return TheCall->getArg(FormatIdx + i);  
 }
 
+bool CheckPrintfHandler::MatchType(QualType A, QualType B, bool ignoreSign) {
+  A = S.Context.getCanonicalType(A).getUnqualifiedType();
+  B = S.Context.getCanonicalType(B).getUnqualifiedType();
+  
+  if (A == B)
+    return true;
+  
+  if (ignoreSign) {
+    if (const BuiltinType *BT = B->getAs<BuiltinType>()) {
+      switch (BT->getKind()) {
+        default:
+          return false;
+        case BuiltinType::Char_S:          
+        case BuiltinType::SChar:
+          return A == S.Context.UnsignedCharTy;
+        case BuiltinType::Char_U:
+        case BuiltinType::UChar:
+          return A == S.Context.SignedCharTy;
+        case BuiltinType::Short:
+          return A == S.Context.UnsignedShortTy;
+        case BuiltinType::UShort:
+          return A == S.Context.ShortTy;          
+        case BuiltinType::Int:
+          return A == S.Context.UnsignedIntTy;
+        case BuiltinType::UInt:
+          return A == S.Context.IntTy;
+        case BuiltinType::Long:
+          return A == S.Context.UnsignedLongTy;
+        case BuiltinType::ULong:
+          return A == S.Context.LongTy;
+        case BuiltinType::LongLong:
+          return A == S.Context.UnsignedLongLongTy;
+        case BuiltinType::ULongLong:
+          return A == S.Context.LongLongTy;          
+      }
+      return A == B;
+    }
+  }
+  return false;  
+}
+
 bool
 CheckPrintfHandler::HandleAmount(const analyze_printf::OptionalAmount &Amt,
                                  unsigned MissingArgDiag,
@@ -1156,13 +1199,11 @@
       // doesn't emit a warning for that case.
       const Expr *Arg = getDataArg(NumConversions);
       QualType T = Arg->getType();
-      const BuiltinType *BT = T->getAs<BuiltinType>();            
-      if (!BT || (BT->getKind() != BuiltinType::Int &&
-                  BT->getKind() != BuiltinType::UInt)) {
+      if (!MatchType(T, S.Context.IntTy, true)) {
         S.Diag(getLocationOfByte(Amt.getStart()), BadTypeDiag)
-          << T
-		  << getFormatSpecifierRange(startSpecifier, specifierLen)
-		  << Arg->getSourceRange();
+          << S.Context.IntTy << T
+          << getFormatSpecifierRange(startSpecifier, specifierLen)
+          << Arg->getSourceRange();
         // Don't do any more checking.  We will just emit
         // spurious errors.
         return false;
@@ -1234,6 +1275,22 @@
     // Don't do any more checking.
     return false;
   }
+  
+  // Now type check the data expression that matches the
+  // format specifier.
+  const Expr *Ex = getDataArg(NumConversions);
+  const analyze_printf::ArgTypeResult &ATR = FS.getArgType(S.Context);
+    
+  if (const QualType *T = ATR.getSpecificType()) {
+    if (!MatchType(*T, Ex->getType(), true)) {
+      S.Diag(getLocationOfByte(CS.getStart()),
+             diag::warn_printf_conversion_argument_type_mismatch)
+        << *T << Ex->getType()
+        << getFormatSpecifierRange(startSpecifier, specifierLen)
+        << Ex->getSourceRange();
+    }
+    return true;
+  }
 
   return true;
 }

Modified: cfe/trunk/test/Sema/format-strings.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/format-strings.c?rev=94864&r1=94863&r2=94864&view=diff

==============================================================================
--- cfe/trunk/test/Sema/format-strings.c (original)
+++ cfe/trunk/test/Sema/format-strings.c Fri Jan 29 18:49:51 2010
@@ -71,8 +71,8 @@
 {
   printf("%s%lb%d","unix",10,20); // expected-warning {{invalid conversion specifier 'b'}}
   fprintf(fp,"%%%l"); // expected-warning {{incomplete format specifier}}
-  sprintf(buf,"%%%%%ld%d%d", 1, 2, 3); // no-warning
-  snprintf(buf, 2, "%%%%%ld%;%d", 1, 2, 3); // expected-warning {{invalid conversion specifier ';'}}
+  sprintf(buf,"%%%%%ld%d%d", 1, 2, 3); // expected-warning{{conversion specifies type 'long' but the argument has type 'int'}}
+  snprintf(buf, 2, "%%%%%ld%;%d", 1, 2, 3); // expected-warning{{conversion specifies type 'long' but the argument has type 'int'}} expected-warning {{invalid conversion specifier ';'}}
 }
 
 void check_null_char_string(char* b)
@@ -162,3 +162,11 @@
   printf("%.", x);  // expected-warning{{incomplete format specifier}}
 } 
 
+typedef struct __aslclient *aslclient;
+typedef struct __aslmsg *aslmsg;
+int asl_log(aslclient asl, aslmsg msg, int level, const char *format, ...) __attribute__((__format__ (__printf__, 4, 5)));
+void test_asl(aslclient asl) {
+  // Test case from <rdar://problem/7341605>.
+  asl_log(asl, 0, 3, "Error: %m"); // no-warning
+  asl_log(asl, 0, 3, "Error: %W"); // expected-warning{{invalid conversion specifier 'W'}}
+}

Modified: cfe/trunk/test/Sema/ucn-cstring.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/ucn-cstring.c?rev=94864&r1=94863&r2=94864&view=diff

==============================================================================
--- cfe/trunk/test/Sema/ucn-cstring.c (original)
+++ cfe/trunk/test/Sema/ucn-cstring.c Fri Jan 29 18:49:51 2010
@@ -5,8 +5,8 @@
 int main(void) {
   int a[sizeof("hello \u2192 \u2603 \u2190 world") == 24 ? 1 : -1];
   
-  printf("%s (%d)\n", "hello \u2192 \u2603 \u2190 world", sizeof("hello \u2192 \u2603 \u2190 world"));
-  printf("%s (%d)\n", "\U00010400\U0001D12B", sizeof("\U00010400\U0001D12B"));
+  printf("%s (%zd)\n", "hello \u2192 \u2603 \u2190 world", sizeof("hello \u2192 \u2603 \u2190 world"));
+  printf("%s (%zd)\n", "\U00010400\U0001D12B", sizeof("\U00010400\U0001D12B"));
   // Some error conditions...
   printf("%s\n", "\U"); // expected-error{{\u used with no following hex digits}}
   printf("%s\n", "\U00"); // expected-error{{incomplete universal character name}}





More information about the cfe-commits mailing list