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 functionDecl
->getBeginLoc())
48 << pair
.second
<< functionDecl
->getSourceRange();
49 if (functionDecl
!= functionDecl
->getCanonicalDecl())
50 report(DiagnosticsEngine::Note
, "decl here",
51 functionDecl
->getCanonicalDecl()->getBeginLoc())
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 if (compiler
.getSourceManager().isMacroBodyExpansion(functionDecl
->getBeginLoc())
122 || compiler
.getSourceManager().isMacroArgExpansion(functionDecl
->getBeginLoc()))
124 StringRef name
{ Lexer::getImmediateMacroName(
125 functionDecl
->getBeginLoc(), compiler
.getSourceManager(), compiler
.getLangOpts()) };
126 if (name
.find("IMPL_LINK") != StringRef::npos
127 || name
.find("IMPL_STATIC_LINK") != StringRef::npos
128 || name
.find("DECL_LINK") != StringRef::npos
129 || name
.find("SFX_IMPL_POS_CHILDWINDOW_WITHID") != StringRef::npos
)
133 m_functionStack
.emplace_back();
134 bool ret
= RecursiveASTVisitor
<ReturnConstant
>::TraverseCXXMethodDecl(functionDecl
);
135 Context
& rContext
= m_functionStack
.back();
136 if (!rContext
.ignore
&& rContext
.values
.size() == 1
137 && rContext
.values
.find("unknown") == rContext
.values
.end())
139 problemFunctions
.push_back({ functionDecl
, *rContext
.values
.begin() });
141 m_functionStack
.pop_back();
145 bool ReturnConstant::VisitReturnStmt(ReturnStmt
const* returnStmt
)
147 if (ignoreLocation(returnStmt
))
149 if (m_functionStack
.empty())
151 Context
& rContext
= m_functionStack
.back();
153 if (!returnStmt
->getRetValue())
155 if (returnStmt
->getRetValue()->isTypeDependent())
157 rContext
.ignore
= true;
160 if (const UnaryOperator
* unaryOp
= dyn_cast
<UnaryOperator
>(returnStmt
->getRetValue()))
162 if (unaryOp
->getOpcode() == UO_AddrOf
)
164 rContext
.ignore
= true;
168 rContext
.values
.insert(getExprValue(returnStmt
->getRetValue()));
172 std::string
ReturnConstant::getExprValue(Expr
const* arg
)
174 arg
= arg
->IgnoreParenCasts();
175 if (isa
<CXXDefaultArgExpr
>(arg
))
177 arg
= dyn_cast
<CXXDefaultArgExpr
>(arg
)->getExpr();
179 arg
= arg
->IgnoreParenCasts();
180 // ignore this, it seems to trigger an infinite recursion
181 if (isa
<UnaryExprOrTypeTraitExpr
>(arg
))
186 if (compat::EvaluateAsInt(arg
, x1
, compiler
.getASTContext()))
188 return compat::toString(x1
, 10);
190 if (isa
<CXXNullPtrLiteralExpr
>(arg
))
194 if (isa
<MaterializeTemporaryExpr
>(arg
))
196 const CXXBindTemporaryExpr
* strippedArg
197 = dyn_cast_or_null
<CXXBindTemporaryExpr
>(arg
->IgnoreParenCasts());
198 if (strippedArg
&& strippedArg
->getSubExpr())
200 auto temp
= dyn_cast
<CXXTemporaryObjectExpr
>(strippedArg
->getSubExpr());
201 if (temp
->getNumArgs() == 0)
203 if (loplugin::TypeCheck(temp
->getType())
210 if (loplugin::TypeCheck(temp
->getType())
223 loplugin::Plugin::Registration
<ReturnConstant
> X("returnconstant", false);
226 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */