aboutsummaryrefslogtreecommitdiff
path: root/clang/examples
diff options
context:
space:
mode:
authorYafei Liu <psionic12@outlook.com>2020-11-20 08:50:39 -0500
committerAaron Ballman <aaron@aaronballman.com>2020-11-20 08:51:12 -0500
commit2ce6352e46344d97fed6a3ca6de7228345956191 (patch)
tree2d2d68b2676e2ac819e083863e07bb34edf08b82 /clang/examples
parent2033fa29b09f9402a9f09f9de20414e82f7d174c (diff)
downloadllvm-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.txt1
-rw-r--r--clang/examples/CallSuperAttribute/CMakeLists.txt13
-rw-r--r--clang/examples/CallSuperAttribute/CallSuperAttrInfo.cpp190
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");