diff --git a/clang/examples/CallSuperAttribute/CallSuperAttrInfo.cpp b/clang/examples/CallSuperAttribute/CallSuperAttrInfo.cpp --- a/clang/examples/CallSuperAttribute/CallSuperAttrInfo.cpp +++ b/clang/examples/CallSuperAttribute/CallSuperAttrInfo.cpp @@ -12,15 +12,10 @@ // This example shows that attribute plugins combined with ``PluginASTAction`` // in Clang can do some of the same things which Java Annotations do. // -// Unlike the other attribute plugin examples, this one does not attach an -// attribute AST node to the declaration AST node. Instead, it keeps a separate -// list of attributed declarations, which may be faster than using -// ``Decl::getAttr()`` in some cases. The disadvantage of this approach is -// that the attribute is not part of the AST, which means that dumping the AST -// will lose the attribute information, pretty printing the AST won't write the -// attribute back out to source, and AST matchers will not be able to match -// against the attribute on the declaration. -// +// Notice the part of traversing decls, it only focuses on nodes which belong to +// the current parsing file, all other nodes are not loaded, this is especially +// useful to improve the performance when using PCH, or the upcoming module +// feature in C++20. //===----------------------------------------------------------------------===// #include "clang/AST/ASTContext.h" @@ -34,11 +29,14 @@ using namespace clang; namespace { -// Cached methods which are marked as 'call_super'. -llvm::SmallPtrSet MarkedMethods; +constexpr char AnnotateStr[] = "call_super"; bool isMarkedAsCallSuper(const CXXMethodDecl *D) { - // Uses this way to avoid add an annotation attr to the AST. - return MarkedMethods.contains(D); + for (auto It = D->specific_attr_begin(); + It != D->specific_attr_end(); It++) { + if (AnnotateStr == It->getAnnotation()) + return true; + } + return false; } class MethodUsageVisitor : public RecursiveASTVisitor { @@ -78,6 +76,12 @@ DiagnosticsEngine::Note, "function marked 'call_super' here"); } bool VisitCXXMethodDecl(CXXMethodDecl *MethodDecl) { + if (isMarkedAsCallSuper(MethodDecl) && + MethodDecl->isFirstDecl() /* avoid warning multiple times */ + ) { + lateDiagAppertainsToDecl(MethodDecl); + } + if (MethodDecl->isThisDeclarationADefinition() && MethodDecl->hasBody()) { // First find out which overridden methods are marked as 'call_super' llvm::SmallPtrSet OverriddenMarkedMethods; @@ -106,27 +110,11 @@ DiagnosticsEngine &Diags; unsigned WarningSuperNotCalled; unsigned NotePreviousCallSuperDeclaration; -}; - -class CallSuperConsumer : public ASTConsumer { -public: - void HandleTranslationUnit(ASTContext &Context) override { - auto &Diags = Context.getDiagnostics(); - for (const auto *Method : MarkedMethods) { - lateDiagAppertainsToDecl(Diags, Method); - } - - CallSuperVisitor Visitor(Context.getDiagnostics()); - Visitor.TraverseDecl(Context.getTranslationUnitDecl()); - } - -private: // This function does checks which cannot be done in `diagAppertainsToDecl()`, // typical example is checking Attributes (such as `FinalAttr`), on the time // when `diagAppertainsToDecl()` is called, `FinalAttr` is not added into // the AST yet. - void lateDiagAppertainsToDecl(DiagnosticsEngine &Diags, - const CXXMethodDecl *MethodDecl) { + void lateDiagAppertainsToDecl(const CXXMethodDecl *MethodDecl) { if (MethodDecl->hasAttr()) { unsigned ID = Diags.getCustomDiagID( DiagnosticsEngine::Warning, @@ -136,6 +124,21 @@ } }; +class CallSuperConsumer : public ASTConsumer { +public: + void HandleTranslationUnit(ASTContext &Context) override { + CallSuperVisitor Visitor(Context.getDiagnostics()); + // Do not load external sources (usually a PCH), to speed up the traverse. + auto *TU = Context.getTranslationUnitDecl(); + std::vector V; + for (auto *D : TU->noload_decls()) { + V.push_back(D); + } + Context.setTraversalScope(V); + Visitor.TraverseAST(Context); + } +}; + class CallSuperAction : public PluginASTAction { public: std::unique_ptr CreateASTConsumer(CompilerInstance &CI, @@ -170,14 +173,12 @@ << Attr << "virtual functions"; return false; } - MarkedMethods.insert(TheMethod); return true; } AttrHandling handleDeclAttribute(Sema &S, Decl *D, const ParsedAttr &Attr) const override { - // No need to add an attr object (usually an `AnnotateAttr` is added). - // Save the address of the Decl in a set, it maybe faster than compare to - // strings. + D->addAttr(AnnotateAttr::Create(S.Context, AnnotateStr, nullptr, 0, + Attr.getRange())); return AttributeNotApplied; } }; diff --git a/clang/test/Frontend/plugin-call-super-pch.cpp b/clang/test/Frontend/plugin-call-super-pch.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Frontend/plugin-call-super-pch.cpp @@ -0,0 +1,19 @@ +// RUN: split-file %s %t +// RUN: %clang -cc1 -verify=verify-pch -x c++-header %t/call_super_test.h -emit-pch -o %t/call_super_test.h.pch -load %llvmshlibdir/CallSuperAttr%pluginext +// RUN: %clang -cc1 -verify=verify-source -fsyntax-only -include-pch %t/call_super_test.h.pch %t/call_super_test.cpp -load %llvmshlibdir/CallSuperAttr%pluginext +//--- call_super_test.h +struct Foo { + [[clang::call_super]] + virtual void test() {} + [[clang::call_super]] + virtual void test2() final {} // verify-pch-warning {{'call_super' attribute marked on a final method}} +}; + +struct Bar : public Foo { + void test() override final; +}; + +//--- call_super_test.cpp +void Bar::test() { // verify-source-warning {{virtual function 'Foo::test' is marked as 'call_super' but this overriding method does not call the base version}} +// verify-source-note@call_super_test.h:7 {{function marked 'call_super' here}} +} \ No newline at end of file