bump product version to 6.3.0.0.beta1
[LibreOffice.git] / compilerplugins / clang / store / constantfunction.cxx
blob0673df7a25215e3bad7584f2a6cc04b99e46fe10
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 #include "plugin.hxx"
11 #include <iostream>
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.
19 namespace {
21 class ConstantFunction:
22 public loplugin::FilteringPlugin<ConstantFunction>
24 StringRef getFilename(const FunctionDecl* functionDecl);
25 public:
26 explicit ConstantFunction(InstantiationData const & data): FilteringRewritePlugin(data) {}
28 void run() override
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) {
33 return;
35 if (strstr(compiler.getSourceManager().getFileEntryForID(mainFileID)->getName(), "gtk3gtkinst.cxx") != 0) {
36 return;
37 }*/
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) };
49 return name;
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)) {
58 return true;
60 if (!pFunctionDecl->hasBody()) {
61 return true;
63 if (!pFunctionDecl->isThisDeclarationADefinition()) {
64 return true;
66 // stuff declared extern-C is almost always used as a some kind of callback
67 if (pFunctionDecl->isExternC()) {
68 return true;
70 if (pFunctionDecl->isConstexpr()) {
71 return true;
73 if (pFunctionDecl->isMain()) {
74 return true;
77 StringRef aFileName = getFilename(pFunctionDecl);
79 // various tests in here are empty stubs under Linux
80 if (aFileName.startswith(SRCDIR "/sal/qa/")) {
81 return true;
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/")) {
85 return true;
87 // bridges has some weird stuff in it....
88 if (aFileName.startswith(SRCDIR "/bridges/")) {
89 return true;
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") {
94 return true;
96 // fancy templates at work here
97 if (aFileName == SRCDIR "/vcl/source/gdi/bmpfast.cxx") {
98 return true;
100 // bunch of stuff used as callbacks here
101 if (aFileName == SRCDIR "/vcl/generic/glyphs/gcach_layout.cxx") {
102 return true;
104 // salplug runtime-loading mechanism at work
105 if (aFileName == SRCDIR "/vcl/inc/salinst.hxx") {
106 return true;
108 // lots of callbacks here
109 if (aFileName == SRCDIR "/extensions/source/plugin/unx/npnapi.cxx") {
110 return true;
112 // vcl/unx/gtk3 re-using vcl/unx/gtk:
113 if (aFileName.find("/../../gtk/") != std::string::npos) {
114 return true;
116 // used by code generated by python
117 if (aFileName == SRCDIR "/writerfilter/source/ooxml/OOXMLFastContextHandler.hxx") {
118 return true;
120 // this test just test the include of some headers
121 if (aFileName == SRCDIR "/officecfg/qa/cppheader.cxx") {
122 return true;
124 // just ignore this for now, people furiously hacking in there
125 if (startsWith(aFileName, SRCDIR "/libreofficekit")) {
126 return true;
130 const CXXMethodDecl *pCXXMethodDecl = dyn_cast<CXXMethodDecl>(pFunctionDecl);
131 if (pCXXMethodDecl) {
132 if (pCXXMethodDecl->isVirtual()) {
133 return true;
135 // static with inline body will be optimised at compile-time to a constant anyway
136 if (pCXXMethodDecl->isStatic() && (pCXXMethodDecl->hasInlineBody() || pCXXMethodDecl->isInlineSpecified())) {
137 return true;
139 // this catches some stuff in templates
140 if (pFunctionDecl->hasAttr<OverrideAttr>()) {
141 return true;
144 // a free function with an inline body will be optimised at compile-time to a constant anyway
145 if (!pCXXMethodDecl && pFunctionDecl->isInlineSpecified()) {
146 return true;
148 if (isa<CXXConstructorDecl>(pFunctionDecl) || isa<CXXDestructorDecl>(pFunctionDecl) || isa<CXXConversionDecl>(pFunctionDecl)) {
149 return true;
151 if (isInUnoIncludeFile(pFunctionDecl)) {
152 return true;
155 switch (pFunctionDecl->getOverloadedOperator()) {
156 case OO_Delete:
157 case OO_EqualEqual:
158 case OO_Call:
159 return true;
160 default:
161 break;
164 std::string aFunctionName = pFunctionDecl->getQualifiedNameAsString();
166 // something to do with dynamic loading in sal/textenc/textenc.cxx
167 if (aFunctionName == "thisModule") {
168 return true;
170 // an empty stub under certain conditions, sal/osl/unx/thread.cxx
171 if (aFunctionName == "osl_thread_priority_init_Impl") {
172 return true;
174 // a pointer to this function is taken and passed to an underlying API, cppu/source/uno/lbenv.cxx
175 if (aFunctionName == "defenv_dispose") {
176 return true;
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") {
180 return true;
182 // used as a callback, /vcl/source/filter/jpeg/JpegReader.cxx
183 if (aFunctionName == "term_source") {
184 return true;
186 // only valid for windows, extensions/source/update/check/updatecheck.cxx
187 if (aFunctionName == "(anonymous namespace)::UpdateCheckThread::hasInternetConnection") {
188 return true;
190 // used as callback, extensions/source/plugin/unx/npwrap.cxx
191 if (aFunctionName == "plugin_x_error_handler" || aFunctionName == "noClosure") {
192 return true;
194 // used as callback, sax/source/expatwrap/sax_expat.cxx
195 if (aFunctionName == "(anonymous namespace)::SaxExpatParser_Impl::callbackUnknownEncoding") {
196 return true;
198 // used as callback, i18npool/source/textconversion/textconversion.cxx
199 if (aFunctionName == "com::sun::star::i18n::nullFunc") {
200 return true;
202 // used as callback, xmloff/source/text/txtparae.cxx
203 if (aFunctionName == "(anonymous namespace)::lcl_TextContentsUnfiltered") {
204 return true;
206 // template magic, include/canvas/verifyinput.hxx
207 if (aFunctionName == "canvas::tools::verifyInput") {
208 return true;
210 // template magic, cppcanvas/source/mtfrenderer/implrenderer.cxx
211 if (aFunctionName == "cppcanvas::internal::(anonymous namespace)::AreaQuery::result") {
212 return true;
214 // callback, drawinglayer/source/dumper/XShapeDumper.
215 if (aFunctionName == "(anonymous namespace)::closeCallback") {
216 return true;
218 // callback, basic/source/runtime/runtime.cxx
219 if (aFunctionName == "SbiRuntime::StepNOP") {
220 return true;
222 // DLL stuff, only used on windows, basic/source/runtime/dllmgr.hxx
223 if (aFunctionName == "SbiDllMgr::FreeDll") {
224 return true;
226 // only used on Windows, basic/source/sbx/sbxdec.cxx
227 if (aFunctionName == "SbxDecimal::neg" || aFunctionName == "SbxDecimal::isZero") {
228 return true;
230 // used as a callback, include/sfx2/shell.hxx
231 if (aFunctionName == "SfxShell::EmptyExecStub" || aFunctionName == "SfxShell::EmptyStateStub"
232 || aFunctionName == "SfxShell::VerbState") {
233 return true;
235 // SFX_IMPL_POS_CHILDWINDOW_WITHID macro
236 if (aFunctionName.find("GetChildWindowId") != std::string::npos) {
237 return true;
239 // SFX_IMPL_SUPERCLASS_INTERFACE macro
240 if (aFunctionName.find("InitInterface_Impl") != std::string::npos) {
241 return true;
243 // callback, vcl/unx/generic/app/sm.cxx
244 if (aFunctionName == "IgnoreIceIOErrors" || aFunctionName == "IgnoreIceErrors") {
245 return true;
247 // callback, vcl/unx/gtk/a11y/atkcomponent.cxx
248 if (aFunctionName == "component_wrapper_get_mdi_zorder") {
249 return true;
251 // callback, vcl/unx/gtk/a11y/atkaction.cxx
252 if (aFunctionName == "action_wrapper_set_description") {
253 return true;
255 // callback, vcl/unx/gtk/a11y/atkutil.cxx
256 if (aFunctionName == "ooo_atk_util_get_toolkit_version" || aFunctionName == "ooo_atk_util_get_toolkit_name") {
257 return true;
259 // callback, vcl/unx/gtk/a11y/atktextattributes.cxx
260 if (aFunctionName == "InvalidValue") {
261 return true;
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") {
269 return true;
271 // callbacks, vcl/unx/gtk/window/gtksalframe.cxx
272 if (startsWith(aFunctionName, "GtkSalFrame::IMHandler::signal")) {
273 return true;
275 // callbacks, vcl/unx/gtk/window/glomenu.cxx
276 if (startsWith(aFunctionName, "g_lo_menu_is_mutable")) {
277 return true;
279 // only contains code for certain versions of GTK, /vcl/unx/gtk/window/gtksalframe.cx
280 if (aFunctionName == "GtkSalFrame::AllocateFrame") {
281 return true;
283 // only valid for Windows, embeddedobj/source/msole/olemisc.cxx
284 if (aFunctionName == "OleEmbeddedObject::GetRidOfComponent") {
285 return true;
287 // callback, svx/source/accessibility/ShapeTypeHandler.cxx
288 if (aFunctionName == "accessibility::CreateEmptyShapeReference") {
289 return true;
291 // chart2/source/view/main/AbstractShapeFactory.cxx
292 if (aFunctionName == "chart::(anonymous namespace)::thisModule") {
293 return true;
295 // chart2/source/tools/InternalData.cxx
296 if (aFunctionName == "chart::InternalData::dump") {
297 return true;
299 // hwpfilter/
300 if (aFunctionName == "debug" || aFunctionName == "token_debug") {
301 return true;
303 // callback, sdext/source/presenter/PresenterFrameworkObserver.cxx
304 if (aFunctionName == "sdext::presenter::PresenterFrameworkObserver::True") {
305 return true;
307 // callback, sw/source/core/doc/tblrwcl.cxx
308 if (aFunctionName == "lcl_DelOtherBox") {
309 return true;
311 // callback, sw/source/filter/ww8/ww8par.cxx
312 if (aFunctionName == "SwWW8ImplReader::Read_Majority") {
313 return true;
315 // callback, sw/source/filter/ww8/ww8par5.cxx
316 if (aFunctionName == "SwWW8ImplReader::Read_F_Shape") {
317 return true;
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") {
322 return true;
324 // only used in debug mode, sd/source/filter/ppt/pptinanimations.cxx
325 if (startsWith(aFunctionName, "ppt::AnimationImporter::dump")) {
326 return true;
328 // only used in ENABLE_SDREMOTE_BLUETOOTH mode, sd/source/ui/dlg/tpoption.cx
329 if (aFunctionName == "SdTpOptionsMisc::SetImpressMode") {
330 return true;
332 // template magic, sc/source/ui/docshell/datastream.cxx
333 if (startsWith(aFunctionName, "sc::(anonymous namespace)::CSVHandler::")) {
334 return true;
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") {
338 return true;
340 // template magic, sc/source/filter/excel/xepivot.cxx
341 if (aFunctionName == "XclExpPivotCache::SaveXml") {
342 return true;
344 // template magic, sc/source/filter/html/htmlpars.cxx
345 if (startsWith(aFunctionName, "(anonymous namespace)::CSSHandler::")) {
346 return true;
348 // callbacks, sc/source/filter/oox/formulaparser.cxx
349 if (startsWith(aFunctionName, "oox::xls::BiffFormulaParserImpl::import")) {
350 return true;
352 // template magic, sc/qa/unit/helper/csv_handler.hxx
353 if (startsWith(aFunctionName, "csv_handler::") || startsWith(aFunctionName, "conditional_format_handler::")) {
354 return true;
356 // template magic, slideshow/source/inc/listenercontainer.hxx
357 if (startsWith(aFunctionName, "slideshow::internal::EmptyBase::EmptyClearableGuard::")) {
358 return true;
360 // callback, scripting/source/vbaevents/eventhelper.cxx
361 if (aFunctionName == "ApproveAll") {
362 return true;
364 // only on WNT, basic/qa/cppunit/test_vba.cx
365 if (aFunctionName == "(anonymous namespace)::VBATest::testMiscOLEStuff") {
366 return true;
368 // GtkSalFrame::TriggerPaintEvent() is only compiled under certain versions of GTK
369 if (aFunctionName == "GtkSalFrame::TriggerPaintEvent") {
370 return true;
372 if (aFunctionName == "SwVectorModifyBase::dumpAsXml") {
373 return true;
375 // vcl/unx/gtk3 re-using vcl/unx/gtk:
376 if (aFunctionName == "DeInitAtkBridge"
377 || aFunctionName == "GtkData::initNWF"
378 || aFunctionName == "GtkSalFrame::EnsureAppMenuWatch"
379 || aFunctionName == "InitAtkBridge")
381 return true;
383 if (aFunctionName == "sc::AlignedAllocator::operator!=") {
384 return true;
386 if (aFunctionName == "clipboard_owner_init") {
387 return true;
389 // returns sizeof(struct) vcl/source/gdi/dibtools.cxx
390 if (aFunctionName == "getDIBV5HeaderSize") {
391 return true;
393 // windows only
394 if (aFunctionName == "InitAccessBridge") {
395 return true;
397 // callbacks
398 if (aFunctionName == "disabled_initSystray" || aFunctionName == "disabled_deInitSystray") {
399 return true;
401 // behind a BREAKPAD option
402 if (aFunctionName == "desktop::(anonymous namespace)::crashReportInfoExists") {
403 return true;
405 // LOK stuff
406 if (aFunctionName == "doc_getTileMode") {
407 return true;
409 // apparently this will be useful at sometime in the future
410 if (aFunctionName == "LocaleDataWrapper::getCurrZeroChar") {
411 return true;
413 // marked with TODO
414 if (aFunctionName == "oglcanvas::TextLayout::draw") {
415 return true;
417 // called from the .sdi files
418 if (aFunctionName == "SfxObjectShell::StateView_Impl") {
419 return true;
421 // gtk callback
422 if (aFunctionName == "GtkSalFrame::signalVisibility") {
423 return true;
425 // platform-version-dependent code
426 if (aFunctionName == "(anonymous namespace)::ACTIVE_TAB") {
427 return true;
429 // SMIL callbacks
430 if (aFunctionName == "boost::sp_scalar_constructor_hook" || aFunctionName == "boost::sp_scalar_destructor_hook") {
431 return true;
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_") )
444 return true;
448 const CompoundStmt *pCompoundStmt = dyn_cast<CompoundStmt>(pFunctionDecl->getBody());
449 bool bEmptyBody = false;
450 if (pCompoundStmt) {
451 if (pCompoundStmt->size() > 1) {
452 return true;
454 if (pCompoundStmt->size() > 0) {
455 const ReturnStmt *pReturnStmt = dyn_cast<ReturnStmt>(*pCompoundStmt->body_begin());
456 if (!pReturnStmt) {
457 return true;
459 if (const UnaryOperator* unaryOp = dyn_cast<UnaryOperator>(pReturnStmt->getRetValue())) {
460 if (unaryOp->getOpcode() == UO_AddrOf) {
461 return true;
464 if (pReturnStmt->getRetValue() != nullptr) {
465 // && !pReturnStmt->getRetValue()->isEvaluatable(compiler.getASTContext())) {
466 bool aBoolResult;
467 llvm::APSInt aIntResult;
468 if (pReturnStmt->getRetValue()->isTypeDependent()
469 || (!pReturnStmt->getRetValue()->EvaluateAsBooleanCondition(aBoolResult, compiler.getASTContext())
470 && !pReturnStmt->getRetValue()->EvaluateAsInt(aIntResult, compiler.getASTContext())))
472 return true;
475 } else {
476 bEmptyBody = true;
480 std::string aMessage = "this ";
481 aMessage += pCXXMethodDecl ? "method" : "function";
482 if (bEmptyBody) {
483 aMessage += " is empty and should be removed, " + aFunctionName;
484 } else {
485 aMessage += " returns a constant value and should be converted to a constant "
486 "or to static inline, " + aFunctionName + ", " + aImmediateMacro;
488 report(
489 DiagnosticsEngine::Warning,
490 aMessage,
491 pFunctionDecl->getLocStart())
492 << pFunctionDecl->getSourceRange();
493 if (pFunctionDecl != pFunctionDecl->getCanonicalDecl())
494 report(
495 DiagnosticsEngine::Note,
496 aMessage,
497 pFunctionDecl->getCanonicalDecl()->getLocStart())
498 << pFunctionDecl->getCanonicalDecl()->getSourceRange();
499 return true;
502 loplugin::Plugin::Registration<ConstantFunction> X("constantfunction");
506 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */