mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-27 23:51:56 +00:00
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.
This commit is contained in:
parent
2033fa29b0
commit
2ce6352e46
@ -7,3 +7,4 @@ add_subdirectory(clang-interpreter)
|
||||
add_subdirectory(PrintFunctionNames)
|
||||
add_subdirectory(AnnotateFunctions)
|
||||
add_subdirectory(Attribute)
|
||||
add_subdirectory(CallSuperAttribute)
|
||||
|
13
clang/examples/CallSuperAttribute/CMakeLists.txt
Normal file
13
clang/examples/CallSuperAttribute/CMakeLists.txt
Normal file
@ -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()
|
190
clang/examples/CallSuperAttribute/CallSuperAttrInfo.cpp
Normal file
190
clang/examples/CallSuperAttribute/CallSuperAttrInfo.cpp
Normal file
@ -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");
|
@ -92,6 +92,7 @@ if (CLANG_BUILD_EXAMPLES)
|
||||
list(APPEND CLANG_TEST_DEPS
|
||||
Attribute
|
||||
AnnotateFunctions
|
||||
CallSuperAttr
|
||||
clang-interpreter
|
||||
PrintFunctionNames
|
||||
)
|
||||
|
34
clang/test/Frontend/plugin-call-super.cpp
Normal file
34
clang/test/Frontend/plugin-call-super.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
// RUN: %clang -fplugin=%llvmshlibdir/CallSuperAttr%pluginext -fsyntax-only -Xclang -verify=callsuper %s
|
||||
// RUN: %clang -fplugin=%llvmshlibdir/CallSuperAttr%pluginext -DBAD_CALLSUPER -fsyntax-only -Xclang -verify=badcallsuper %s
|
||||
// REQUIRES: plugins, examples
|
||||
|
||||
// callsuper-no-diagnostics
|
||||
struct Base1 {
|
||||
[[clang::call_super]] virtual void Test() {}
|
||||
};
|
||||
struct Base2 {
|
||||
[[clang::call_super]] virtual void Test() {}
|
||||
};
|
||||
struct Derive : public Base1, public Base2 {
|
||||
#ifndef BAD_CALLSUPER
|
||||
void Test() override;
|
||||
#else
|
||||
[[clang::call_super]] virtual void Test() override final;
|
||||
// badcallsuper-warning@16 {{'call_super' attribute marked on a final method}}
|
||||
#endif
|
||||
};
|
||||
void Derive::Test() {
|
||||
Base1::Test();
|
||||
#ifndef BAD_CALLSUPER
|
||||
Base2::Test();
|
||||
#else
|
||||
// badcallsuper-warning@20 {{virtual function 'Base2::Test' is marked as 'call_super' but this overriding method does not call the base version}}
|
||||
// badcallsuper-note@10 {{function marked 'call_super' here}}
|
||||
#endif
|
||||
}
|
||||
struct Derive2 : public Base1, public Base2 {
|
||||
void Test() override {
|
||||
Base1::Test();
|
||||
Base2::Test();
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user