]> granicus.if.org Git - clang/commitdiff
Add explicit attributes to mark functions as having had their
authorJohn McCall <rjmccall@apple.com>
Fri, 30 Sep 2011 05:12:12 +0000 (05:12 +0000)
committerJohn McCall <rjmccall@apple.com>
Fri, 30 Sep 2011 05:12:12 +0000 (05:12 +0000)
CoreFoundation object-transfer properties audited, and add a #pragma
to cause them to be automatically applied to functions in a particular
span of code.  This has to be implemented largely in the preprocessor
because of the requirement that the region be entirely contained in
a single file;  that's hard to impose from the parser without registering
for a ton of callbacks.

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

15 files changed:
include/clang/Basic/Attr.td
include/clang/Basic/DiagnosticCommonKinds.td
include/clang/Basic/DiagnosticLexKinds.td
include/clang/Lex/Preprocessor.h
include/clang/Sema/AttributeList.h
include/clang/Sema/Sema.h
lib/Lex/PPDirectives.cpp
lib/Lex/PPLexerChange.cpp
lib/Lex/Pragma.cpp
lib/Sema/AttributeList.cpp
lib/Sema/SemaAttr.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclAttr.cpp
test/Sema/Inputs/pragma-arc-cf-code-audited.h [new file with mode: 0644]
test/Sema/pragma-arc-cf-code-audited.c [new file with mode: 0644]

index 32ebbf9443cfae51f109a3a992798831b49ff019..05540340fd0d87cd233532ddf309f4445d70b6a3 100644 (file)
@@ -171,6 +171,23 @@ def CDecl : InheritableAttr {
   let Spellings = ["cdecl", "__cdecl"];
 }
 
+// cf_audited_transfer indicates that the given function has been
+// audited and has been marked with the appropriate cf_consumed and
+// cf_returns_retained attributes.  It is generally applied by
+// '#pragma clang arc_cf_code_audited' rather than explicitly.
+def CFAuditedTransfer : InheritableAttr {
+  let Spellings = ["cf_audited_transfer"];
+  let Subjects = [Function];
+}
+
+// cf_unknown_transfer is an explicit opt-out of cf_audited_transfer.
+// It indicates that the function has unknown or unautomatable
+// transfer semantics.
+def CFUnknownTransfer : InheritableAttr {
+  let Spellings = ["cf_unknown_transfer"];
+  let Subjects = [Function];
+}
+
 def CFReturnsRetained : InheritableAttr {
   let Spellings = ["cf_returns_retained"];
   let Subjects = [ObjCMethod, Function];
index 1313174b1678b0049e033490fc66b680ddb768b1..603e1b9cb76c41307a621dd3be8c544b1991ecd7 100644 (file)
@@ -65,6 +65,7 @@ def err_module_cycle : Error<"cyclic dependency in module '%0': %1">,
   DefaultFatal;
 def warn_module_build : Warning<"building module '%0' from source">, 
   InGroup<ModuleBuild>, DefaultIgnore;
+def note_pragma_entered_here : Note<"#pragma entered here">;  
 
 // Sema && Lex
 def ext_longlong : Extension<
index 481adc43a424fd57bf7045cf85b200e1778050ff..031a54134e21bfbf542b7fc702b31aa3b82e8d0e 100644 (file)
@@ -326,4 +326,14 @@ def ext_pp_line_too_big : Extension<
 
 def err_pp_export_non_macro : Error<"no macro named %0 to export">;
 
+def err_pp_arc_cf_code_audited_syntax : Error<"expected 'begin' or 'end'">;
+def err_pp_double_begin_of_arc_cf_code_audited : Error<
+  "already inside '#pragma clang arc_cf_code_audited'">;
+def err_pp_unmatched_end_of_arc_cf_code_audited : Error<
+  "not currently inside '#pragma clang arc_cf_code_audited'">;
+def err_pp_include_in_arc_cf_code_audited : Error<
+  "cannot #include files inside '#pragma clang arc_cf_code_audited'">;
+def err_pp_eof_in_arc_cf_code_audited : Error<
+  "'#pragma clang arc_cf_code_audited' was not ended within this file">;
+
 }
index 6ed6aa5c052a4ddc1c877166c9b1acb287f1bb46..328a18a59dd6c59c70a88c358372799bcf6f1dd4 100644 (file)
@@ -165,6 +165,10 @@ class Preprocessor : public llvm::RefCountedBase<Preprocessor> {
   /// \brief The source location of the __import_module__ keyword we just
   /// lexed, if any.
   SourceLocation ModuleImportLoc;
+
+  /// \brief The source location of the currently-active
+  /// #pragma clang arc_cf_code_audited begin.
+  SourceLocation PragmaARCCFCodeAuditedLoc;
   
   /// \brief True if we hit the code-completion point.
   bool CodeCompletionReached;
@@ -720,6 +724,19 @@ public:
     getDiagnostics().setSuppressAllDiagnostics(true);
   }
 
