From 91ed7e19418181ae385c2626cedd3b08b6ba43a6 Mon Sep 17 00:00:00 2001 From: =?utf8?q?F=C3=A9lix=20Cloutier?= Date: Thu, 12 May 2022 11:09:06 -0700 Subject: [PATCH] [clang] Allow all string types for all attribute(format) styles This allows using any recognized kind of string for any __attribute__((format)) archetype. Before this change, for instance, the printf archetype would only accept char pointer types and the NSString archetype would only accept NSString pointers. This is more restrictive than necessary as there exist functions to convert between string types that can be annotated with __attribute__((format_arg)) to transfer format information. Reviewed By: ahatanak Differential Revision: https://reviews.llvm.org/D125254 rdar://89060618 --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +- clang/lib/Sema/SemaDeclAttr.cpp | 28 +++++------------------- clang/test/SemaObjC/format-strings-objc.m | 19 ++++++++++++++-- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 96feef6..d8f8774 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3108,7 +3108,7 @@ def err_format_strftime_third_parameter : Error< "strftime format attribute requires 3rd parameter to be 0">; def err_format_attribute_requires_variadic : Error< "format attribute requires variadic function">; -def err_format_attribute_not : Error<"format argument not %0">; +def err_format_attribute_not : Error<"format argument not a string type">; def err_format_attribute_result_not : Error<"function does not return %0">; def err_format_attribute_implicit_this_format_string : Error< "format attribute cannot specify the implicit this argument as the format " diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index c4a3b18..1b05554 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -3661,8 +3661,7 @@ static void handleFormatArgAttr(Sema &S, Decl *D, const ParsedAttr &AL) { (!Ty->isPointerType() || !Ty->castAs()->getPointeeType()->isCharType())) { S.Diag(AL.getLoc(), diag::err_format_attribute_not) - << "a string type" << IdxExpr->getSourceRange() - << getFunctionOrMethodParamRange(D, 0); + << IdxExpr->getSourceRange() << getFunctionOrMethodParamRange(D, 0); return; } Ty = getFunctionOrMethodResultType(D); @@ -3862,27 +3861,12 @@ static void handleFormatAttr(Sema &S, Decl *D, const ParsedAttr &AL) { // make sure the format string is really a string QualType Ty = getFunctionOrMethodParamType(D, ArgIdx); - if (Kind == CFStringFormat) { - if (!isCFStringType(Ty, S.Context)) { - S.Diag(AL.getLoc(), diag::err_format_attribute_not) - << "a CFString" << IdxExpr->getSourceRange() - << getFunctionOrMethodParamRange(D, ArgIdx); - return; - } - } else if (Kind == NSStringFormat) { - // FIXME: do we need to check if the type is NSString*? What are the - // semantics? - if (!isNSStringType(Ty, S.Context, /*AllowNSAttributedString=*/true)) { - S.Diag(AL.getLoc(), diag::err_format_attribute_not) - << "an NSString" << IdxExpr->getSourceRange() - << getFunctionOrMethodParamRange(D, ArgIdx); - return; - } - } else if (!Ty->isPointerType() || - !Ty->castAs()->getPointeeType()->isCharType()) { + if (!isNSStringType(Ty, S.Context, true) && + !isCFStringType(Ty, S.Context) && + (!Ty->isPointerType() || + !Ty->castAs()->getPointeeType()->isCharType())) { S.Diag(AL.getLoc(), diag::err_format_attribute_not) - << "a string type" << IdxExpr->getSourceRange() - << getFunctionOrMethodParamRange(D, ArgIdx); + << IdxExpr->getSourceRange() << getFunctionOrMethodParamRange(D, ArgIdx); return; } diff --git a/clang/test/SemaObjC/format-strings-objc.m b/clang/test/SemaObjC/format-strings-objc.m index 6850ebf..71f22c0 100644 --- a/clang/test/SemaObjC/format-strings-objc.m +++ b/clang/test/SemaObjC/format-strings-objc.m @@ -60,8 +60,23 @@ void check_nslog(unsigned k) { } // 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}} +extern void NSLog2(int format, ...) __attribute__((format(__NSString__, 1, 2))); // expected-error {{format argument not a string type}} +extern void CFStringCreateWithFormat2(int *format, ...) __attribute__((format(CFString, 1, 2))); // expected-error {{format argument not a string type}} + +// Check interoperability of strings +extern void NSLog3(const char *, ...) __attribute__((format(__NSString__, 1, 2))); +extern void CFStringCreateWithFormat3(CFStringRef, ...) __attribute__((format(__NSString__, 1, 2))); +extern void printf2(NSString *format, ...) __attribute__((format(printf, 1, 2))); + +extern NSString *CStringToNSString(const char *) __attribute__((format_arg(1))); + +void NSLog3(const char *fmt, ...) { + NSString *const nsFmt = CStringToNSString(fmt); + va_list ap; + va_start(ap, fmt); + NSLogv(nsFmt, ap); + va_end(ap); +} // - Catch use of long long with int arguments. void rdar_7068334(void) { -- 2.7.4