psionic12 created this revision.
psionic12 added a reviewer: aaron.ballman.
Herald added subscribers: cfe-commits, mgorny.
Herald added a project: clang.
psionic12 requested review of this revision.

If a virtual method is marked as call_super, the
override method must call it, simpler feature like @CallSuper
in Android Java.


Repository:
  rG LLVM Github Monorepo

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,22 @@
+// RUN: %clang -fplugin=%llvmshlibdir/CallSuperAttr%pluginext -emit-llvm -S %s -o - 2>&1 | FileCheck %s --check-prefix=CALLSUPER
+// RUN: not %clang -fplugin=%llvmshlibdir/CallSuperAttr%pluginext -emit-llvm -DBAD_CALLSUPER -S %s -o - 2>&1 | FileCheck %s --check-prefix=BADCALLSUPER
+// REQUIRES: plugins, examples
+
+struct Base1 { [[call_super]] virtual void Test() {} };
+struct Base2 { [[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();}};
+// CALLSUPER: [[STR1_VAR:@.+]] = private unnamed_addr constant [10 x i8] c"example()\00"
+// CALLSUPER: [[STR2_VAR:@.+]] = private unnamed_addr constant [20 x i8] c"example(somestring)\00"
+// CALLSUPER: @llvm.global.annotations = {{.*}}@{{.*}}fn1a{{.*}}[[STR1_VAR]]{{.*}}@{{.*}}fn1b{{.*}}[[STR1_VAR]]{{.*}}@{{.*}}fn1c{{.*}}[[STR1_VAR]]{{.*}}@{{.*}}fn2{{.*}}[[STR2_VAR]]
+
+#ifdef BAD_CALLSUPER
+struct Base1 { [[call_super]] virtual void Test() {} };
+struct Base2 { [[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();}};
+// BADCALLSUPER: warning: Base2::Test is marked as call_super but override method Derive::Test does not call it
+// BADCALLSUPER: note: overridden method which is marked as 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,153 @@
+//===- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// clang plugin, checks every overridden virtual function whether called
+// this function or not.
+//
+//===----------------------------------------------------------------------===//
+
+#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 {
+
+llvm::SmallPtrSet<const CXXMethodDecl *, 16> ForcingCalledMethods;
+bool isMarkedAsCallSuper(const CXXMethodDecl *D) {
+  return ForcingCalledMethods.contains(D);
+}
+
+std::string FullName(const CXXMethodDecl *D) {
+  std::string FullName;
+  FullName += D->getParent()->getName();
+  FullName += "::";
+  FullName += D->getName();
+  return FullName;
+}
+
+class MethodUsageVisitor : public RecursiveASTVisitor<MethodUsageVisitor> {
+public:
+  bool IsOvrriddenUsed = false;
+  MethodUsageVisitor(const clang::CXXMethodDecl *MethodDecl)
+      : MethodDecl(MethodDecl) {}
+  bool VisitCallExpr(clang::CallExpr *CallExpr) {
+    if (CallExpr->getCalleeDecl() == MethodDecl) {
+      IsOvrriddenUsed = true;
+      return false;
+    }
+    return true;
+  }
+
+private:
+  const clang::CXXMethodDecl *MethodDecl;
+};
+
+class CallSuperVisitor : public RecursiveASTVisitor<CallSuperVisitor> {
+public:
+  CallSuperVisitor(DiagnosticsEngine &Diags) : Diags(Diags) {
+    WarningSuperNotCalled = Diags.getCustomDiagID(
+        DiagnosticsEngine::Warning,
+        "%0 is marked as call_super but override method %1 does not call it");
+    NotePreviousCallSuperDeclaration = Diags.getCustomDiagID(
+        DiagnosticsEngine::Note,
+        "overridden method which is marked as call_super here");
+  }
+  bool VisitCXXMethodDecl(clang::CXXMethodDecl *MethodDecl) {
+    for (const auto *Overridden : MethodDecl->overridden_methods()) {
+      if (isMarkedAsCallSuper(Overridden)) {
+        if (!MethodDecl->isThisDeclarationADefinition())
+          continue;
+        if (MethodDecl->hasBody()) {
+          MethodUsageVisitor Visitor(Overridden);
+          Visitor.TraverseDecl(MethodDecl);
+          if (!Visitor.IsOvrriddenUsed) {
+            Diags.Report(MethodDecl->getLocation(), WarningSuperNotCalled)
+                << FullName(Overridden) << FullName(MethodDecl);
+            Diags.Report(Overridden->getLocation(),
+                         NotePreviousCallSuperDeclaration);
+          }
+        }
+      }
+    }
+    return true;
+  }
+
+private:
+  DiagnosticsEngine &Diags;
+  unsigned WarningSuperNotCalled;
+  unsigned NotePreviousCallSuperDeclaration;
+};
+
+class CallSuperConsumer : public ASTConsumer {
+public:
+  void HandleTranslationUnit(clang::ASTContext &Context) override {
+    CallSuperVisitor Visitor(Context.getDiagnostics());
+    Visitor.TraverseDecl(Context.getTranslationUnitDecl());
+  }
+};
+
+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 AddAfterMainAction;
+  }
+};
+
+struct CallSuperAttrInfo : public ParsedAttrInfo {
+  CallSuperAttrInfo() {
+    OptArgs = 0;
+    static constexpr Spelling S[] = {{ParsedAttr::AS_GNU, "call_super"},
+                                     {ParsedAttr::AS_CXX11, "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) {
+      S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type_str)
+          << Attr << "methods";
+      return false;
+    }
+    if (!TheMethod->isVirtual()) {
+      unsigned ID = S.Diags.getCustomDiagID(
+          DiagnosticsEngine::Error,
+          "call_super attribute must attached on a virtual function");
+      S.Diags.Report(D->getLocation(), ID);
+    }
+    ForcingCalledMethods.insert(TheMethod);
+    return true;
+  }
+  AttrHandling handleDeclAttribute(Sema &S, Decl *D,
+                                   const ParsedAttr &Attr) const override {
+    return AttributeApplied;
+  }
+};
+
+} // 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");
\ No newline at end of file
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,11 @@
 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, sub-classes must call it in overridden the method.
+
 Putting it all together
 =======================
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to