+  /// \brief The location of the currently-active #pragma clang
+  /// arc_cf_code_audited begin.  Returns an invalid location if there
+  /// is no such pragma active.
+  SourceLocation getPragmaARCCFCodeAuditedLoc() const {
+    return PragmaARCCFCodeAuditedLoc;
+  }
+
+  /// \brief Set the location of the currently-active #pragma clang
+  /// arc_cf_code_audited begin.  An invalid location ends the pragma.
+  void setPragmaARCCFCodeAuditedLoc(SourceLocation Loc) {
+    PragmaARCCFCodeAuditedLoc = Loc;
+  }
+
   /// \brief Instruct the preprocessor to skip part of the main
   /// the main source file.
   ///
index b0012e7c933f7a9e34d3ed8c67754df486591448..c02d89dd11ff249dfeeee0ff9c5e8be012bc5c91 100644 (file)
@@ -166,10 +166,12 @@ public:
     AT_blocks,
     AT_carries_dependency,
     AT_cdecl,
+    AT_cf_audited_transfer,     // Clang-specific.
     AT_cf_consumed,             // Clang-specific.
     AT_cf_returns_autoreleased, // Clang-specific.
     AT_cf_returns_not_retained, // Clang-specific.
     AT_cf_returns_retained,     // Clang-specific.
+    AT_cf_unknown_transfer,     // Clang-specific.
     AT_cleanup,
     AT_common,
     AT_const,
index f812556e0fe9787ef1f4f38b939d46fee8469c16..b272250d9126949475912c557c8b77c6ab291e68 100644 (file)
@@ -5403,6 +5403,11 @@ public:
   /// FreeVisContext - Deallocate and null out VisContext.
   void FreeVisContext();
 
+  /// AddCFAuditedAttribute - Check whether we're currently within
+  /// '#pragma clang arc_cf_code_audited' and, if so, consider adding
+  /// the appropriate attribute.
+  void AddCFAuditedAttribute(Decl *D);
+
   /// AddAlignedAttr - Adds an aligned attribute to a particular declaration.
   void AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E);
   void AddAlignedAttr(SourceRange AttrRange, Decl *D, TypeSourceInfo *T);
index fc147e420e103093d268a6b7ad85a0a576790205..95f7ca9644bcbe37557ec21fe38c519c735e36fe 100644 (file)
@@ -1217,6 +1217,15 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc,
     return;
   }
 
+  // Complain about attempts to #include files in an audit pragma.
+  if (PragmaARCCFCodeAuditedLoc.isValid()) {
+    Diag(HashLoc, diag::err_pp_include_in_arc_cf_code_audited);
+    Diag(PragmaARCCFCodeAuditedLoc, diag::note_pragma_entered_here);
+
+    // Immediately leave the pragma.
+    PragmaARCCFCodeAuditedLoc = SourceLocation();
+  }
+
   // Search include directories.
   const DirectoryLookup *CurDir;
   llvm::SmallString<1024> SearchPath;
index 921218208c9f7881e1aa59c23e5771fd61e97598..8ba2df9b03691c1a4a085f7fbf961b2212026a17 100644 (file)
@@ -216,6 +216,14 @@ bool Preprocessor::HandleEndOfFile(Token &Result, bool isEndOfMacro) {
     }
   }
 
+  // Complain about reaching an EOF within arc_cf_code_audited.
+  if (PragmaARCCFCodeAuditedLoc.isValid()) {
+    Diag(PragmaARCCFCodeAuditedLoc, diag::err_pp_eof_in_arc_cf_code_audited);
+
+    // Recover by leaving immediately.
+    PragmaARCCFCodeAuditedLoc = SourceLocation();
+  }
+
   // If this is a #include'd file, pop it off the include stack and continue
   // lexing the #includer file.
   if (!IncludeMacroStack.empty()) {
index d855baf9e2907b31ae46670603601463d226954f..f6532c2175a17758f5178745b2d80b2ecd5b8cd6 100644 (file)
@@ -1005,6 +1005,60 @@ struct PragmaSTDC_UnknownHandler : public PragmaHandler {
   }
 };
 
