]> granicus.if.org Git - clang/commitdiff
Teach format string checking about compile-time CFString constants.
authorJordan Rose <jordan_rose@apple.com>
Mon, 4 Jun 2012 23:52:23 +0000 (23:52 +0000)
committerJordan Rose <jordan_rose@apple.com>
Mon, 4 Jun 2012 23:52:23 +0000 (23:52 +0000)
Within the guts of CheckFormatHandler, the IsObjCLiteral flag was being used in
two ways: to see if null bytes were allowed, and to see if the '%@' specifier
is allowed.* The former usage has been changed to an explicit test and the
latter pushed down to CheckPrintfHandler and renamed ObjCContext, since it
applies to CFStrings as well.

* This also changes how wide chars are interpreted; in OS X Foundation, the
wide character type is 'unichar', a typedef for short, rather than wchar_t.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@157968 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Sema/SemaChecking.cpp
test/SemaObjC/format-strings-objc.m

index 0046b86f420a21706bef06bed0059ef4efd0a330..e818f5f3e07271731848e6417f311c4b29844277 100644 (file)
@@ -1636,6 +1636,15 @@ bool Sema::SemaCheckStringLiteral(const Expr *E, Expr **Args,
         return SemaCheckStringLiteral(Arg, Args, NumArgs, HasVAListArg,
                                       format_idx, firstDataArg, Type,
                                       inFunctionCall);
+      } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)) {
+        unsigned BuiltinID = FD->getBuiltinID();
+        if (BuiltinID == Builtin::BI__builtin___CFStringMakeConstantString ||
+            BuiltinID == Builtin::BI__builtin___NSStringMakeConstantString) {
+          const Expr *Arg = CE->getArg(0);
+          return SemaCheckStringLiteral(Arg, Args, NumArgs, HasVAListArg,
+                                        format_idx, firstDataArg, Type,
+                                        inFunctionCall);
+        }
       }
     }
 
@@ -1780,7 +1789,6 @@ protected:
   const Expr *OrigFormatExpr;
   const unsigned FirstDataArg;
   const unsigned NumDataArgs;
-  const bool IsObjCLiteral;
   const char *Beg; // Start of format string.
   const bool HasVAListArg;
   const Expr * const *Args;
@@ -1793,15 +1801,12 @@ protected:
 public:
   CheckFormatHandler(Sema &s, const StringLiteral *fexpr,
                      const Expr *origFormatExpr, unsigned firstDataArg,
-                     unsigned numDataArgs, bool isObjCLiteral,
-                     const char *beg, bool hasVAListArg,
+                     unsigned numDataArgs, const char *beg, bool hasVAListArg,
                      Expr **args, unsigned numArgs,
                      unsigned formatIdx, bool inFunctionCall)
     : S(s), FExpr(fexpr), OrigFormatExpr(origFormatExpr),
