Skip to content

Commit

Permalink
Add explicit attributes to mark functions as having had their
Browse files Browse the repository at this point in the history
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
  • Loading branch information
rjmccall committed Sep 30, 2011
1 parent e4c6675 commit 8dfac0b
Show file tree
Hide file tree
Showing 15 changed files with 215 additions and 0 deletions.
17 changes: 17 additions & 0 deletions include/clang/Basic/Attr.td
Expand Up @@ -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];
Expand Down
1 change: 1 addition & 0 deletions include/clang/Basic/DiagnosticCommonKinds.td
Expand Up @@ -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<
Expand Down
10 changes: 10 additions & 0 deletions include/clang/Basic/DiagnosticLexKinds.td
Expand Up @@ -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">;

}
17 changes: 17 additions & 0 deletions include/clang/Lex/Preprocessor.h
Expand Up @@ -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;
Expand Down Expand Up @@ -720,6 +724,19 @@ class Preprocessor : public llvm::RefCountedBase<Preprocessor> {
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.
///
Expand Down
2 changes: 2 additions & 0 deletions include/clang/Sema/AttributeList.h
Expand Up @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions include/clang/Sema/Sema.h
Expand Up @@ -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);
Expand Down
9 changes: 9 additions & 0 deletions lib/Lex/PPDirectives.cpp
Expand Up @@ -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;
Expand Down
8 changes: 8 additions & 0 deletions lib/Lex/PPLexerChange.cpp
Expand Up @@ -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()) {
Expand Down
55 changes: 55 additions & 0 deletions lib/Lex/Pragma.cpp
Expand Up @@ -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


Expand All @@ -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());
Expand Down
2 changes: 2 additions & 0 deletions lib/Sema/AttributeList.cpp
Expand Up @@ -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)
Expand Down
12 changes: 12 additions & 0 deletions lib/Sema/SemaAttr.cpp
Expand Up @@ -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 };

Expand Down
4 changes: 4 additions & 0 deletions lib/Sema/SemaDecl.cpp
Expand Up @@ -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()
Expand Down
39 changes: 39 additions & 0 deletions lib/Sema/SemaDeclAttr.cpp
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
16 changes: 16 additions & 0 deletions test/Sema/Inputs/pragma-arc-cf-code-audited.h
@@ -0,0 +1,16 @@















#pragma clang arc_cf_code_audited begin
18 changes: 18 additions & 0 deletions 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}}

0 comments on commit 8dfac0b

Please sign in to comment.