psionic12 updated this revision to Diff 305725. psionic12 added a comment. use `VerifyDiagnosticConsumer` (-verify) instead of `FileCheck` for syntax only feature test. remove illustration in ClangPlugins.rst (which is not very appropriate)
Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D91047/new/ https://reviews.llvm.org/D91047 Files: clang/examples/CMakeLists.txt clang/examples/CallSuperAttribute/CMakeLists.txt clang/examples/CallSuperAttribute/CallSuperAttrInfo.cpp clang/test/CMakeLists.txt clang/test/Frontend/plugin-call-super.cpp
Index: clang/test/Frontend/plugin-call-super.cpp =================================================================== --- /dev/null +++ clang/test/Frontend/plugin-call-super.cpp @@ -0,0 +1,21 @@ +// RUN: %clang -fplugin=%llvmshlibdir/CallSuperAttr%pluginext -fsyntax-only -Xclang -verify %s +// RUN: %clang -fplugin=%llvmshlibdir/CallSuperAttr%pluginext -DBAD_CALLSUPER -fsyntax-only -Xclang -verify %s +// REQUIRES: plugins, examples + +#ifndef BAD_CALLSUPER +// expected-no-diagnostics +struct Base1 { [[clang::call_super]] virtual void Test() {} }; +struct Base2 { [[clang::call_super]] virtual void Test() {} }; +struct Derive : public Base1, public Base2 { void Test() override; }; +void Derive::Test() { Base1::Test(); Base2::Test(); } +struct Derive2 : public Base1, public Base2 { void Test() override { Base1::Test(); Base2::Test();}}; +#else +struct Base1 { [[clang::call_super]] virtual void Test() {} }; +struct Base2 { [[clang::call_super]] virtual void Test() {} }; +struct Derive : public Base1, public Base2 { [[clang::call_super]] virtual void Test() override final; }; +// expected-warning@15 {{'call_super' attribute marked on a final method}} +void Derive::Test() { Base1::Test(); /*Base2::Test();*/ } +// expected-warning@17 {{virtual function 'Base2::Test' is marked as 'call_super' but this overriding method does not call the base version}} +// expected-note@14 {{function marked 'call_super' here}} +struct Derive2 : public Base1, public Base2 { void Test() override { Base1::Test(); Base2::Test();}}; +#endif Index: clang/test/CMakeLists.txt =================================================================== --- clang/test/CMakeLists.txt +++ clang/test/CMakeLists.txt @@ -91,6 +91,7 @@ list(APPEND CLANG_TEST_DEPS Attribute AnnotateFunctions + CallSuperAttr clang-interpreter PrintFunctionNames ) Index: clang/examples/CallSuperAttribute/CallSuperAttrInfo.cpp =================================================================== --- /dev/null +++ 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"); Index: clang/examples/CallSuperAttribute/CMakeLists.txt =================================================================== --- /dev/null +++ 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() Index: clang/examples/CMakeLists.txt =================================================================== --- clang/examples/CMakeLists.txt +++ clang/examples/CMakeLists.txt @@ -7,3 +7,4 @@ add_subdirectory(PrintFunctionNames) add_subdirectory(AnnotateFunctions) add_subdirectory(Attribute) +add_subdirectory(CallSuperAttribute)
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits