Suggest '%@' for Objective-C objects in ObjC format strings.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@157716 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Analysis/PrintfFormatString.cpp b/lib/Analysis/PrintfFormatString.cpp
index e1049b3..3b3a0b1 100644
--- a/lib/Analysis/PrintfFormatString.cpp
+++ b/lib/Analysis/PrintfFormatString.cpp
@@ -342,7 +342,29 @@
bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt,
ASTContext &Ctx, bool IsObjCLiteral) {
- // Handle strings first (char *, wchar_t *)
+ // Handle Objective-C objects first. Note that while the '%@' specifier will
+ // not warn for structure pointer or void pointer arguments (because that's
+ // how CoreFoundation objects are implemented), we only show a fixit for '%@'
+ // if we know it's an object (block, id, class, or __attribute__((NSObject))).
+ if (QT->isObjCRetainableType()) {
+ if (!IsObjCLiteral)
+ return false;
+
+ CS.setKind(ConversionSpecifier::ObjCObjArg);
+
+ // Disable irrelevant flags
+ HasThousandsGrouping = false;
+ HasPlusPrefix = false;
+ HasSpacePrefix = false;
+ HasAlternativeForm = false;
+ HasLeadingZeroes = false;
+ Precision.setHowSpecified(OptionalAmount::NotSpecified);
+ LM.setKind(LengthModifier::None);
+
+ return true;
+ }
+
+ // Handle strings next (char *, wchar_t *)
if (QT->isPointerType() && (QT->getPointeeType()->isAnyCharacterType())) {
CS.setKind(ConversionSpecifier::sArg);
diff --git a/test/FixIt/format-no-fixit.m b/test/FixIt/format-no-fixit.m
new file mode 100644
index 0000000..36ecd3f
--- /dev/null
+++ b/test/FixIt/format-no-fixit.m
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fdiagnostics-parseable-fixits -fsyntax-only %s 2>&1 | FileCheck %s
+
+// CHECK-NOT: fix-it:
+
+@class NSString;
+extern void NSLog(NSString *format, ...);
+int printf(const char * restrict, ...) ;
+
+
+void test_object_correction (id x) {
+ printf("%d", x); // expected-warning{{format specifies type 'int' but the argument has type 'id'}}
+ printf("%s", x); // expected-warning{{format specifies type 'char *' but the argument has type 'id'}}
+ printf("%lf", x); // expected-warning{{format specifies type 'double' but the argument has type 'id'}}
+}
+
+
+// Old-style Core Foundation types do not have __attribute__((NSObject)).
+// This is okay, but we won't suggest a fixit; arbitrary structure pointers may
+// not be objects.
+typedef const struct __CFString * CFStringRef;
+
+void test_cf_object_correction (CFStringRef x) {
+ NSLog(@"%@", x); // no-warning
+
+ NSLog(@"%d", x); // expected-warning{{format specifies type 'int' but the argument has type 'CFStringRef'}}
+ NSLog(@"%s", x); // expected-warning{{format specifies type 'char *' but the argument has type 'CFStringRef'}}
+ NSLog(@"%lf", x); // expected-warning{{format specifies type 'double' but the argument has type 'CFStringRef'}}
+}
+
diff --git a/test/FixIt/format.m b/test/FixIt/format.m
new file mode 100644
index 0000000..d412b8d
--- /dev/null
+++ b/test/FixIt/format.m
@@ -0,0 +1,70 @@
+// RUN: %clang_cc1 -fsyntax-only -fblocks -verify %s
+// RUN: %clang_cc1 -fdiagnostics-parseable-fixits -fblocks %s 2>&1 | FileCheck %s
+
+@class NSString;
+extern void NSLog(NSString *, ...);
+int printf(const char * restrict, ...) ;
+
+void test_integer_correction (int x) {
+ printf("%d", x); // no-warning
+ printf("%s", x); // expected-warning{{format specifies type 'char *' but the argument has type 'int'}}
+ printf("%lf", x); // expected-warning{{format specifies type 'double' but the argument has type 'int'}}
+ // CHECK: fix-it:"{{.*}}":{10:11-10:13}:"%d"
+ // CHECK: fix-it:"{{.*}}":{11:11-11:14}:"%d"
+
+ NSLog(@"%d", x); // no-warning
+ NSLog(@"%s", x); // expected-warning{{format specifies type 'char *' but the argument has type 'int'}}
+ NSLog(@"%lf", x); // expected-warning{{format specifies type 'double' but the argument has type 'int'}}
+ NSLog(@"%@", x); // expected-warning{{format specifies type 'id' but the argument has type 'int'}}
+ // CHECK: fix-it:"{{.*}}":{16:11-16:13}:"%d"
+ // CHECK: fix-it:"{{.*}}":{17:11-17:14}:"%d"
+ // CHECK: fix-it:"{{.*}}":{18:11-18:13}:"%d"
+}
+
+void test_string_correction (char *x) {
+ printf("%d", x); // expected-warning{{format specifies type 'int' but the argument has type 'char *'}}
+ printf("%s", x); // no-warning
+ printf("%lf", x); // expected-warning{{format specifies type 'double' but the argument has type 'char *'}}
+ // CHECK: fix-it:"{{.*}}":{25:11-25:13}:"%s"
+ // CHECK: fix-it:"{{.*}}":{27:11-27:14}:"%s"
+
+ NSLog(@"%d", x); // expected-warning{{format specifies type 'int' but the argument has type 'char *'}}
+ NSLog(@"%s", x); // no-warning
+ NSLog(@"%lf", x); // expected-warning{{format specifies type 'double' but the argument has type 'char *'}}
+ NSLog(@"%@", x); // expected-warning{{format specifies type 'id' but the argument has type 'char *'}}
+ // CHECK: fix-it:"{{.*}}":{31:11-31:13}:"%s"
+ // CHECK: fix-it:"{{.*}}":{33:11-33:14}:"%s"
+ // CHECK: fix-it:"{{.*}}":{34:11-34:13}:"%s"
+}
+
+void test_object_correction (id x) {
+ NSLog(@"%d", x); // expected-warning{{format specifies type 'int' but the argument has type 'id'}}
+ NSLog(@"%s", x); // expected-warning{{format specifies type 'char *' but the argument has type 'id'}}
+ NSLog(@"%lf", x); // expected-warning{{format specifies type 'double' but the argument has type 'id'}}
+ NSLog(@"%@", x); // no-warning
+ // CHECK: fix-it:"{{.*}}":{41:11-41:13}:"%@"
+ // CHECK: fix-it:"{{.*}}":{42:11-42:13}:"%@"
+ // CHECK: fix-it:"{{.*}}":{43:11-43:14}:"%@"
+}
+
+typedef const struct __CFString * __attribute__((NSObject)) CFStringRef;
+void test_cf_object_correction (CFStringRef x) {
+ NSLog(@"%d", x); // expected-warning{{format specifies type 'int' but the argument has type 'CFStringRef'}}
+ NSLog(@"%s", x); // expected-warning{{format specifies type 'char *' but the argument has type 'CFStringRef'}}
+ NSLog(@"%lf", x); // expected-warning{{format specifies type 'double' but the argument has type 'CFStringRef'}}
+ NSLog(@"%@", x); // no-warning
+ // CHECK: fix-it:"{{.*}}":{52:11-52:13}:"%@"
+ // CHECK: fix-it:"{{.*}}":{53:11-53:13}:"%@"
+ // CHECK: fix-it:"{{.*}}":{54:11-54:14}:"%@"
+}
+
+typedef void (^block_t)(void);
+void test_block_correction (block_t x) {
+ NSLog(@"%d", x); // expected-warning{{format specifies type 'int' but the argument has type 'block_t'}}
+ NSLog(@"%s", x); // expected-warning{{format specifies type 'char *' but the argument has type 'block_t'}}
+ NSLog(@"%lf", x); // expected-warning{{format specifies type 'double' but the argument has type 'block_t'}}
+ NSLog(@"%@", x); // no-warning
+ // CHECK: fix-it:"{{.*}}":{63:11-63:13}:"%@"
+ // CHECK: fix-it:"{{.*}}":{64:11-64:13}:"%@"
+ // CHECK: fix-it:"{{.*}}":{65:11-65:14}:"%@"
+}