| //===- AnnotateFunctions.cpp ----------------------------------------------===// | 
 | // | 
 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
 | // See https://llvm.org/LICENSE.txt for license information. | 
 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 | // | 
 | // Attribute plugin to mark a virtual method as ``call_super``, subclasses must | 
 | // call it in the overridden method. | 
 | // | 
 | // 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<T>()`` 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. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "clang/AST/ASTContext.h" | 
 | #include "clang/AST/Attr.h" | 
 | #include "clang/AST/RecursiveASTVisitor.h" | 
 | #include "clang/Frontend/FrontendPluginRegistry.h" | 
 | #include "clang/Sema/ParsedAttr.h" | 
 | #include "clang/Sema/Sema.h" | 
 | #include "clang/Sema/SemaDiagnostic.h" | 
 | #include "llvm/ADT/SmallPtrSet.h" | 
 | using namespace clang; | 
 |  | 
 | namespace { | 
 | // Cached methods which are marked as 'call_super'. | 
 | llvm::SmallPtrSet<const CXXMethodDecl *, 16> MarkedMethods; | 
 | bool isMarkedAsCallSuper(const CXXMethodDecl *D) { | 
 |   // Uses this way to avoid add an annotation attr to the AST. | 
 |   return MarkedMethods.contains(D); | 
 | } | 
 |  | 
 | class MethodUsageVisitor : public RecursiveASTVisitor<MethodUsageVisitor> { | 
 | public: | 
 |   bool IsOverriddenUsed = false; | 
 |   explicit MethodUsageVisitor( | 
 |       llvm::SmallPtrSet<const CXXMethodDecl *, 16> &MustCalledMethods) | 
 |       : MustCalledMethods(MustCalledMethods) {} | 
 |   bool VisitCallExpr(CallExpr *CallExpr) { | 
 |     const CXXMethodDecl *Callee = nullptr; | 
 |     for (const auto &MustCalled : MustCalledMethods) { | 
 |       if (CallExpr->getCalleeDecl() == MustCalled) { | 
 |         // Super is called. | 
 |         // Notice that we cannot do delete or insert in the iteration | 
 |         // when using SmallPtrSet. | 
 |         Callee = MustCalled; | 
 |       } | 
 |     } | 
 |     if (Callee) | 
 |       MustCalledMethods.erase(Callee); | 
 |  | 
 |     return true; | 
 |   } | 
 |  | 
 | private: | 
 |   llvm::SmallPtrSet<const CXXMethodDecl *, 16> &MustCalledMethods; | 
 | }; | 
 |  | 
 | class CallSuperVisitor : public RecursiveASTVisitor<CallSuperVisitor> { | 
 | public: | 
 |   CallSuperVisitor(DiagnosticsEngine &Diags) : Diags(Diags) { | 
 |     WarningSuperNotCalled = Diags.getCustomDiagID( | 
 |         DiagnosticsEngine::Warning, | 
 |         "virtual function %q0 is marked as 'call_super' but this overriding " | 
 |         "method does not call the base version"); | 
 |     NotePreviousCallSuperDeclaration = Diags.getCustomDiagID( | 
 |         DiagnosticsEngine::Note, "function marked 'call_super' here"); | 
 |   } | 
 |   bool VisitCXXMethodDecl(CXXMethodDecl *MethodDecl) { | 
 |     if (MethodDecl->isThisDeclarationADefinition() && MethodDecl->hasBody()) { | 
 |       // First find out which overridden methods are marked as 'call_super' | 
 |       llvm::SmallPtrSet<const CXXMethodDecl *, 16> OverriddenMarkedMethods; | 
 |       for (const auto *Overridden : MethodDecl->overridden_methods()) { | 
 |         if (isMarkedAsCallSuper(Overridden)) { | 
 |           OverriddenMarkedMethods.insert(Overridden); | 
 |         } | 
 |       } | 
 |  | 
 |       // Now find if the superclass method is called in `MethodDecl`. | 
 |       MethodUsageVisitor Visitor(OverriddenMarkedMethods); | 
 |       Visitor.TraverseDecl(MethodDecl); | 
 |       // After traversing, all methods left in `OverriddenMarkedMethods` | 
 |       // are not called, warn about these. | 
 |       for (const auto &LeftOverriddens : OverriddenMarkedMethods) { | 
 |         Diags.Report(MethodDecl->getLocation(), WarningSuperNotCalled) | 
 |             << LeftOverriddens << MethodDecl; | 
 |         Diags.Report(LeftOverriddens->getLocation(), | 
 |                      NotePreviousCallSuperDeclaration); | 
 |       } | 
 |     } | 
 |     return true; | 
 |   } | 
 |  | 
 | private: | 
 |   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) { | 
 |     if (MethodDecl->hasAttr<FinalAttr>()) { | 
 |       unsigned ID = Diags.getCustomDiagID( | 
 |           DiagnosticsEngine::Warning, | 
 |           "'call_super' attribute marked on a final method"); | 
 |       Diags.Report(MethodDecl->getLocation(), ID); | 
 |     } | 
 |   } | 
 | }; | 
 |  | 
 | class CallSuperAction : public PluginASTAction { | 
 | public: | 
 |   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, | 
 |                                                  llvm::StringRef) override { | 
 |     return std::make_unique<CallSuperConsumer>(); | 
 |   } | 
 |  | 
 |   bool ParseArgs(const CompilerInstance &CI, | 
 |                  const std::vector<std::string> &args) override { | 
 |     if (!args.empty() && args[0] == "help") | 
 |       llvm::errs() << "Help for the CallSuperAttr plugin goes here\n"; | 
 |     return true; | 
 |   } | 
 |  | 
 |   PluginASTAction::ActionType getActionType() override { | 
 |     return AddBeforeMainAction; | 
 |   } | 
 | }; | 
 |  | 
 | struct CallSuperAttrInfo : public ParsedAttrInfo { | 
 |   CallSuperAttrInfo() { | 
 |     OptArgs = 0; | 
 |     static constexpr Spelling S[] = { | 
 |         {ParsedAttr::AS_GNU, "call_super"}, | 
 |         {ParsedAttr::AS_CXX11, "clang::call_super"}}; | 
 |     Spellings = S; | 
 |   } | 
 |  | 
 |   bool diagAppertainsToDecl(Sema &S, const ParsedAttr &Attr, | 
 |                             const Decl *D) const override { | 
 |     const auto *TheMethod = dyn_cast_or_null<CXXMethodDecl>(D); | 
 |     if (!TheMethod || !TheMethod->isVirtual()) { | 
 |       S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) | 
 |           << Attr << Attr.isRegularKeywordAttribute() | 
 |           << ExpectedVirtualFunction; | 
 |       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. | 
 |     return AttributeNotApplied; | 
 |   } | 
 | }; | 
 |  | 
 | } // namespace | 
 | static FrontendPluginRegistry::Add<CallSuperAction> | 
 |     X("call_super_plugin", "clang plugin, checks every overridden virtual " | 
 |                            "function whether called this function or not."); | 
 | static ParsedAttrInfoRegistry::Add<CallSuperAttrInfo> | 
 |     Y("call_super_attr", "Attr plugin to define 'call_super' attribute"); |