diff options
author | Fariborz Jahanian <fjahanian@apple.com> | 2009-05-20 17:41:43 +0000 |
---|---|---|
committer | Fariborz Jahanian <fjahanian@apple.com> | 2009-05-20 17:41:43 +0000 |
commit | 5b160927672440076aa53c31d84149f70fd8d40e (patch) | |
tree | 7f5ceb21be6d804c486410500c8f3110ffe1ac54 | |
parent | 918441255162c1a1c77c13752aaa1a3c43ac2ab9 (diff) |
implementation of format_arg for ObjC methods/functions.
Still more to do.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@72173 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/AST/Attr.h | 16 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 1 | ||||
-rw-r--r-- | include/clang/Parse/AttributeList.h | 1 | ||||
-rw-r--r-- | lib/Frontend/PCHReaderDecl.cpp | 6 | ||||
-rw-r--r-- | lib/Frontend/PCHWriter.cpp | 6 | ||||
-rw-r--r-- | lib/Parse/AttributeList.cpp | 3 | ||||
-rw-r--r-- | lib/Sema/SemaDeclAttr.cpp | 70 | ||||
-rw-r--r-- | test/SemaObjC/format-arg-attribute.m | 28 |
8 files changed, 129 insertions, 2 deletions
diff --git a/include/clang/AST/Attr.h b/include/clang/AST/Attr.h index feed54b7ec..0427f0003a 100644 --- a/include/clang/AST/Attr.h +++ b/include/clang/AST/Attr.h @@ -50,6 +50,7 @@ public: Destructor, FastCall, Format, + FormatArg, GNUInline, IBOutletKind, // Clang-specific. Use "Kind" suffix to not conflict with NoReturn, @@ -360,6 +361,21 @@ public: static bool classof(const FormatAttr *A) { return true; } }; +class FormatArgAttr : public Attr { + int formatIdx; +public: + FormatArgAttr(int idx) : Attr(FormatArg), formatIdx(idx) {} + int getFormatIdx() const { return formatIdx; } + + virtual Attr *clone(ASTContext &C) const { + return ::new (C) FormatArgAttr(formatIdx); + } + + // Implement isa/cast/dyncast/etc. + static bool classof(const Attr *A) { return A->getKind() == FormatArg; } + static bool classof(const FormatArgAttr *A) { return true; } +}; + class SentinelAttr : public Attr { int sentinel, NullPos; public: diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 4a6437d34f..f9a462b2de 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -404,6 +404,7 @@ def err_format_strftime_third_parameter : Error< 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_result_not : Error<"function does not return %0">; def err_attribute_invalid_size : Error< "vector size not an integral multiple of component size">; def err_attribute_zero_size : Error<"zero vector size">; diff --git a/include/clang/Parse/AttributeList.h b/include/clang/Parse/AttributeList.h index 7f67213ae9..8225c9d33a 100644 --- a/include/clang/Parse/AttributeList.h +++ b/include/clang/Parse/AttributeList.h @@ -66,6 +66,7 @@ public: AT_ext_vector_type, AT_fastcall, AT_format, + AT_format_arg, AT_gnu_inline, AT_mode, AT_nodebug, diff --git a/lib/Frontend/PCHReaderDecl.cpp b/lib/Frontend/PCHReaderDecl.cpp index 865dd97db6..870392b07b 100644 --- a/lib/Frontend/PCHReaderDecl.cpp +++ b/lib/Frontend/PCHReaderDecl.cpp @@ -454,6 +454,12 @@ Attr *PCHReader::ReadAttributes() { break; } + case Attr::FormatArg: { + unsigned FormatIdx = Record[Idx++]; + New = ::new (*Context) FormatArgAttr(FormatIdx); + break; + } + case Attr::Sentinel: { int sentinel = Record[Idx++]; int nullPos = Record[Idx++]; diff --git a/lib/Frontend/PCHWriter.cpp b/lib/Frontend/PCHWriter.cpp index 883825418e..5edf03c6ff 100644 --- a/lib/Frontend/PCHWriter.cpp +++ b/lib/Frontend/PCHWriter.cpp @@ -1558,6 +1558,12 @@ void PCHWriter::WriteAttributeRecord(const Attr *Attr) { break; } + case Attr::FormatArg: { + const FormatArgAttr *Format = cast<FormatArgAttr>(Attr); + Record.push_back(Format->getFormatIdx()); + break; + } + case Attr::Sentinel : { const SentinelAttr *Sentinel = cast<SentinelAttr>(Attr); Record.push_back(Sentinel->getSentinel()); diff --git a/lib/Parse/AttributeList.cpp b/lib/Parse/AttributeList.cpp index c9d32ecc0d..0170a0671d 100644 --- a/lib/Parse/AttributeList.cpp +++ b/lib/Parse/AttributeList.cpp @@ -103,8 +103,7 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) { if (!memcmp(Str, "deprecated", 10)) return AT_deprecated; if (!memcmp(Str, "visibility", 10)) return AT_visibility; if (!memcmp(Str, "destructor", 10)) return AT_destructor; - if (!memcmp(Str, "format_arg", 10)) - return IgnoredAttribute; // FIXME: printf format string checking. + if (!memcmp(Str, "format_arg", 10)) return AT_format_arg; if (!memcmp(Str, "gnu_inline", 10)) return AT_gnu_inline; break; case 11: diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index c5099319dd..99b4d77fad 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -99,6 +99,12 @@ static QualType getFunctionOrMethodArgType(Decl *d, unsigned Idx) { return cast<ObjCMethodDecl>(d)->param_begin()[Idx]->getType(); } +static QualType getFunctionOrMethodResultType(Decl *d) { + if (const FunctionType *FnTy = getFunctionType(d)) + return cast<FunctionProtoType>(FnTy)->getResultType(); + return cast<ObjCMethodDecl>(d)->getResultType(); +} + static bool isFunctionOrMethodVariadic(Decl *d) { if (const FunctionType *FnTy = getFunctionType(d)) { const FunctionProtoType *proto = cast<FunctionProtoType>(FnTy); @@ -1069,6 +1075,69 @@ static void HandleCleanupAttr(Decl *d, const AttributeList &Attr, Sema &S) { d->addAttr(::new (S.Context) CleanupAttr(FD)); } +/// Handle __attribute__((format_arg((idx)))) attribute +/// based on http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html +static void HandleFormatArgAttr(Decl *d, const AttributeList &Attr, Sema &S) { + if (Attr.getNumArgs() != 1) { + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1; + return; + } + if (!isFunctionOrMethod(d) || !hasFunctionProto(d)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) + << Attr.getName() << 0 /*function*/; + return; + } + // FIXME: in C++ the implicit 'this' function parameter also counts. + // this is needed in order to be compatible with GCC + // the index must start with 1. + unsigned NumArgs = getFunctionOrMethodNumArgs(d); + unsigned FirstIdx = 1; + // checks for the 2nd argument + Expr *IdxExpr = static_cast<Expr *>(Attr.getArg(0)); + llvm::APSInt Idx(32); + if (!IdxExpr->isIntegerConstantExpr(Idx, S.Context)) { + S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_not_int) + << "format" << 2 << IdxExpr->getSourceRange(); + return; + } + + if (Idx.getZExtValue() < FirstIdx || Idx.getZExtValue() > NumArgs) { + S.Diag(Attr.getLoc(), diag::err_attribute_argument_out_of_bounds) + << "format" << 2 << IdxExpr->getSourceRange(); + return; + } + + unsigned ArgIdx = Idx.getZExtValue() - 1; + + // make sure the format string is really a string + QualType Ty = getFunctionOrMethodArgType(d, ArgIdx); + + bool not_nsstring_type = !isNSStringType(Ty, S.Context); + if (not_nsstring_type && + !isCFStringType(Ty, S.Context) && + (!Ty->isPointerType() || + !Ty->getAsPointerType()->getPointeeType()->isCharType())) { + // FIXME: Should highlight the actual expression that has the wrong type. + S.Diag(Attr.getLoc(), diag::err_format_attribute_not) + << (not_nsstring_type ? "a string type" : "an NSString") + << IdxExpr->getSourceRange(); + return; + } + Ty = getFunctionOrMethodResultType(d); + if (!isNSStringType(Ty, S.Context) && + !isCFStringType(Ty, S.Context) && + (!Ty->isPointerType() || + !Ty->getAsPointerType()->getPointeeType()->isCharType())) { + // FIXME: Should highlight the actual expression that has the wrong type. + S.Diag(Attr.getLoc(), diag::err_format_attribute_result_not) + << (not_nsstring_type ? "string type" : "NSString") + << IdxExpr->getSourceRange(); + return; + } + + d->addAttr(::new (S.Context) FormatArgAttr(Idx.getZExtValue())); +} + /// Handle __attribute__((format(type,idx,firstarg))) attributes /// based on http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html static void HandleFormatAttr(Decl *d, const AttributeList &Attr, Sema &S) { @@ -1653,6 +1722,7 @@ static void ProcessDeclAttribute(Decl *D, const AttributeList &Attr, Sema &S) { break; case AttributeList::AT_fastcall: HandleFastCallAttr (D, Attr, S); break; case AttributeList::AT_format: HandleFormatAttr (D, Attr, S); break; + case AttributeList::AT_format_arg: HandleFormatArgAttr (D, Attr, S); break; case AttributeList::AT_gnu_inline: HandleGNUInlineAttr(D, Attr, S); break; case AttributeList::AT_mode: HandleModeAttr (D, Attr, S); break; case AttributeList::AT_nonnull: HandleNonNullAttr (D, Attr, S); break; diff --git a/test/SemaObjC/format-arg-attribute.m b/test/SemaObjC/format-arg-attribute.m new file mode 100644 index 0000000000..60cc7cb44f --- /dev/null +++ b/test/SemaObjC/format-arg-attribute.m @@ -0,0 +1,28 @@ +// RUN: clang-cc -verify -fsyntax-only %s + +@class NSString; + +extern NSString *fa2 (const NSString *) __attribute__((format_arg(1))); +extern NSString *fa3 (NSString *) __attribute__((format_arg(1))); + +extern void fc1 (const NSString *) __attribute__((format_arg)); // expected-error {{attribute requires 1 argument(s)}} +extern void fc2 (const NSString *) __attribute__((format_arg())); // expected-error {{attribute requires 1 argument(s)}} +extern void fc3 (const NSString *) __attribute__((format_arg(1, 2))); // expected-error {{attribute requires 1 argument(s)}} + +struct s1 { int i; } __attribute__((format_arg(1))); // expected-warning {{'format_arg' attribute only applies to function types}} +union u1 { int i; } __attribute__((format_arg(1))); // expected-warning {{'format_arg' attribute only applies to function types}} +// FIXME: We don't flag this yet. +enum e1 { E1V0 } __attribute__((format_arg(1))); /* { dg-error "does not apply|only applies" "format_arg on enum" } */ + +extern NSString *ff3 (const NSString *) __attribute__((format_arg(3-2))); +extern NSString *ff4 (const NSString *) __attribute__((format_arg(foo))); // expected-error {{attribute requires 1 argument(s)}} + +/* format_arg formats must take and return a string. */ +extern NSString *fi0 (int) __attribute__((format_arg(1))); // expected-error {{format argument not a string type}} +extern NSString *fi1 (NSString *) __attribute__((format_arg(1))); + +extern NSString *fi2 (NSString *) __attribute__((format_arg(1))); + +extern int fi3 (const NSString *) __attribute__((format_arg(1))); // expected-error {{function does not return NSString}} +extern NSString *fi4 (const NSString *) __attribute__((format_arg(1))); +extern NSString *fi5 (const NSString *) __attribute__((format_arg(1))); |