Add explicit attributes to mark functions as having had their

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
This commit is contained in:
John McCall 2011-09-30 05:12:12 +00:00
Родитель e4c6675ccc
Коммит 8dfac0baaf
15 изменённых файлов: 215 добавлений и 0 удалений

Просмотреть файл

@ -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];

Просмотреть файл

@ -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<

Просмотреть файл

@ -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">;
}

Просмотреть файл

@ -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.
///

Просмотреть файл

@ -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,

Просмотреть файл

@ -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);

Просмотреть файл

@ -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;

Просмотреть файл

@ -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()) {

Просмотреть файл

@ -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());

Просмотреть файл

@ -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)

Просмотреть файл

@ -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 };

Просмотреть файл

@ -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()

Просмотреть файл

@ -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;

Просмотреть файл

@ -0,0 +1,16 @@
#pragma clang arc_cf_code_audited begin

Просмотреть файл

@ -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}}