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