bump product version to 5.0.4.1
[LibreOffice.git] / compilerplugins / clang / store / constantfunction.cxx
blob4f367fc709fb8e7018c9556d6fb418a5ed7d4db9
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 "compat.hxx"
12 #include <iostream>
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.
20 namespace {
22 class ConstantFunction:
23 public RecursiveASTVisitor<ConstantFunction>, public loplugin::Plugin
25 StringRef getFilename(SourceLocation loc);
26 public:
27 explicit ConstantFunction(InstantiationData const & data): Plugin(data) {}
29 void run() override
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) {
34 return;
36 if (strstr(compiler.getSourceManager().getFileEntryForID(mainFileID)->getDir()->getName(), "sc/source/ui/app") != 0) {
37 return;
39 if (strstr(compiler.getSourceManager().getFileEntryForID(mainFileID)->getDir()->getName(), "sc/qa/unit") != 0) {
40 return;
42 if (strstr(compiler.getSourceManager().getFileEntryForID(mainFileID)->getName(), "docuno.cxx") != 0) {
43 return;
45 if (strstr(compiler.getSourceManager().getFileEntryForID(mainFileID)->getName(), "viewdata.cxx") != 0) {
46 return;
48 if (strstr(compiler.getSourceManager().getFileEntryForID(mainFileID)->getName(), "calcoptionsdlg.cxx") != 0) {
49 return;
51 if (strstr(compiler.getSourceManager().getFileEntryForID(mainFileID)->getDir()->getName(), "sc/source/core/opencl") != 0) {
52 return;
54 if (strstr(compiler.getSourceManager().getFileEntryForID(mainFileID)->getDir()->getName(), "sc/source/core/tool") != 0) {
55 return;
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) };
68 return name;
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)) {
77 return true;
79 if (!pFunctionDecl->hasBody()) {
80 return true;
82 // stuff declared extern-C is almost always used as a some kind of callback
83 if (pFunctionDecl->isExternC()) {
84 return true;
87 StringRef aFileName = getFilename(pFunctionDecl->getLocStart());
89 // various tests in here are empty stubs under Linux
90 if (aFileName.startswith(SRCDIR "/sal/qa/")) {
91 return true;
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/")) {
95 return true;
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") {
100 return true;
102 // bridges has some weird stuff in it....
103 if (aFileName.startswith(SRCDIR "/bridges/")) {
104 return true;
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") {
109 return true;
111 // fancy templates at work here
112 if (aFileName == SRCDIR "/vcl/source/gdi/bmpfast.cxx") {
113 return true;
115 // bunch of stuff used as callbacks here
116 if (aFileName == SRCDIR "/vcl/generic/glyphs/gcach_layout.cxx") {
117 return true;
119 // salplug runtime-loading mechanism at work
120 if (getFilename(pFunctionDecl->getCanonicalDecl()->getLocStart()) == SRCDIR "/vcl/inc/salinst.hxx") {
121 return true;
123 // lots of callbacks here
124 if (aFileName == SRCDIR "/extensions/source/plugin/unx/npnapi.cxx") {
125 return true;
127 // template magic
128 if (aFileName == SRCDIR "/filter/source/svg/svgreader.cxx") {
129 return true;
131 // vcl/unx/gtk3 re-using vcl/unx/gtk:
132 if (aFileName.find("/../../gtk/") != std::string::npos) {
133 return true;
135 // used by code generated by python
136 if (getFilename(pFunctionDecl->getCanonicalDecl()->getLocStart()) == SRCDIR "/writerfilter/source/ooxml/OOXMLFastContextHandler.hxx") {
137 return true;
141 const CXXMethodDecl *pCXXMethodDecl = dyn_cast<CXXMethodDecl>(pFunctionDecl);
142 if (pCXXMethodDecl) {
143 if (pCXXMethodDecl->isVirtual()) {
144 return true;
146 // static with inline body will be optimised at compile-time to a constant anyway
147 if (pCXXMethodDecl->isStatic() && pCXXMethodDecl->hasInlineBody()) {
148 return true;
150 // this catches some stuff in templates
151 if (pFunctionDecl->hasAttr<OverrideAttr>()) {
152 return true;
155 // a free function with an inline body will be optimised at compile-time to a constant anyway
156 if (!pCXXMethodDecl && pFunctionDecl->isInlineSpecified()) {
157 return true;
159 if (isa<CXXConstructorDecl>(pFunctionDecl) || isa<CXXDestructorDecl>(pFunctionDecl) || isa<CXXConversionDecl>(pFunctionDecl)) {
160 return true;
162 SourceLocation canonicalLoc = pFunctionDecl->getCanonicalDecl()->getLocStart();
163 if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(canonicalLoc))) {
164 return true;
167 switch (pFunctionDecl->getOverloadedOperator()) {
168 case OO_Delete:
169 case OO_EqualEqual:
170 case OO_Call:
171 return true;
172 default:
173 break;
176 std::string aFunctionName = pFunctionDecl->getQualifiedNameAsString();
178 // something to do with dynamic loading in sal/textenc/textenc.cxx
179 if (aFunctionName == "thisModule") {
180 return true;
182 // an empty stub under certain conditions, sal/osl/unx/thread.cxx
183 if (aFunctionName == "osl_thread_priority_init_Impl") {
184 return true;
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") {
188 return true;
190 // a pointer to this function is taken and passed to an underlying API, cppu/source/uno/lbenv.cxx
191 if (aFunctionName == "defenv_dispose") {
192 return true;
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") {
196 return true;
198 // differetnt hook function is called on different platforms, /vcl/source/app/svmainhook.cxx
199 if (aFunctionName == "ImplSVMainHook") {
200 return true;
202 // used as a callback, /vcl/source/filter/jpeg/JpegReader.cxx
203 if (aFunctionName == "term_source") {
204 return true;
206 // only valid for windows, extensions/source/update/check/updatecheck.cxx
207 if (aFunctionName == "(anonymous namespace)::UpdateCheckThread::hasInternetConnection") {
208 return true;
210 // used as callback, extensions/source/plugin/unx/npwrap.cxx
211 if (aFunctionName == "plugin_x_error_handler" || aFunctionName == "noClosure") {
212 return true;
214 // used as callback, sax/source/expatwrap/sax_expat.cxx
215 if (aFunctionName == "(anonymous namespace)::SaxExpatParser_Impl::callbackUnknownEncoding") {
216 return true;
218 // used as callback, i18npool/source/textconversion/textconversion.cxx
219 if (aFunctionName == "com::sun::star::i18n::nullFunc") {
220 return true;
222 // used as callback, xmloff/source/text/txtparae.cxx
223 if (aFunctionName == "(anonymous namespace)::lcl_TextContentsUnfiltered") {
224 return true;
226 // template magic, include/canvas/verifyinput.hxx
227 if (aFunctionName == "canvas::tools::verifyInput") {
228 return true;
230 // template magic, cppcanvas/source/mtfrenderer/implrenderer.cxx
231 if (aFunctionName == "cppcanvas::internal::(anonymous namespace)::AreaQuery::result") {
232 return true;
234 // callback, drawinglayer/source/dumper/XShapeDumper.
235 if (aFunctionName == "(anonymous namespace)::closeCallback") {
236 return true;
238 // callback, basic/source/runtime/runtime.cxx
239 if (aFunctionName == "SbiRuntime::StepNOP") {
240 return true;
242 // DLL stuff, only used on windows, basic/source/runtime/dllmgr.hxx
243 if (aFunctionName == "SbiDllMgr::FreeDll") {
244 return true;
246 // only used on Windows, basic/source/sbx/sbxdec.cxx
247 if (aFunctionName == "SbxDecimal::neg" || aFunctionName == "SbxDecimal::isZero") {
248 return true;
250 // used as a callback, include/sfx2/shell.hxx
251 if (aFunctionName == "SfxShell::EmptyExecStub" || aFunctionName == "SfxShell::EmptyStateStub"
252 || aFunctionName == "SfxShell::VerbState") {
253 return true;
255 // SFX_IMPL_POS_CHILDWINDOW_WITHID macro
256 if (aFunctionName.find("GetChildWindowId") != std::string::npos) {
257 return true;
259 // SFX_IMPL_SUPERCLASS_INTERFACE macro
260 if (aFunctionName.find("InitInterface_Impl") != std::string::npos) {
261 return true;
263 // callback, vcl/unx/generic/app/sm.cxx
264 if (aFunctionName == "IgnoreIceIOErrors" || aFunctionName == "IgnoreIceErrors") {
265 return true;
267 // callback, vcl/unx/gtk/a11y/atkcomponent.cxx
268 if (aFunctionName == "component_wrapper_get_mdi_zorder") {
269 return true;
271 // callback, vcl/unx/gtk/a11y/atkaction.cxx
272 if (aFunctionName == "action_wrapper_set_description") {
273 return true;
275 // callback, vcl/unx/gtk/a11y/atkutil.cxx
276 if (aFunctionName == "ooo_atk_util_get_toolkit_version" || aFunctionName == "ooo_atk_util_get_toolkit_name") {
277 return true;
279 // callback, vcl/unx/gtk/a11y/atktextattributes.cxx
280 if (aFunctionName == "InvalidValue") {
281 return true;
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") {
289 return true;
291 // callbacks, vcl/unx/gtk/window/gtksalframe.cxx
292 if (startsWith(aFunctionName, "GtkSalFrame::IMHandler::signal")) {
293 return true;
295 // callbacks, vcl/unx/gtk/window/glomenu.cxx
296 if (startsWith(aFunctionName, "g_lo_menu_is_mutable")) {
297 return true;
299 // only contains code for certain versions of GTK, /vcl/unx/gtk/window/gtksalframe.cx
300 if (aFunctionName == "GtkSalFrame::AllocateFrame") {
301 return true;
303 // only valid for Windows, embeddedobj/source/msole/olemisc.cxx
304 if (aFunctionName == "OleEmbeddedObject::GetRidOfComponent") {
305 return true;
307 // callback, svx/source/accessibility/ShapeTypeHandler.cxx
308 if (aFunctionName == "accessibility::CreateEmptyShapeReference") {
309 return true;
311 // chart2/source/view/main/AbstractShapeFactory.cxx
312 if (aFunctionName == "chart::(anonymous namespace)::thisModule") {
313 return true;
315 // chart2/source/tools/InternalData.cxx
316 if (aFunctionName == "chart::InternalData::dump") {
317 return true;
319 // hwpfilter/
320 if (aFunctionName == "debug" || aFunctionName == "token_debug") {
321 return true;
323 // callback, sdext/source/presenter/PresenterFrameworkObserver.cxx
324 if (aFunctionName == "sdext::presenter::PresenterFrameworkObserver::True") {
325 return true;
327 // hidden behind the ENABLE_PANE_RESIZING macro
328 if (aFunctionName == "sdext::presenter::PresenterWindowManager::UpdateWindowList") {
329 return true;
331 // callback, sw/source/core/doc/tblrwcl.cxx
332 if (aFunctionName == "lcl_DelOtherBox") {
333 return true;
335 // callback, sw/source/filter/ww8/ww8par.cxx
336 if (aFunctionName == "SwWW8ImplReader::Read_Majority") {
337 return true;
339 // callback, sw/source/filter/ww8/ww8par5.cxx
340 if (aFunctionName == "SwWW8ImplReader::Read_F_Shape") {
341 return true;
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") {
346 return true;
348 // only used in debug mode, sd/source/filter/ppt/pptinanimations.cxx
349 if (startsWith(aFunctionName, "ppt::AnimationImporter::dump")) {
350 return true;
352 // only used in ENABLE_SDREMOTE_BLUETOOTH mode, sd/source/ui/dlg/tpoption.cx
353 if (aFunctionName == "SdTpOptionsMisc::SetImpressMode") {
354 return true;
356 // template magic, sc/source/ui/docshell/datastream.cxx
357 if (startsWith(aFunctionName, "sc::(anonymous namespace)::CSVHandler::")) {
358 return true;
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") {
362 return true;
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") {
366 return true;
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") {
370 return true;
372 // template magic, sc/source/filter/excel/xepivot.cxx
373 if (aFunctionName == "XclExpPivotCache::SaveXml") {
374 return true;
376 // template magic, sc/source/filter/html/htmlpars.cxx
377 if (startsWith(aFunctionName, "(anonymous namespace)::CSSHandler::")) {
378 return true;
380 // callbacks, sc/source/filter/oox/formulaparser.cxx
381 if (startsWith(aFunctionName, "oox::xls::BiffFormulaParserImpl::import")) {
382 return true;
384 // template magic, sc/qa/unit/helper/csv_handler.hxx
385 if (startsWith(aFunctionName, "csv_handler::") || startsWith(aFunctionName, "conditional_format_handler::")) {
386 return true;
388 // template magic, slideshow/source/inc/listenercontainer.hxx
389 if (startsWith(aFunctionName, "slideshow::internal::EmptyBase::EmptyClearableGuard::")) {
390 return true;
392 // callback, scripting/source/vbaevents/eventhelper.cxx
393 if (aFunctionName == "ApproveAll") {
394 return true;
396 // only on WNT, basic/qa/cppunit/test_vba.cx
397 if (aFunctionName == "(anonymous namespace)::VBATest::testMiscOLEStuff") {
398 return true;
400 // GtkSalFrame::TriggerPaintEvent() is only compiled under certain versions of GTK
401 if (aFunctionName == "GtkSalFrame::TriggerPaintEvent") {
402 return true;
404 if (aFunctionName == "SwVectorModifyBase::dumpAsXml") {
405 return true;
407 // vcl/unx/gtk3 re-using vcl/unx/gtk:
408 if (aFunctionName == "DeInitAtkBridge"
409 || aFunctionName == "GtkData::initNWF"
410 || aFunctionName == "GtkSalFrame::EnsureAppMenuWatch"
411 || aFunctionName == "InitAtkBridge")
413 return true;
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")
425 return true;
429 const CompoundStmt *pCompoundStmt = dyn_cast<CompoundStmt>(pFunctionDecl->getBody());
430 bool aEmptyBody = false;
431 if (pCompoundStmt) {
432 if (pCompoundStmt->size() > 1) {
433 return true;
435 if (pCompoundStmt->size() > 0) {
436 const ReturnStmt *pReturnStmt = dyn_cast<ReturnStmt>(*pCompoundStmt->body_begin());
437 if (!pReturnStmt) {
438 return true;
440 if (pReturnStmt->getRetValue() != nullptr) {
441 // && !pReturnStmt->getRetValue()->isEvaluatable(compiler.getASTContext())) {
442 bool aBoolResult;
443 llvm::APSInt aIntResult;
444 if (pReturnStmt->getRetValue()->isTypeDependent()
445 || (!pReturnStmt->getRetValue()->EvaluateAsBooleanCondition(aBoolResult, compiler.getASTContext())
446 && !pReturnStmt->getRetValue()->EvaluateAsInt(aIntResult, compiler.getASTContext())))
448 return true;
451 } else {
452 aEmptyBody = true;
456 std::string aMessage = "this ";
457 aMessage += pCXXMethodDecl ? "method" : "function";
458 if (aEmptyBody) {
459 aMessage += " is empty and should be removed, " + aFunctionName;
460 } else {
461 aMessage += " returns a constant value and should be converted to a constant "
462 "or to static inline, " + aFunctionName + ", " + aImmediateMacro;
464 report(
465 DiagnosticsEngine::Warning,
466 aMessage,
467 pFunctionDecl->getLocStart())
468 << pFunctionDecl->getSourceRange()
469 << pFunctionDecl->getCanonicalDecl()->getSourceRange();
470 return true;
473 loplugin::Plugin::Registration<ConstantFunction> X("constantfunction");
477 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */