Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -731,6 +731,8 @@ - Added ``CXBinaryOperatorKind`` and ``CXUnaryOperatorKind``. (`#29138 `_) +- Exposed arguments of ``clang::annotate`` and ``clang::annotate_type``. + Static Analyzer --------------- - Fix incorrect alignment attribute on the this parameter of certain Index: clang/tools/libclang/CIndex.cpp =================================================================== --- clang/tools/libclang/CIndex.cpp +++ clang/tools/libclang/CIndex.cpp @@ -22,6 +22,7 @@ #include "CursorVisitor.h" #include "clang-c/FatalErrorHandler.h" #include "clang/AST/Attr.h" +#include "clang/AST/AttrVisitor.h" #include "clang/AST/DeclObjCCommon.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" @@ -526,6 +527,13 @@ return false; } + if (clang_isAttribute(Cursor.kind)) { + if (const Attr *A = getCursorAttr(Cursor)) + return Visit(A); + + return false; + } + if (clang_isTranslationUnit(Cursor.kind)) { CXTranslationUnit TU = getCursorTU(Cursor); ASTUnit *CXXUnit = cxtu::getASTUnit(TU); @@ -2088,7 +2096,8 @@ (SourceLocation::UIntTy)(uintptr_t)data[1]); } }; -class EnqueueVisitor : public ConstStmtVisitor { +class EnqueueVisitor : public ConstStmtVisitor, + public ConstAttrVisitor { friend class OMPClauseEnqueue; VisitorWorkList &WL; CXCursor Parent; @@ -2230,6 +2239,10 @@ void VisitOMPTargetTeamsDistributeSimdDirective( const OMPTargetTeamsDistributeSimdDirective *D); + // Attributes + void VisitAnnotateAttr(const AnnotateAttr *A); + void VisitAnnotateTypeAttr(const AnnotateTypeAttr *A); + private: void AddDeclarationNameInfo(const Stmt *S); void AddNestedNameSpecifierLoc(NestedNameSpecifierLoc Qualifier); @@ -2241,6 +2254,11 @@ void AddTypeLoc(TypeSourceInfo *TI); void EnqueueChildren(const Stmt *S); void EnqueueChildren(const OMPClause *S); + template ::value || + std::is_same::value, + bool> = true> + void EnqueueChildren(const AnnAttr *A); }; } // namespace @@ -2730,6 +2748,24 @@ VisitorWorkList::iterator I = WL.begin() + size, E = WL.end(); std::reverse(I, E); } + +template ::value || + std::is_same::value, + bool>> +void EnqueueVisitor::EnqueueChildren(const AnnAttr *A) { + unsigned size = WL.size(); + for (const Expr *Arg : A->args()) { + VisitStmt(Arg); + } + if (size == WL.size()) + return; + // Now reverse the entries we just added. This will match the DFS + // ordering performed by the worklist. + VisitorWorkList::iterator I = WL.begin() + size, E = WL.end(); + std::reverse(I, E); +} + void EnqueueVisitor::VisitAddrLabelExpr(const AddrLabelExpr *E) { WL.push_back(LabelRefVisit(E->getLabel(), E->getLabelLoc(), Parent)); } @@ -3002,7 +3038,7 @@ // If the opaque value has a source expression, just transparently // visit that. This is useful for (e.g.) pseudo-object expressions. if (Expr *SourceExpr = E->getSourceExpr()) - return Visit(SourceExpr); + return ConstStmtVisitor::Visit(SourceExpr); } void EnqueueVisitor::VisitLambdaExpr(const LambdaExpr *E) { AddStmt(E->getBody()); @@ -3022,7 +3058,7 @@ } void EnqueueVisitor::VisitPseudoObjectExpr(const PseudoObjectExpr *E) { // Treat the expression like its syntactic form. - Visit(E->getSyntacticForm()); + ConstStmtVisitor::Visit(E->getSyntacticForm()); } void EnqueueVisitor::VisitOMPExecutableDirective( @@ -3332,9 +3368,32 @@ VisitOMPLoopDirective(D); } +void EnqueueVisitor::VisitAnnotateAttr(const AnnotateAttr *A) { + EnqueueChildren(A); +} + +void EnqueueVisitor::VisitAnnotateTypeAttr(const AnnotateTypeAttr *A) { + EnqueueChildren(A); +} + void CursorVisitor::EnqueueWorkList(VisitorWorkList &WL, const Stmt *S) { EnqueueVisitor(WL, MakeCXCursor(S, StmtParent, TU, RegionOfInterest)) - .Visit(S); + .ConstStmtVisitor::Visit(S); +} + +void CursorVisitor::EnqueueWorkList(VisitorWorkList &WL, const Attr *A) { + // Parent is the attribute itself when this is indirectly called from + // VisitChildren. Because we need to make a CXCursor for A, we need *its* + // parent. + auto AttrCursor = Parent; + + // Get the attribute's parent as stored in + // cxcursor::MakeCXCursor(const Attr *A, const Decl *Parent, CXTranslationUnit + // TU) + const Decl *AttrParent = static_cast(AttrCursor.data[1]); + + EnqueueVisitor(WL, MakeCXCursor(A, AttrParent, TU)) + .ConstAttrVisitor::Visit(A); } bool CursorVisitor::IsInRegionOfInterest(CXCursor C) { @@ -3599,6 +3658,22 @@ return result; } +bool CursorVisitor::Visit(const Attr *A) { + VisitorWorkList *WL = nullptr; + if (!WorkListFreeList.empty()) { + WL = WorkListFreeList.back(); + WL->clear(); + WorkListFreeList.pop_back(); + } else { + WL = new VisitorWorkList(); + WorkListCache.push_back(WL); + } + EnqueueWorkList(*WL, A); + bool result = RunVisitorWorkList(*WL); + WorkListFreeList.push_back(WL); + return result; +} + namespace { typedef SmallVector RefNamePieces; RefNamePieces buildPieces(unsigned NameFlags, bool IsMemberRefExpr, Index: clang/tools/libclang/CursorVisitor.h =================================================================== --- clang/tools/libclang/CursorVisitor.h +++ clang/tools/libclang/CursorVisitor.h @@ -276,7 +276,9 @@ bool IsInRegionOfInterest(CXCursor C); bool RunVisitorWorkList(VisitorWorkList &WL); void EnqueueWorkList(VisitorWorkList &WL, const Stmt *S); + void EnqueueWorkList(VisitorWorkList &WL, const Attr *A); LLVM_ATTRIBUTE_NOINLINE bool Visit(const Stmt *S); + LLVM_ATTRIBUTE_NOINLINE bool Visit(const Attr *A); private: std::optional handleDeclForVisitation(const Decl *D); Index: clang/unittests/libclang/LibclangTest.cpp =================================================================== --- clang/unittests/libclang/LibclangTest.cpp +++ clang/unittests/libclang/LibclangTest.cpp @@ -1172,6 +1172,48 @@ }); } +TEST_F(LibclangParseTest, ExposedAnnotateArgs) { + // TODO + const char testSource[] = R"cpp( +int [[clang::annotate_type("category_type", 42)]] f; + +[[clang::annotate("category", 42)]] +void func() {} +)cpp"; + std::string fileName = "main.cpp"; + WriteFile(fileName, testSource); + + const char *Args[] = {"-xc++"}; + ClangTU = clang_parseTranslationUnit(Index, fileName.c_str(), Args, 1, + nullptr, 0, TUFlags); + + Traverse([&](CXCursor cursor, CXCursor parent) -> CXChildVisitResult { + if (cursor.kind != CXCursor_AnnotateAttr) + return CXChildVisit_Recurse; + + int childCount = 0; + + clang_visitChildren( + cursor, + [](CXCursor child, CXCursor, CXClientData data) -> CXChildVisitResult { + int *pcount = static_cast(data); + + // we only expect one argument here, so bail + EXPECT_EQ(*pcount, 0); + + auto *result = clang_Cursor_Evaluate(child); + EXPECT_NE(result, nullptr); + EXPECT_EQ(clang_EvalResult_getAsInt(result), 42); + ++*pcount; + + return CXChildVisit_Recurse; + }, + &childCount); + + return CXChildVisit_Continue; + }); +} + class LibclangRewriteTest : public LibclangParseTest { public: CXRewriter Rew = nullptr;