diff options
Diffstat (limited to '')
-rw-r--r-- | compilerplugins/clang/staticmethods.cxx | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/compilerplugins/clang/staticmethods.cxx b/compilerplugins/clang/staticmethods.cxx new file mode 100644 index 000000000..9b631c7eb --- /dev/null +++ b/compilerplugins/clang/staticmethods.cxx @@ -0,0 +1,240 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "clang/AST/Attr.h" + +#include "check.hxx" +#include "plugin.hxx" + +/* + Look for member functions that can be static +*/ +namespace { + +class StaticMethods: + public loplugin::FilteringPlugin<StaticMethods> +{ +private: + bool bVisitedThis; +public: + explicit StaticMethods(loplugin::InstantiationData const & data): FilteringPlugin(data), bVisitedThis(false) {} + + void run() override + { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); } + + bool TraverseCXXMethodDecl(const CXXMethodDecl * decl); + + bool VisitCXXThisExpr(const CXXThisExpr *) { bVisitedThis = true; return true; } + // these two indicate that we hit something that makes our analysis unreliable + bool VisitUnresolvedMemberExpr(const UnresolvedMemberExpr *) { bVisitedThis = true; return true; } + bool VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr *) { bVisitedThis = true; return true; } +private: + StringRef getFilename(SourceLocation loc); +}; + +bool BaseCheckNotTestFixtureSubclass(const CXXRecordDecl *BaseDefinition) { + if (loplugin::TypeCheck(BaseDefinition).Class("TestFixture").Namespace("CppUnit").GlobalNamespace()) { + return false; + } + return true; +} + +bool isDerivedFromTestFixture(const CXXRecordDecl *decl) { + if (!decl->hasDefinition()) + return false; + if (// not sure what hasAnyDependentBases() does, + // but it avoids classes we don't want, e.g. WeakAggComponentImplHelper1 + !decl->hasAnyDependentBases() && + !decl->forallBases(BaseCheckNotTestFixtureSubclass)) { + return true; + } + return false; +} + +StringRef StaticMethods::getFilename(SourceLocation loc) +{ + SourceLocation spellingLocation = compiler.getSourceManager().getSpellingLoc(loc); + return getFilenameOfLocation(spellingLocation); +} + +bool startsWith(const std::string& rStr, const char* pSubStr) { + return rStr.compare(0, strlen(pSubStr), pSubStr) == 0; +} + +bool StaticMethods::TraverseCXXMethodDecl(const CXXMethodDecl * pCXXMethodDecl) { + if (ignoreLocation(pCXXMethodDecl)) { + return true; + } + if (!pCXXMethodDecl->isInstance() || pCXXMethodDecl->isVirtual() || !pCXXMethodDecl->doesThisDeclarationHaveABody() || pCXXMethodDecl->isLateTemplateParsed()) { + return true; + } + if (pCXXMethodDecl->getOverloadedOperator() != OverloadedOperatorKind::OO_None || pCXXMethodDecl->hasAttr<OverrideAttr>()) { + return true; + } + if (isa<CXXConstructorDecl>(pCXXMethodDecl) || isa<CXXDestructorDecl>(pCXXMethodDecl) || isa<CXXConversionDecl>(pCXXMethodDecl)) { + return true; + } + if (isInUnoIncludeFile(pCXXMethodDecl)) { + return true; + } + if (pCXXMethodDecl->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) + return true; + + // the CppUnit stuff uses macros and methods that can't be changed + if (isDerivedFromTestFixture(pCXXMethodDecl->getParent())) { + return true; + } + // don't mess with the backwards compatibility stuff + if (loplugin::isSamePathname(getFilename(compat::getBeginLoc(pCXXMethodDecl)), SRCDIR "/cppuhelper/source/compat.cxx")) { + return true; + } + // the DDE has a dummy implementation on Linux and a real one on Windows + auto aFilename = getFilename(compat::getBeginLoc(pCXXMethodDecl->getCanonicalDecl())); + if (loplugin::isSamePathname(aFilename, SRCDIR "/include/svl/svdde.hxx")) { + return true; + } + auto cdc = loplugin::DeclCheck(pCXXMethodDecl->getParent()); + // special case having something to do with static initialisation + // sal/osl/all/utility.cxx + if (cdc.Class("OGlobalTimer").Namespace("osl").GlobalNamespace()) { + return true; + } + // leave the TopLeft() method alone for consistency with the other "corner" methods + if (cdc.Class("BitmapInfoAccess").GlobalNamespace()) { + return true; + } + // the unotools and svl config code stuff is doing weird stuff with a reference-counted statically allocated pImpl class + if (loplugin::hasPathnamePrefix(aFilename, SRCDIR "/include/unotools/")) { + return true; + } + if (loplugin::hasPathnamePrefix(aFilename, SRCDIR "/include/svl/")) { + return true; + } + if (loplugin::hasPathnamePrefix(aFilename, SRCDIR "/include/framework/") || loplugin::hasPathnamePrefix(aFilename, SRCDIR "/framework/")) { + return true; + } + // there is some odd stuff happening here I don't fully understand, leave it for now + if (loplugin::hasPathnamePrefix(aFilename, SRCDIR "/include/canvas/") || loplugin::hasPathnamePrefix(aFilename, SRCDIR "/canvas/")) { + return true; + } + // classes that have static data and some kind of weird reference-counting trick in its constructor + if (cdc.Class("LinguOptions").GlobalNamespace() + || (cdc.Class("EditableExtendedColorConfig").Namespace("svtools") + .GlobalNamespace()) + || (cdc.Class("ExtendedColorConfig").Namespace("svtools") + .GlobalNamespace()) + || cdc.Class("SvtMiscOptions").GlobalNamespace() + || cdc.Class("SvtAccessibilityOptions").GlobalNamespace() + || cdc.Class("ColorConfig").Namespace("svtools").GlobalNamespace() + || cdc.Class("SvtOptionsDrawinglayer").GlobalNamespace() + || cdc.Class("SvtMenuOptions").GlobalNamespace() + || cdc.Class("SvtToolPanelOptions").GlobalNamespace() + || cdc.Class("SvtSlideSorterBarOptions").GlobalNamespace() + || (cdc.Class("SharedResources").Namespace("connectivity") + .GlobalNamespace()) + || (cdc.Class("OParseContextClient").Namespace("svxform") + .GlobalNamespace()) + || cdc.Class("OLimitedFormats").Namespace("frm").GlobalNamespace()) + { + return true; + } + auto fdc = loplugin::DeclCheck(pCXXMethodDecl); + // only empty on Linux, not on windows + if ((fdc.Function("GetVisualRepresentationInNativeFormat_Impl") + .Class("OleEmbeddedObject").GlobalNamespace()) + || (fdc.Function("GetRidOfComponent").Class("OleEmbeddedObject") + .GlobalNamespace()) + || (fdc.Function("isProfileLocked").Class("ProfileAccess") + .Namespace("mozab").Namespace("connectivity").GlobalNamespace()) + || cdc.Class("SbxDecimal").GlobalNamespace() + || fdc.Function("Call").Class("SbiDllMgr").GlobalNamespace() + || fdc.Function("FreeDll").Class("SbiDllMgr").GlobalNamespace() + || (fdc.Function("InitializeDde").Class("SfxApplication") + .GlobalNamespace()) + || (fdc.Function("RemoveDdeTopic").Class("SfxApplication") + .GlobalNamespace()) + || (fdc.Function("UpdateSkiaStatus").Class("OfaViewTabPage") + .GlobalNamespace()) + || (fdc.Function("ReleaseData").Class("ScannerManager") + .GlobalNamespace())) + { + return true; + } + // debugging stuff + if (fdc.Function("dump").Class("InternalData").Namespace("chart") + .GlobalNamespace()) + { + return true; + } + // used in a function-pointer-table + if ((cdc.Class("SbiRuntime").GlobalNamespace() + && startsWith(pCXXMethodDecl->getNameAsString(), "Step")) + || (cdc.Class("OoxFormulaParserImpl").AnonymousNamespace().Namespace("xls").Namespace("oox") + .GlobalNamespace()) + || cdc.Class("SwTableFormula").GlobalNamespace() + || (cdc.Class("BiffFormulaParserImpl").Namespace("xls").Namespace("oox") + .GlobalNamespace()) + || (fdc.Function("Read_F_Shape").Class("SwWW8ImplReader") + .GlobalNamespace()) + || (fdc.Function("Read_Majority").Class("SwWW8ImplReader") + .GlobalNamespace()) + || fdc.Function("Ignore").Class("SwWrtShell").GlobalNamespace()) + { + return true; + } + // have no idea why this can't be static, but 'make check' fails with it so... + if (fdc.Function("resolveRelationshipsOfTypeFromOfficeDoc").Class("Shape") + .Namespace("drawingml").Namespace("oox").GlobalNamespace()) + { + return true; + } + // template magic + if (fdc.Function("getValue").Class("ColumnBatch").GlobalNamespace() + || cdc.Class("TitleImpl").GlobalNamespace() + || (fdc.Function("getDefaultPropertyName").Class("DefaultReturnHelper") + .Namespace("vba").Namespace("ooo").GlobalNamespace())) + { + return true; + } + // depends on config options + if ((fdc.Function("autoInstallFontLangSupport").Class("PrintFontManager") + .Namespace("psp").GlobalNamespace()) + || fdc.Function("AllocateFrame").Class("GtkSalFrame").GlobalNamespace() + || (fdc.Function("TriggerPaintEvent").Class("GtkSalFrame") + .GlobalNamespace())) + { + return true; + } + + bVisitedThis = false; + TraverseStmt(pCXXMethodDecl->getBody()); + if (bVisitedThis) { + return true; + } + + report( + DiagnosticsEngine::Warning, + "this member function can be declared static", + pCXXMethodDecl->getCanonicalDecl()->getLocation()) + << pCXXMethodDecl->getCanonicalDecl()->getSourceRange(); + FunctionDecl const * def; + if (pCXXMethodDecl->isDefined(def) + && def != pCXXMethodDecl->getCanonicalDecl()) + { + report(DiagnosticsEngine::Note, "defined here:", def->getLocation()) + << def->getSourceRange(); + } + return true; +} + +loplugin::Plugin::Registration<StaticMethods> X("staticmethods"); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |