зеркало из https://github.com/microsoft/clang-1.git
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:
Родитель
e4c6675ccc
Коммит
8dfac0baaf
|
@ -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">;
|
||||
|
||||
}
|
||||
|
|
|
@ -166,6 +166,10 @@ class Preprocessor : public llvm::RefCountedBase<Preprocessor> {
|
|||
/// 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}}
|
Загрузка…
Ссылка в новой задаче