psionic12 updated this revision to Diff 305405. psionic12 added a comment. Fix the grammar Simplify the code logic Simplify the test case
Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D91047/new/ https://reviews.llvm.org/D91047 Files: clang/docs/ClangPlugins.rst 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,20 @@ +// RUN: %clang -fplugin=%llvmshlibdir/CallSuperAttr%pluginext -fsyntax-only -Xclang -verify %s +// RUN: not %clang -fplugin=%llvmshlibdir/CallSuperAttr%pluginext -emit-llvm -DBAD_CALLSUPER -S %s -o - 2>&1 | FileCheck %s --check-prefix=BADCALLSUPER +// REQUIRES: plugins, examples + +// 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();}}; +#ifdef BAD_CALLSUPER +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; }; +// CALLSUPER: warning: 'call_super' attribute marked on a final method +void Derive::Test() { Base1::Test(); /*Base2::Test();*/ } +struct Derive2 : public Base1, public Base2 { void Test() override { Base1::Test(); Base2::Test();}}; +// BADCALLSUPER: warning: virtual function 'Base2::Test' is marked as 'call_super' but this overriding method does not call the base version +// BADCALLSUPER: note: function marked 'call_super' here +#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,178 @@ +//===- 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 +// +//===----------------------------------------------------------------------===// +// +// This Clang plugin checks that overrides of the marked virtual function +// directly call the marked function. +// +//===----------------------------------------------------------------------===// + +#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) Index: clang/docs/ClangPlugins.rst =================================================================== --- clang/docs/ClangPlugins.rst +++ clang/docs/ClangPlugins.rst @@ -113,6 +113,23 @@ To see a working example of an attribute plugin, see `the Attribute.cpp example <https://github.com/llvm/llvm-project/blob/master/clang/examples/Attribute/Attribute.cpp>`_. +Defining CallSuperAttr +====================== + +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. + Putting it all together =======================
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits