1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
11 #include <unordered_map>
12 #include <unordered_set>
15 #include "config_clang.h"
21 Look for methods that are final i.e. nothing overrides them
23 Making the method final means the compiler can devirtualise
26 The process goes something like this:
28 $ make FORCE_COMPILE=all COMPILER_PLUGIN_TOOL='finalmethods' check
29 $ ./compilerplugins/clang/finalmethods.py
35 // try to limit the voluminous output a little
36 static std::unordered_set
<std::string
> overriddenSet
;
37 static std::unordered_map
<std::string
, std::string
> definitionMap
; // methodName -> location
39 class FinalMethods
: public RecursiveASTVisitor
<FinalMethods
>, public loplugin::Plugin
42 explicit FinalMethods(loplugin::InstantiationData
const& data
)
47 virtual void run() override
49 handler
.enableTreeWideAnalysisMode();
51 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
53 // dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
54 // writing to the same logfile
56 for (const std::string
& s
: overriddenSet
)
57 output
+= "overridden:\t" + s
+ "\n";
58 for (const auto& s
: definitionMap
)
59 output
+= "definition:\t" + s
.first
+ "\t" + s
.second
+ "\n";
61 myfile
.open(WORKDIR
"/loplugin.finalmethods.log", std::ios::app
| std::ios::out
);
66 bool shouldVisitTemplateInstantiations() const { return true; }
68 bool shouldVisitImplicitCode() const { return true; }
70 bool VisitCXXMethodDecl(const CXXMethodDecl
*);
73 std::string
toString(SourceLocation loc
);
74 std::string
niceName(const CXXMethodDecl
*);
77 bool FinalMethods::VisitCXXMethodDecl(const CXXMethodDecl
* methodDecl
)
79 if (ignoreLocation(methodDecl
))
81 if (!methodDecl
->isThisDeclarationADefinition())
83 // don't care about compiler-generated functions
84 if (methodDecl
->isImplicit())
86 if (!methodDecl
->isVirtual())
88 if (!methodDecl
->getLocation().isValid())
90 if (isa
<CXXDestructorDecl
>(methodDecl
) || isa
<CXXConstructorDecl
>(methodDecl
))
93 methodDecl
= methodDecl
->getCanonicalDecl();
94 if (isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(methodDecl
->getLocation())))
97 std::string methodNiceName
= niceName(methodDecl
);
99 // If the containing class/struct is final, then the method is effectively final too.
100 if (!methodDecl
->hasAttr
<FinalAttr
>() && !methodDecl
->getParent()->hasAttr
<FinalAttr
>())
101 definitionMap
.insert({ methodNiceName
, toString(methodDecl
->getBeginLoc()) });
103 for (auto it
= methodDecl
->begin_overridden_methods();
104 it
!= methodDecl
->end_overridden_methods(); ++it
)
105 overriddenSet
.insert(niceName(*it
));
110 std::string
FinalMethods::niceName(const CXXMethodDecl
* methodDecl
)
112 if (methodDecl
->getInstantiatedFromMemberFunction())
113 methodDecl
= dyn_cast
<CXXMethodDecl
>(methodDecl
->getInstantiatedFromMemberFunction());
114 else if (methodDecl
->getTemplateInstantiationPattern())
115 methodDecl
= dyn_cast
<CXXMethodDecl
>(methodDecl
->getTemplateInstantiationPattern());
117 std::string returnType
= methodDecl
->getReturnType().getCanonicalType().getAsString();
119 const CXXRecordDecl
* recordDecl
= methodDecl
->getParent();
120 std::string nameAndParams
121 = recordDecl
->getQualifiedNameAsString() + "::" + methodDecl
->getNameAsString() + "(";
124 for (const ParmVarDecl
* pParmVarDecl
: methodDecl
->parameters())
129 nameAndParams
+= ",";
130 nameAndParams
+= pParmVarDecl
->getType().getCanonicalType().getAsString();
132 nameAndParams
+= ")";
133 if (methodDecl
->isConst())
134 nameAndParams
+= " const";
136 return returnType
+ " " + nameAndParams
+ " " + returnType
;
139 std::string
FinalMethods::toString(SourceLocation loc
)
141 SourceLocation expansionLoc
= compiler
.getSourceManager().getExpansionLoc(loc
);
142 StringRef name
= getFilenameOfLocation(expansionLoc
);
143 std::string sourceLocation
144 = std::string(name
.substr(strlen(SRCDIR
) + 1)) + ":"
145 + std::to_string(compiler
.getSourceManager().getSpellingLineNumber(expansionLoc
));
146 loplugin::normalizeDotDotInFilePath(sourceLocation
);
147 return sourceLocation
;
150 loplugin::Plugin::Registration
<FinalMethods
> X("finalmethods", false);
153 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */