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/.
10 #include "clang/AST/Attr.h"
17 Look for member functions that can be static
22 public RecursiveASTVisitor
<StaticMethods
>, public loplugin::Plugin
27 explicit StaticMethods(InstantiationData
const & data
): Plugin(data
), bVisitedThis(false) {}
30 { TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl()); }
32 bool TraverseCXXMethodDecl(const CXXMethodDecl
* decl
);
34 bool VisitCXXThisExpr(const CXXThisExpr
*) { bVisitedThis
= true; return true; }
35 // these two indicate that we hit something that makes our analysis unreliable
36 bool VisitUnresolvedMemberExpr(const UnresolvedMemberExpr
*) { bVisitedThis
= true; return true; }
37 bool VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr
*) { bVisitedThis
= true; return true; }
39 std::string
getFilename(SourceLocation loc
);
42 bool BaseCheckNotTestFixtureSubclass(
43 const CXXRecordDecl
*BaseDefinition
44 #if CLANG_VERSION < 30800
49 if (BaseDefinition
->getQualifiedNameAsString().compare("CppUnit::TestFixture") == 0) {
55 bool isDerivedFromTestFixture(const CXXRecordDecl
*decl
) {
56 if (!decl
->hasDefinition())
58 if (// not sure what hasAnyDependentBases() does,
59 // but it avoids classes we don't want, e.g. WeakAggComponentImplHelper1
60 !decl
->hasAnyDependentBases() &&
61 !compat::forallBases(*decl
, BaseCheckNotTestFixtureSubclass
, nullptr, true)) {
67 std::string
StaticMethods::getFilename(SourceLocation loc
)
69 SourceLocation spellingLocation
= compiler
.getSourceManager().getSpellingLoc(loc
);
70 return compiler
.getSourceManager().getFilename(spellingLocation
);
73 bool startsWith(const std::string
& rStr
, const char* pSubStr
) {
74 return rStr
.compare(0, strlen(pSubStr
), pSubStr
) == 0;
77 bool StaticMethods::TraverseCXXMethodDecl(const CXXMethodDecl
* pCXXMethodDecl
) {
78 if (ignoreLocation(pCXXMethodDecl
)) {
81 if (!pCXXMethodDecl
->isInstance() || pCXXMethodDecl
->isVirtual() || !pCXXMethodDecl
->hasBody()) {
84 if (pCXXMethodDecl
->getOverloadedOperator() != OverloadedOperatorKind::OO_None
|| pCXXMethodDecl
->hasAttr
<OverrideAttr
>()) {
87 if (isa
<CXXConstructorDecl
>(pCXXMethodDecl
) || isa
<CXXDestructorDecl
>(pCXXMethodDecl
) || isa
<CXXConversionDecl
>(pCXXMethodDecl
)) {
90 if (isInUnoIncludeFile(pCXXMethodDecl
)) {
93 if ( pCXXMethodDecl
!= pCXXMethodDecl
->getCanonicalDecl() ) {
97 // the CppUnit stuff uses macros and methods that can't be changed
98 if (isDerivedFromTestFixture(pCXXMethodDecl
->getParent())) {
101 // don't mess with the backwards compatibility stuff
102 if (getFilename(pCXXMethodDecl
->getLocStart()) == SRCDIR
"/cppuhelper/source/compat.cxx") {
105 // the DDE has a dummy implementation on Linux and a real one on Windows
106 std::string aFilename
= getFilename(pCXXMethodDecl
->getCanonicalDecl()->getLocStart());
107 if (aFilename
== SRCDIR
"/include/svl/svdde.hxx") {
110 auto cdc
= loplugin::DeclCheck(pCXXMethodDecl
->getParent());
111 // special case having something to do with static initialisation
112 // sal/osl/all/utility.cxx
113 if (cdc
.Class("OGlobalTimer").Namespace("osl").GlobalNamespace()) {
116 // leave the TopLeft() method alone for consistency with the other "corner" methods
117 if (cdc
.Class("BitmapInfoAccess").GlobalNamespace()) {
120 // in this case, the code is taking the address of the member function
121 // shell/source/unix/sysshell/recently_used_file_handler.cxx
122 if (cdc
.Struct("recently_used_item").AnonymousNamespace().GlobalNamespace())
126 // the unotools and svl config code stuff is doing weird stuff with a reference-counted statically allocated pImpl class
127 if (startsWith(aFilename
, SRCDIR
"/include/unotools")) {
130 if (startsWith(aFilename
, SRCDIR
"/include/svl")) {
133 if (startsWith(aFilename
, SRCDIR
"/include/framework") || startsWith(aFilename
, SRCDIR
"/framework")) {
136 // there is some odd stuff happening here I don't fully understand, leave it for now
137 if (startsWith(aFilename
, SRCDIR
"/include/canvas") || startsWith(aFilename
, SRCDIR
"/canvas")) {
140 // classes that have static data and some kind of weird reference-counting trick in its constructor
141 if (cdc
.Class("LinguOptions").GlobalNamespace()
142 || (cdc
.Class("EditableExtendedColorConfig").Namespace("svtools")
144 || (cdc
.Class("ExtendedColorConfig").Namespace("svtools")
146 || cdc
.Class("SvtMiscOptions").GlobalNamespace()
147 || cdc
.Class("SvtAccessibilityOptions").GlobalNamespace()
148 || cdc
.Class("ColorConfig").Namespace("svtools").GlobalNamespace()
149 || cdc
.Class("SvtOptionsDrawinglayer").GlobalNamespace()
150 || cdc
.Class("SvtMenuOptions").GlobalNamespace()
151 || cdc
.Class("SvtToolPanelOptions").GlobalNamespace()
152 || cdc
.Class("SvtSlideSorterBarOptions").GlobalNamespace()
153 || (cdc
.Class("SharedResources").Namespace("connectivity")
155 || (cdc
.Class("OParseContextClient").Namespace("svxform")
157 || cdc
.Class("OLimitedFormats").Namespace("frm").GlobalNamespace())
161 auto fdc
= loplugin::DeclCheck(pCXXMethodDecl
);
162 // only empty on Linux, not on windows
163 if ((fdc
.Function("GetVisualRepresentationInNativeFormat_Impl")
164 .Class("OleEmbeddedObject").GlobalNamespace())
165 || (fdc
.Function("GetRidOfComponent").Class("OleEmbeddedObject")
167 || (fdc
.Function("isProfileLocked").Class("ProfileAccess")
168 .Namespace("mozab").Namespace("connectivity").GlobalNamespace())
169 || cdc
.Class("SbxDecimal").GlobalNamespace()
170 || fdc
.Function("Call").Class("SbiDllMgr").GlobalNamespace()
171 || fdc
.Function("FreeDll").Class("SbiDllMgr").GlobalNamespace()
172 || (fdc
.Function("InitializeDde").Class("SfxApplication")
174 || (fdc
.Function("RemoveDdeTopic").Class("SfxApplication")
176 || (fdc
.Function("ReleaseData").Class("ScannerManager")
182 if (fdc
.Function("dump").Class("InternalData").Namespace("chart")
187 // used in a function-pointer-table
188 if ((cdc
.Class("SbiRuntime").GlobalNamespace()
189 && startsWith(pCXXMethodDecl
->getNameAsString(), "Step"))
190 || (cdc
.Class("OoxFormulaParserImpl").Namespace("xls").Namespace("oox")
192 || cdc
.Class("SwTableFormula").GlobalNamespace()
193 || (cdc
.Class("BiffFormulaParserImpl").Namespace("xls").Namespace("oox")
195 || (fdc
.Function("Read_F_Shape").Class("SwWW8ImplReader")
197 || (fdc
.Function("Read_Majority").Class("SwWW8ImplReader")
199 || fdc
.Function("Ignore").Class("SwWrtShell").GlobalNamespace())
203 // have no idea why this can't be static, but 'make check' fails with it so...
204 if (fdc
.Function("resolveRelationshipsOfTypeFromOfficeDoc").Class("Shape")
205 .Namespace("drawingml").Namespace("oox").GlobalNamespace())
210 if (fdc
.Function("getValue").Class("ColumnBatch").GlobalNamespace()
211 || cdc
.Class("TitleImpl").GlobalNamespace()
212 || (fdc
.Function("getDefaultPropertyName").Class("DefaultReturnHelper")
213 .Namespace("vba").Namespace("ooo").GlobalNamespace()))
217 // depends on config options
218 if ((fdc
.Function("autoInstallFontLangSupport").Class("PrintFontManager")
219 .Namespace("psp").GlobalNamespace())
220 || fdc
.Function("AllocateFrame").Class("GtkSalFrame").GlobalNamespace()
221 || (fdc
.Function("TriggerPaintEvent").Class("GtkSalFrame")
227 bVisitedThis
= false;
228 TraverseStmt(pCXXMethodDecl
->getBody());
234 DiagnosticsEngine::Warning
,
235 "this member function can be declared static",
236 pCXXMethodDecl
->getCanonicalDecl()->getLocation())
237 << pCXXMethodDecl
->getCanonicalDecl()->getSourceRange();
238 FunctionDecl
const * def
;
239 if (pCXXMethodDecl
->isDefined(def
)
240 && def
!= pCXXMethodDecl
->getCanonicalDecl())
242 report(DiagnosticsEngine::Note
, "defined here:", def
->getLocation())
243 << def
->getSourceRange();
248 loplugin::Plugin::Registration
<StaticMethods
> X("staticmethods");
252 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */