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"
34 using namespace clang;
35 
36 namespace {
37 // Cached methods which are marked as 'call_super'.
38 llvm::SmallPtrSet<const CXXMethodDecl *, 16> MarkedMethods;
isMarkedAsCallSuper(const CXXMethodDecl * D)39 bool isMarkedAsCallSuper(const CXXMethodDecl *D) {
40   // Uses this way to avoid add an annotation attr to the AST.
41   return MarkedMethods.contains(D);
42 }
43 
44 class MethodUsageVisitor : public RecursiveASTVisitor<MethodUsageVisitor> {
45 public:
46   bool IsOverriddenUsed = false;
MethodUsageVisitor(llvm::SmallPtrSet<const CXXMethodDecl *,16> & MustCalledMethods)47   explicit MethodUsageVisitor(
48       llvm::SmallPtrSet<const CXXMethodDecl *, 16> &MustCalledMethods)
49       : MustCalledMethods(MustCalledMethods) {}
VisitCallExpr(CallExpr * CallExpr)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 
66 private:
67   llvm::SmallPtrSet<const CXXMethodDecl *, 16> &MustCalledMethods;
68 };
69 
70 class CallSuperVisitor : public RecursiveASTVisitor<CallSuperVisitor> {
71 public:
CallSuperVisitor(DiagnosticsEngine & Diags)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   }
VisitCXXMethodDecl(CXXMethodDecl * MethodDecl)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 
105 private:
106   DiagnosticsEngine &Diags;
107   unsigned WarningSuperNotCalled;
108   unsigned NotePreviousCallSuperDeclaration;
109 };
110 
111 class CallSuperConsumer : public ASTConsumer {
112 public:
HandleTranslationUnit(ASTContext & Context)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 
123 private:
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.
lateDiagAppertainsToDecl(DiagnosticsEngine & Diags,const CXXMethodDecl * MethodDecl)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 
139 class CallSuperAction : public PluginASTAction {
140 public:
CreateASTConsumer(CompilerInstance & CI,llvm::StringRef)141   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
142                                                  llvm::StringRef) override {
143     return std::make_unique<CallSuperConsumer>();
144   }
145 
ParseArgs(const CompilerInstance & CI,const std::vector<std::string> & args)146   bool ParseArgs(const CompilerInstance &CI,
147                  const std::vector<std::string> &args) override {
148     return true;
149   }
150 
getActionType()151   PluginASTAction::ActionType getActionType() override {
152     return AddBeforeMainAction;
153   }
154 };
155 
156 struct CallSuperAttrInfo : public ParsedAttrInfo {
CallSuperAttrInfo__anon8e849d320111::CallSuperAttrInfo157   CallSuperAttrInfo() {
158     OptArgs = 0;
159     static constexpr Spelling S[] = {
160         {ParsedAttr::AS_GNU, "call_super"},
161         {ParsedAttr::AS_CXX11, "clang::call_super"}};
162     Spellings = S;
163   }
164 
diagAppertainsToDecl__anon8e849d320111::CallSuperAttrInfo165   bool diagAppertainsToDecl(Sema &S, const ParsedAttr &Attr,
166                             const Decl *D) const override {
167     const auto *TheMethod = dyn_cast_or_null<CXXMethodDecl>(D);
168     if (!TheMethod || !TheMethod->isVirtual()) {
169       S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type_str)
170           << Attr << "virtual functions";
171       return false;
172     }
173     MarkedMethods.insert(TheMethod);
174     return true;
175   }
handleDeclAttribute__anon8e849d320111::CallSuperAttrInfo176   AttrHandling handleDeclAttribute(Sema &S, Decl *D,
177                                    const ParsedAttr &Attr) const override {
178     // No need to add an attr object (usually an `AnnotateAttr` is added).
179     // Save the address of the Decl in a set, it maybe faster than compare to
180     // strings.
181     return AttributeNotApplied;
182   }
183 };
184 
185 } // namespace
186 static FrontendPluginRegistry::Add<CallSuperAction>
187     X("call_super_plugin", "clang plugin, checks every overridden virtual "
188                            "function whether called this function or not.");
189 static ParsedAttrInfoRegistry::Add<CallSuperAttrInfo>
190     Y("call_super_attr", "Attr plugin to define 'call_super' attribute");
191