brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.3 KiB · f206a84 Raw
194 lines · cpp
1//===- AnnotateFunctions.cpp ----------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Attribute plugin to mark a virtual method as ``call_super``, subclasses must10// call it in the overridden method.11//12// This example shows that attribute plugins combined with ``PluginASTAction``13// in Clang can do some of the same things which Java Annotations do.14//15// Unlike the other attribute plugin examples, this one does not attach an16// attribute AST node to the declaration AST node. Instead, it keeps a separate17// list of attributed declarations, which may be faster than using18// ``Decl::getAttr<T>()`` in some cases. The disadvantage of this approach is19// that the attribute is not part of the AST, which means that dumping the AST20// will lose the attribute information, pretty printing the AST won't write the21// attribute back out to source, and AST matchers will not be able to match22// against the attribute on the declaration.23//24//===----------------------------------------------------------------------===//25 26#include "clang/AST/ASTContext.h"27#include "clang/AST/Attr.h"28#include "clang/AST/RecursiveASTVisitor.h"29#include "clang/Frontend/FrontendPluginRegistry.h"30#include "clang/Sema/ParsedAttr.h"31#include "clang/Sema/Sema.h"32#include "clang/Sema/SemaDiagnostic.h"33#include "llvm/ADT/SmallPtrSet.h"34using namespace clang;35 36namespace {37// Cached methods which are marked as 'call_super'.38llvm::SmallPtrSet<const CXXMethodDecl *, 16> MarkedMethods;39bool isMarkedAsCallSuper(const CXXMethodDecl *D) {40  // Uses this way to avoid add an annotation attr to the AST.41  return MarkedMethods.contains(D);42}43 44class MethodUsageVisitor : public RecursiveASTVisitor<MethodUsageVisitor> {45public:46  bool IsOverriddenUsed = false;47  explicit MethodUsageVisitor(48      llvm::SmallPtrSet<const CXXMethodDecl *, 16> &MustCalledMethods)49      : MustCalledMethods(MustCalledMethods) {}50  bool VisitCallExpr(CallExpr *CallExpr) {51    const CXXMethodDecl *Callee = nullptr;52    for (const auto &MustCalled : MustCalledMethods) {53      if (CallExpr->getCalleeDecl() == MustCalled) {54        // Super is called.55        // Notice that we cannot do delete or insert in the iteration56        // when using SmallPtrSet.57        Callee = MustCalled;58      }59    }60    if (Callee)61      MustCalledMethods.erase(Callee);62 63    return true;64  }65 66private:67  llvm::SmallPtrSet<const CXXMethodDecl *, 16> &MustCalledMethods;68};69 70class CallSuperVisitor : public RecursiveASTVisitor<CallSuperVisitor> {71public:72  CallSuperVisitor(DiagnosticsEngine &Diags) : Diags(Diags) {73    WarningSuperNotCalled = Diags.getCustomDiagID(74        DiagnosticsEngine::Warning,75        "virtual function %q0 is marked as 'call_super' but this overriding "76        "method does not call the base version");77    NotePreviousCallSuperDeclaration = Diags.getCustomDiagID(78        DiagnosticsEngine::Note, "function marked 'call_super' here");79  }80  bool VisitCXXMethodDecl(CXXMethodDecl *MethodDecl) {81    if (MethodDecl->isThisDeclarationADefinition() && MethodDecl->hasBody()) {82      // First find out which overridden methods are marked as 'call_super'83      llvm::SmallPtrSet<const CXXMethodDecl *, 16> OverriddenMarkedMethods;84      for (const auto *Overridden : MethodDecl->overridden_methods()) {85        if (isMarkedAsCallSuper(Overridden)) {86          OverriddenMarkedMethods.insert(Overridden);87        }88      }89 90      // Now find if the superclass method is called in `MethodDecl`.91      MethodUsageVisitor Visitor(OverriddenMarkedMethods);92      Visitor.TraverseDecl(MethodDecl);93      // After traversing, all methods left in `OverriddenMarkedMethods`94      // are not called, warn about these.95      for (const auto &LeftOverriddens : OverriddenMarkedMethods) {96        Diags.Report(MethodDecl->getLocation(), WarningSuperNotCalled)97            << LeftOverriddens << MethodDecl;98        Diags.Report(LeftOverriddens->getLocation(),99                     NotePreviousCallSuperDeclaration);100      }101    }102    return true;103  }104 105private:106  DiagnosticsEngine &Diags;107  unsigned WarningSuperNotCalled;108  unsigned NotePreviousCallSuperDeclaration;109};110 111class CallSuperConsumer : public ASTConsumer {112public:113  void HandleTranslationUnit(ASTContext &Context) override {114    auto &Diags = Context.getDiagnostics();115    for (const auto *Method : MarkedMethods) {116      lateDiagAppertainsToDecl(Diags, Method);117    }118 119    CallSuperVisitor Visitor(Context.getDiagnostics());120    Visitor.TraverseDecl(Context.getTranslationUnitDecl());121  }122 123private:124  // This function does checks which cannot be done in `diagAppertainsToDecl()`,125  // typical example is checking Attributes (such as `FinalAttr`), on the time126  // when `diagAppertainsToDecl()` is called, `FinalAttr` is not added into127  // the AST yet.128  void lateDiagAppertainsToDecl(DiagnosticsEngine &Diags,129                                const CXXMethodDecl *MethodDecl) {130    if (MethodDecl->hasAttr<FinalAttr>()) {131      unsigned ID = Diags.getCustomDiagID(132          DiagnosticsEngine::Warning,133          "'call_super' attribute marked on a final method");134      Diags.Report(MethodDecl->getLocation(), ID);135    }136  }137};138 139class CallSuperAction : public PluginASTAction {140public:141  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,142                                                 llvm::StringRef) override {143    return std::make_unique<CallSuperConsumer>();144  }145 146  bool ParseArgs(const CompilerInstance &CI,147                 const std::vector<std::string> &args) override {148    if (!args.empty() && args[0] == "help")149      llvm::errs() << "Help for the CallSuperAttr plugin goes here\n";150    return true;151  }152 153  PluginASTAction::ActionType getActionType() override {154    return AddBeforeMainAction;155  }156};157 158struct CallSuperAttrInfo : public ParsedAttrInfo {159  CallSuperAttrInfo() {160    OptArgs = 0;161    static constexpr Spelling S[] = {162        {ParsedAttr::AS_GNU, "call_super"},163        {ParsedAttr::AS_CXX11, "clang::call_super"}};164    Spellings = S;165  }166 167  bool diagAppertainsToDecl(Sema &S, const ParsedAttr &Attr,168                            const Decl *D) const override {169    const auto *TheMethod = dyn_cast_or_null<CXXMethodDecl>(D);170    if (!TheMethod || !TheMethod->isVirtual()) {171      S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)172          << Attr << Attr.isRegularKeywordAttribute()173          << ExpectedVirtualFunction;174      return false;175    }176    MarkedMethods.insert(TheMethod);177    return true;178  }179  AttrHandling handleDeclAttribute(Sema &S, Decl *D,180                                   const ParsedAttr &Attr) const override {181    // No need to add an attr object (usually an `AnnotateAttr` is added).182    // Save the address of the Decl in a set, it maybe faster than compare to183    // strings.184    return AttributeNotApplied;185  }186};187 188} // namespace189static FrontendPluginRegistry::Add<CallSuperAction>190    X("call_super_plugin", "clang plugin, checks every overridden virtual "191                           "function whether called this function or not.");192static ParsedAttrInfoRegistry::Add<CallSuperAttrInfo>193    Y("call_super_attr", "Attr plugin to define 'call_super' attribute");194