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];
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<
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">;
+
}
/// \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;
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.
///
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,
/// 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);
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;
}
}
+ // 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()) {
}
};
+/// 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
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());
.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)
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 };
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()
::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);
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;
--- /dev/null
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#pragma clang arc_cf_code_audited begin
--- /dev/null
+// 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}}