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/.
13 #include "functionaddress.hxx"
19 Look for member functions that merely return a compile-time constant, or they are empty, and can thus
20 be either removed, or converted into a constant.
22 This mostly tends to happen as a side-effect of other cleanups.
26 class ReturnConstant
: public loplugin::FunctionAddress
<loplugin::FilteringPlugin
<ReturnConstant
>>
29 explicit ReturnConstant(loplugin::InstantiationData
const& data
)
30 : FunctionAddress(data
)
36 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
38 for (auto& pair
: problemFunctions
)
40 auto functionDecl
= pair
.first
;
41 auto canonicalDecl
= functionDecl
->getCanonicalDecl();
42 if (getFunctionsWithAddressTaken().find(canonicalDecl
)
43 != getFunctionsWithAddressTaken().end())
45 report(DiagnosticsEngine::Warning
,
46 "Method only returns a single constant value %0, does it make sense?",
47 compat::getBeginLoc(functionDecl
))
48 << pair
.second
<< functionDecl
->getSourceRange();
49 if (functionDecl
!= functionDecl
->getCanonicalDecl())
50 report(DiagnosticsEngine::Note
, "decl here",
51 compat::getBeginLoc(functionDecl
->getCanonicalDecl()))
52 << functionDecl
->getCanonicalDecl()->getSourceRange();
56 bool TraverseCXXMethodDecl(CXXMethodDecl
*);
57 bool VisitReturnStmt(ReturnStmt
const*);
60 std::string
getExprValue(Expr
const* arg
);
65 std::set
<std::string
> values
;
67 std::vector
<Context
> m_functionStack
;
68 std::vector
<std::pair
<FunctionDecl
const*, std::string
>> problemFunctions
;
71 bool ReturnConstant::TraverseCXXMethodDecl(CXXMethodDecl
* functionDecl
)
73 if (ignoreLocation(functionDecl
))
75 if (isInUnoIncludeFile(functionDecl
))
78 if (!functionDecl
->hasBody())
80 if (!functionDecl
->isThisDeclarationADefinition())
82 if (functionDecl
->isConstexpr())
84 if (functionDecl
->getReturnType()->isVoidType())
86 if (functionDecl
->isVirtual())
88 // static with inline body will be optimised at compile-time to a constant anyway
89 if (functionDecl
->isStatic()
90 && (functionDecl
->hasInlineBody() || functionDecl
->isInlineSpecified()))
92 // this catches some stuff in templates
93 if (functionDecl
->hasAttr
<OverrideAttr
>())
96 // include/unotools/localedatawrapper.hxx
97 if (functionDecl
->getIdentifier() && functionDecl
->getName() == "getCurrZeroChar")
99 // sc/inc/stlalgorithm.hxx
100 if (loplugin::DeclCheck(functionDecl
->getParent())
101 .Class("AlignedAllocator")
106 switch (functionDecl
->getOverloadedOperator())
116 // gtk signals and slots stuff
117 if (loplugin::TypeCheck(functionDecl
->getReturnType()).Typedef("gboolean"))
120 // ignore LINK macro stuff
121 std::string aImmediateMacro
= "";
122 if (compiler
.getSourceManager().isMacroBodyExpansion(compat::getBeginLoc(functionDecl
))
123 || compiler
.getSourceManager().isMacroArgExpansion(compat::getBeginLoc(functionDecl
)))
125 StringRef name
{ Lexer::getImmediateMacroName(compat::getBeginLoc(functionDecl
),
126 compiler
.getSourceManager(),
127 compiler
.getLangOpts()) };
128 aImmediateMacro
= name
;
129 if (name
.find("IMPL_LINK") != StringRef::npos
130 || name
.find("IMPL_STATIC_LINK") != StringRef::npos
131 || name
.find("DECL_LINK") != StringRef::npos
132 || name
.find("SFX_IMPL_POS_CHILDWINDOW_WITHID") != StringRef::npos
)
136 m_functionStack
.emplace_back();
137 bool ret
= RecursiveASTVisitor
<ReturnConstant
>::TraverseCXXMethodDecl(functionDecl
);
138 Context
& rContext
= m_functionStack
.back();
139 if (!rContext
.ignore
&& rContext
.values
.size() == 1
140 && rContext
.values
.find("unknown") == rContext
.values
.end())
142 problemFunctions
.push_back({ functionDecl
, *rContext
.values
.begin() });
144 m_functionStack
.pop_back();
148 bool ReturnConstant::VisitReturnStmt(ReturnStmt
const* returnStmt
)
150 if (ignoreLocation(returnStmt
))
152 if (m_functionStack
.empty())
154 Context
& rContext
= m_functionStack
.back();
156 if (!returnStmt
->getRetValue())
158 if (returnStmt
->getRetValue()->isTypeDependent())
160 rContext
.ignore
= true;
163 if (const UnaryOperator
* unaryOp
= dyn_cast
<UnaryOperator
>(returnStmt
->getRetValue()))
165 if (unaryOp
->getOpcode() == UO_AddrOf
)
167 rContext
.ignore
= true;
171 rContext
.values
.insert(getExprValue(returnStmt
->getRetValue()));
175 std::string
ReturnConstant::getExprValue(Expr
const* arg
)
177 arg
= arg
->IgnoreParenCasts();
178 if (isa
<CXXDefaultArgExpr
>(arg
))
180 arg
= dyn_cast
<CXXDefaultArgExpr
>(arg
)->getExpr();
182 arg
= arg
->IgnoreParenCasts();
183 // ignore this, it seems to trigger an infinite recursion
184 if (isa
<UnaryExprOrTypeTraitExpr
>(arg
))
189 if (compat::EvaluateAsInt(arg
, x1
, compiler
.getASTContext()))
191 return x1
.toString(10);
193 if (isa
<CXXNullPtrLiteralExpr
>(arg
))
197 if (isa
<MaterializeTemporaryExpr
>(arg
))
199 const CXXBindTemporaryExpr
* strippedArg
200 = dyn_cast_or_null
<CXXBindTemporaryExpr
>(arg
->IgnoreParenCasts());
201 if (strippedArg
&& strippedArg
->getSubExpr())
203 auto temp
= dyn_cast
<CXXTemporaryObjectExpr
>(strippedArg
->getSubExpr());
204 if (temp
->getNumArgs() == 0)
206 if (loplugin::TypeCheck(temp
->getType())
213 if (loplugin::TypeCheck(temp
->getType())
226 loplugin::Plugin::Registration
<ReturnConstant
> X("returnconstant", false);
229 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */