| //===- 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_str) |
| << 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. |
| 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"); |