-      FirstDataArg(firstDataArg),
-      NumDataArgs(numDataArgs),
-      IsObjCLiteral(isObjCLiteral), Beg(beg),
-      HasVAListArg(hasVAListArg),
+      FirstDataArg(firstDataArg), NumDataArgs(numDataArgs),
+      Beg(beg), HasVAListArg(hasVAListArg),
       Args(args), NumArgs(numArgs), FormatIdx(formatIdx),
       usesPositionalArgs(false), atFirstArg(true),
       inFunctionCall(inFunctionCall) {
@@ -1961,7 +1966,7 @@ void CheckFormatHandler::HandleZeroPosition(const char *startPos,
 }
 
 void CheckFormatHandler::HandleNullChar(const char *nullCharacter) {
-  if (!IsObjCLiteral) {
+  if (!isa<ObjCStringLiteral>(OrigFormatExpr)) {
     // The presence of a null character is likely an error.
     EmitFormatDiagnostic(
       S.PDiag(diag::warn_printf_format_string_contains_null_char),
@@ -2112,16 +2117,17 @@ void CheckFormatHandler::EmitFormatDiagnostic(Sema &S, bool InFunctionCall,
 
 namespace {
 class CheckPrintfHandler : public CheckFormatHandler {
+  bool ObjCContext;
 public:
   CheckPrintfHandler(Sema &s, const StringLiteral *fexpr,
                      const Expr *origFormatExpr, unsigned firstDataArg,
-                     unsigned numDataArgs, bool isObjCLiteral,
+                     unsigned numDataArgs, bool isObjC,
                      const char *beg, bool hasVAListArg,
                      Expr **Args, unsigned NumArgs,
                      unsigned formatIdx, bool inFunctionCall)
   : CheckFormatHandler(s, fexpr, origFormatExpr, firstDataArg,
-                       numDataArgs, isObjCLiteral, beg, hasVAListArg,
-                       Args, NumArgs, formatIdx, inFunctionCall) {}
+                       numDataArgs, beg, hasVAListArg, Args, NumArgs,
+                       formatIdx, inFunctionCall), ObjCContext(isObjC) {}
   
   
   bool HandleInvalidPrintfConversionSpecifier(
@@ -2314,7 +2320,7 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier
 
   // Check for using an Objective-C specific conversion specifier
   // in a non-ObjC literal.
-  if (!IsObjCLiteral && CS.isObjCArg()) {
+  if (!ObjCContext && CS.isObjCArg()) {
     return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier,
                                                   specifierLen);
   }
@@ -2394,7 +2400,7 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier
   // format specifier.
   const Expr *Ex = getDataArg(argIndex);
   const analyze_printf::ArgTypeResult &ATR = FS.getArgType(S.Context,
-                                                           IsObjCLiteral);
+                                                           ObjCContext);
   if (ATR.isValid() && !ATR.matchesType(S.Context, Ex->getType())) {
     // Look through argument promotions for our error message's reported type.
     // This includes the integral and floating promotions, but excludes array
@@ -2420,7 +2426,7 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier
     // We may be able to offer a FixItHint if it is a supported type.
     PrintfSpecifier fixedFS = FS;
     bool success = fixedFS.fixType(Ex->getType(), S.getLangOpts(),
-                                   S.Context, IsObjCLiteral);
+                                   S.Context, ObjCContext);
 
     if (success) {
       // Get the fix string from the fixed format specifier
@@ -2461,12 +2467,11 @@ class CheckScanfHandler : public CheckFormatHandler {
 public:
   CheckScanfHandler(Sema &s, const StringLiteral *fexpr,
                     const Expr *origFormatExpr, unsigned firstDataArg,
-                    unsigned numDataArgs, bool isObjCLiteral,
-                    const char *beg, bool hasVAListArg,
+                    unsigned numDataArgs, const char *beg, bool hasVAListArg,
                     Expr **Args, unsigned NumArgs,
                     unsigned formatIdx, bool inFunctionCall)
   : CheckFormatHandler(s, fexpr, origFormatExpr, firstDataArg,
-                       numDataArgs, isObjCLiteral, beg, hasVAListArg,
+                       numDataArgs, beg, hasVAListArg,
                        Args, NumArgs, formatIdx, inFunctionCall) {}
   
   bool HandleScanfSpecifier(const analyze_scanf::ScanfSpecifier &FS,
@@ -2653,7 +2658,7 @@ void Sema::CheckFormatString(const StringLiteral *FExpr,
   
   if (Type == FST_Printf || Type == FST_NSString) {
     CheckPrintfHandler H(*this, FExpr, OrigFormatExpr, firstDataArg,
-                         numDataArgs, isa<ObjCStringLiteral>(OrigFormatExpr),
+                         numDataArgs, (Type == FST_NSString),
                          Str, HasVAListArg, Args, NumArgs, format_idx,
                          inFunctionCall);
   
@@ -2661,8 +2666,7 @@ void Sema::CheckFormatString(const StringLiteral *FExpr,
                                                   getLangOpts()))
       H.DoneProcessing();
   } else if (Type == FST_Scanf) {
-    CheckScanfHandler H(*this, FExpr, OrigFormatExpr, firstDataArg,
-                        numDataArgs, isa<ObjCStringLiteral>(OrigFormatExpr),
+    CheckScanfHandler H(*this, FExpr, OrigFormatExpr, firstDataArg, numDataArgs,
                         Str, HasVAListArg, Args, NumArgs, format_idx,
                         inFunctionCall);
     
index c1285b2e13d41896bdaac7e41c5cb5ac5ae0f3c2..043cb5d8d20871926a068277db931cb2533b8cee 100644 (file)
@@ -31,6 +31,12 @@ extern void *_NSConstantStringClassReference;
 
 typedef const struct __CFString * CFStringRef;
 extern void CFStringCreateWithFormat(CFStringRef format, ...) __attribute__((format(CFString, 1, 2)));
+#define CFSTR(cStr)  ((CFStringRef) __builtin___CFStringMakeConstantString ("" cStr ""))
+
+// This function is used instead of the builtin if -fno-constant-cfstrings.
+// The definition on Mac OS X is NOT annotated with format_arg as of 10.7,
+// but if it were, we want the same checking behavior as with the builtin.
+extern CFStringRef __CFStringMakeConstantString(const char *) __attribute__((format_arg(1)));
 
 int printf(const char * restrict, ...) ;
 
@@ -52,6 +58,7 @@ void rdar_7068334() {
   long long test = 500;  
   printf("%i ",test); // expected-warning{{format specifies type 'int' but the argument has type 'long long'}}
   NSLog(@"%i ",test); // expected-warning{{format specifies type 'int' but the argument has type 'long long'}}
+  CFStringCreateWithFormat(CFSTR("%i"),test); // expected-warning{{format specifies type 'int' but the argument has type 'long long'}}
 }
 
 // <rdar://problem/7697748>
@@ -72,7 +79,7 @@ extern void MyCFStringCreateWithFormat(CFStringRef format, ...) __attribute__((f
 
 void check_mylog() {
   MyNSLog(@"%@"); // expected-warning {{more '%' conversions than data arguments}}
-  // FIXME: find a way to test CFString too, but I don't know how to create constant CFString.
+  MyCFStringCreateWithFormat(CFSTR("%@")); // expected-warning {{more '%' conversions than data arguments}}
 }
 
 // PR 10275 - format function attribute isn't checked in Objective-C methods
@@ -159,8 +166,12 @@ void test_percent_C() {
 }
 
 // Test that %@ works with toll-free bridging (<rdar://problem/10814120>).
-void test_toll_free_bridging(CFStringRef x) {
+void test_toll_free_bridging(CFStringRef x, id y) {
   NSLog(@"%@", x); // no-warning
+  CFStringCreateWithFormat(CFSTR("%@"), x); // no-warning
+
+  NSLog(@"%@", y); // no-warning
+  CFStringCreateWithFormat(CFSTR("%@"), y); // no-warning
 }
 
 @interface Bar
@@ -195,3 +206,7 @@ int rdar11049844() {
   printf("%p", x);  // no-warning
 }
 
+void test_nonBuiltinCFStrings() {
+  CFStringCreateWithFormat(__CFStringMakeConstantString("%@"), 1); // expected-warning{{format specifies type 'id' but the argument has type 'int'}}
+}
+