xref: /llvm-project/clang/examples/CallSuperAttribute/CallSuperAttrInfo.cpp (revision 41a94de75caacb979070ec7a010dfe3c4e9f116f)
12ce6352eSYafei Liu //===- AnnotateFunctions.cpp ----------------------------------------------===//
22ce6352eSYafei Liu //
32ce6352eSYafei Liu // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42ce6352eSYafei Liu // See https://llvm.org/LICENSE.txt for license information.
52ce6352eSYafei Liu // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
62ce6352eSYafei Liu //
72ce6352eSYafei Liu //===----------------------------------------------------------------------===//
82ce6352eSYafei Liu //
92ce6352eSYafei Liu // Attribute plugin to mark a virtual method as ``call_super``, subclasses must
102ce6352eSYafei Liu // call it in the overridden method.
112ce6352eSYafei Liu //
122ce6352eSYafei Liu // This example shows that attribute plugins combined with ``PluginASTAction``
132ce6352eSYafei Liu // in Clang can do some of the same things which Java Annotations do.
142ce6352eSYafei Liu //
152ce6352eSYafei Liu // Unlike the other attribute plugin examples, this one does not attach an
162ce6352eSYafei Liu // attribute AST node to the declaration AST node. Instead, it keeps a separate
172ce6352eSYafei Liu // list of attributed declarations, which may be faster than using
182ce6352eSYafei Liu // ``Decl::getAttr<T>()`` in some cases. The disadvantage of this approach is
192ce6352eSYafei Liu // that the attribute is not part of the AST, which means that dumping the AST
202ce6352eSYafei Liu // will lose the attribute information, pretty printing the AST won't write the
212ce6352eSYafei Liu // attribute back out to source, and AST matchers will not be able to match
222ce6352eSYafei Liu // against the attribute on the declaration.
232ce6352eSYafei Liu //
242ce6352eSYafei Liu //===----------------------------------------------------------------------===//
252ce6352eSYafei Liu 
262ce6352eSYafei Liu #include "clang/AST/ASTContext.h"
272ce6352eSYafei Liu #include "clang/AST/Attr.h"
282ce6352eSYafei Liu #include "clang/AST/RecursiveASTVisitor.h"
292ce6352eSYafei Liu #include "clang/Frontend/FrontendPluginRegistry.h"
302ce6352eSYafei Liu #include "clang/Sema/ParsedAttr.h"
312ce6352eSYafei Liu #include "clang/Sema/Sema.h"
322ce6352eSYafei Liu #include "clang/Sema/SemaDiagnostic.h"
332ce6352eSYafei Liu #include "llvm/ADT/SmallPtrSet.h"
342ce6352eSYafei Liu using namespace clang;
352ce6352eSYafei Liu 
362ce6352eSYafei Liu namespace {
372ce6352eSYafei Liu // Cached methods which are marked as 'call_super'.
382ce6352eSYafei Liu llvm::SmallPtrSet<const CXXMethodDecl *, 16> MarkedMethods;
392ce6352eSYafei Liu bool isMarkedAsCallSuper(const CXXMethodDecl *D) {
402ce6352eSYafei Liu   // Uses this way to avoid add an annotation attr to the AST.
412ce6352eSYafei Liu   return MarkedMethods.contains(D);
422ce6352eSYafei Liu }
432ce6352eSYafei Liu 
442ce6352eSYafei Liu class MethodUsageVisitor : public RecursiveASTVisitor<MethodUsageVisitor> {
452ce6352eSYafei Liu public:
462ce6352eSYafei Liu   bool IsOverriddenUsed = false;
472ce6352eSYafei Liu   explicit MethodUsageVisitor(
482ce6352eSYafei Liu       llvm::SmallPtrSet<const CXXMethodDecl *, 16> &MustCalledMethods)
492ce6352eSYafei Liu       : MustCalledMethods(MustCalledMethods) {}
502ce6352eSYafei Liu   bool VisitCallExpr(CallExpr *CallExpr) {
512ce6352eSYafei Liu     const CXXMethodDecl *Callee = nullptr;
522ce6352eSYafei Liu     for (const auto &MustCalled : MustCalledMethods) {
532ce6352eSYafei Liu       if (CallExpr->getCalleeDecl() == MustCalled) {
542ce6352eSYafei Liu         // Super is called.
552ce6352eSYafei Liu         // Notice that we cannot do delete or insert in the iteration
562ce6352eSYafei Liu         // when using SmallPtrSet.
572ce6352eSYafei Liu         Callee = MustCalled;
582ce6352eSYafei Liu       }
592ce6352eSYafei Liu     }
602ce6352eSYafei Liu     if (Callee)
612ce6352eSYafei Liu       MustCalledMethods.erase(Callee);
622ce6352eSYafei Liu 
632ce6352eSYafei Liu     return true;
642ce6352eSYafei Liu   }
652ce6352eSYafei Liu 
662ce6352eSYafei Liu private:
672ce6352eSYafei Liu   llvm::SmallPtrSet<const CXXMethodDecl *, 16> &MustCalledMethods;
682ce6352eSYafei Liu };
692ce6352eSYafei Liu 
702ce6352eSYafei Liu class CallSuperVisitor : public RecursiveASTVisitor<CallSuperVisitor> {
712ce6352eSYafei Liu public:
722ce6352eSYafei Liu   CallSuperVisitor(DiagnosticsEngine &Diags) : Diags(Diags) {
732ce6352eSYafei Liu     WarningSuperNotCalled = Diags.getCustomDiagID(
742ce6352eSYafei Liu         DiagnosticsEngine::Warning,
752ce6352eSYafei Liu         "virtual function %q0 is marked as 'call_super' but this overriding "
762ce6352eSYafei Liu         "method does not call the base version");
772ce6352eSYafei Liu     NotePreviousCallSuperDeclaration = Diags.getCustomDiagID(
782ce6352eSYafei Liu         DiagnosticsEngine::Note, "function marked 'call_super' here");
792ce6352eSYafei Liu   }
802ce6352eSYafei Liu   bool VisitCXXMethodDecl(CXXMethodDecl *MethodDecl) {
812ce6352eSYafei Liu     if (MethodDecl->isThisDeclarationADefinition() && MethodDecl->hasBody()) {
822ce6352eSYafei Liu       // First find out which overridden methods are marked as 'call_super'
832ce6352eSYafei Liu       llvm::SmallPtrSet<const CXXMethodDecl *, 16> OverriddenMarkedMethods;
842ce6352eSYafei Liu       for (const auto *Overridden : MethodDecl->overridden_methods()) {
852ce6352eSYafei Liu         if (isMarkedAsCallSuper(Overridden)) {
862ce6352eSYafei Liu           OverriddenMarkedMethods.insert(Overridden);
872ce6352eSYafei Liu         }
882ce6352eSYafei Liu       }
892ce6352eSYafei Liu 
902ce6352eSYafei Liu       // Now find if the superclass method is called in `MethodDecl`.
912ce6352eSYafei Liu       MethodUsageVisitor Visitor(OverriddenMarkedMethods);
922ce6352eSYafei Liu       Visitor.TraverseDecl(MethodDecl);
932ce6352eSYafei Liu       // After traversing, all methods left in `OverriddenMarkedMethods`
942ce6352eSYafei Liu       // are not called, warn about these.
952ce6352eSYafei Liu       for (const auto &LeftOverriddens : OverriddenMarkedMethods) {
962ce6352eSYafei Liu         Diags.Report(MethodDecl->getLocation(), WarningSuperNotCalled)
972ce6352eSYafei Liu             << LeftOverriddens << MethodDecl;
982ce6352eSYafei Liu         Diags.Report(LeftOverriddens->getLocation(),
992ce6352eSYafei Liu                      NotePreviousCallSuperDeclaration);
1002ce6352eSYafei Liu       }
1012ce6352eSYafei Liu     }
1022ce6352eSYafei Liu     return true;
1032ce6352eSYafei Liu   }
1042ce6352eSYafei Liu 
1052ce6352eSYafei Liu private:
1062ce6352eSYafei Liu   DiagnosticsEngine &Diags;
1072ce6352eSYafei Liu   unsigned WarningSuperNotCalled;
1082ce6352eSYafei Liu   unsigned NotePreviousCallSuperDeclaration;
1092ce6352eSYafei Liu };
1102ce6352eSYafei Liu 
1112ce6352eSYafei Liu class CallSuperConsumer : public ASTConsumer {
1122ce6352eSYafei Liu public:
1132ce6352eSYafei Liu   void HandleTranslationUnit(ASTContext &Context) override {
1142ce6352eSYafei Liu     auto &Diags = Context.getDiagnostics();
1152ce6352eSYafei Liu     for (const auto *Method : MarkedMethods) {
1162ce6352eSYafei Liu       lateDiagAppertainsToDecl(Diags, Method);
1172ce6352eSYafei Liu     }
1182ce6352eSYafei Liu 
1192ce6352eSYafei Liu     CallSuperVisitor Visitor(Context.getDiagnostics());
1202ce6352eSYafei Liu     Visitor.TraverseDecl(Context.getTranslationUnitDecl());
1212ce6352eSYafei Liu   }
1222ce6352eSYafei Liu 
1232ce6352eSYafei Liu private:
1242ce6352eSYafei Liu   // This function does checks which cannot be done in `diagAppertainsToDecl()`,
1252ce6352eSYafei Liu   // typical example is checking Attributes (such as `FinalAttr`), on the time
1262ce6352eSYafei Liu   // when `diagAppertainsToDecl()` is called, `FinalAttr` is not added into
1272ce6352eSYafei Liu   // the AST yet.
1282ce6352eSYafei Liu   void lateDiagAppertainsToDecl(DiagnosticsEngine &Diags,
1292ce6352eSYafei Liu                                 const CXXMethodDecl *MethodDecl) {
1302ce6352eSYafei Liu     if (MethodDecl->hasAttr<FinalAttr>()) {
1312ce6352eSYafei Liu       unsigned ID = Diags.getCustomDiagID(
1322ce6352eSYafei Liu           DiagnosticsEngine::Warning,
1332ce6352eSYafei Liu           "'call_super' attribute marked on a final method");
1342ce6352eSYafei Liu       Diags.Report(MethodDecl->getLocation(), ID);
1352ce6352eSYafei Liu     }
1362ce6352eSYafei Liu   }
1372ce6352eSYafei Liu };
1382ce6352eSYafei Liu 
1392ce6352eSYafei Liu class CallSuperAction : public PluginASTAction {
1402ce6352eSYafei Liu public:
1412ce6352eSYafei Liu   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
1422ce6352eSYafei Liu                                                  llvm::StringRef) override {
1432ce6352eSYafei Liu     return std::make_unique<CallSuperConsumer>();
1442ce6352eSYafei Liu   }
1452ce6352eSYafei Liu 
1462ce6352eSYafei Liu   bool ParseArgs(const CompilerInstance &CI,
1472ce6352eSYafei Liu                  const std::vector<std::string> &args) override {
1483e67cf21STimm Bäder     if (!args.empty() && args[0] == "help")
1493e67cf21STimm Bäder       llvm::errs() << "Help for the CallSuperAttr plugin goes here\n";
1502ce6352eSYafei Liu     return true;
1512ce6352eSYafei Liu   }
1522ce6352eSYafei Liu 
1532ce6352eSYafei Liu   PluginASTAction::ActionType getActionType() override {
1542ce6352eSYafei Liu     return AddBeforeMainAction;
1552ce6352eSYafei Liu   }
1562ce6352eSYafei Liu };
1572ce6352eSYafei Liu 
1582ce6352eSYafei Liu struct CallSuperAttrInfo : public ParsedAttrInfo {
1592ce6352eSYafei Liu   CallSuperAttrInfo() {
1602ce6352eSYafei Liu     OptArgs = 0;
1612ce6352eSYafei Liu     static constexpr Spelling S[] = {
1622ce6352eSYafei Liu         {ParsedAttr::AS_GNU, "call_super"},
1632ce6352eSYafei Liu         {ParsedAttr::AS_CXX11, "clang::call_super"}};
1642ce6352eSYafei Liu     Spellings = S;
1652ce6352eSYafei Liu   }
1662ce6352eSYafei Liu 
1672ce6352eSYafei Liu   bool diagAppertainsToDecl(Sema &S, const ParsedAttr &Attr,
1682ce6352eSYafei Liu                             const Decl *D) const override {
1692ce6352eSYafei Liu     const auto *TheMethod = dyn_cast_or_null<CXXMethodDecl>(D);
1702ce6352eSYafei Liu     if (!TheMethod || !TheMethod->isVirtual()) {
171*41a94de7SMaksim Ivanov       S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
172*41a94de7SMaksim Ivanov           << Attr << Attr.isRegularKeywordAttribute()
173*41a94de7SMaksim Ivanov           << ExpectedVirtualFunction;
1742ce6352eSYafei Liu       return false;
1752ce6352eSYafei Liu     }
1762ce6352eSYafei Liu     MarkedMethods.insert(TheMethod);
1772ce6352eSYafei Liu     return true;
1782ce6352eSYafei Liu   }
1792ce6352eSYafei Liu   AttrHandling handleDeclAttribute(Sema &S, Decl *D,
1802ce6352eSYafei Liu                                    const ParsedAttr &Attr) const override {
1812ce6352eSYafei Liu     // No need to add an attr object (usually an `AnnotateAttr` is added).
1822ce6352eSYafei Liu     // Save the address of the Decl in a set, it maybe faster than compare to
1832ce6352eSYafei Liu     // strings.
1842ce6352eSYafei Liu     return AttributeNotApplied;
1852ce6352eSYafei Liu   }
1862ce6352eSYafei Liu };
1872ce6352eSYafei Liu 
1882ce6352eSYafei Liu } // namespace
1892ce6352eSYafei Liu static FrontendPluginRegistry::Add<CallSuperAction>
1902ce6352eSYafei Liu     X("call_super_plugin", "clang plugin, checks every overridden virtual "
1912ce6352eSYafei Liu                            "function whether called this function or not.");
1922ce6352eSYafei Liu static ParsedAttrInfoRegistry::Add<CallSuperAttrInfo>
1932ce6352eSYafei Liu     Y("call_super_attr", "Attr plugin to define 'call_super' attribute");
194