diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 32ebbf9443..05540340fd 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -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]; diff --git a/include/clang/Basic/DiagnosticCommonKinds.td b/include/clang/Basic/DiagnosticCommonKinds.td index 1313174b16..603e1b9cb7 100644 --- a/include/clang/Basic/DiagnosticCommonKinds.td +++ b/include/clang/Basic/DiagnosticCommonKinds.td @@ -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, DefaultIgnore; +def note_pragma_entered_here : Note<"#pragma entered here">; // Sema && Lex def ext_longlong : Extension< diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td index 481adc43a4..031a54134e 100644 --- a/include/clang/Basic/DiagnosticLexKinds.td +++ b/include/clang/Basic/DiagnosticLexKinds.td @@ -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">; + } diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index 6ed6aa5c05..328a18a59d 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -165,6 +165,10 @@ class Preprocessor : public llvm::RefCountedBase { /// \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 @@ class Preprocessor : public llvm::RefCountedBase { 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. /// diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h index b0012e7c93..c02d89dd11 100644 --- a/include/clang/Sema/AttributeList.h +++ b/include/clang/Sema/AttributeList.h @@ -166,10 +166,12 @@ class AttributeList { // TODO: This should really be called ParsedAttribute 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, diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index f812556e0f..b272250d91 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -5403,6 +5403,11 @@ class Sema { /// 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); diff --git a/lib/Lex/PPDirectives.cpp b/lib/Lex/PPDirectives.cpp index fc147e420e..95f7ca9644 100644 --- a/lib/Lex/PPDirectives.cpp +++ b/lib/Lex/PPDirectives.cpp @@ -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; diff --git a/lib/Lex/PPLexerChange.cpp b/lib/Lex/PPLexerChange.cpp index 921218208c..8ba2df9b03 100644 --- a/lib/Lex/PPLexerChange.cpp +++ b/lib/Lex/PPLexerChange.cpp @@ -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()) { diff --git a/lib/Lex/Pragma.cpp b/lib/Lex/Pragma.cpp index d855baf9e2..f6532c2175 100644 --- a/lib/Lex/Pragma.cpp +++ b/lib/Lex/Pragma.cpp @@ -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()); diff --git a/lib/Sema/AttributeList.cpp b/lib/Sema/AttributeList.cpp index 5cd21a645d..9d366ad255 100644 --- a/lib/Sema/AttributeList.cpp +++ b/lib/Sema/AttributeList.cpp @@ -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) diff --git a/lib/Sema/SemaAttr.cpp b/lib/Sema/SemaAttr.cpp index cd03069ec8..77410db01f 100644 --- a/lib/Sema/SemaAttr.cpp +++ b/lib/Sema/SemaAttr.cpp @@ -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() || + D->hasAttr()) + return; + + D->addAttr(::new (Context) CFAuditedTransferAttr(Loc, Context)); +} + typedef std::vector > VisStack; enum { NoVisibility = (unsigned) -1 }; diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 3ba9464cb1..983ed3eb99 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -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() diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index eca6874b36..0ffb92f00f 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -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(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(); + } else { + Existing = D->getAttr(); + } + 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(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 index 0000000000..6ea360c34b --- /dev/null +++ b/test/Sema/Inputs/pragma-arc-cf-code-audited.h @@ -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 index 0000000000..b646e8966c --- /dev/null +++ b/test/Sema/pragma-arc-cf-code-audited.c @@ -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}}