1 | // RUN: %clang_cc1 -triple x86_64-apple-darwin -Wformat-nonliteral -fsyntax-only -fblocks -verify -Wno-objc-root-class %s |
2 | |
3 | //===----------------------------------------------------------------------===// |
4 | // The following code is reduced using delta-debugging from |
5 | // Foundation.h (Mac OS X). |
6 | // |
7 | // It includes the basic definitions for the test cases below. |
8 | // Not including Foundation.h directly makes this test case both svelt and |
9 | // portable to non-Mac platforms. |
10 | //===----------------------------------------------------------------------===// |
11 | |
12 | #include <stdarg.h> |
13 | |
14 | typedef signed char BOOL; |
15 | typedef unsigned int NSUInteger; |
16 | typedef long NSInteger; |
17 | @class NSString, Protocol; |
18 | extern void NSLog(NSString *format, ...); |
19 | extern void NSLogv(NSString *format, va_list args); |
20 | typedef struct _NSZone NSZone; |
21 | @class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator; |
22 | @protocol NSObject - (BOOL)isEqual:(id)object; @end |
23 | @protocol NSCopying - (id)copyWithZone:(NSZone *)zone; @end |
24 | @protocol NSMutableCopying - (id)mutableCopyWithZone:(NSZone *)zone; @end |
25 | @protocol NSCoding - (void)encodeWithCoder:(NSCoder *)aCoder; @end |
26 | @interface NSObject <NSObject> {} @end |
27 | typedef float CGFloat; |
28 | @interface NSString : NSObject <NSCopying, NSMutableCopying, NSCoding> - (NSUInteger)length; @end |
29 | @interface NSSimpleCString : NSString {} @end |
30 | @interface NSConstantString : NSSimpleCString @end |
31 | extern void *_NSConstantStringClassReference; |
32 | |
33 | typedef const struct __CFString * CFStringRef; |
34 | extern void CFStringCreateWithFormat(CFStringRef format, ...) __attribute__((format(CFString, 1, 2))); |
35 | #define CFSTR(cStr) ((CFStringRef) __builtin___CFStringMakeConstantString ("" cStr "")) |
36 | |
37 | // This function is used instead of the builtin if -fno-constant-cfstrings. |
38 | // The definition on Mac OS X is NOT annotated with format_arg as of 10.8, |
39 | // but clang will implicitly add the attribute if it's not written. |
40 | extern CFStringRef __CFStringMakeConstantString(const char *); |
41 | |
42 | int printf(const char * restrict, ...) ; |
43 | |
44 | //===----------------------------------------------------------------------===// |
45 | // Test cases. |
46 | //===----------------------------------------------------------------------===// |
47 | |
48 | void check_nslog(unsigned k) { |
49 | NSLog(@"%d%%", k); // no-warning |
50 | NSLog(@"%s%lb%d", "unix", 10, 20); // expected-warning {{invalid conversion specifier 'b'}} expected-warning {{data argument not used by format string}} |
51 | } |
52 | |
53 | // Check type validation |
54 | extern void NSLog2(int format, ...) __attribute__((format(__NSString__, 1, 2))); // expected-error {{format argument not an NSString}} |
55 | extern void CFStringCreateWithFormat2(int *format, ...) __attribute__((format(CFString, 1, 2))); // expected-error {{format argument not a CFString}} |
56 | |
57 | // <rdar://problem/7068334> - Catch use of long long with int arguments. |
58 | void rdar_7068334() { |
59 | long long test = 500; |
60 | printf("%i ",test); // expected-warning{{format specifies type 'int' but the argument has type 'long long'}} |
61 | NSLog(@"%i ",test); // expected-warning{{format specifies type 'int' but the argument has type 'long long'}} |
62 | CFStringCreateWithFormat(CFSTR("%i"),test); // expected-warning{{format specifies type 'int' but the argument has type 'long long'}} |
63 | } |
64 | |
65 | // <rdar://problem/7697748> |
66 | void rdar_7697748() { |
67 | NSLog(@"%@!"); // expected-warning{{more '%' conversions than data arguments}} |
68 | } |
69 | |
70 | @protocol Foo; |
71 | |
72 | void test_p_conversion_with_objc_pointer(id x, id<Foo> y) { |
73 | printf("%p", x); // no-warning |
74 | printf("%p", y); // no-warning |
75 | } |
76 | |
77 | // <rdar://problem/10696348>, PR 10274 - CFString and NSString formats are ignored |
78 | extern void MyNSLog(NSString *format, ...) __attribute__((format(__NSString__, 1, 2))); |
79 | extern void MyCFStringCreateWithFormat(CFStringRef format, ...) __attribute__((format(__CFString__, 1, 2))); |
80 | |
81 | void check_mylog() { |
82 | MyNSLog(@"%@"); // expected-warning {{more '%' conversions than data arguments}} |
83 | MyCFStringCreateWithFormat(CFSTR("%@")); // expected-warning {{more '%' conversions than data arguments}} |
84 | } |
85 | |
86 | // PR 10275 - format function attribute isn't checked in Objective-C methods |
87 | @interface Foo |
88 | + (id)fooWithFormat:(NSString *)fmt, ... __attribute__((format(__NSString__, 1, 2))); |
89 | + (id)fooWithCStringFormat:(const char *)format, ... __attribute__((format(__printf__, 1, 2))); |
90 | @end |
91 | |
92 | void check_method() { |
93 | [Foo fooWithFormat:@"%@"]; // expected-warning {{more '%' conversions than data arguments}} |
94 | [Foo fooWithCStringFormat:"%@"]; // expected-warning {{invalid conversion specifier '@'}} |
95 | } |
96 | |
97 | // Warn about using BOOL with %@ |
98 | void rdar10743758(id x) { |
99 | NSLog(@"%@ %@", x, (BOOL) 1); // expected-warning {{format specifies type 'id' but the argument has type 'BOOL' (aka 'signed char')}} |
100 | } |
101 | |
102 | NSString *test_literal_propagation(void) { |
103 | const char * const s1 = "constant string %s"; // expected-note {{format string is defined here}} |
104 | printf(s1); // expected-warning {{more '%' conversions than data arguments}} |
105 | const char * const s5 = "constant string %s"; // expected-note {{format string is defined here}} |
106 | const char * const s2 = s5; |
107 | printf(s2); // expected-warning {{more '%' conversions than data arguments}} |
108 | |
109 | const char * const s3 = (const char *)0; |
110 | printf(s3); // no-warning (NULL is a valid format string) |
111 | |
112 | NSString * const ns1 = @"constant string %s"; // expected-note {{format string is defined here}} |
113 | NSLog(ns1); // expected-warning {{more '%' conversions than data arguments}} |
114 | NSString * const ns5 = @"constant string %s"; // expected-note {{format string is defined here}} |
115 | NSString * const ns2 = ns5; |
116 | NSLog(ns2); // expected-warning {{more '%' conversions than data arguments}} |
117 | NSString * ns3 = ns1; |
118 | NSLog(ns3); // expected-warning {{format string is not a string literal}}} |
119 | // expected-note@-1{{treat the string as an argument to avoid this}} |
120 | |
121 | NSString * const ns6 = @"split" " string " @"%s"; // expected-note {{format string is defined here}} |
122 | NSLog(ns6); // expected-warning {{more '%' conversions than data arguments}} |
123 | } |
124 | |
125 | // Do not emit warnings when using NSLocalizedString |
126 | #include "format-strings-system.h" |
127 | |
128 | // Test it inhibits diag only for macros in system headers |
129 | #define MyNSLocalizedString(key) GetLocalizedString(key) |
130 | #define MyNSAssert(fmt, arg) NSLog(fmt, arg, 0, 0) |
131 | |
132 | void check_NSLocalizedString() { |
133 | [Foo fooWithFormat:NSLocalizedString(@"format"), @"arg"]; // no-warning |
134 | [Foo fooWithFormat:MyNSLocalizedString(@"format"), @"arg"]; // expected-warning {{format string is not a string literal}}} |
135 | } |
136 | |
137 | void check_NSAssert() { |
138 | NSAssert(@"Hello %@", @"World"); // no-warning |
139 | MyNSAssert(@"Hello %@", @"World"); // expected-warning {{data argument not used by format string}} |
140 | } |
141 | |
142 | typedef __WCHAR_TYPE__ wchar_t; |
143 | |
144 | // Test that %S, %C, %ls check for 16 bit types in ObjC strings, as described at |
145 | // http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html#//apple_ref/doc/uid/TP40004265 |
146 | |
147 | void test_percent_S() { |
148 | const unsigned short data[] = { 'a', 'b', 0 }; |
149 | const unsigned short* ptr = data; |
150 | NSLog(@"%S", ptr); // no-warning |
151 | |
152 | const wchar_t* wchar_ptr = L"ab"; |
153 | NSLog(@"%S", wchar_ptr); // expected-warning{{format specifies type 'const unichar *' (aka 'const unsigned short *') but the argument has type 'const wchar_t *'}} |
154 | } |
155 | |
156 | void test_percent_ls() { |
157 | const unsigned short data[] = { 'a', 'b', 0 }; |
158 | const unsigned short* ptr = data; |
159 | NSLog(@"%ls", ptr); // no-warning |
160 | |
161 | const wchar_t* wchar_ptr = L"ab"; |
162 | NSLog(@"%ls", wchar_ptr); // expected-warning{{format specifies type 'const unichar *' (aka 'const unsigned short *') but the argument has type 'const wchar_t *'}} |
163 | } |
164 | |
165 | void test_percent_C() { |
166 | const unsigned short data = 'a'; |
167 | NSLog(@"%C", data); // no-warning |
168 | |
169 | const wchar_t wchar_data = L'a'; |
170 | NSLog(@"%C", wchar_data); // expected-warning{{format specifies type 'unichar' (aka 'unsigned short') but the argument has type 'wchar_t'}} |
171 | } |
172 | |
173 | // Test that %@ works with toll-free bridging (<rdar://problem/10814120>). |
174 | void test_toll_free_bridging(CFStringRef x, id y) { |
175 | NSLog(@"%@", x); // no-warning |
176 | CFStringCreateWithFormat(CFSTR("%@"), x); // no-warning |
177 | |
178 | NSLog(@"%@", y); // no-warning |
179 | CFStringCreateWithFormat(CFSTR("%@"), y); // no-warning |
180 | } |
181 | |
182 | @interface Bar |
183 | + (void)log:(NSString *)fmt, ...; |
184 | + (void)log2:(NSString *)fmt, ... __attribute__((format(NSString, 1, 2))); |
185 | @end |
186 | |
187 | @implementation Bar |
188 | |
189 | + (void)log:(NSString *)fmt, ... { |
190 | va_list ap; |
191 | va_start(ap,fmt); |
192 | NSLogv(fmt, ap); // expected-warning{{format string is not a string literal}} |
193 | va_end(ap); |
194 | } |
195 | |
196 | + (void)log2:(NSString *)fmt, ... { |
197 | va_list ap; |
198 | va_start(ap,fmt); |
199 | NSLogv(fmt, ap); // no-warning |
200 | va_end(ap); |
201 | } |
202 | |
203 | @end |
204 | |
205 | |
206 | // Test that it is okay to use %p with the address of a block. |
207 | void rdar11049844_aux(); |
208 | int rdar11049844() { |
209 | typedef void (^MyBlock)(void); |
210 | MyBlock x = ^void() { rdar11049844_aux(); }; |
211 | printf("%p", x); // no-warning |
212 | } |
213 | |
214 | void test_nonBuiltinCFStrings() { |
215 | CFStringCreateWithFormat(__CFStringMakeConstantString("%@"), 1); // expected-warning{{format specifies type 'id' but the argument has type 'int'}} |
216 | } |
217 | |
218 | |
219 | // Don't crash on an invalid argument expression. |
220 | // <rdar://problem/11890818> |
221 | @interface NSDictionary : NSObject |
222 | - (id)objectForKeyedSubscript:(id)key; |
223 | @end |
224 | |
225 | void testInvalidFormatArgument(NSDictionary *dict) { |
226 | NSLog(@"no specifiers", dict[CFSTR("abc")]); // expected-error{{indexing expression is invalid because subscript type 'CFStringRef' (aka 'const struct __CFString *') is not an integral or Objective-C pointer type}} |
227 | NSLog(@"%@", dict[CFSTR("abc")]); // expected-error{{indexing expression is invalid because subscript type 'CFStringRef' (aka 'const struct __CFString *') is not an integral or Objective-C pointer type}} |
228 | NSLog(@"%@ %@", dict[CFSTR("abc")]); // expected-error{{indexing expression is invalid because subscript type 'CFStringRef' (aka 'const struct __CFString *') is not an integral or Objective-C pointer type}} |
229 | |
230 | [Foo fooWithFormat:@"no specifiers", dict[CFSTR("abc")]]; // expected-error{{indexing expression is invalid because subscript type 'CFStringRef' (aka 'const struct __CFString *') is not an integral or Objective-C pointer type}} |
231 | [Foo fooWithFormat:@"%@", dict[CFSTR("abc")]]; // expected-error{{indexing expression is invalid because subscript type 'CFStringRef' (aka 'const struct __CFString *') is not an integral or Objective-C pointer type}} |
232 | [Foo fooWithFormat:@"%@ %@", dict[CFSTR("abc")]]; // expected-error{{indexing expression is invalid because subscript type 'CFStringRef' (aka 'const struct __CFString *') is not an integral or Objective-C pointer type}} expected-warning{{more '%' conversions than data arguments}} |
233 | } |
234 | |
235 | |
236 | // <rdar://problem/11825593> |
237 | void testByValueObjectInFormat(Foo *obj) { |
238 | printf("%d %d %d", 1L, *obj, 1L); // expected-error {{cannot pass object with interface type 'Foo' by value to variadic function; expected type from format string was 'int'}} expected-warning 2 {{format specifies type 'int' but the argument has type 'long'}} |
239 | printf("%!", *obj); // expected-error {{cannot pass object with interface type 'Foo' by value through variadic function}} expected-warning {{invalid conversion specifier}} |
240 | printf(0, *obj); // expected-error {{cannot pass object with interface type 'Foo' by value through variadic function}} |
241 | |
242 | [Bar log2:@"%d", *obj]; // expected-error {{cannot pass object with interface type 'Foo' by value to variadic method; expected type from format string was 'int'}} |
243 | } |
244 | |
245 | // <rdar://problem/13557053> |
246 | void testTypeOf(NSInteger dW, NSInteger dH) { |
247 | NSLog(@"dW %d dH %d",({ __typeof__(dW) __a = (dW); __a < 0 ? -__a : __a; }),({ __typeof__(dH) __a = (dH); __a < 0 ? -__a : __a; })); // expected-warning 2 {{format specifies type 'int' but the argument has type 'long'}} |
248 | } |
249 | |
250 | void testUnicode() { |
251 | NSLog(@"%C", 0x2022); // no-warning |
252 | NSLog(@"%C", 0x202200); // expected-warning{{format specifies type 'unichar' (aka 'unsigned short') but the argument has type 'int'}} |
253 | } |
254 | |
255 | // Test Objective-C modifier flags. |
256 | void testObjCModifierFlags() { |
257 | NSLog(@"%[]@", @"Foo"); // expected-warning {{missing object format flag}} |
258 | NSLog(@"%[", @"Foo"); // expected-warning {{incomplete format specifier}} |
259 | NSLog(@"%[tt", @"Foo"); // expected-warning {{incomplete format specifier}} |
260 | NSLog(@"%[tt]@", @"Foo"); // no-warning |
261 | NSLog(@"%[tt]@ %s", @"Foo", "hello"); // no-warning |
262 | NSLog(@"%s %[tt]@", "hello", @"Foo"); // no-warning |
263 | NSLog(@"%[blark]@", @"Foo"); // expected-warning {{'blark' is not a valid object format flag}} |
264 | NSLog(@"%2$[tt]@ %1$[tt]@", @"Foo", @"Bar"); // no-warning |
265 | NSLog(@"%2$[tt]@ %1$[tt]s", @"Foo", @"Bar"); // expected-warning {{object format flags cannot be used with 's' conversion specifier}} |
266 | } |
267 | |
268 | // rdar://23622446 |
269 | @interface RD23622446_Tester: NSObject |
270 | |
271 | + (void)stringWithFormat:(const char *)format, ... __attribute__((format(__printf__, 1, 2))); |
272 | |
273 | @end |
274 | |
275 | @implementation RD23622446_Tester |
276 | |
277 | __attribute__ ((format_arg(1))) |
278 | const char *rd23622446(const char *format) { |
279 | return format; |
280 | } |
281 | |
282 | + (void)stringWithFormat:(const char *)format, ... { |
283 | return; |
284 | } |
285 | |
286 | - (const char *)test:(const char *)format __attribute__ ((format_arg(1))) { |
287 | return format; |
288 | } |
289 | |
290 | - (NSString *)str:(NSString *)format __attribute__ ((format_arg(1))) { |
291 | return format; |
292 | } |
293 | |
294 | - (void)foo { |
295 | [RD23622446_Tester stringWithFormat:rd23622446("%u"), 1, 2]; // expected-warning {{data argument not used by format string}} |
296 | [RD23622446_Tester stringWithFormat:[self test: "%u"], 1, 2]; // expected-warning {{data argument not used by format string}} |
297 | [RD23622446_Tester stringWithFormat:[self test: "%s %s"], "name"]; // expected-warning {{more '%' conversions than data arguments}} |
298 | NSLog([self str: @"%@ %@"], @"name"); // expected-warning {{more '%' conversions than data arguments}} |
299 | [RD23622446_Tester stringWithFormat:rd23622446("%d"), 1]; // ok |
300 | [RD23622446_Tester stringWithFormat:[self test: "%d %d"], 1, 2]; // ok |
301 | NSLog([self str: @"%@"], @"string"); // ok |
302 | } |
303 | |
304 | @end |
305 | |