+/// PragmaARCCFCodeAuditedHandler - 
+///   #pragma clang arc_cf_code_audited begin/end
+struct PragmaARCCFCodeAuditedHandler : public PragmaHandler {
+  PragmaARCCFCodeAuditedHandler() : PragmaHandler("arc_cf_code_audited") {}
+  virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
+                            Token &NameTok) {
+    SourceLocation Loc = NameTok.getLocation();
+    bool IsBegin;
+
+    Token Tok;
+
+    // Lex the 'begin' or 'end'.
+    PP.LexUnexpandedToken(Tok);
+    const IdentifierInfo *BeginEnd = Tok.getIdentifierInfo();
+    if (BeginEnd && BeginEnd->isStr("begin")) {
+      IsBegin = true;
+    } else if (BeginEnd && BeginEnd->isStr("end")) {
+      IsBegin = false;
+    } else {
+      PP.Diag(Tok.getLocation(), diag::err_pp_arc_cf_code_audited_syntax);
+      return;
+    }
+
+    // Verify that this is followed by EOD.
+    PP.LexUnexpandedToken(Tok);
+    if (Tok.isNot(tok::eod))
+      PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma";
+
+    // The start location of the active audit.
+    SourceLocation BeginLoc = PP.getPragmaARCCFCodeAuditedLoc();
+
+    // The start location we want after processing this.
+    SourceLocation NewLoc;
+
+    if (IsBegin) {
+      // Complain about attempts to re-enter an audit.
+      if (BeginLoc.isValid()) {
+        PP.Diag(Loc, diag::err_pp_double_begin_of_arc_cf_code_audited);
+        PP.Diag(BeginLoc, diag::note_pragma_entered_here);
+      }
+      NewLoc = Loc;
+    } else {
+      // Complain about attempts to leave an audit that doesn't exist.
+      if (!BeginLoc.isValid()) {
+        PP.Diag(Loc, diag::err_pp_unmatched_end_of_arc_cf_code_audited);
+        return;
+      }
+      NewLoc = SourceLocation();
+    }
+
+    PP.setPragmaARCCFCodeAuditedLoc(NewLoc);
+  }
+};
+
 }  // end anonymous namespace
 
 
