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/.
15 // Methods that purely return a local field should be declared in the header and be declared inline.
16 // So that the compiler can elide the function call and turn it into a simple fixed-offset-load instruction.
20 class InlineSimpleMemberFunctions
:
21 public loplugin::FilteringRewritePlugin
<InlineSimpleMemberFunctions
>
24 explicit InlineSimpleMemberFunctions(loplugin::InstantiationData
const & data
): FilteringRewritePlugin(data
) {}
26 virtual void run() override
{ TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl()); }
28 bool VisitCXXMethodDecl(const CXXMethodDecl
* decl
);
30 bool rewrite(const CXXMethodDecl
* functionDecl
);
33 static bool oneAndOnlyOne(clang::Stmt::const_child_range range
) {
34 if (range
.begin() == range
.end()) {
37 if (++range
.begin() != range
.end()) {
44 bool InlineSimpleMemberFunctions::VisitCXXMethodDecl(const CXXMethodDecl
* functionDecl
) {
45 if (ignoreLocation(functionDecl
)) {
48 // no point in doing virtual methods, the compiler always has to generate a vtable entry and a method
49 if (functionDecl
->isVirtual()) {
52 if (functionDecl
->getTemplatedKind() != FunctionDecl::TK_NonTemplate
) {
55 if (!functionDecl
->isInstance()) {
58 if (!functionDecl
->isOutOfLine()) {
61 if( !functionDecl
->hasBody()) {
64 if( functionDecl
->isInlineSpecified()) {
67 if( functionDecl
->getCanonicalDecl()->isInlineSpecified()) {
70 if( functionDecl
->getNameAsString().find("Impl") != std::string::npos
) {
73 // ignore stuff that forms part of the stable URE interface
74 if (isInUnoIncludeFile(functionDecl
)) {
78 // template<class E> E * Sequence<E>::begin() { return getArray(); }
79 if( functionDecl
->getParent()->getDescribedClassTemplate() != nullptr ) {
84 The chain here looks like
91 const CompoundStmt
* compoundStmt
= dyn_cast
< CompoundStmt
>( functionDecl
->getBody() );
92 if (compoundStmt
== nullptr) {
95 if (compoundStmt
->body_begin() == compoundStmt
->body_end()) {
100 const Stmt
* childStmt
= *compoundStmt
->child_begin();
102 if (dyn_cast
<ReturnStmt
>( childStmt
) == nullptr) {
105 if (!oneAndOnlyOne(childStmt
->children())) {
110 /* Don't warn if we see a method definition like
126 childStmt
= *childStmt
->child_begin();
127 if (dyn_cast
<ImplicitCastExpr
>( childStmt
) != nullptr
128 && oneAndOnlyOne( childStmt
->children() ))
130 const Stmt
* childStmt2
= *childStmt
->child_begin();
131 if (dyn_cast
<UnaryOperator
>( childStmt2
) != nullptr
132 && oneAndOnlyOne(childStmt2
->children()))
134 childStmt2
= *childStmt2
->child_begin();
135 if (dyn_cast
<CXXThisExpr
>( childStmt2
) != nullptr
136 && childStmt2
->children().begin() == childStmt2
->children().end())
142 if (dyn_cast
<UnaryOperator
>( childStmt
) != nullptr
143 && oneAndOnlyOne( childStmt
->children() ))
145 const Stmt
* childStmt2
= *childStmt
->child_begin();
146 if (dyn_cast
<CXXThisExpr
>( childStmt2
) != nullptr
147 && childStmt2
->children().begin() == childStmt2
->children().end())
153 /* look for a chains like:
159 childStmt
= *(*compoundStmt
->child_begin())->child_begin();
161 if (dyn_cast
<CallExpr
>( childStmt
) != nullptr)
163 if (dyn_cast
<CXXNewExpr
>( childStmt
) != nullptr)
165 if (dyn_cast
<CXXConstructExpr
>( childStmt
) != nullptr)
167 if (dyn_cast
<ConditionalOperator
>( childStmt
) != nullptr)
169 if (dyn_cast
<BinaryOperator
>( childStmt
) != nullptr)
171 // exclude methods that return fields on incomplete types .e.g the pImpl pattern
172 const MemberExpr
* memberExpr
= dyn_cast
<MemberExpr
>( childStmt
);
173 if (memberExpr
!= nullptr && memberExpr
->getMemberDecl()) {
174 const FieldDecl
* fieldDecl
= dyn_cast
<FieldDecl
>(memberExpr
->getMemberDecl());
175 if (fieldDecl
!= nullptr)
177 // yes, a little bit of a hack. However, it is quite hard to determine if the method
178 // in question is accessing a field via a pImpl pattern.
179 if (fieldDecl
->getType()->isIncompleteType())
181 if (fieldDecl
->getNameAsString().find("Impl") != std::string::npos
)
183 if (fieldDecl
->getNameAsString().find("pImp") != std::string::npos
)
186 if (fieldDecl
->getNameAsString().find("mpGlobalSyncData") != std::string::npos
)
188 std::string s
= fieldDecl
->getType().getAsString();
189 if (s
.find("Impl") != std::string::npos
|| s
.find("pImp") != std::string::npos
|| s
.find("Internal") != std::string::npos
)
193 if (dyn_cast
<CXXThisExpr
>( childStmt
) != nullptr) {
194 if (!rewrite(functionDecl
))
197 DiagnosticsEngine::Warning
,
198 "inlinesimpleaccessmethods",
199 functionDecl
->getSourceRange().getBegin())
200 << functionDecl
->getSourceRange();
201 // display the location of the class member declaration so I don't have to search for it by hand
203 DiagnosticsEngine::Note
,
204 "inlinesimpleaccessmethods",
205 functionDecl
->getCanonicalDecl()->getSourceRange().getBegin())
206 << functionDecl
->getCanonicalDecl()->getSourceRange();
210 if ( childStmt
->children().begin() == childStmt
->children().end() )
212 childStmt
= *childStmt
->child_begin();
217 static std::string
ReplaceString(std::string subject
, const std::string
& search
,
218 const std::string
& replace
) {
220 while ((pos
= subject
.find(search
, pos
)) != std::string::npos
) {
221 subject
.replace(pos
, search
.length(), replace
);
222 pos
+= replace
.length();
227 bool InlineSimpleMemberFunctions::rewrite(const CXXMethodDecl
* functionDecl
) {
228 if (rewriter
== nullptr) {
231 // Only rewrite declarations in include files if a
232 // definition is also seen, to avoid compilation of a
233 // definition (in a main file only processed later) to fail
234 // with a "mismatch" error before the rewriter had a chance
235 // to act upon the definition.
236 if (!compiler
.getSourceManager().isInMainFile(
237 compiler
.getSourceManager().getSpellingLoc(
238 functionDecl
->getNameInfo().getLoc())))
245 // get the function body contents
246 p1
= compiler
.getSourceManager().getCharacterData( compat::getBeginLoc(functionDecl
->getBody()) );
247 p2
= compiler
.getSourceManager().getCharacterData( compat::getEndLoc(functionDecl
->getBody()) );
248 std::string
s1( p1
, p2
- p1
+ 1);
250 /* we can't safely move around stuff containing comments, we mess up the resulting code */
251 if ( s1
.find("/*") != std::string::npos
|| s1
.find("//") != std::string::npos
) {
255 // strip linefeeds and any double-spaces, so we have a max of one space between tokens
256 s1
= ReplaceString(s1
, "\r", "");
257 s1
= ReplaceString(s1
, "\n", "");
258 s1
= ReplaceString(s1
, "\t", " ");
259 s1
= ReplaceString(s1
, " ", " ");
260 s1
= ReplaceString(s1
, " ", " ");
261 s1
= ReplaceString(s1
, " ", " ");
264 // scan from the end of the function's body through the trailing whitespace, so we can do a nice clean remove
265 // commented out because for some reason it will sometimes chomp an extra token
266 // SourceLocation endOfRemoveLoc = functionDecl->getBody()->getLocEnd();
268 // endOfRemoveLoc = endOfRemoveLoc.getLocWithOffset(1);
269 // p1 = compiler.getSourceManager().getCharacterData( endOfRemoveLoc );
270 // if (*p1 != ' ' && *p1 != '\r' && *p1 != '\n' && *p1 != '\t')
274 // remove the function's out of line body and declaration
276 opts
.RemoveLineIfEmpty
= true;
277 if (!removeText(SourceRange(compat::getBeginLoc(functionDecl
), compat::getEndLoc(functionDecl
->getBody())), opts
)) {
281 // scan forward until we find the semicolon
282 const FunctionDecl
* canonicalDecl
= functionDecl
->getCanonicalDecl();
283 p1
= compiler
.getSourceManager().getCharacterData( compat::getEndLoc(canonicalDecl
) );
285 while (*p2
!= 0 && *p2
!= ';') p2
++;
287 // insert the function body into the inline function definition (i.e. the one inside the class definition)
288 return replaceText(compat::getEndLoc(canonicalDecl
).getLocWithOffset(p2
- p1
+ 1), 1, s1
);
291 loplugin::Plugin::Registration
< InlineSimpleMemberFunctions
> X("inlinesimplememberfunctions");
295 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */