1 This patch adds support for the FreeBSD kernel specific printf format
2 specifiers: %b, %D, %r and %y, via a new __freebsd_kprintf__ format
5 Sent upstream as http://reviews.llvm.org/D7154
7 Index: tools/clang/include/clang/Analysis/Analyses/FormatString.h
8 ===================================================================
9 --- tools/clang/include/clang/Analysis/Analyses/FormatString.h
10 +++ tools/clang/include/clang/Analysis/Analyses/FormatString.h
11 @@ -161,6 +161,12 @@ class ConversionSpecifier {
13 ObjCBeg = ObjCObjArg, ObjCEnd = ObjCObjArg,
15 + // FreeBSD kernel specific specifiers.
21 // GlibC specific specifiers.
24 @@ -204,7 +210,8 @@ class ConversionSpecifier {
25 return EndScanList ? EndScanList - Position : 1;
28 - bool isIntArg() const { return kind >= IntArgBeg && kind <= IntArgEnd; }
29 + bool isIntArg() const { return (kind >= IntArgBeg && kind <= IntArgEnd) ||
30 + kind == FreeBSDrArg || kind == FreeBSDyArg; }
31 bool isUIntArg() const { return kind >= UIntArgBeg && kind <= UIntArgEnd; }
32 bool isAnyIntArg() const { return kind >= IntArgBeg && kind <= UIntArgEnd; }
33 const char *toString() const;
34 @@ -646,7 +653,7 @@ class FormatStringHandler {
36 bool ParsePrintfString(FormatStringHandler &H,
37 const char *beg, const char *end, const LangOptions &LO,
38 - const TargetInfo &Target);
39 + const TargetInfo &Target, bool isFreeBSDKPrintf);
41 bool ParseFormatStringHasSArg(const char *beg, const char *end, const LangOptions &LO,
42 const TargetInfo &Target);
43 Index: tools/clang/include/clang/Sema/Sema.h
44 ===================================================================
45 --- tools/clang/include/clang/Sema/Sema.h
46 +++ tools/clang/include/clang/Sema/Sema.h
47 @@ -8566,6 +8566,7 @@ class Sema {
54 static FormatStringType GetFormatStringType(const FormatAttr *Format);
55 Index: tools/clang/lib/Analysis/FormatString.cpp
56 ===================================================================
57 --- tools/clang/lib/Analysis/FormatString.cpp
58 +++ tools/clang/lib/Analysis/FormatString.cpp
59 @@ -552,6 +552,12 @@ const char *ConversionSpecifier::toString() const
60 // Objective-C specific specifiers.
61 case ObjCObjArg: return "@";
63 + // FreeBSD kernel specific specifiers.
64 + case FreeBSDbArg: return "b";
65 + case FreeBSDDArg: return "D";
66 + case FreeBSDrArg: return "r";
67 + case FreeBSDyArg: return "y";
69 // GlibC specific specifiers.
70 case PrintErrno: return "m";
72 @@ -647,6 +653,9 @@ bool FormatSpecifier::hasValidLengthModifier(const
73 case ConversionSpecifier::XArg:
74 case ConversionSpecifier::nArg:
76 + case ConversionSpecifier::FreeBSDrArg:
77 + case ConversionSpecifier::FreeBSDyArg:
78 + return Target.getTriple().isOSFreeBSD();
82 @@ -677,6 +686,9 @@ bool FormatSpecifier::hasValidLengthModifier(const
83 case ConversionSpecifier::ScanListArg:
84 case ConversionSpecifier::ZArg:
86 + case ConversionSpecifier::FreeBSDrArg:
87 + case ConversionSpecifier::FreeBSDyArg:
88 + return Target.getTriple().isOSFreeBSD();
92 @@ -807,6 +819,10 @@ bool FormatSpecifier::hasStandardConversionSpecifi
93 case ConversionSpecifier::SArg:
94 return LangOpt.ObjC1 || LangOpt.ObjC2;
95 case ConversionSpecifier::InvalidSpecifier:
96 + case ConversionSpecifier::FreeBSDbArg:
97 + case ConversionSpecifier::FreeBSDDArg:
98 + case ConversionSpecifier::FreeBSDrArg:
99 + case ConversionSpecifier::FreeBSDyArg:
100 case ConversionSpecifier::PrintErrno:
101 case ConversionSpecifier::DArg:
102 case ConversionSpecifier::OArg:
103 Index: tools/clang/lib/Analysis/PrintfFormatString.cpp
104 ===================================================================
105 --- tools/clang/lib/Analysis/PrintfFormatString.cpp
106 +++ tools/clang/lib/Analysis/PrintfFormatString.cpp
107 @@ -55,7 +55,8 @@ static PrintfSpecifierResult ParsePrintfSpecifier(
109 const LangOptions &LO,
110 const TargetInfo &Target,
113 + bool isFreeBSDKPrintf) {
115 using namespace clang::analyze_format_string;
116 using namespace clang::analyze_printf;
117 @@ -206,9 +207,24 @@ static PrintfSpecifierResult ParsePrintfSpecifier(
118 case '@': k = ConversionSpecifier::ObjCObjArg; break;
120 case 'm': k = ConversionSpecifier::PrintErrno; break;
121 + // FreeBSD kernel specific.
123 + if (isFreeBSDKPrintf)
124 + k = ConversionSpecifier::FreeBSDbArg; // int followed by char *
127 + if (isFreeBSDKPrintf)
128 + k = ConversionSpecifier::FreeBSDrArg; // int
131 + if (isFreeBSDKPrintf)
132 + k = ConversionSpecifier::FreeBSDyArg; // int
136 - if (Target.getTriple().isOSDarwin())
137 + if (isFreeBSDKPrintf)
138 + k = ConversionSpecifier::FreeBSDDArg; // void * followed by char *
139 + else if (Target.getTriple().isOSDarwin())
140 k = ConversionSpecifier::DArg;
143 @@ -228,6 +244,10 @@ static PrintfSpecifierResult ParsePrintfSpecifier(
144 FS.setConversionSpecifier(CS);
145 if (CS.consumesDataArgument() && !FS.usesPositionalArg())
146 FS.setArgIndex(argIndex++);
147 + // FreeBSD kernel specific.
148 + if (k == ConversionSpecifier::FreeBSDbArg ||
149 + k == ConversionSpecifier::FreeBSDDArg)
152 if (k == ConversionSpecifier::InvalidSpecifier) {
153 // Assume the conversion takes one argument.
154 @@ -240,7 +260,8 @@ bool clang::analyze_format_string::ParsePrintfStri
157 const LangOptions &LO,
158 - const TargetInfo &Target) {
159 + const TargetInfo &Target,
160 + bool isFreeBSDKPrintf) {
162 unsigned argIndex = 0;
164 @@ -247,7 +268,8 @@ bool clang::analyze_format_string::ParsePrintfStri
165 // Keep looking for a format specifier until we have exhausted the string.
167 const PrintfSpecifierResult &FSR = ParsePrintfSpecifier(H, I, E, argIndex,
171 // Did a fail-stop error of any kind occur when parsing the specifier?
172 // If so, don't do any more processing.
173 if (FSR.shouldStop())
174 @@ -276,7 +298,8 @@ bool clang::analyze_format_string::ParseFormatStri
175 FormatStringHandler H;
177 const PrintfSpecifierResult &FSR = ParsePrintfSpecifier(H, I, E, argIndex,
178 - LO, Target, false);
181 // Did a fail-stop error of any kind occur when parsing the specifier?
182 // If so, don't do any more processing.
183 if (FSR.shouldStop())
184 @@ -674,6 +697,8 @@ bool PrintfSpecifier::hasValidPlusPrefix() const {
185 case ConversionSpecifier::GArg:
186 case ConversionSpecifier::aArg:
187 case ConversionSpecifier::AArg:
188 + case ConversionSpecifier::FreeBSDrArg:
189 + case ConversionSpecifier::FreeBSDyArg:
193 @@ -699,6 +724,8 @@ bool PrintfSpecifier::hasValidAlternativeForm() co
194 case ConversionSpecifier::FArg:
195 case ConversionSpecifier::gArg:
196 case ConversionSpecifier::GArg:
197 + case ConversionSpecifier::FreeBSDrArg:
198 + case ConversionSpecifier::FreeBSDyArg:
202 @@ -729,6 +756,8 @@ bool PrintfSpecifier::hasValidLeadingZeros() const
203 case ConversionSpecifier::FArg:
204 case ConversionSpecifier::gArg:
205 case ConversionSpecifier::GArg:
206 + case ConversionSpecifier::FreeBSDrArg:
207 + case ConversionSpecifier::FreeBSDyArg:
211 @@ -753,6 +782,8 @@ bool PrintfSpecifier::hasValidSpacePrefix() const
212 case ConversionSpecifier::GArg:
213 case ConversionSpecifier::aArg:
214 case ConversionSpecifier::AArg:
215 + case ConversionSpecifier::FreeBSDrArg:
216 + case ConversionSpecifier::FreeBSDyArg:
220 @@ -818,6 +849,8 @@ bool PrintfSpecifier::hasValidPrecision() const {
221 case ConversionSpecifier::gArg:
222 case ConversionSpecifier::GArg:
223 case ConversionSpecifier::sArg:
224 + case ConversionSpecifier::FreeBSDrArg:
225 + case ConversionSpecifier::FreeBSDyArg:
229 Index: tools/clang/lib/Sema/SemaChecking.cpp
230 ===================================================================
231 --- tools/clang/lib/Sema/SemaChecking.cpp
232 +++ tools/clang/lib/Sema/SemaChecking.cpp
233 @@ -2584,6 +2584,7 @@ Sema::FormatStringType Sema::GetFormatStringType(c
234 .Case("strftime", FST_Strftime)
235 .Case("strfmon", FST_Strfmon)
236 .Cases("kprintf", "cmn_err", "vcmn_err", "zcmn_err", FST_Kprintf)
237 + .Case("freebsd_kprintf", FST_FreeBSDKPrintf)
238 .Default(FST_Unknown);
241 @@ -3365,6 +3366,43 @@ CheckPrintfHandler::HandlePrintfSpecifier(const an
242 CoveredArgs.set(argIndex);
245 + // FreeBSD kernel extensions.
246 + if (CS.getKind() == ConversionSpecifier::FreeBSDbArg ||
247 + CS.getKind() == ConversionSpecifier::FreeBSDDArg) {
248 + // We need at least two arguments.
249 + if (!CheckNumArgs(FS, CS, startSpecifier, specifierLen, argIndex + 1))
252 + // Claim the second argument.
253 + CoveredArgs.set(argIndex + 1);
255 + // Type check the first argument (int for %b, pointer for %D)
256 + const Expr *Ex = getDataArg(argIndex);
257 + const analyze_printf::ArgType &AT =
258 + (CS.getKind() == ConversionSpecifier::FreeBSDbArg) ?
259 + ArgType(S.Context.IntTy) : ArgType::CPointerTy;
260 + if (AT.isValid() && !AT.matchesType(S.Context, Ex->getType()))
261 + EmitFormatDiagnostic(
262 + S.PDiag(diag::warn_format_conversion_argument_type_mismatch)
263 + << AT.getRepresentativeTypeName(S.Context) << Ex->getType()
264 + << false << Ex->getSourceRange(),
265 + Ex->getLocStart(), /*IsStringLocation*/false,
266 + getSpecifierRange(startSpecifier, specifierLen));
268 + // Type check the second argument (char * for both %b and %D)
269 + Ex = getDataArg(argIndex + 1);
270 + const analyze_printf::ArgType &AT2 = ArgType::CStrTy;
271 + if (AT2.isValid() && !AT2.matchesType(S.Context, Ex->getType()))
272 + EmitFormatDiagnostic(
273 + S.PDiag(diag::warn_format_conversion_argument_type_mismatch)
274 + << AT2.getRepresentativeTypeName(S.Context) << Ex->getType()
275 + << false << Ex->getSourceRange(),
276 + Ex->getLocStart(), /*IsStringLocation*/false,
277 + getSpecifierRange(startSpecifier, specifierLen));
282 // Check for using an Objective-C specific conversion specifier
283 // in a non-ObjC literal.
284 if (!ObjCContext && CS.isObjCArg()) {
285 @@ -3988,7 +4026,8 @@ void Sema::CheckFormatString(const StringLiteral *
289 - if (Type == FST_Printf || Type == FST_NSString) {
290 + if (Type == FST_Printf || Type == FST_NSString ||
291 + Type == FST_FreeBSDKPrintf) {
292 CheckPrintfHandler H(*this, FExpr, OrigFormatExpr, firstDataArg,
293 numDataArgs, (Type == FST_NSString),
294 Str, HasVAListArg, Args, format_idx,
295 @@ -3996,7 +4035,8 @@ void Sema::CheckFormatString(const StringLiteral *
297 if (!analyze_format_string::ParsePrintfString(H, Str, Str + StrLen,
299 - Context.getTargetInfo()))
300 + Context.getTargetInfo(),
301 + Type == FST_FreeBSDKPrintf))
303 } else if (Type == FST_Scanf) {
304 CheckScanfHandler H(*this, FExpr, OrigFormatExpr, firstDataArg, numDataArgs,
305 Index: tools/clang/lib/Sema/SemaDeclAttr.cpp
306 ===================================================================
307 --- tools/clang/lib/Sema/SemaDeclAttr.cpp
308 +++ tools/clang/lib/Sema/SemaDeclAttr.cpp
309 @@ -2481,6 +2481,7 @@ static FormatAttrKind getFormatAttrKind(StringRef
310 .Cases("scanf", "printf", "printf0", "strfmon", SupportedFormat)
311 .Cases("cmn_err", "vcmn_err", "zcmn_err", SupportedFormat)
312 .Case("kprintf", SupportedFormat) // OpenBSD.
313 + .Case("freebsd_kprintf", SupportedFormat) // FreeBSD.
315 .Cases("gcc_diag", "gcc_cdiag", "gcc_cxxdiag", "gcc_tdiag", IgnoredFormat)
316 .Default(InvalidFormat);
317 Index: tools/clang/test/Sema/attr-format.c
318 ===================================================================
319 --- tools/clang/test/Sema/attr-format.c
320 +++ tools/clang/test/Sema/attr-format.c
321 @@ -57,8 +57,15 @@ void callnull(void){
322 null(0, (int*)0); // expected-warning {{incompatible pointer types}}
325 +// FreeBSD kernel extensions
326 +void a3(const char *a, ...) __attribute__((format(freebsd_kprintf, 1,2))); // no-error
327 +void b3(const char *a, ...) __attribute__((format(freebsd_kprintf, 1,1))); // expected-error {{'format' attribute parameter 3 is out of bounds}}
328 +void c3(const char *a, ...) __attribute__((format(freebsd_kprintf, 0,2))); // expected-error {{'format' attribute parameter 2 is out of bounds}}
329 +void d3(const char *a, int c) __attribute__((format(freebsd_kprintf, 1,2))); // expected-error {{format attribute requires variadic function}}
330 +void e3(char *str, int c, ...) __attribute__((format(freebsd_kprintf, 2,3))); // expected-error {{format argument not a string type}}
335 int xx_vprintf(const char *, va_list);
337 Index: tools/clang/test/Sema/format-strings-freebsd.c
338 ===================================================================
339 --- tools/clang/test/Sema/format-strings-freebsd.c
340 +++ tools/clang/test/Sema/format-strings-freebsd.c
342 +// RUN: %clang_cc1 -fsyntax-only -verify -triple i386-unknown-freebsd %s
343 +// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-unknown-freebsd %s
345 +// Test FreeBSD kernel printf extensions.
346 +int freebsd_kernel_printf(const char *, ...) __attribute__((__format__(__freebsd_kprintf__, 1, 2)));
348 +void check_freebsd_kernel_extensions(int i, long l, char *s)
350 + // %b expects an int and a char *
351 + freebsd_kernel_printf("reg=%b\n", i, "\10\2BITTWO\1BITONE\n"); // no-warning
352 + freebsd_kernel_printf("reg=%b\n", l, "\10\2BITTWO\1BITONE\n"); // expected-warning{{format specifies type 'int' but the argument has type 'long'}}
353 + freebsd_kernel_printf("reg=%b\n", i, l); // expected-warning{{format specifies type 'char *' but the argument has type 'long'}}
354 + freebsd_kernel_printf("reg=%b\n", i); // expected-warning{{more '%' conversions than data arguments}}
355 + freebsd_kernel_printf("reg=%b\n", i, "\10\2BITTWO\1BITONE\n", l); // expected-warning{{data argument not used by format string}}
357 + // %D expects an unsigned char * and a char *
358 + freebsd_kernel_printf("%6D", s, ":"); // no-warning
359 + freebsd_kernel_printf("%6D", i, ":"); // expected-warning{{format specifies type 'void *' but the argument has type 'int'}}
360 + freebsd_kernel_printf("%6D", s, i); // expected-warning{{format specifies type 'char *' but the argument has type 'int'}}
361 + freebsd_kernel_printf("%6D", s); // expected-warning{{more '%' conversions than data arguments}}
362 + freebsd_kernel_printf("%6D", s, ":", i); // expected-warning{{data argument not used by format string}}
364 + freebsd_kernel_printf("%*D", 42, s, ":"); // no-warning
365 + freebsd_kernel_printf("%*D", 42, i, ":"); // expected-warning{{format specifies type 'void *' but the argument has type 'int'}}
366 + freebsd_kernel_printf("%*D", 42, s, i); // expected-warning{{format specifies type 'char *' but the argument has type 'int'}}
367 + freebsd_kernel_printf("%*D", 42, s); // expected-warning{{more '%' conversions than data arguments}}
368 + freebsd_kernel_printf("%*D", 42, s, ":", i); // expected-warning{{data argument not used by format string}}
370 + // %r expects an int
371 + freebsd_kernel_printf("%r", i); // no-warning
372 + freebsd_kernel_printf("%r", l); // expected-warning{{format specifies type 'int' but the argument has type 'long'}}
373 + freebsd_kernel_printf("%lr", i); // expected-warning{{format specifies type 'long' but the argument has type 'int'}}
374 + freebsd_kernel_printf("%lr", l); // no-warning
376 + // %y expects an int
377 + freebsd_kernel_printf("%y", i); // no-warning
378 + freebsd_kernel_printf("%y", l); // expected-warning{{format specifies type 'int' but the argument has type 'long'}}
379 + freebsd_kernel_printf("%ly", i); // expected-warning{{format specifies type 'long' but the argument has type 'int'}}
380 + freebsd_kernel_printf("%ly", l); // no-warning