diff options
author | Yafei Liu <psionic12@outlook.com> | 2020-11-20 08:50:39 -0500 |
---|---|---|
committer | Aaron Ballman <aaron@aaronballman.com> | 2020-11-20 08:51:12 -0500 |
commit | 2ce6352e46344d97fed6a3ca6de7228345956191 (patch) | |
tree | 2d2d68b2676e2ac819e083863e07bb34edf08b82 /clang/examples | |
parent | 2033fa29b09f9402a9f09f9de20414e82f7d174c (diff) | |
download | llvm-2ce6352e46344d97fed6a3ca6de7228345956191.zip llvm-2ce6352e46344d97fed6a3ca6de7228345956191.tar.gz llvm-2ce6352e46344d97fed6a3ca6de7228345956191.tar.bz2 |
Add a call super attribute plugin example
If a virtual method is marked as call_super, the
override method must call it, simpler feature like @CallSuper
in Android Java.
Diffstat (limited to 'clang/examples')
-rw-r--r-- | clang/examples/CMakeLists.txt | 1 | ||||
-rw-r--r-- | clang/examples/CallSuperAttribute/CMakeLists.txt | 13 | ||||
-rw-r--r-- | clang/examples/CallSuperAttribute/CallSuperAttrInfo.cpp | 190 |
3 files changed, 204 insertions, 0 deletions
diff --git a/clang/examples/CMakeLists.txt b/clang/examples/CMakeLists.txt index c014b3d..300d8d7 100644 --- a/clang/examples/CMakeLists.txt +++ b/clang/examples/CMakeLists.txt @@ -7,3 +7,4 @@ add_subdirectory(clang-interpreter) add_subdirectory(PrintFunctionNames) add_subdirectory(AnnotateFunctions) add_subdirectory(Attribute) +add_subdirectory(CallSuperAttribute) diff --git a/clang/examples/CallSuperAttribute/CMakeLists.txt b/clang/examples/CallSuperAttribute/CMakeLists.txt new file mode 100644 index 0000000..922f0cf --- /dev/null +++ b/clang/examples/CallSuperAttribute/CMakeLists.txt @@ -0,0 +1,13 @@ +add_llvm_library(CallSuperAttr MODULE CallSuperAttrInfo.cpp PLUGIN_TOOL clang) + +if(LLVM_ENABLE_PLUGINS AND (WIN32 OR CYGWIN)) + set(LLVM_LINK_COMPONENTS + Support + ) + clang_target_link_libraries(CallSuperAttr PRIVATE + clangAST + clangBasic + clangFrontend + clangLex + ) +endif() diff --git a/clang/examples/CallSuperAttribute/CallSuperAttrInfo.cpp b/clang/examples/CallSuperAttribute/CallSuperAttrInfo.cpp new file mode 100644 index 0000000..331f635 --- /dev/null +++ b/clang/examples/CallSuperAttribute/CallSuperAttrInfo.cpp @@ -0,0 +1,190 @@ +//===- AnnotateFunctions.cpp ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Attribute plugin to mark a virtual method as ``call_super``, subclasses must +// call it in the overridden method. +// +// This example shows that attribute plugins combined with ``PluginASTAction`` +// in Clang can do some of the same things which Java Annotations do. +// +// Unlike the other attribute plugin examples, this one does not attach an +// attribute AST node to the declaration AST node. Instead, it keeps a separate +// list of attributed declarations, which may be faster than using +// ``Decl::getAttr<T>()`` in some cases. The disadvantage of this approach is +// that the attribute is not part of the AST, which means that dumping the AST +// will lose the attribute information, pretty printing the AST won't write the +// attribute back out to source, and AST matchers will not be able to match +// against the attribute on the declaration. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Attr.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Sema/ParsedAttr.h" +#include "clang/Sema/Sema.h" +#include "clang/Sema/SemaDiagnostic.h" +#include "llvm/ADT/SmallPtrSet.h" +using namespace clang; + +namespace { +// Cached methods which are marked as 'call_super'. +llvm::SmallPtrSet<const CXXMethodDecl *, 16> MarkedMethods; +bool isMarkedAsCallSuper(const CXXMethodDecl *D) { + // Uses this way to avoid add an annotation attr to the AST. + return MarkedMethods.contains(D); +} + +class MethodUsageVisitor : public RecursiveASTVisitor<MethodUsageVisitor> { +public: + bool IsOverriddenUsed = false; + explicit MethodUsageVisitor( + llvm::SmallPtrSet<const CXXMethodDecl *, 16> &MustCalledMethods) + : MustCalledMethods(MustCalledMethods) {} + bool VisitCallExpr(CallExpr *CallExpr) { + const CXXMethodDecl *Callee = nullptr; + for (const auto &MustCalled : MustCalledMethods) { + if (CallExpr->getCalleeDecl() == MustCalled) { + // Super is called. + // Notice that we cannot do delete or insert in the iteration + // when using SmallPtrSet. + Callee = MustCalled; + } + } + if (Callee) + MustCalledMethods.erase(Callee); + + return true; + } + +private: + llvm::SmallPtrSet<const CXXMethodDecl *, 16> &MustCalledMethods; +}; + +class CallSuperVisitor : public RecursiveASTVisitor<CallSuperVisitor> { +public: + CallSuperVisitor(DiagnosticsEngine &Diags) : Diags(Diags) { + WarningSuperNotCalled = Diags.getCustomDiagID( + DiagnosticsEngine::Warning, + "virtual function %q0 is marked as 'call_super' but this overriding " + "method does not call the base version"); + NotePreviousCallSuperDeclaration = Diags.getCustomDiagID( + DiagnosticsEngine::Note, "function marked 'call_super' here"); + } + bool VisitCXXMethodDecl(CXXMethodDecl *MethodDecl) { + if (MethodDecl->isThisDeclarationADefinition() && MethodDecl->hasBody()) { + // First find out which overridden methods are marked as 'call_super' + llvm::SmallPtrSet<const CXXMethodDecl *, 16> OverriddenMarkedMethods; + for (const auto *Overridden : MethodDecl->overridden_methods()) { + if (isMarkedAsCallSuper(Overridden)) { + OverriddenMarkedMethods.insert(Overridden); + } + } + + // Now find if the superclass method is called in `MethodDecl`. + MethodUsageVisitor Visitor(OverriddenMarkedMethods); + Visitor.TraverseDecl(MethodDecl); + // After traversing, all methods left in `OverriddenMarkedMethods` + // are not called, warn about these. + for (const auto &LeftOverriddens : OverriddenMarkedMethods) { + Diags.Report(MethodDecl->getLocation(), WarningSuperNotCalled) + << LeftOverriddens << MethodDecl; + Diags.Report(LeftOverriddens->getLocation(), + NotePreviousCallSuperDeclaration); + } + } + return true; + } + +private: + DiagnosticsEngine &Diags; + unsigned WarningSuperNotCalled; + unsigned NotePreviousCallSuperDeclaration; +}; + +class CallSuperConsumer : public ASTConsumer { +public: + void HandleTranslationUnit(ASTContext &Context) override { + auto &Diags = Context.getDiagnostics(); + for (const auto *Method : MarkedMethods) { + lateDiagAppertainsToDecl(Diags, Method); + } + + CallSuperVisitor Visitor(Context.getDiagnostics()); + Visitor.TraverseDecl(Context.getTranslationUnitDecl()); + } + +private: + // This function does checks which cannot be done in `diagAppertainsToDecl()`, + // typical example is checking Attributes (such as `FinalAttr`), on the time + // when `diagAppertainsToDecl()` is called, `FinalAttr` is not added into + // the AST yet. + void lateDiagAppertainsToDecl(DiagnosticsEngine &Diags, + const CXXMethodDecl *MethodDecl) { + if (MethodDecl->hasAttr<FinalAttr>()) { + unsigned ID = Diags.getCustomDiagID( + DiagnosticsEngine::Warning, + "'call_super' attribute marked on a final method"); + Diags.Report(MethodDecl->getLocation(), ID); + } + } +}; + +class CallSuperAction : public PluginASTAction { +public: + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef) override { + return std::make_unique<CallSuperConsumer>(); + } + + bool ParseArgs(const CompilerInstance &CI, + const std::vector<std::string> &args) override { + return true; + } + + PluginASTAction::ActionType getActionType() override { + return AddBeforeMainAction; + } +}; + +struct CallSuperAttrInfo : public ParsedAttrInfo { + CallSuperAttrInfo() { + OptArgs = 0; + static constexpr Spelling S[] = { + {ParsedAttr::AS_GNU, "call_super"}, + {ParsedAttr::AS_CXX11, "clang::call_super"}}; + Spellings = S; + } + + bool diagAppertainsToDecl(Sema &S, const ParsedAttr &Attr, + const Decl *D) const override { + const auto *TheMethod = dyn_cast_or_null<CXXMethodDecl>(D); + if (!TheMethod || !TheMethod->isVirtual()) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type_str) + << Attr << "virtual functions"; + return false; + } + MarkedMethods.insert(TheMethod); + return true; + } + AttrHandling handleDeclAttribute(Sema &S, Decl *D, + const ParsedAttr &Attr) const override { + // No need to add an attr object (usually an `AnnotateAttr` is added). + // Save the address of the Decl in a set, it maybe faster than compare to + // strings. + return AttributeNotApplied; + } +}; + +} // namespace +static FrontendPluginRegistry::Add<CallSuperAction> + X("call_super_plugin", "clang plugin, checks every overridden virtual " + "function whether called this function or not."); +static ParsedAttrInfoRegistry::Add<CallSuperAttrInfo> + Y("call_super_attr", "Attr plugin to define 'call_super' attribute"); |