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 can be static
19 public RecursiveASTVisitor
<StaticMethods
>, public loplugin::Plugin
24 explicit StaticMethods(InstantiationData
const & data
): Plugin(data
) {}
27 { TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl()); }
29 bool TraverseCXXMethodDecl(const CXXMethodDecl
* decl
);
31 bool VisitCXXThisExpr(const CXXThisExpr
*) { bVisitedThis
= true; return true; }
32 // these two indicate that we hit something that makes our analysis unreliable
33 bool VisitUnresolvedMemberExpr(const UnresolvedMemberExpr
*) { bVisitedThis
= true; return true; }
34 bool VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr
*) { bVisitedThis
= true; return true; }
36 std::string
getFilename(SourceLocation loc
);
39 bool BaseCheckNotTestFixtureSubclass(const CXXRecordDecl
*BaseDefinition
, void *) {
40 if (BaseDefinition
->getQualifiedNameAsString().compare("CppUnit::TestFixture") == 0) {
46 bool isDerivedFromTestFixture(const CXXRecordDecl
*decl
) {
47 if (!decl
->hasDefinition())
49 if (// not sure what hasAnyDependentBases() does,
50 // but it avoids classes we don't want, e.g. WeakAggComponentImplHelper1
51 !decl
->hasAnyDependentBases() &&
52 !decl
->forallBases(BaseCheckNotTestFixtureSubclass
, nullptr, true)) {
58 std::string
StaticMethods::getFilename(SourceLocation loc
)
60 SourceLocation spellingLocation
= compiler
.getSourceManager().getSpellingLoc(loc
);
61 return compiler
.getSourceManager().getFilename(spellingLocation
);
64 static bool startsWith(const std::string
& rStr
, const char* pSubStr
) {
65 return rStr
.compare(0, strlen(pSubStr
), pSubStr
) == 0;
68 bool StaticMethods::TraverseCXXMethodDecl(const CXXMethodDecl
* pCXXMethodDecl
) {
69 if (ignoreLocation(pCXXMethodDecl
)) {
72 if (!pCXXMethodDecl
->isInstance() || pCXXMethodDecl
->isVirtual() || !pCXXMethodDecl
->hasBody()) {
75 if (pCXXMethodDecl
->getOverloadedOperator() != OverloadedOperatorKind::OO_None
|| pCXXMethodDecl
->hasAttr
<OverrideAttr
>()) {
78 if (isa
<CXXConstructorDecl
>(pCXXMethodDecl
) || isa
<CXXDestructorDecl
>(pCXXMethodDecl
) || isa
<CXXConversionDecl
>(pCXXMethodDecl
)) {
81 if (isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(pCXXMethodDecl
->getCanonicalDecl()->getLocStart()))) {
84 if ( pCXXMethodDecl
!= pCXXMethodDecl
->getCanonicalDecl() ) {
88 // the CppUnit stuff uses macros and methods that can't be changed
89 if (isDerivedFromTestFixture(pCXXMethodDecl
->getParent())) {
92 // don't mess with the backwards compatibility stuff
93 if (getFilename(pCXXMethodDecl
->getLocStart()) == SRCDIR
"/cppuhelper/source/compat.cxx") {
96 // the DDE has a dummy implementation on Linux and a real one on Windows
97 std::string aFilename
= getFilename(pCXXMethodDecl
->getCanonicalDecl()->getLocStart());
98 if (aFilename
== SRCDIR
"/include/svl/svdde.hxx") {
101 std::string aParentName
= pCXXMethodDecl
->getParent()->getQualifiedNameAsString();
102 // special case having something to do with static initialisation
103 // sal/osl/all/utility.cxx
104 if (aParentName
== "osl::OGlobalTimer") {
107 // leave the TopLeft() method alone for consistency with the other "corner" methods
108 if (aParentName
== "BitmapInfoAccess") {
111 // can't change it because in debug mode it can't be static
112 // sal/cpprt/operators_new_delete.cxx
113 if (aParentName
== "(anonymous namespace)::AllocatorTraits") {
116 // in this case, the code is taking the address of the member function
117 // shell/source/unix/sysshell/recently_used_file_handler.cxx
118 if (aParentName
== "(anonymous namespace)::recently_used_item") {
121 // the unotools and svl config code stuff is doing weird stuff with a reference-counted statically allocated pImpl class
122 if (startsWith(aFilename
, SRCDIR
"/include/unotools")) {
125 if (startsWith(aFilename
, SRCDIR
"/include/svl")) {
128 if (startsWith(aFilename
, SRCDIR
"/include/framework") || startsWith(aFilename
, SRCDIR
"/framework")) {
131 // there is some odd stuff happening here I don't fully understand, leave it for now
132 if (startsWith(aFilename
, SRCDIR
"/include/canvas") || startsWith(aFilename
, SRCDIR
"/canvas")) {
135 // classes that have static data and some kind of weird reference-counting trick in it's constructor
136 if (aParentName
== "LinguOptions" || aParentName
== "svtools::EditableExtendedColorConfig"
137 || aParentName
== "svtools::ExtendedColorConfig" || aParentName
== "SvtMiscOptions"
138 || aParentName
== "SvtAccessibilityOptions" || aParentName
== "svtools::ColorConfig"
139 || aParentName
== "SvtOptionsDrawinglayer" || aParentName
== "SvtMenuOptions"
140 || aParentName
== "SvtToolPanelOptions" || aParentName
== "SvtSlideSorterBarOptions"
141 || aParentName
== "connectivity::SharedResources"
142 || aParentName
== "svxform::OParseContextClient"
143 || aParentName
== "frm::OLimitedFormats" )
147 std::string fqn
= aParentName
+ "::" + pCXXMethodDecl
->getNameAsString();
148 // only empty on Linux, not on windows
149 if (fqn
== "OleEmbeddedObject::GetVisualRepresentationInNativeFormat_Impl"
150 || fqn
== "OleEmbeddedObject::GetRidOfComponent"
151 || fqn
== "connectivity::mozab::ProfileAccess::isProfileLocked"
152 || startsWith(fqn
, "SbxDecimal::")
153 || fqn
== "SbiDllMgr::Call" || fqn
== "SbiDllMgr::FreeDll"
154 || fqn
== "SfxApplication::InitializeDde" || fqn
== "SfxApplication::RemoveDdeTopic"
155 || fqn
== "ScannerManager::ReleaseData") {
159 if (fqn
== "chart::InternalData::dump") {
162 // used in a function-pointer-table
163 if (startsWith(fqn
, "SbiRuntime::Step") || startsWith(fqn
, "oox::xls::OoxFormulaParserImpl::")
164 || startsWith(fqn
, "SwTableFormula::")
165 || startsWith(fqn
, "oox::xls::BiffFormulaParserImpl::")
166 || fqn
== "SwWW8ImplReader::Read_F_Shape"
167 || fqn
== "SwWW8ImplReader::Read_Majority") {
170 // have no idea why this can't be static, but 'make check' fails with it so...
171 if (fqn
== "oox::drawingml::Shape::resolveRelationshipsOfTypeFromOfficeDoc") {
175 if (fqn
== "ColumnBatch::getValue" || startsWith(fqn
, "TitleImpl::")
176 || fqn
== "ooo::vba::DefaultReturnHelper::getDefaultPropertyName") {
179 // depends on config options
180 if (fqn
== "psp::PrintFontManager::autoInstallFontLangSupport"
181 || fqn
== "GtkSalFrame::AllocateFrame"
182 || fqn
== "GtkSalFrame::TriggerPaintEvent") {
186 bVisitedThis
= false;
187 TraverseStmt(pCXXMethodDecl
->getBody());
193 DiagnosticsEngine::Warning
,
194 "this method can be declared static " + fqn
,
195 pCXXMethodDecl
->getCanonicalDecl()->getLocation())
196 << pCXXMethodDecl
->getCanonicalDecl()->getSourceRange();
197 FunctionDecl
const * def
;
198 if (pCXXMethodDecl
->isDefined(def
)
199 && def
!= pCXXMethodDecl
->getCanonicalDecl())
201 report(DiagnosticsEngine::Note
, "defined here:", def
->getLocation())
202 << def
->getSourceRange();
207 loplugin::Plugin::Registration
<StaticMethods
> X("staticmethods");
211 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */