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; 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; 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 66 private: 67 llvm::SmallPtrSet<const CXXMethodDecl *, 16> &MustCalledMethods; 68 }; 69 70 class CallSuperVisitor : public RecursiveASTVisitor<CallSuperVisitor> { 71 public: 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 105 private: 106 DiagnosticsEngine &Diags; 107 unsigned WarningSuperNotCalled; 108 unsigned NotePreviousCallSuperDeclaration; 109 }; 110 111 class CallSuperConsumer : public ASTConsumer { 112 public: 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. 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: 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 return true; 149 } 150 151 PluginASTAction::ActionType getActionType() override { 152 return AddBeforeMainAction; 153 } 154 }; 155 156 struct CallSuperAttrInfo : public ParsedAttrInfo { 157 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 165 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 } 176 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