1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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  */
9 
10 #include "clang/AST/Attr.h"
11 
12 #include "check.hxx"
13 #include "plugin.hxx"
14 
15 /*
16   Look for member functions that can be static
17 */
18 namespace {
19 
20 class StaticMethods:
21     public loplugin::FilteringPlugin<StaticMethods>
22 {
23 private:
24     bool bVisitedThis;
25 public:
StaticMethods(loplugin::InstantiationData const & data)26     explicit StaticMethods(loplugin::InstantiationData const & data): FilteringPlugin(data), bVisitedThis(false) {}
27 
run()28     void run() override
29     { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
30 
31     bool TraverseCXXMethodDecl(const CXXMethodDecl * decl);
32 
VisitCXXThisExpr(const CXXThisExpr *)33     bool VisitCXXThisExpr(const CXXThisExpr *) { bVisitedThis = true; return true; }
34     // these two indicate that we hit something that makes our analysis unreliable
VisitUnresolvedMemberExpr(const UnresolvedMemberExpr *)35     bool VisitUnresolvedMemberExpr(const UnresolvedMemberExpr *) { bVisitedThis = true; return true; }
VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr *)36     bool VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr *) { bVisitedThis = true; return true; }
37 private:
38     StringRef getFilename(SourceLocation loc);
39 };
40 
BaseCheckNotTestFixtureSubclass(const CXXRecordDecl * BaseDefinition)41 bool BaseCheckNotTestFixtureSubclass(const CXXRecordDecl *BaseDefinition) {
42     if (loplugin::TypeCheck(BaseDefinition).Class("TestFixture").Namespace("CppUnit").GlobalNamespace()) {
43         return false;
44     }
45     return true;
46 }
47 
isDerivedFromTestFixture(const CXXRecordDecl * decl)48 bool isDerivedFromTestFixture(const CXXRecordDecl *decl) {
49     if (!decl->hasDefinition())
50         return false;
51     if (// not sure what hasAnyDependentBases() does,
52         // but it avoids classes we don't want, e.g. WeakAggComponentImplHelper1
53         !decl->hasAnyDependentBases() &&
54         !decl->forallBases(BaseCheckNotTestFixtureSubclass, true)) {
55         return true;
56     }
57     return false;
58 }
59 
getFilename(SourceLocation loc)60 StringRef StaticMethods::getFilename(SourceLocation loc)
61 {
62     SourceLocation spellingLocation = compiler.getSourceManager().getSpellingLoc(loc);
63     return getFilenameOfLocation(spellingLocation);
64 }
65 
startsWith(const std::string & rStr,const char * pSubStr)66 bool startsWith(const std::string& rStr, const char* pSubStr) {
67     return rStr.compare(0, strlen(pSubStr), pSubStr) == 0;
68 }
69 
TraverseCXXMethodDecl(const CXXMethodDecl * pCXXMethodDecl)70 bool StaticMethods::TraverseCXXMethodDecl(const CXXMethodDecl * pCXXMethodDecl) {
71     if (ignoreLocation(pCXXMethodDecl)) {
72         return true;
73     }
74     if (!pCXXMethodDecl->isInstance() || pCXXMethodDecl->isVirtual() || !pCXXMethodDecl->doesThisDeclarationHaveABody() || pCXXMethodDecl->isLateTemplateParsed()) {
75         return true;
76     }
77     if (pCXXMethodDecl->getOverloadedOperator() != OverloadedOperatorKind::OO_None || pCXXMethodDecl->hasAttr<OverrideAttr>()) {
78         return true;
79     }
80     if (isa<CXXConstructorDecl>(pCXXMethodDecl) || isa<CXXDestructorDecl>(pCXXMethodDecl) || isa<CXXConversionDecl>(pCXXMethodDecl)) {
81         return true;
82     }
83     if (isInUnoIncludeFile(pCXXMethodDecl)) {
84         return true;
85     }
86     if (pCXXMethodDecl->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
87         return true;
88 
89     // the CppUnit stuff uses macros and methods that can't be changed
90     if (isDerivedFromTestFixture(pCXXMethodDecl->getParent())) {
91         return true;
92     }
93     // don't mess with the backwards compatibility stuff
94     if (loplugin::isSamePathname(getFilename(compat::getBeginLoc(pCXXMethodDecl)), SRCDIR "/cppuhelper/source/compat.cxx")) {
95         return true;
96     }
97     // the DDE has a dummy implementation on Linux and a real one on Windows
98     auto aFilename = getFilename(compat::getBeginLoc(pCXXMethodDecl->getCanonicalDecl()));
99     if (loplugin::isSamePathname(aFilename, SRCDIR "/include/svl/svdde.hxx")) {
100         return true;
101     }
102     auto cdc = loplugin::DeclCheck(pCXXMethodDecl->getParent());
103     // special case having something to do with static initialisation
104     // sal/osl/all/utility.cxx
105     if (cdc.Class("OGlobalTimer").Namespace("osl").GlobalNamespace()) {
106         return true;
107     }
108     // leave the TopLeft() method alone for consistency with the other "corner" methods
109     if (cdc.Class("BitmapInfoAccess").GlobalNamespace()) {
110         return true;
111     }
112     // the unotools and svl config code stuff is doing weird stuff with a reference-counted statically allocated pImpl class
113     if (loplugin::hasPathnamePrefix(aFilename, SRCDIR "/include/unotools/")) {
114         return true;
115     }
116     if (loplugin::hasPathnamePrefix(aFilename, SRCDIR "/include/svl/")) {
117         return true;
118     }
119     if (loplugin::hasPathnamePrefix(aFilename, SRCDIR "/include/framework/") || loplugin::hasPathnamePrefix(aFilename, SRCDIR "/framework/")) {
120         return true;
121     }
122     // there is some odd stuff happening here I don't fully understand, leave it for now
123     if (loplugin::hasPathnamePrefix(aFilename, SRCDIR "/include/canvas/") || loplugin::hasPathnamePrefix(aFilename, SRCDIR "/canvas/")) {
124         return true;
125     }
126     // classes that have static data and some kind of weird reference-counting trick in its constructor
127     if (cdc.Class("LinguOptions").GlobalNamespace()
128         || (cdc.Class("EditableExtendedColorConfig").Namespace("svtools")
129             .GlobalNamespace())
130         || (cdc.Class("ExtendedColorConfig").Namespace("svtools")
131             .GlobalNamespace())
132         || cdc.Class("SvtMiscOptions").GlobalNamespace()
133         || cdc.Class("SvtAccessibilityOptions").GlobalNamespace()
134         || cdc.Class("ColorConfig").Namespace("svtools").GlobalNamespace()
135         || cdc.Class("SvtOptionsDrawinglayer").GlobalNamespace()
136         || cdc.Class("SvtMenuOptions").GlobalNamespace()
137         || cdc.Class("SvtToolPanelOptions").GlobalNamespace()
138         || cdc.Class("SvtSlideSorterBarOptions").GlobalNamespace()
139         || (cdc.Class("SharedResources").Namespace("connectivity")
140             .GlobalNamespace())
141         || (cdc.Class("OParseContextClient").Namespace("svxform")
142             .GlobalNamespace())
143         || cdc.Class("OLimitedFormats").Namespace("frm").GlobalNamespace())
144     {
145         return true;
146     }
147     auto fdc = loplugin::DeclCheck(pCXXMethodDecl);
148     // only empty on Linux, not on windows
149     if ((fdc.Function("GetVisualRepresentationInNativeFormat_Impl")
150          .Class("OleEmbeddedObject").GlobalNamespace())
151         || (fdc.Function("GetRidOfComponent").Class("OleEmbeddedObject")
152             .GlobalNamespace())
153         || (fdc.Function("isProfileLocked").Class("ProfileAccess")
154             .Namespace("mozab").Namespace("connectivity").GlobalNamespace())
155         || cdc.Class("SbxDecimal").GlobalNamespace()
156         || fdc.Function("Call").Class("SbiDllMgr").GlobalNamespace()
157         || fdc.Function("FreeDll").Class("SbiDllMgr").GlobalNamespace()
158         || (fdc.Function("InitializeDde").Class("SfxApplication")
159             .GlobalNamespace())
160         || (fdc.Function("RemoveDdeTopic").Class("SfxApplication")
161             .GlobalNamespace())
162         || (fdc.Function("ReleaseData").Class("ScannerManager")
163             .GlobalNamespace()))
164     {
165         return true;
166     }
167     // debugging stuff
168     if (fdc.Function("dump").Class("InternalData").Namespace("chart")
169         .GlobalNamespace())
170     {
171         return true;
172     }
173     // used in a function-pointer-table
174     if ((cdc.Class("SbiRuntime").GlobalNamespace()
175          && startsWith(pCXXMethodDecl->getNameAsString(), "Step"))
176         || (cdc.Class("OoxFormulaParserImpl").Namespace("xls").Namespace("oox")
177             .GlobalNamespace())
178         || cdc.Class("SwTableFormula").GlobalNamespace()
179         || (cdc.Class("BiffFormulaParserImpl").Namespace("xls").Namespace("oox")
180             .GlobalNamespace())
181         || (fdc.Function("Read_F_Shape").Class("SwWW8ImplReader")
182             .GlobalNamespace())
183         || (fdc.Function("Read_Majority").Class("SwWW8ImplReader")
184             .GlobalNamespace())
185         || fdc.Function("Ignore").Class("SwWrtShell").GlobalNamespace())
186     {
187         return true;
188     }
189     // have no idea why this can't be static, but 'make check' fails with it so...
190     if (fdc.Function("resolveRelationshipsOfTypeFromOfficeDoc").Class("Shape")
191         .Namespace("drawingml").Namespace("oox").GlobalNamespace())
192     {
193         return true;
194     }
195     // template magic
196     if (fdc.Function("getValue").Class("ColumnBatch").GlobalNamespace()
197         || cdc.Class("TitleImpl").GlobalNamespace()
198         || (fdc.Function("getDefaultPropertyName").Class("DefaultReturnHelper")
199             .Namespace("vba").Namespace("ooo").GlobalNamespace()))
200     {
201         return true;
202     }
203     // depends on config options
204     if ((fdc.Function("autoInstallFontLangSupport").Class("PrintFontManager")
205          .Namespace("psp").GlobalNamespace())
206         || fdc.Function("AllocateFrame").Class("GtkSalFrame").GlobalNamespace()
207         || (fdc.Function("TriggerPaintEvent").Class("GtkSalFrame")
208             .GlobalNamespace()))
209     {
210         return true;
211     }
212 
213     bVisitedThis = false;
214     TraverseStmt(pCXXMethodDecl->getBody());
215     if (bVisitedThis) {
216         return true;
217     }
218 
219     report(
220         DiagnosticsEngine::Warning,
221         "this member function can be declared static",
222         pCXXMethodDecl->getCanonicalDecl()->getLocation())
223       << pCXXMethodDecl->getCanonicalDecl()->getSourceRange();
224     FunctionDecl const * def;
225     if (pCXXMethodDecl->isDefined(def)
226         && def != pCXXMethodDecl->getCanonicalDecl())
227     {
228         report(DiagnosticsEngine::Note, "defined here:", def->getLocation())
229             << def->getSourceRange();
230     }
231     return true;
232 }
233 
234 loplugin::Plugin::Registration<StaticMethods> X("staticmethods");
235 
236 }
237 
238 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
239