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