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