aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFariborz Jahanian <fjahanian@apple.com>2009-05-20 17:41:43 +0000
committerFariborz Jahanian <fjahanian@apple.com>2009-05-20 17:41:43 +0000
commit5b160927672440076aa53c31d84149f70fd8d40e (patch)
tree7f5ceb21be6d804c486410500c8f3110ffe1ac54
parent918441255162c1a1c77c13752aaa1a3c43ac2ab9 (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.h16
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.td1
-rw-r--r--include/clang/Parse/AttributeList.h1
-rw-r--r--lib/Frontend/PCHReaderDecl.cpp6
-rw-r--r--lib/Frontend/PCHWriter.cpp6
-rw-r--r--lib/Parse/AttributeList.cpp3
-rw-r--r--lib/Sema/SemaDeclAttr.cpp70
-rw-r--r--test/SemaObjC/format-arg-attribute.m28
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)));