]> granicus.if.org Git - clang/commitdiff
implementation of format_arg for ObjC methods/functions.
authorFariborz Jahanian <fjahanian@apple.com>
Wed, 20 May 2009 17:41:43 +0000 (17:41 +0000)
committerFariborz Jahanian <fjahanian@apple.com>
Wed, 20 May 2009 17:41:43 +0000 (17:41 +0000)
Still more to do.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@72173 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/Attr.h
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Parse/AttributeList.h
lib/Frontend/PCHReaderDecl.cpp
lib/Frontend/PCHWriter.cpp
lib/Parse/AttributeList.cpp
lib/Sema/SemaDeclAttr.cpp
test/SemaObjC/format-arg-attribute.m [new file with mode: 0644]

index feed54b7ecd5f296394da6c40724b16a8cba1dea..0427f0003a3a1608420914366ab14f1b89fe25e4 100644 (file)
@@ -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:
index 4a6437d34fdbf30f0d6bda359d754820dc41c189..f9a462b2deea6203d6f226c2613d2f7bfc2d6cc0 100644 (file)
@@ -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">;
index 7f67213ae972b4c978abda5d2f4a1f447237d439..8225c9d33abb6790788b654ad6a31cc1d84d41de 100644 (file)
@@ -66,6 +66,7 @@ public:
     AT_ext_vector_type,
     AT_fastcall,
     AT_format,
+    AT_format_arg,
     AT_gnu_inline,
     AT_mode,
     AT_nodebug,
index 865dd97db6054cade61d56cc786f727c2009687a..870392b07b28e5d59e82212298ad43bcda389db9 100644 (file)
@@ -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++];
index 883825418e79eb9faab15c89c1e2d934ac7c9a9d..5edf03c6ff1587e2d653adb47acf79701902cf65 100644 (file)
@@ -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());
index c9d32ecc0dbe07b9cd4ad8dc8de967afbb9f093d..0170a0671db7f83216c15f9b4d7b469895ee0f34 100644 (file)
@@ -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:
index c5099319ddb03b6a3cf7e4c586c3823011ea3aa5..99b4d77fad42f4a2860c68f688618dd805106e92 100644 (file)
@@ -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 (file)
index 0000000..60cc7cb
--- /dev/null
@@ -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)));