diff --git a/clang/include/clang/3C/Utils.h b/clang/include/clang/3C/Utils.h index b14041381605..f14891370686 100644 --- a/clang/include/clang/3C/Utils.h +++ b/clang/include/clang/3C/Utils.h @@ -103,6 +103,11 @@ getCheckedPointerKind(clang::InteropTypeExpr *ItypeExpr); std::string getStorageQualifierString(clang::Decl *D); +void forEachAttribute(clang::Decl *D, + llvm::function_ref F); + +std::string getAttributeString(clang::Decl *D); + std::error_code tryGetCanonicalFilePath(const std::string &FileName, std::string &AbsoluteFp); diff --git a/clang/lib/3C/DeclRewriter.cpp b/clang/lib/3C/DeclRewriter.cpp index 72ae4289df83..99b49111c4d7 100644 --- a/clang/lib/3C/DeclRewriter.cpp +++ b/clang/lib/3C/DeclRewriter.cpp @@ -188,7 +188,8 @@ void DeclRewriter::rewriteDecls(ASTContext &Context, ProgramInfo &Info, if (VDLToStmtMap.find(D) != VDLToStmtMap.end()) DS = VDLToStmtMap[D]; - std::string NewTy = getStorageQualifierString(D); + std::string NewTy = + getAttributeString(D) + getStorageQualifierString(D); bool IsExternGlobalVar = isa(D) && cast(D)->getFormalLinkage() == Linkage::ExternalLinkage; @@ -625,7 +626,8 @@ bool FunctionDeclBuilder::VisitFunctionDecl(FunctionDecl *FD) { this->buildDeclVar(CV, PVDecl, Type, IType, PVDecl->getQualifiedNameAsString(), RewriteGeneric, RewriteParams, RewriteReturn, FD->isStatic()); - ParmStrs.push_back(Type + IType); + std::string AttrStr = getAttributeString(PVDecl); + ParmStrs.push_back(AttrStr + Type + IType); ProtoHasItype |= !IType.empty(); } } else if (FDConstraint->numParams() != 0) { @@ -704,6 +706,14 @@ bool FunctionDeclBuilder::VisitFunctionDecl(FunctionDecl *FD) { RewriteReturn = true; } + + std::string AttrStr = getAttributeString(FD); + if ((RewriteReturn || RewriteParams) && !AttrStr.empty()) { + ReturnVar = AttrStr + ReturnVar; + RewriteParams = true; + RewriteReturn = true; + } + // Mirrors the check above that sets RewriteGeneric to true. // If we've decided against making this generic, remove the generic params // so later rewrites (of typeparams) don't happen diff --git a/clang/lib/3C/RewriteUtils.cpp b/clang/lib/3C/RewriteUtils.cpp index 31bbc31e0422..9770e77c68e7 100644 --- a/clang/lib/3C/RewriteUtils.cpp +++ b/clang/lib/3C/RewriteUtils.cpp @@ -521,6 +521,23 @@ SourceLocation FunctionDeclReplacement::getDeclEnd(SourceManager &SM) const { (!End.isValid() || SM.isBeforeInTranslationUnit(End, AnnotationsEnd))) End = AnnotationsEnd; + // Functions attributes can appear after the the closing paren for the + // parameter list. + forEachAttribute(Decl, [&End, &SM, this](const clang::Attr *A) { + SourceLocation AttrEnd = A->getRange().getEnd(); + + llvm::Optional NextTok = Lexer::findNextToken(AttrEnd, SM, + Decl->getLangOpts()); + + SourceLocation NewEnd = NextTok.hasValue() ? NextTok->getEndLoc() + : A->getRange().getEnd(); + NewEnd = SM.getExpansionLoc(NewEnd); + + if (!End.isValid() || + (NewEnd.isValid() && SM.isBeforeInTranslationUnit(End, NewEnd))) + End = NewEnd; + }); + // SourceLocations are weird and turn up invalid for reasons I don't // understand. Fallback to extracting r paren location from source // character buffer. diff --git a/clang/lib/3C/Utils.cpp b/clang/lib/3C/Utils.cpp index 84ede9a2f98e..49f7e238e475 100644 --- a/clang/lib/3C/Utils.cpp +++ b/clang/lib/3C/Utils.cpp @@ -15,6 +15,7 @@ #include "clang/Sema/Sema.h" #include "llvm/Support/Path.h" #include +#include using namespace llvm; using namespace clang; @@ -131,6 +132,39 @@ std::string getStorageQualifierString(Decl *D) { return ""; } +void forEachAttribute(Decl *D, llvm::function_ref F) { + std::ostringstream AttrStr; + if (D->hasAttrs()) + for (auto *A : D->getAttrs()) + F(A); + if (auto *FD = dyn_cast(D)) { + if (auto *TSInfo = FD->getTypeSourceInfo()) { + auto ATLoc = getBaseTypeLoc( + TSInfo->getTypeLoc()).getAs(); + if (!ATLoc.isNull()) + F(ATLoc.getAttr()); + } + } +} + +std::string getAttributeString(Decl *D) { + std::string AttrStr; + llvm::raw_string_ostream AttrStream(AttrStr); + forEachAttribute(D, [&AttrStream, D](const clang::Attr *A) { + A->printPretty(AttrStream, PrintingPolicy(D->getLangOpts())); + }); + AttrStream.flush(); + + // Attr::printPretty puts a space before each attribute resulting in a space + // appearing before any attributes, and no space following the attributes. + // I need this to be the other way around. + if (!AttrStr.empty()) { + assert(AttrStr[0] == ' '); + return AttrStr.substr(1) + ' '; + } + return AttrStr; +} + bool isNULLExpression(clang::Expr *E, ASTContext &C) { QualType Typ = E->getType(); E = removeAuxillaryCasts(E); @@ -331,7 +365,6 @@ static bool castCheck(clang::QualType DstType, clang::QualType SrcType) { if (SrcPtrTypePtr || DstPtrTypePtr) return false; - // Check function cast by comparing parameter and return types individually. const auto *SrcFnType = dyn_cast(SrcTypePtr); const auto *DstFnType = dyn_cast(DstTypePtr); if (SrcFnType && DstFnType) { @@ -459,6 +492,7 @@ TypeLoc getBaseTypeLoc(TypeLoc T) { assert(!T.isNull() && "Can't get base location from Null."); while (!T.getNextTypeLoc().isNull() && (!T.getAs().isNull() || + !T.getAs().isNull() || T.getTypePtr()->isPointerType() || T.getTypePtr()->isArrayType())) T = T.getNextTypeLoc(); return T; diff --git a/clang/test/3C/attr.c b/clang/test/3C/attr.c new file mode 100644 index 000000000000..6deccc30a9e0 --- /dev/null +++ b/clang/test/3C/attr.c @@ -0,0 +1,93 @@ +// RUN: rm -rf %t* +// RUN: 3c -base-dir=%S -alltypes -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_ALL","CHECK" %s +// RUN: 3c -base-dir=%S -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_NOALL","CHECK" %s +// RUN: 3c -base-dir=%S -addcr %s -- | %clang -c -fcheckedc-extension -x c -o /dev/null - +// RUN: 3c -base-dir=%S -output-dir=%t.checked -alltypes %s -- +// RUN: 3c -base-dir=%t.checked -alltypes %t.checked/attr.c -- | diff %t.checked/attr.c - + +// function attributes + +__attribute__((disable_tail_calls)) +int *a() { +//CHECK: __attribute__((disable_tail_calls)) _Ptr a(void) _Checked { + return 0; +} + +__attribute__((disable_tail_calls)) +void b(int *x) { +//CHECK: __attribute__((disable_tail_calls)) void b(_Ptr x) _Checked { + return; +} + +__attribute__((disable_tail_calls)) +int *c(int *x) { +//CHECK: __attribute__((disable_tail_calls)) _Ptr c(_Ptr x) _Checked { + return 0; +} +int *e(int *x) +__attribute__((disable_tail_calls)) +//CHECK: __attribute__((disable_tail_calls)) int *e(_Ptr x) : itype(_Ptr) +{ + return 1; +} + +__attribute__((no_stack_protector)) +int *f(int *x) +__attribute__((disable_tail_calls)) +//CHECK: __attribute__((no_stack_protector)) __attribute__((disable_tail_calls)) _Ptr f(_Ptr x) +{ +//CHECK: _Checked { + while (1){} +} + +// variable attribute on param + +void g(__attribute__((noescape)) int *x) { +//CHECK: void g(__attribute__((noescape)) _Ptr x) _Checked { + return; +} + +void h(__attribute__((noescape)) int *x) { +//CHECK: void h(__attribute__((noescape)) int *x : itype(_Ptr)) { + x = 1; +} + +int *i(__attribute__((noescape)) void *x) { +//CHECK: _For_any(T) _Ptr i(__attribute__((noescape)) _Ptr x) { + return 0; +} + +// variable attribute on local + +void j() { + __attribute__((nodebug)) int *a; + __attribute__((nodebug)) int *b = 0; + __attribute__((nodebug)) int *c = 1; + + __attribute__((nodebug)) int *d, *e = 1, **f, g, *h; +//CHECK: __attribute__((nodebug)) _Ptr a = ((void *)0); +//CHECK: __attribute__((nodebug)) _Ptr b = 0; +//CHECK: __attribute__((nodebug)) int *c = 1; +//CHECK: __attribute__((nodebug)) _Ptr d = ((void *)0); +//CHECK: int *e __attribute__((nodebug)) = 1; +//CHECK: __attribute__((nodebug)) _Ptr<_Ptr> f = ((void *)0); +//CHECK: int g __attribute__((nodebug)); +//CHECK: __attribute__((nodebug)) _Ptr h = ((void *)0); +} + +#define FOO __attribute__((ms_abi)) +int *foo() FOO ; +int *foo() { return 0; } +//CHECK: __attribute__((ms_abi)) _Ptr foo(void) ; +//CHECK: _Ptr foo(void) _Checked { return 0; } + +// Attribute parameter is preserved +__attribute__((deprecated("bar"))) int *bar(); +int *bar() { return 0; } +//CHECK: __attribute__((deprecated("bar"))) _Ptr bar(void); +//CHECK: __attribute__((deprecated("bar"))) _Ptr bar(void) _Checked { return 0; } + +// Because toupper is a standard libary function, it has attributes in the AST +// even though there are none in the source. This was causing issues when +// trying to get the name of the attribute. +int toupper(int c) { return 0; } diff --git a/clang/test/3C/valist.c b/clang/test/3C/valist.c index 8283d3ba2d1e..f9bce1c74056 100644 --- a/clang/test/3C/valist.c +++ b/clang/test/3C/valist.c @@ -51,3 +51,16 @@ typedef void fuz(va_list, int *); /*force output*/ int *p; //CHECK: _Ptr p = ((void *)0); + +// This tests va_list correctness using windows builtins +void ms_test_foo( const char *fmt, __builtin_ms_va_list argp); +__attribute__((ms_abi)) int *ms_test_bar(const char *fmt, ...) { +//CHECK: __attribute__((ms_abi)) _Ptr ms_test_bar(const char *fmt : itype(_Ptr), ...) { + __builtin_ms_va_list argp; + __builtin_ms_va_start(argp, fmt); + //CHECK: __builtin_ms_va_start(argp, fmt); + ms_test_foo(fmt, argp); + __builtin_ms_va_end(argp); + //CHECK: __builtin_ms_va_end(argp); + return 0; +}