[cfe-commits] r72173 - in /cfe/trunk: 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

Fariborz Jahanian fjahanian at apple.com
Wed May 20 10:41:44 PDT 2009


Author: fjahanian
Date: Wed May 20 12:41:43 2009
New Revision: 72173

URL: http://llvm.org/viewvc/llvm-project?rev=72173&view=rev
Log:
implementation of format_arg for ObjC methods/functions.
Still more to do.

Added:
    cfe/trunk/test/SemaObjC/format-arg-attribute.m
Modified:
    cfe/trunk/include/clang/AST/Attr.h
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Parse/AttributeList.h
    cfe/trunk/lib/Frontend/PCHReaderDecl.cpp
    cfe/trunk/lib/Frontend/PCHWriter.cpp
    cfe/trunk/lib/Parse/AttributeList.cpp
    cfe/trunk/lib/Sema/SemaDeclAttr.cpp

Modified: cfe/trunk/include/clang/AST/Attr.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Attr.h?rev=72173&r1=72172&r2=72173&view=diff

==============================================================================
--- cfe/trunk/include/clang/AST/Attr.h (original)
+++ cfe/trunk/include/clang/AST/Attr.h Wed May 20 12:41:43 2009
@@ -50,6 +50,7 @@
     Destructor,
     FastCall,    
     Format,
+    FormatArg,
     GNUInline,
     IBOutletKind, // Clang-specific.  Use "Kind" suffix to not conflict with
     NoReturn,
@@ -360,6 +361,21 @@
   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:

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=72173&r1=72172&r2=72173&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Wed May 20 12:41:43 2009
@@ -404,6 +404,7 @@
 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">;

Modified: cfe/trunk/include/clang/Parse/AttributeList.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/AttributeList.h?rev=72173&r1=72172&r2=72173&view=diff

==============================================================================
--- cfe/trunk/include/clang/Parse/AttributeList.h (original)
+++ cfe/trunk/include/clang/Parse/AttributeList.h Wed May 20 12:41:43 2009
@@ -66,6 +66,7 @@
     AT_ext_vector_type,
     AT_fastcall,
     AT_format,
+    AT_format_arg,
     AT_gnu_inline,
     AT_mode,
     AT_nodebug,

Modified: cfe/trunk/lib/Frontend/PCHReaderDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/PCHReaderDecl.cpp?rev=72173&r1=72172&r2=72173&view=diff

==============================================================================
--- cfe/trunk/lib/Frontend/PCHReaderDecl.cpp (original)
+++ cfe/trunk/lib/Frontend/PCHReaderDecl.cpp Wed May 20 12:41:43 2009
@@ -454,6 +454,12 @@
       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++];

Modified: cfe/trunk/lib/Frontend/PCHWriter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/PCHWriter.cpp?rev=72173&r1=72172&r2=72173&view=diff

==============================================================================
--- cfe/trunk/lib/Frontend/PCHWriter.cpp (original)
+++ cfe/trunk/lib/Frontend/PCHWriter.cpp Wed May 20 12:41:43 2009
@@ -1558,6 +1558,12 @@
       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());

Modified: cfe/trunk/lib/Parse/AttributeList.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/AttributeList.cpp?rev=72173&r1=72172&r2=72173&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/AttributeList.cpp (original)
+++ cfe/trunk/lib/Parse/AttributeList.cpp Wed May 20 12:41:43 2009
@@ -103,8 +103,7 @@
     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:

Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=72173&r1=72172&r2=72173&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Wed May 20 12:41:43 2009
@@ -99,6 +99,12 @@
   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 @@
   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 @@
     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;

Added: cfe/trunk/test/SemaObjC/format-arg-attribute.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/format-arg-attribute.m?rev=72173&view=auto

==============================================================================
--- cfe/trunk/test/SemaObjC/format-arg-attribute.m (added)
+++ cfe/trunk/test/SemaObjC/format-arg-attribute.m Wed May 20 12:41:43 2009
@@ -0,0 +1,28 @@
+// RUN: clang-cc -verify -fsyntax-only %s
+
+ at 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))); 





More information about the cfe-commits mailing list