@@ -1028,6 +1082,7 @@ void Preprocessor::RegisterBuiltinPragmas() {
   AddPragmaHandler("clang", new PragmaDebugHandler());
   AddPragmaHandler("clang", new PragmaDependencyHandler());
   AddPragmaHandler("clang", new PragmaDiagnosticHandler("clang"));
+  AddPragmaHandler("clang", new PragmaARCCFCodeAuditedHandler());
 
   AddPragmaHandler("STDC", new PragmaSTDC_FENV_ACCESSHandler());
   AddPragmaHandler("STDC", new PragmaSTDC_CX_LIMITED_RANGEHandler());
index 5cd21a645d2afd9ee58b062ce938517e7e961c5a..9d366ad255b7e880ef63abdc88af9b594b0b41a4 100644 (file)
@@ -177,10 +177,12 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) {
     .Case("ns_returns_autoreleased", AT_ns_returns_autoreleased)
     .Case("ns_returns_not_retained", AT_ns_returns_not_retained)
     .Case("ns_returns_retained", AT_ns_returns_retained)
+    .Case("cf_audited_transfer", AT_cf_audited_transfer)
     .Case("cf_consumed", AT_cf_consumed)
     .Case("cf_returns_not_retained", AT_cf_returns_not_retained)
     .Case("cf_returns_retained", AT_cf_returns_retained)
     .Case("cf_returns_autoreleased", AT_cf_returns_autoreleased)
+    .Case("cf_unknown_transfer", AT_cf_unknown_transfer)
     .Case("ns_consumes_self", AT_ns_consumes_self)
     .Case("ns_consumed", AT_ns_consumed)
     .Case("objc_ownership", AT_objc_ownership)
index cd03069ec854409a807ee3901af1cefaad9d879f..77410db01f5804d55fc2bdabe2a75b1ab37bb45a 100644 (file)
@@ -300,6 +300,18 @@ void Sema::ActOnPragmaUnused(const Token &IdTok, Scope *curScope,
   VD->addAttr(::new (Context) UnusedAttr(IdTok.getLocation(), Context));
 }
 
+void Sema::AddCFAuditedAttribute(Decl *D) {
+  SourceLocation Loc = PP.getPragmaARCCFCodeAuditedLoc();
+  if (!Loc.isValid()) return;
+
+  // Don't add a redundant or conflicting attribute.
+  if (D->hasAttr<CFAuditedTransferAttr>() ||
+      D->hasAttr<CFUnknownTransferAttr>())
+    return;
+
+  D->addAttr(::new (Context) CFAuditedTransferAttr(Loc, Context));
+}
+
 typedef std::vector<std::pair<unsigned, SourceLocation> > VisStack;
 enum { NoVisibility = (unsigned) -1 };
 
index 3ba9464cb12f3946b83fdaba4712ba31e235f876..983ed3eb995734284100fdb2cba15d7683c10de9 100644 (file)
@@ -5187,6 +5187,10 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
   if (NewFD->getLinkage() == ExternalLinkage && !DC->isRecord())
     AddPushedVisibilityAttribute(NewFD);
 
+  // If there's a #pragma clang arc_cf_code_audited in scope, consider
+  // marking the function.
+  AddCFAuditedAttribute(NewFD);
+
   // If this is a locally-scoped extern C function, update the
   // map of such names.
   if (CurContext->isFunctionOrMethod() && NewFD->isExternC()
index eca6874b3679bf56f3c3e6b162bec0cb5a428507..0ffb92f00f06a3df0ba983e95444bf7f948d45dd 100644 (file)
@@ -3263,6 +3263,41 @@ static void handleObjCReturnsInnerPointerAttr(Sema &S, Decl *D,
     ::new (S.Context) ObjCReturnsInnerPointerAttr(attr.getRange(), S.Context));
 }
 
+/// Handle cf_audited_transfer and cf_unknown_transfer.
+static void handleCFTransferAttr(Sema &S, Decl *D, const AttributeList &A) {
+  if (!isa<FunctionDecl>(D)) {
+    S.Diag(D->getLocStart(), diag::err_attribute_wrong_decl_type)
+      << A.getRange() << A.getName() << 0 /*function*/;
+    return;
+  }
+
+  bool IsAudited = (A.getKind() == AttributeList::AT_cf_audited_transfer);
+
+  // Check whether there's a conflicting attribute already present.
+  Attr *Existing;
+  if (IsAudited) {
+    Existing = D->getAttr<CFUnknownTransferAttr>();
+  } else {
+    Existing = D->getAttr<CFAuditedTransferAttr>();
+  }
+  if (Existing) {
+    S.Diag(D->getLocStart(), diag::err_attributes_are_not_compatible)
+      << A.getName()
+      << (IsAudited ? "cf_unknown_transfer" : "cf_audited_transfer")
+      << A.getRange() << Existing->getRange();
+    return;
+  }
+
+  // All clear;  add the attribute.
+  if (IsAudited) {
+    D->addAttr(
+      ::new (S.Context) CFAuditedTransferAttr(A.getRange(), S.Context));
+  } else {
+    D->addAttr(
+      ::new (S.Context) CFUnknownTransferAttr(A.getRange(), S.Context));
+  }
+}
+
 static void handleNSBridgedAttr(Sema &S, Scope *Sc, Decl *D,
                                 const AttributeList &Attr) {
   RecordDecl *RD = dyn_cast<RecordDecl>(D);
@@ -3499,6 +3534,10 @@ static void ProcessInheritableDeclAttr(Sema &S, Scope *scope, Decl *D,
   case AttributeList::AT_ns_bridged:
     handleNSBridgedAttr(S, scope, D, Attr); break;
 
+  case AttributeList::AT_cf_audited_transfer:
+  case AttributeList::AT_cf_unknown_transfer:
+    handleCFTransferAttr(S, D, Attr); break;
+
   // Checker-specific.
   case AttributeList::AT_cf_consumed:
   case AttributeList::AT_ns_consumed: handleNSConsumedAttr  (S, D, Attr); break;
diff --git a/test/Sema/Inputs/pragma-arc-cf-code-audited.h b/test/Sema/Inputs/pragma-arc-cf-code-audited.h
new file mode 100644 (file)
index 0000000..6ea360c
--- /dev/null
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#pragma clang arc_cf_code_audited begin
diff --git a/test/Sema/pragma-arc-cf-code-audited.c b/test/Sema/pragma-arc-cf-code-audited.c
new file mode 100644 (file)
index 0000000..b646e89
--- /dev/null
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+#pragma clang arc_cf_code_audited foo // expected-error {{expected 'begin' or 'end'}}
+
+#pragma clang arc_cf_code_audited begin foo // expected-warning {{extra tokens at end of #pragma directive}}
+
+#pragma clang arc_cf_code_audited end
+#pragma clang arc_cf_code_audited end // expected-error {{not currently inside '#pragma clang arc_cf_code_audited'}}
+
+#pragma clang arc_cf_code_audited begin // expected-note {{#pragma entered here}}
+#pragma clang arc_cf_code_audited begin // expected-error {{already inside '#pragma clang arc_cf_code_audited'}} expected-note {{#pragma entered here}}
+
+#include "Inputs/pragma-arc-cf-code-audited.h" // expected-error {{cannot #include files inside '#pragma clang arc_cf_code_audited'}}
+
+// This is actually on the #pragma line in the header.
+// expected-error {{'#pragma clang arc_cf_code_audited' was not ended within this file}}
+
+#pragma clang arc_cf_code_audited begin // expected-error {{'#pragma clang arc_cf_code_audited' was not ended within this file}}