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-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Attribute plugin to mark a virtual method as ``call_super``, subclasses must
10// 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 an
16// attribute AST node to the declaration AST node. Instead, it keeps a separate
17// list of attributed declarations, which may be faster than using
18// ``Decl::getAttr<T>()`` in some cases. The disadvantage of this approach is
19// that the attribute is not part of the AST, which means that dumping the AST
20// will lose the attribute information, pretty printing the AST won't write the
21// attribute back out to source, and AST matchers will not be able to match
22// 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 iteration
56        // 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 time
126  // when `diagAppertainsToDecl()` is called, `FinalAttr` is not added into
127  // 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_str)
172          << Attr << "virtual functions";
173      return false;
174    }
175    MarkedMethods.insert(TheMethod);
176    return true;
177  }
178  AttrHandling handleDeclAttribute(Sema &S, Decl *D,
179                                   const ParsedAttr &Attr) const override {
180    // No need to add an attr object (usually an `AnnotateAttr` is added).
181    // Save the address of the Decl in a set, it maybe faster than compare to
182    // strings.
183    return AttributeNotApplied;
184  }
185};
186
187} // namespace
188static FrontendPluginRegistry::Add<CallSuperAction>
189    X("call_super_plugin", "clang plugin, checks every overridden virtual "
190                           "function whether called this function or not.");
191static ParsedAttrInfoRegistry::Add<CallSuperAttrInfo>
192    Y("call_super_attr", "Attr plugin to define 'call_super' attribute");
193