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 Look for member functions that merely return a compile-time constant, or they are empty, and can thus
16 be either removed, or converted into a constant.
18 This mostly tends to happen as a side-effect of other cleanups.
22 class ConstantFunction
:
23 public RecursiveASTVisitor
<ConstantFunction
>, public loplugin::Plugin
25 StringRef
getFilename(SourceLocation loc
);
27 explicit ConstantFunction(InstantiationData
const & data
): Plugin(data
) {}
31 // these files crash clang-3.5 somewhere in the isEvaluatable/EvaluateAsXXX stuff
32 FileID mainFileID
= compiler
.getSourceManager().getMainFileID();
33 if (strstr(compiler
.getSourceManager().getFileEntryForID(mainFileID
)->getDir()->getName(), "sc/source/core/data") != 0) {
36 if (strstr(compiler
.getSourceManager().getFileEntryForID(mainFileID
)->getDir()->getName(), "sc/source/ui/app") != 0) {
39 if (strstr(compiler
.getSourceManager().getFileEntryForID(mainFileID
)->getDir()->getName(), "sc/qa/unit") != 0) {
42 if (strstr(compiler
.getSourceManager().getFileEntryForID(mainFileID
)->getName(), "docuno.cxx") != 0) {
45 if (strstr(compiler
.getSourceManager().getFileEntryForID(mainFileID
)->getName(), "viewdata.cxx") != 0) {
48 if (strstr(compiler
.getSourceManager().getFileEntryForID(mainFileID
)->getName(), "calcoptionsdlg.cxx") != 0) {
51 if (strstr(compiler
.getSourceManager().getFileEntryForID(mainFileID
)->getDir()->getName(), "sc/source/core/opencl") != 0) {
54 if (strstr(compiler
.getSourceManager().getFileEntryForID(mainFileID
)->getDir()->getName(), "sc/source/core/tool") != 0) {
58 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
61 bool VisitFunctionDecl(const FunctionDecl
*);
64 StringRef
ConstantFunction::getFilename(SourceLocation loc
)
66 SourceLocation spellingLocation
= compiler
.getSourceManager().getSpellingLoc(loc
);
67 StringRef name
{ compiler
.getSourceManager().getFilename(spellingLocation
) };
71 static bool startsWith(const std::string
& rStr
, const char* pSubStr
) {
72 return rStr
.compare(0, strlen(pSubStr
), pSubStr
) == 0;
75 bool ConstantFunction::VisitFunctionDecl(const FunctionDecl
* pFunctionDecl
) {
76 if (ignoreLocation(pFunctionDecl
)) {
79 if (!pFunctionDecl
->hasBody()) {
82 // stuff declared extern-C is almost always used as a some kind of callback
83 if (pFunctionDecl
->isExternC()) {
87 StringRef aFileName
= getFilename(pFunctionDecl
->getLocStart());
89 // various tests in here are empty stubs under Linux
90 if (aFileName
.startswith(SRCDIR
"/sal/qa/")) {
93 // lots of empty stuff here where it looks like someone is still going to "fill in the blanks"
94 if (aFileName
.startswith(SRCDIR
"/basegfx/test/")) {
97 // some stuff is just stubs under Linux, although this appears to be a SOLARIS-specific hack, so it
98 // should probably not even be compiling under Linux.
99 if (aFileName
== SRCDIR
"/setup_native/scripts/source/getuid.c") {
102 // bridges has some weird stuff in it....
103 if (aFileName
.startswith(SRCDIR
"/bridges/")) {
106 // dummy implementation of DDE, since it is only active on Windows
107 if (aFileName
== SRCDIR
"/svl/unx/source/svdde/ddedummy.cxx"
108 || aFileName
== SRCDIR
"/include/svl/svdde.hxx") {
111 // fancy templates at work here
112 if (aFileName
== SRCDIR
"/vcl/source/gdi/bmpfast.cxx") {
115 // bunch of stuff used as callbacks here
116 if (aFileName
== SRCDIR
"/vcl/generic/glyphs/gcach_layout.cxx") {
119 // salplug runtime-loading mechanism at work
120 if (getFilename(pFunctionDecl
->getCanonicalDecl()->getLocStart()) == SRCDIR
"/vcl/inc/salinst.hxx") {
123 // lots of callbacks here
124 if (aFileName
== SRCDIR
"/extensions/source/plugin/unx/npnapi.cxx") {
128 if (aFileName
== SRCDIR
"/filter/source/svg/svgreader.cxx") {
131 // vcl/unx/gtk3 re-using vcl/unx/gtk:
132 if (aFileName
.find("/../../gtk/") != std::string::npos
) {
135 // used by code generated by python
136 if (getFilename(pFunctionDecl
->getCanonicalDecl()->getLocStart()) == SRCDIR
"/writerfilter/source/ooxml/OOXMLFastContextHandler.hxx") {
141 const CXXMethodDecl
*pCXXMethodDecl
= dyn_cast
<CXXMethodDecl
>(pFunctionDecl
);
142 if (pCXXMethodDecl
) {
143 if (pCXXMethodDecl
->isVirtual()) {
146 // static with inline body will be optimised at compile-time to a constant anyway
147 if (pCXXMethodDecl
->isStatic() && pCXXMethodDecl
->hasInlineBody()) {
150 // this catches some stuff in templates
151 if (pFunctionDecl
->hasAttr
<OverrideAttr
>()) {
155 // a free function with an inline body will be optimised at compile-time to a constant anyway
156 if (!pCXXMethodDecl
&& pFunctionDecl
->isInlineSpecified()) {
159 if (isa
<CXXConstructorDecl
>(pFunctionDecl
) || isa
<CXXDestructorDecl
>(pFunctionDecl
) || isa
<CXXConversionDecl
>(pFunctionDecl
)) {
162 SourceLocation canonicalLoc
= pFunctionDecl
->getCanonicalDecl()->getLocStart();
163 if (isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(canonicalLoc
))) {
167 switch (pFunctionDecl
->getOverloadedOperator()) {
176 std::string aFunctionName
= pFunctionDecl
->getQualifiedNameAsString();
178 // something to do with dynamic loading in sal/textenc/textenc.cxx
179 if (aFunctionName
== "thisModule") {
182 // an empty stub under certain conditions, sal/osl/unx/thread.cxx
183 if (aFunctionName
== "osl_thread_priority_init_Impl") {
186 // a pointer to this function is taken and passed to an underlying API, shell/source/unix/sysshell/recently_used_file_handler.cxx
187 if (aFunctionName
== "(anonymous namespace)::recently_used_item::set_nothing") {
190 // a pointer to this function is taken and passed to an underlying API, cppu/source/uno/lbenv.cxx
191 if (aFunctionName
== "defenv_dispose") {
194 // a pointer to this function is taken and passed to an underlying API, cppuhelper/source/exc_thrower.cxx
195 if (aFunctionName
== "ExceptionThrower_acquire_release_nop") {
198 // differetnt hook function is called on different platforms, /vcl/source/app/svmainhook.cxx
199 if (aFunctionName
== "ImplSVMainHook") {
202 // used as a callback, /vcl/source/filter/jpeg/JpegReader.cxx
203 if (aFunctionName
== "term_source") {
206 // only valid for windows, extensions/source/update/check/updatecheck.cxx
207 if (aFunctionName
== "(anonymous namespace)::UpdateCheckThread::hasInternetConnection") {
210 // used as callback, extensions/source/plugin/unx/npwrap.cxx
211 if (aFunctionName
== "plugin_x_error_handler" || aFunctionName
== "noClosure") {
214 // used as callback, sax/source/expatwrap/sax_expat.cxx
215 if (aFunctionName
== "(anonymous namespace)::SaxExpatParser_Impl::callbackUnknownEncoding") {
218 // used as callback, i18npool/source/textconversion/textconversion.cxx
219 if (aFunctionName
== "com::sun::star::i18n::nullFunc") {
222 // used as callback, xmloff/source/text/txtparae.cxx
223 if (aFunctionName
== "(anonymous namespace)::lcl_TextContentsUnfiltered") {
226 // template magic, include/canvas/verifyinput.hxx
227 if (aFunctionName
== "canvas::tools::verifyInput") {
230 // template magic, cppcanvas/source/mtfrenderer/implrenderer.cxx
231 if (aFunctionName
== "cppcanvas::internal::(anonymous namespace)::AreaQuery::result") {
234 // callback, drawinglayer/source/dumper/XShapeDumper.
235 if (aFunctionName
== "(anonymous namespace)::closeCallback") {
238 // callback, basic/source/runtime/runtime.cxx
239 if (aFunctionName
== "SbiRuntime::StepNOP") {
242 // DLL stuff, only used on windows, basic/source/runtime/dllmgr.hxx
243 if (aFunctionName
== "SbiDllMgr::FreeDll") {
246 // only used on Windows, basic/source/sbx/sbxdec.cxx
247 if (aFunctionName
== "SbxDecimal::neg" || aFunctionName
== "SbxDecimal::isZero") {
250 // used as a callback, include/sfx2/shell.hxx
251 if (aFunctionName
== "SfxShell::EmptyExecStub" || aFunctionName
== "SfxShell::EmptyStateStub"
252 || aFunctionName
== "SfxShell::VerbState") {
255 // SFX_IMPL_POS_CHILDWINDOW_WITHID macro
256 if (aFunctionName
.find("GetChildWindowId") != std::string::npos
) {
259 // SFX_IMPL_SUPERCLASS_INTERFACE macro
260 if (aFunctionName
.find("InitInterface_Impl") != std::string::npos
) {
263 // callback, vcl/unx/generic/app/sm.cxx
264 if (aFunctionName
== "IgnoreIceIOErrors" || aFunctionName
== "IgnoreIceErrors") {
267 // callback, vcl/unx/gtk/a11y/atkcomponent.cxx
268 if (aFunctionName
== "component_wrapper_get_mdi_zorder") {
271 // callback, vcl/unx/gtk/a11y/atkaction.cxx
272 if (aFunctionName
== "action_wrapper_set_description") {
275 // callback, vcl/unx/gtk/a11y/atkutil.cxx
276 if (aFunctionName
== "ooo_atk_util_get_toolkit_version" || aFunctionName
== "ooo_atk_util_get_toolkit_name") {
279 // callback, vcl/unx/gtk/a11y/atktextattributes.cxx
280 if (aFunctionName
== "InvalidValue") {
283 // callback, vcl/unx/gtk/a11y/atktable.cxx
284 if (aFunctionName
== "table_wrapper_set_summary" || aFunctionName
== "table_wrapper_set_row_header"
285 || aFunctionName
== "table_wrapper_set_row_description"
286 || aFunctionName
== "table_wrapper_set_column_header"
287 || aFunctionName
== "table_wrapper_set_column_description"
288 || aFunctionName
== "table_wrapper_set_caption") {
291 // callbacks, vcl/unx/gtk/window/gtksalframe.cxx
292 if (startsWith(aFunctionName
, "GtkSalFrame::IMHandler::signal")) {
295 // callbacks, vcl/unx/gtk/window/glomenu.cxx
296 if (startsWith(aFunctionName
, "g_lo_menu_is_mutable")) {
299 // only contains code for certain versions of GTK, /vcl/unx/gtk/window/gtksalframe.cx
300 if (aFunctionName
== "GtkSalFrame::AllocateFrame") {
303 // only valid for Windows, embeddedobj/source/msole/olemisc.cxx
304 if (aFunctionName
== "OleEmbeddedObject::GetRidOfComponent") {
307 // callback, svx/source/accessibility/ShapeTypeHandler.cxx
308 if (aFunctionName
== "accessibility::CreateEmptyShapeReference") {
311 // chart2/source/view/main/AbstractShapeFactory.cxx
312 if (aFunctionName
== "chart::(anonymous namespace)::thisModule") {
315 // chart2/source/tools/InternalData.cxx
316 if (aFunctionName
== "chart::InternalData::dump") {
320 if (aFunctionName
== "debug" || aFunctionName
== "token_debug") {
323 // callback, sdext/source/presenter/PresenterFrameworkObserver.cxx
324 if (aFunctionName
== "sdext::presenter::PresenterFrameworkObserver::True") {
327 // hidden behind the ENABLE_PANE_RESIZING macro
328 if (aFunctionName
== "sdext::presenter::PresenterWindowManager::UpdateWindowList") {
331 // callback, sw/source/core/doc/tblrwcl.cxx
332 if (aFunctionName
== "lcl_DelOtherBox") {
335 // callback, sw/source/filter/ww8/ww8par.cxx
336 if (aFunctionName
== "SwWW8ImplReader::Read_Majority") {
339 // callback, sw/source/filter/ww8/ww8par5.cxx
340 if (aFunctionName
== "SwWW8ImplReader::Read_F_Shape") {
343 // called from SDI file, I don't know what that stuff is about, sd/source/ui/slidesorter/shell/SlideSorterViewShell.cx
344 if (aFunctionName
== "sd::slidesorter::SlideSorterViewShell::ExecStatusBar"
345 || aFunctionName
== "sd::OutlineViewShell::ExecStatusBar") {
348 // only used in debug mode, sd/source/filter/ppt/pptinanimations.cxx
349 if (startsWith(aFunctionName
, "ppt::AnimationImporter::dump")) {
352 // only used in ENABLE_SDREMOTE_BLUETOOTH mode, sd/source/ui/dlg/tpoption.cx
353 if (aFunctionName
== "SdTpOptionsMisc::SetImpressMode") {
356 // template magic, sc/source/ui/docshell/datastream.cxx
357 if (startsWith(aFunctionName
, "sc::(anonymous namespace)::CSVHandler::")) {
360 // called from SDI file, I don't know what that stuff is about, sc/source/ui/docshell/docsh7.cxx
361 if (aFunctionName
== "ScDocShell::GetDrawObjState") {
364 // called from SDI file, I don't know what that stuff is about, sc/source/ui/view/cellsh4.cxx
365 if (aFunctionName
== "ScCellShell::GetStateCursor") {
368 // called from SDI file, I don't know what that stuff is about, sc/source/ui/view/tabvwshh.cxx
369 if (aFunctionName
== "ScTabViewShell::ExecuteSbx" || aFunctionName
== "ScTabViewShell::GetSbxState") {
372 // template magic, sc/source/filter/excel/xepivot.cxx
373 if (aFunctionName
== "XclExpPivotCache::SaveXml") {
376 // template magic, sc/source/filter/html/htmlpars.cxx
377 if (startsWith(aFunctionName
, "(anonymous namespace)::CSSHandler::")) {
380 // callbacks, sc/source/filter/oox/formulaparser.cxx
381 if (startsWith(aFunctionName
, "oox::xls::BiffFormulaParserImpl::import")) {
384 // template magic, sc/qa/unit/helper/csv_handler.hxx
385 if (startsWith(aFunctionName
, "csv_handler::") || startsWith(aFunctionName
, "conditional_format_handler::")) {
388 // template magic, slideshow/source/inc/listenercontainer.hxx
389 if (startsWith(aFunctionName
, "slideshow::internal::EmptyBase::EmptyClearableGuard::")) {
392 // callback, scripting/source/vbaevents/eventhelper.cxx
393 if (aFunctionName
== "ApproveAll") {
396 // only on WNT, basic/qa/cppunit/test_vba.cx
397 if (aFunctionName
== "(anonymous namespace)::VBATest::testMiscOLEStuff") {
400 // GtkSalFrame::TriggerPaintEvent() is only compiled under certain versions of GTK
401 if (aFunctionName
== "GtkSalFrame::TriggerPaintEvent") {
404 if (aFunctionName
== "SwVectorModifyBase::dumpAsXml") {
407 // vcl/unx/gtk3 re-using vcl/unx/gtk:
408 if (aFunctionName
== "DeInitAtkBridge"
409 || aFunctionName
== "GtkData::initNWF"
410 || aFunctionName
== "GtkSalFrame::EnsureAppMenuWatch"
411 || aFunctionName
== "InitAtkBridge")
416 // can't mess with the TYPEINIT macros in include/tools/rtti.hxx or the LINK macros in include/tools/link.hxx
417 std::string aImmediateMacro
= "";
418 if (compat::isMacroBodyExpansion(compiler
, pFunctionDecl
->getLocStart()) ) {
419 StringRef name
{ Lexer::getImmediateMacroName(
420 pFunctionDecl
->getLocStart(), compiler
.getSourceManager(), compiler
.getLangOpts()) };
421 aImmediateMacro
= name
;
422 if (name
== "TYPEINIT_FACTORY" || name
== "TYPEINFO" || name
== "TYPEINFO_OVERRIDE"
423 || name
.startswith("IMPL_LINK") || name
== "DECL_LINK")
429 const CompoundStmt
*pCompoundStmt
= dyn_cast
<CompoundStmt
>(pFunctionDecl
->getBody());
430 bool aEmptyBody
= false;
432 if (pCompoundStmt
->size() > 1) {
435 if (pCompoundStmt
->size() > 0) {
436 const ReturnStmt
*pReturnStmt
= dyn_cast
<ReturnStmt
>(*pCompoundStmt
->body_begin());
440 if (pReturnStmt
->getRetValue() != nullptr) {
441 // && !pReturnStmt->getRetValue()->isEvaluatable(compiler.getASTContext())) {
443 llvm::APSInt aIntResult
;
444 if (pReturnStmt
->getRetValue()->isTypeDependent()
445 || (!pReturnStmt
->getRetValue()->EvaluateAsBooleanCondition(aBoolResult
, compiler
.getASTContext())
446 && !pReturnStmt
->getRetValue()->EvaluateAsInt(aIntResult
, compiler
.getASTContext())))
456 std::string aMessage
= "this ";
457 aMessage
+= pCXXMethodDecl
? "method" : "function";
459 aMessage
+= " is empty and should be removed, " + aFunctionName
;
461 aMessage
+= " returns a constant value and should be converted to a constant "
462 "or to static inline, " + aFunctionName
+ ", " + aImmediateMacro
;
465 DiagnosticsEngine::Warning
,
467 pFunctionDecl
->getLocStart())
468 << pFunctionDecl
->getSourceRange()
469 << pFunctionDecl
->getCanonicalDecl()->getSourceRange();
473 loplugin::Plugin::Registration
<ConstantFunction
> X("constantfunction");
477 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */