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/.
14 Look for member functions that merely return a compile-time constant, or they are empty, and can thus
15 be either removed, or converted into a constant.
17 This mostly tends to happen as a side-effect of other cleanups.
21 class ConstantFunction
:
22 public loplugin::FilteringPlugin
<ConstantFunction
>
24 StringRef
getFilename(const FunctionDecl
* functionDecl
);
26 explicit ConstantFunction(InstantiationData
const & data
): FilteringRewritePlugin(data
) {}
30 // these files crash clang-3.5 somewhere in the isEvaluatable/EvaluateAsXXX stuff
31 /* FileID mainFileID = compiler.getSourceManager().getMainFileID();
32 if (strstr(compiler.getSourceManager().getFileEntryForID(mainFileID)->getName(), "bootstrapfixture.cxx") != 0) {
35 if (strstr(compiler.getSourceManager().getFileEntryForID(mainFileID)->getName(), "gtk3gtkinst.cxx") != 0) {
39 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
42 bool VisitFunctionDecl(const FunctionDecl
*);
45 StringRef
ConstantFunction::getFilename(const FunctionDecl
* functionDecl
)
47 SourceLocation spellingLocation
= compiler
.getSourceManager().getSpellingLoc(functionDecl
->getCanonicalDecl()->getNameInfo().getLoc());
48 StringRef name
{ compiler
.getSourceManager().getFilename(spellingLocation
) };
52 static bool startsWith(const std::string
& rStr
, const char* pSubStr
) {
53 return rStr
.compare(0, strlen(pSubStr
), pSubStr
) == 0;
56 bool ConstantFunction::VisitFunctionDecl(const FunctionDecl
* pFunctionDecl
) {
57 if (ignoreLocation(pFunctionDecl
)) {
60 if (!pFunctionDecl
->hasBody()) {
63 if (!pFunctionDecl
->isThisDeclarationADefinition()) {
66 // stuff declared extern-C is almost always used as a some kind of callback
67 if (pFunctionDecl
->isExternC()) {
70 if (pFunctionDecl
->isConstexpr()) {
73 if (pFunctionDecl
->isMain()) {
77 StringRef aFileName
= getFilename(pFunctionDecl
);
79 // various tests in here are empty stubs under Linux
80 if (aFileName
.startswith(SRCDIR
"/sal/qa/")) {
83 // lots of empty stuff here where it looks like someone is still going to "fill in the blanks"
84 if (aFileName
.startswith(SRCDIR
"/basegfx/test/")) {
87 // bridges has some weird stuff in it....
88 if (aFileName
.startswith(SRCDIR
"/bridges/")) {
91 // dummy implementation of DDE, since it is only active on Windows
92 if (aFileName
== SRCDIR
"/svl/unx/source/svdde/ddedummy.cxx"
93 || aFileName
== SRCDIR
"/include/svl/svdde.hxx") {
96 // fancy templates at work here
97 if (aFileName
== SRCDIR
"/vcl/source/gdi/bmpfast.cxx") {
100 // bunch of stuff used as callbacks here
101 if (aFileName
== SRCDIR
"/vcl/generic/glyphs/gcach_layout.cxx") {
104 // salplug runtime-loading mechanism at work
105 if (aFileName
== SRCDIR
"/vcl/inc/salinst.hxx") {
108 // lots of callbacks here
109 if (aFileName
== SRCDIR
"/extensions/source/plugin/unx/npnapi.cxx") {
112 // vcl/unx/gtk3 re-using vcl/unx/gtk:
113 if (aFileName
.find("/../../gtk/") != std::string::npos
) {
116 // used by code generated by python
117 if (aFileName
== SRCDIR
"/writerfilter/source/ooxml/OOXMLFastContextHandler.hxx") {
120 // this test just test the include of some headers
121 if (aFileName
== SRCDIR
"/officecfg/qa/cppheader.cxx") {
124 // just ignore this for now, people furiously hacking in there
125 if (startsWith(aFileName
, SRCDIR
"/libreofficekit")) {
130 const CXXMethodDecl
*pCXXMethodDecl
= dyn_cast
<CXXMethodDecl
>(pFunctionDecl
);
131 if (pCXXMethodDecl
) {
132 if (pCXXMethodDecl
->isVirtual()) {
135 // static with inline body will be optimised at compile-time to a constant anyway
136 if (pCXXMethodDecl
->isStatic() && (pCXXMethodDecl
->hasInlineBody() || pCXXMethodDecl
->isInlineSpecified())) {
139 // this catches some stuff in templates
140 if (pFunctionDecl
->hasAttr
<OverrideAttr
>()) {
144 // a free function with an inline body will be optimised at compile-time to a constant anyway
145 if (!pCXXMethodDecl
&& pFunctionDecl
->isInlineSpecified()) {
148 if (isa
<CXXConstructorDecl
>(pFunctionDecl
) || isa
<CXXDestructorDecl
>(pFunctionDecl
) || isa
<CXXConversionDecl
>(pFunctionDecl
)) {
151 if (isInUnoIncludeFile(pFunctionDecl
)) {
155 switch (pFunctionDecl
->getOverloadedOperator()) {
164 std::string aFunctionName
= pFunctionDecl
->getQualifiedNameAsString();
166 // something to do with dynamic loading in sal/textenc/textenc.cxx
167 if (aFunctionName
== "thisModule") {
170 // an empty stub under certain conditions, sal/osl/unx/thread.cxx
171 if (aFunctionName
== "osl_thread_priority_init_Impl") {
174 // a pointer to this function is taken and passed to an underlying API, cppu/source/uno/lbenv.cxx
175 if (aFunctionName
== "defenv_dispose") {
178 // a pointer to this function is taken and passed to an underlying API, cppuhelper/source/exc_thrower.cxx
179 if (aFunctionName
== "ExceptionThrower_acquire_release_nop") {
182 // used as a callback, /vcl/source/filter/jpeg/JpegReader.cxx
183 if (aFunctionName
== "term_source") {
186 // only valid for windows, extensions/source/update/check/updatecheck.cxx
187 if (aFunctionName
== "(anonymous namespace)::UpdateCheckThread::hasInternetConnection") {
190 // used as callback, extensions/source/plugin/unx/npwrap.cxx
191 if (aFunctionName
== "plugin_x_error_handler" || aFunctionName
== "noClosure") {
194 // used as callback, sax/source/expatwrap/sax_expat.cxx
195 if (aFunctionName
== "(anonymous namespace)::SaxExpatParser_Impl::callbackUnknownEncoding") {
198 // used as callback, i18npool/source/textconversion/textconversion.cxx
199 if (aFunctionName
== "com::sun::star::i18n::nullFunc") {
202 // used as callback, xmloff/source/text/txtparae.cxx
203 if (aFunctionName
== "(anonymous namespace)::lcl_TextContentsUnfiltered") {
206 // template magic, include/canvas/verifyinput.hxx
207 if (aFunctionName
== "canvas::tools::verifyInput") {
210 // template magic, cppcanvas/source/mtfrenderer/implrenderer.cxx
211 if (aFunctionName
== "cppcanvas::internal::(anonymous namespace)::AreaQuery::result") {
214 // callback, drawinglayer/source/dumper/XShapeDumper.
215 if (aFunctionName
== "(anonymous namespace)::closeCallback") {
218 // callback, basic/source/runtime/runtime.cxx
219 if (aFunctionName
== "SbiRuntime::StepNOP") {
222 // DLL stuff, only used on windows, basic/source/runtime/dllmgr.hxx
223 if (aFunctionName
== "SbiDllMgr::FreeDll") {
226 // only used on Windows, basic/source/sbx/sbxdec.cxx
227 if (aFunctionName
== "SbxDecimal::neg" || aFunctionName
== "SbxDecimal::isZero") {
230 // used as a callback, include/sfx2/shell.hxx
231 if (aFunctionName
== "SfxShell::EmptyExecStub" || aFunctionName
== "SfxShell::EmptyStateStub"
232 || aFunctionName
== "SfxShell::VerbState") {
235 // SFX_IMPL_POS_CHILDWINDOW_WITHID macro
236 if (aFunctionName
.find("GetChildWindowId") != std::string::npos
) {
239 // SFX_IMPL_SUPERCLASS_INTERFACE macro
240 if (aFunctionName
.find("InitInterface_Impl") != std::string::npos
) {
243 // callback, vcl/unx/generic/app/sm.cxx
244 if (aFunctionName
== "IgnoreIceIOErrors" || aFunctionName
== "IgnoreIceErrors") {
247 // callback, vcl/unx/gtk/a11y/atkcomponent.cxx
248 if (aFunctionName
== "component_wrapper_get_mdi_zorder") {
251 // callback, vcl/unx/gtk/a11y/atkaction.cxx
252 if (aFunctionName
== "action_wrapper_set_description") {
255 // callback, vcl/unx/gtk/a11y/atkutil.cxx
256 if (aFunctionName
== "ooo_atk_util_get_toolkit_version" || aFunctionName
== "ooo_atk_util_get_toolkit_name") {
259 // callback, vcl/unx/gtk/a11y/atktextattributes.cxx
260 if (aFunctionName
== "InvalidValue") {
263 // callback, vcl/unx/gtk/a11y/atktable.cxx
264 if (aFunctionName
== "table_wrapper_set_summary" || aFunctionName
== "table_wrapper_set_row_header"
265 || aFunctionName
== "table_wrapper_set_row_description"
266 || aFunctionName
== "table_wrapper_set_column_header"
267 || aFunctionName
== "table_wrapper_set_column_description"
268 || aFunctionName
== "table_wrapper_set_caption") {
271 // callbacks, vcl/unx/gtk/window/gtksalframe.cxx
272 if (startsWith(aFunctionName
, "GtkSalFrame::IMHandler::signal")) {
275 // callbacks, vcl/unx/gtk/window/glomenu.cxx
276 if (startsWith(aFunctionName
, "g_lo_menu_is_mutable")) {
279 // only contains code for certain versions of GTK, /vcl/unx/gtk/window/gtksalframe.cx
280 if (aFunctionName
== "GtkSalFrame::AllocateFrame") {
283 // only valid for Windows, embeddedobj/source/msole/olemisc.cxx
284 if (aFunctionName
== "OleEmbeddedObject::GetRidOfComponent") {
287 // callback, svx/source/accessibility/ShapeTypeHandler.cxx
288 if (aFunctionName
== "accessibility::CreateEmptyShapeReference") {
291 // chart2/source/view/main/AbstractShapeFactory.cxx
292 if (aFunctionName
== "chart::(anonymous namespace)::thisModule") {
295 // chart2/source/tools/InternalData.cxx
296 if (aFunctionName
== "chart::InternalData::dump") {
300 if (aFunctionName
== "debug" || aFunctionName
== "token_debug") {
303 // callback, sdext/source/presenter/PresenterFrameworkObserver.cxx
304 if (aFunctionName
== "sdext::presenter::PresenterFrameworkObserver::True") {
307 // callback, sw/source/core/doc/tblrwcl.cxx
308 if (aFunctionName
== "lcl_DelOtherBox") {
311 // callback, sw/source/filter/ww8/ww8par.cxx
312 if (aFunctionName
== "SwWW8ImplReader::Read_Majority") {
315 // callback, sw/source/filter/ww8/ww8par5.cxx
316 if (aFunctionName
== "SwWW8ImplReader::Read_F_Shape") {
319 // called from SDI file, I don't know what that stuff is about, sd/source/ui/slidesorter/shell/SlideSorterViewShell.cx
320 if (aFunctionName
== "sd::slidesorter::SlideSorterViewShell::ExecStatusBar"
321 || aFunctionName
== "sd::OutlineViewShell::ExecStatusBar") {
324 // only used in debug mode, sd/source/filter/ppt/pptinanimations.cxx
325 if (startsWith(aFunctionName
, "ppt::AnimationImporter::dump")) {
328 // only used in ENABLE_SDREMOTE_BLUETOOTH mode, sd/source/ui/dlg/tpoption.cx
329 if (aFunctionName
== "SdTpOptionsMisc::SetImpressMode") {
332 // template magic, sc/source/ui/docshell/datastream.cxx
333 if (startsWith(aFunctionName
, "sc::(anonymous namespace)::CSVHandler::")) {
336 // called from SDI file, I don't know what that stuff is about, sc/source/ui/view/cellsh4.cxx
337 if (aFunctionName
== "ScCellShell::GetStateCursor") {
340 // template magic, sc/source/filter/excel/xepivot.cxx
341 if (aFunctionName
== "XclExpPivotCache::SaveXml") {
344 // template magic, sc/source/filter/html/htmlpars.cxx
345 if (startsWith(aFunctionName
, "(anonymous namespace)::CSSHandler::")) {
348 // callbacks, sc/source/filter/oox/formulaparser.cxx
349 if (startsWith(aFunctionName
, "oox::xls::BiffFormulaParserImpl::import")) {
352 // template magic, sc/qa/unit/helper/csv_handler.hxx
353 if (startsWith(aFunctionName
, "csv_handler::") || startsWith(aFunctionName
, "conditional_format_handler::")) {
356 // template magic, slideshow/source/inc/listenercontainer.hxx
357 if (startsWith(aFunctionName
, "slideshow::internal::EmptyBase::EmptyClearableGuard::")) {
360 // callback, scripting/source/vbaevents/eventhelper.cxx
361 if (aFunctionName
== "ApproveAll") {
364 // only on WNT, basic/qa/cppunit/test_vba.cx
365 if (aFunctionName
== "(anonymous namespace)::VBATest::testMiscOLEStuff") {
368 // GtkSalFrame::TriggerPaintEvent() is only compiled under certain versions of GTK
369 if (aFunctionName
== "GtkSalFrame::TriggerPaintEvent") {
372 if (aFunctionName
== "SwVectorModifyBase::dumpAsXml") {
375 // vcl/unx/gtk3 re-using vcl/unx/gtk:
376 if (aFunctionName
== "DeInitAtkBridge"
377 || aFunctionName
== "GtkData::initNWF"
378 || aFunctionName
== "GtkSalFrame::EnsureAppMenuWatch"
379 || aFunctionName
== "InitAtkBridge")
383 if (aFunctionName
== "sc::AlignedAllocator::operator!=") {
386 if (aFunctionName
== "clipboard_owner_init") {
389 // returns sizeof(struct) vcl/source/gdi/dibtools.cxx
390 if (aFunctionName
== "getDIBV5HeaderSize") {
394 if (aFunctionName
== "InitAccessBridge") {
398 if (aFunctionName
== "disabled_initSystray" || aFunctionName
== "disabled_deInitSystray") {
401 // behind a BREAKPAD option
402 if (aFunctionName
== "desktop::(anonymous namespace)::crashReportInfoExists") {
406 if (aFunctionName
== "doc_getTileMode") {
409 // apparently this will be useful at sometime in the future
410 if (aFunctionName
== "LocaleDataWrapper::getCurrZeroChar") {
414 if (aFunctionName
== "oglcanvas::TextLayout::draw") {
417 // called from the .sdi files
418 if (aFunctionName
== "SfxObjectShell::StateView_Impl") {
422 if (aFunctionName
== "GtkSalFrame::signalVisibility") {
425 // platform-version-dependent code
426 if (aFunctionName
== "(anonymous namespace)::ACTIVE_TAB") {
430 if (aFunctionName
== "boost::sp_scalar_constructor_hook" || aFunctionName
== "boost::sp_scalar_destructor_hook") {
437 std::string aImmediateMacro
= "";
438 if (compiler
.getSourceManager().isMacroBodyExpansion(pFunctionDecl
->getLocStart()) ) {
439 StringRef name
{ Lexer::getImmediateMacroName(
440 pFunctionDecl
->getLocStart(), compiler
.getSourceManager(), compiler
.getLangOpts()) };
441 aImmediateMacro
= name
;
442 if (name
.startswith("IMPL_LINK_") )
448 const CompoundStmt
*pCompoundStmt
= dyn_cast
<CompoundStmt
>(pFunctionDecl
->getBody());
449 bool bEmptyBody
= false;
451 if (pCompoundStmt
->size() > 1) {
454 if (pCompoundStmt
->size() > 0) {
455 const ReturnStmt
*pReturnStmt
= dyn_cast
<ReturnStmt
>(*pCompoundStmt
->body_begin());
459 if (const UnaryOperator
* unaryOp
= dyn_cast
<UnaryOperator
>(pReturnStmt
->getRetValue())) {
460 if (unaryOp
->getOpcode() == UO_AddrOf
) {
464 if (pReturnStmt
->getRetValue() != nullptr) {
465 // && !pReturnStmt->getRetValue()->isEvaluatable(compiler.getASTContext())) {
467 llvm::APSInt aIntResult
;
468 if (pReturnStmt
->getRetValue()->isTypeDependent()
469 || (!pReturnStmt
->getRetValue()->EvaluateAsBooleanCondition(aBoolResult
, compiler
.getASTContext())
470 && !pReturnStmt
->getRetValue()->EvaluateAsInt(aIntResult
, compiler
.getASTContext())))
480 std::string aMessage
= "this ";
481 aMessage
+= pCXXMethodDecl
? "method" : "function";
483 aMessage
+= " is empty and should be removed, " + aFunctionName
;
485 aMessage
+= " returns a constant value and should be converted to a constant "
486 "or to static inline, " + aFunctionName
+ ", " + aImmediateMacro
;
489 DiagnosticsEngine::Warning
,
491 pFunctionDecl
->getLocStart())
492 << pFunctionDecl
->getSourceRange();
493 if (pFunctionDecl
!= pFunctionDecl
->getCanonicalDecl())
495 DiagnosticsEngine::Note
,
497 pFunctionDecl
->getCanonicalDecl()->getLocStart())
498 << pFunctionDecl
->getCanonicalDecl()->getSourceRange();
502 loplugin::Plugin::Registration
<ConstantFunction
> X("constantfunction");
506 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */