1 //===- AnnotateFunctions.cpp ----------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // Attribute plugin to mark a virtual method as ``call_super``, subclasses must
10 // call it in the overridden method.
12 // This example shows that attribute plugins combined with ``PluginASTAction``
13 // in Clang can do some of the same things which Java Annotations do.
15 // Unlike the other attribute plugin examples, this one does not attach an
16 // attribute AST node to the declaration AST node. Instead, it keeps a separate
17 // list of attributed declarations, which may be faster than using
18 // ``Decl::getAttr<T>()`` in some cases. The disadvantage of this approach is
19 // that the attribute is not part of the AST, which means that dumping the AST
20 // will lose the attribute information, pretty printing the AST won't write the
21 // attribute back out to source, and AST matchers will not be able to match
22 // against the attribute on the declaration.
24 //===----------------------------------------------------------------------===//
26 #include "clang/AST/ASTContext.h"
27 #include "clang/AST/Attr.h"
28 #include "clang/AST/RecursiveASTVisitor.h"
29 #include "clang/Frontend/FrontendPluginRegistry.h"
30 #include "clang/Sema/ParsedAttr.h"
31 #include "clang/Sema/Sema.h"
32 #include "clang/Sema/SemaDiagnostic.h"
33 #include "llvm/ADT/SmallPtrSet.h"
34 using namespace clang
;
37 // Cached methods which are marked as 'call_super'.
38 llvm::SmallPtrSet
<const CXXMethodDecl
*, 16> MarkedMethods
;
39 bool isMarkedAsCallSuper(const CXXMethodDecl
*D
) {
40 // Uses this way to avoid add an annotation attr to the AST.
41 return MarkedMethods
.contains(D
);
44 class MethodUsageVisitor
: public RecursiveASTVisitor
<MethodUsageVisitor
> {
46 bool IsOverriddenUsed
= false;
47 explicit MethodUsageVisitor(
48 llvm::SmallPtrSet
<const CXXMethodDecl
*, 16> &MustCalledMethods
)
49 : MustCalledMethods(MustCalledMethods
) {}
50 bool VisitCallExpr(CallExpr
*CallExpr
) {
51 const CXXMethodDecl
*Callee
= nullptr;
52 for (const auto &MustCalled
: MustCalledMethods
) {
53 if (CallExpr
->getCalleeDecl() == MustCalled
) {
55 // Notice that we cannot do delete or insert in the iteration
56 // when using SmallPtrSet.
61 MustCalledMethods
.erase(Callee
);
67 llvm::SmallPtrSet
<const CXXMethodDecl
*, 16> &MustCalledMethods
;
70 class CallSuperVisitor
: public RecursiveASTVisitor
<CallSuperVisitor
> {
72 CallSuperVisitor(DiagnosticsEngine
&Diags
) : Diags(Diags
) {
73 WarningSuperNotCalled
= Diags
.getCustomDiagID(
74 DiagnosticsEngine::Warning
,
75 "virtual function %q0 is marked as 'call_super' but this overriding "
76 "method does not call the base version");
77 NotePreviousCallSuperDeclaration
= Diags
.getCustomDiagID(
78 DiagnosticsEngine::Note
, "function marked 'call_super' here");
80 bool VisitCXXMethodDecl(CXXMethodDecl
*MethodDecl
) {
81 if (MethodDecl
->isThisDeclarationADefinition() && MethodDecl
->hasBody()) {
82 // First find out which overridden methods are marked as 'call_super'
83 llvm::SmallPtrSet
<const CXXMethodDecl
*, 16> OverriddenMarkedMethods
;
84 for (const auto *Overridden
: MethodDecl
->overridden_methods()) {
85 if (isMarkedAsCallSuper(Overridden
)) {
86 OverriddenMarkedMethods
.insert(Overridden
);
90 // Now find if the superclass method is called in `MethodDecl`.
91 MethodUsageVisitor
Visitor(OverriddenMarkedMethods
);
92 Visitor
.TraverseDecl(MethodDecl
);
93 // After traversing, all methods left in `OverriddenMarkedMethods`
94 // are not called, warn about these.
95 for (const auto &LeftOverriddens
: OverriddenMarkedMethods
) {
96 Diags
.Report(MethodDecl
->getLocation(), WarningSuperNotCalled
)
97 << LeftOverriddens
<< MethodDecl
;
98 Diags
.Report(LeftOverriddens
->getLocation(),
99 NotePreviousCallSuperDeclaration
);
106 DiagnosticsEngine
&Diags
;
107 unsigned WarningSuperNotCalled
;
108 unsigned NotePreviousCallSuperDeclaration
;
111 class CallSuperConsumer
: public ASTConsumer
{
113 void HandleTranslationUnit(ASTContext
&Context
) override
{
114 auto &Diags
= Context
.getDiagnostics();
115 for (const auto *Method
: MarkedMethods
) {
116 lateDiagAppertainsToDecl(Diags
, Method
);
119 CallSuperVisitor
Visitor(Context
.getDiagnostics());
120 Visitor
.TraverseDecl(Context
.getTranslationUnitDecl());
124 // This function does checks which cannot be done in `diagAppertainsToDecl()`,
125 // typical example is checking Attributes (such as `FinalAttr`), on the time
126 // when `diagAppertainsToDecl()` is called, `FinalAttr` is not added into
128 void lateDiagAppertainsToDecl(DiagnosticsEngine
&Diags
,
129 const CXXMethodDecl
*MethodDecl
) {
130 if (MethodDecl
->hasAttr
<FinalAttr
>()) {
131 unsigned ID
= Diags
.getCustomDiagID(
132 DiagnosticsEngine::Warning
,
133 "'call_super' attribute marked on a final method");
134 Diags
.Report(MethodDecl
->getLocation(), ID
);
139 class CallSuperAction
: public PluginASTAction
{
141 std::unique_ptr
<ASTConsumer
> CreateASTConsumer(CompilerInstance
&CI
,
142 llvm::StringRef
) override
{
143 return std::make_unique
<CallSuperConsumer
>();
146 bool ParseArgs(const CompilerInstance
&CI
,
147 const std::vector
<std::string
> &args
) override
{
148 if (!args
.empty() && args
[0] == "help")
149 llvm::errs() << "Help for the CallSuperAttr plugin goes here\n";
153 PluginASTAction::ActionType
getActionType() override
{
154 return AddBeforeMainAction
;
158 struct CallSuperAttrInfo
: public ParsedAttrInfo
{
159 CallSuperAttrInfo() {
161 static constexpr Spelling S
[] = {
162 {ParsedAttr::AS_GNU
, "call_super"},
163 {ParsedAttr::AS_CXX11
, "clang::call_super"}};
167 bool diagAppertainsToDecl(Sema
&S
, const ParsedAttr
&Attr
,
168 const Decl
*D
) const override
{
169 const auto *TheMethod
= dyn_cast_or_null
<CXXMethodDecl
>(D
);
170 if (!TheMethod
|| !TheMethod
->isVirtual()) {
171 S
.Diag(Attr
.getLoc(), diag::warn_attribute_wrong_decl_type_str
)
172 << Attr
<< "virtual functions";
175 MarkedMethods
.insert(TheMethod
);
178 AttrHandling
handleDeclAttribute(Sema
&S
, Decl
*D
,
179 const ParsedAttr
&Attr
) const override
{
180 // No need to add an attr object (usually an `AnnotateAttr` is added).
181 // Save the address of the Decl in a set, it maybe faster than compare to
183 return AttributeNotApplied
;
188 static FrontendPluginRegistry::Add
<CallSuperAction
>
189 X("call_super_plugin", "clang plugin, checks every overridden virtual "
190 "function whether called this function or not.");
191 static ParsedAttrInfoRegistry::Add
<CallSuperAttrInfo
>
192 Y("call_super_attr", "Attr plugin to define 'call_super' attribute");