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 <cassert>
11 #include <string>
12 #include <iostream>
13 #include <fstream>
14 #include <set>
15
16 #include <clang/AST/CXXInheritance.h>
17 #include "compat.hxx"
18 #include "plugin.hxx"
19 #include "check.hxx"
20
21 /**
22 Look for calls to the ref-counting methods acquire()/release(), which should only be called by classes like rtl::Reference.
23 */
24
25 namespace {
26
27 class ManualRefCount:
28 public RecursiveASTVisitor<ManualRefCount>, public loplugin::Plugin
29 {
30 public:
ManualRefCount(InstantiationData const & data)31 explicit ManualRefCount(InstantiationData const & data): Plugin(data) {}
32
run()33 virtual void run() override
34 {
35 StringRef fn( compiler.getSourceManager().getFileEntryForID(
36 compiler.getSourceManager().getMainFileID())->getName() );
37
38 // old code, no point in updating
39 if (loplugin::isSamePathname(fn, SRCDIR "/store/source/store.cxx"))
40 return;
41
42 // TODO -----------------------------
43 if (loplugin::isSamePathname(fn, SRCDIR "/registry/source/registry.cxx"))
44 return;
45 if (loplugin::isSamePathname(fn, SRCDIR "/registry/source/regimpl.cxx"))
46 return;
47 if (loplugin::isSamePathname(fn, SRCDIR "/registry/source/reflread.cxx"))
48 return;
49 // TODO MenuAttributes::CreateAttribute
50 if (loplugin::isSamePathname(fn, SRCDIR "/framework/source/fwe/xml/menuconfiguration.cxx"))
51 return;
52 if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/uibase/app/apphdl.cxx"))
53 return;
54 if (loplugin::isSamePathname(fn, SRCDIR "/dbaccess/source/core/dataaccess/ModelImpl.cxx"))
55 return;
56 // need a better replacement for vcl::EventPoster
57 if (loplugin::isSamePathname(fn, SRCDIR "/svtools/source/misc/acceleratorexecute.cxx"))
58 return;
59 // PostUserEvent stuff
60 if (loplugin::isSamePathname(fn, SRCDIR "/toolkit/source/awt/vclxwindow.cxx"))
61 return;
62 // playing games with pointers passed into combobox entries
63 if (loplugin::isSamePathname(fn, SRCDIR "/cui/source/customize/cfgutil.cxx"))
64 return;
65 if (loplugin::isSamePathname(fn, SRCDIR "/cui/source/customize/cfg.cxx"))
66 return;
67 // END TODO -----------------------------
68
69 // can't fix these without breaking stable ABI
70 if (fn.startswith(SRCDIR "/sal/"))
71 return;
72 if (fn.startswith(SRCDIR "/salhelper/"))
73 return;
74 if (fn.startswith(SRCDIR "/cppu/"))
75 return;
76 if (fn.startswith(SRCDIR "/cppuhelper/"))
77 return;
78 if (fn.startswith(SRCDIR "/bridges/"))
79 return;
80
81 // lots of magic here
82 if (fn.startswith(SRCDIR "/stoc/"))
83 return;
84 if (fn.startswith(SRCDIR "/testtools/"))
85 return;
86
87 // mutex games
88 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/app/scheduler.cxx"))
89 return;
90 // opengl games
91 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/app/svdata.cxx"))
92 return;
93
94 // passing the pointer through PostUserEvent
95 if (loplugin::isSamePathname(fn, SRCDIR "/avmedia/source/gstreamer/gstplayer.cxx"))
96 return;
97 if (loplugin::isSamePathname(fn, SRCDIR "/svx/source/form/fmscriptingenv.cxx"))
98 return;
99
100 // thread games
101 if (loplugin::isSamePathname(fn, SRCDIR "/io/source/stm/opump.cxx"))
102 return;
103
104 // ??? no idea what this code is up to
105 if (loplugin::isSamePathname(fn, SRCDIR "/extensions/source/scanner/scanunx.cxx"))
106 return;
107 if (loplugin::isSamePathname(fn, SRCDIR "/stoc/source/invocation_adapterfactory/iafactory.cxx"))
108 return;
109 if (loplugin::isSamePathname(fn, SRCDIR "/fpicker/source/office/asyncfilepicker.cxx"))
110 return;
111 if (loplugin::isSamePathname(fn, SRCDIR "/forms/source/component/FormComponent.cxx"))
112 return;
113 if (loplugin::isSamePathname(fn, SRCDIR "/ucb/source/ucp/file/bc.cxx"))
114 return;
115 if (loplugin::isSamePathname(fn, SRCDIR "/ucb/source/ucp/file/filprp.cxx"))
116 return;
117 // calling release() ?
118 if (loplugin::isSamePathname(fn, SRCDIR "/toolkit/source/helper/accessibilityclient.cxx"))
119 return;
120 if (loplugin::isSamePathname(fn, SRCDIR "/svtools/source/misc/svtaccessiblefactory.cxx"))
121 return;
122
123 // implementing css::uno::XInterface
124 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/animations/motionpathtag.cxx"))
125 return;
126 // UNO factory methods
127 if (fn.startswith(SRCDIR "/comphelper/"))
128 return;
129 if (loplugin::isSamePathname(fn, SRCDIR "/linguistic/source/convdiclist.cxx"))
130 return;
131 if (loplugin::isSamePathname(fn, SRCDIR "/linguistic/source/dlistimp.cxx"))
132 return;
133 if (loplugin::isSamePathname(fn, SRCDIR "/linguistic/source/gciterator.cxx"))
134 return;
135 if (loplugin::isSamePathname(fn, SRCDIR "/linguistic/source/lngsvcmgr.cxx"))
136 return;
137 if (loplugin::isSamePathname(fn, SRCDIR "/linguistic/source/lngopt.cxx"))
138 return;
139 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/unx/generic/gdi/gcach_xpeer.cxx"))
140 return;
141 if (loplugin::isSamePathname(fn, SRCDIR "/dbaccess/source/ui/dlg/dbwizsetup.cxx"))
142 return;
143 if (loplugin::isSamePathname(fn, SRCDIR "/dbaccess/source/ui/dlg/dbwizsetup.cxx"))
144 return;
145 if (loplugin::isSamePathname(fn, SRCDIR "/lingucomponent/source/hyphenator/hyphen/hyphenimp.cxx"))
146 return;
147 if (loplugin::isSamePathname(fn, SRCDIR "/lingucomponent/source/spellcheck/spell/sspellimp.cxx"))
148 return;
149 if (loplugin::isSamePathname(fn, SRCDIR "/lingucomponent/source/thesaurus/libnth/nthesimp.cxx"))
150 return;
151
152
153 // some kind of complicated listener nonsense
154 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/framework/tools/FrameworkHelper.cxx"))
155 return;
156 // more listener nonsense
157 if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/uibase/uno/unomailmerge.cxx"))
158 return;
159 // playing games with it's listener list
160 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/ui/unoobj/cellsuno.cxx"))
161 return;
162 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/ui/unoobj/chart2uno.cxx"))
163 return;
164 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/ui/unoobj/dapiuno.cxx"))
165 return;
166 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/ui/unoobj/datauno.cxx"))
167 return;
168 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/ui/unoobj/linkuno.cxx"))
169 return;
170 // PostUserEvent
171 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/ui/vba/vbaeventshelper.cxx"))
172 return;
173 // thread holding itself
174 if (loplugin::isSamePathname(fn, SRCDIR "/forms/source/component/EventThread.cxx"))
175 return;
176
177
178 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
179 }
180
shouldVisitTemplateInstantiations() const181 bool shouldVisitTemplateInstantiations () const { return true; }
182
183 bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *);
184 bool TraverseCXXRecordDecl(CXXRecordDecl *);
185 bool TraverseCXXMethodDecl(CXXMethodDecl *);
186 bool TraverseFunctionDecl(FunctionDecl *);
187 bool TraverseCXXConstructorDecl(CXXConstructorDecl *);
188 bool TraverseCXXDestructorDecl(CXXDestructorDecl *);
189 bool TraverseCXXConversionDecl(CXXConversionDecl *);
190 bool TraverseClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl *);
191 bool TraverseLinkageSpecDecl(LinkageSpecDecl *);
192 private:
193 bool ignoreCallerClass(CXXRecordDecl*);
194 };
195
TraverseCXXMethodDecl(CXXMethodDecl * cxxMethodDecl)196 bool ManualRefCount::TraverseCXXMethodDecl(CXXMethodDecl* cxxMethodDecl)
197 {
198 if (ignoreCallerClass(cxxMethodDecl->getParent()))
199 return true;
200 // disambiguating forwarding methods for XInterface subclasses
201 if (cxxMethodDecl->getIdentifier() && (cxxMethodDecl->getName() == "acquire" || cxxMethodDecl->getName() == "release"))
202 return true;
203 return RecursiveASTVisitor::TraverseCXXMethodDecl(cxxMethodDecl);
204 }
205
TraverseFunctionDecl(FunctionDecl * functionDecl)206 bool ManualRefCount::TraverseFunctionDecl(FunctionDecl* functionDecl)
207 {
208 auto tc = loplugin::DeclCheck(functionDecl);
209 if (tc.Function("make_shared_from_UNO").Namespace("comphelper").GlobalNamespace())
210 return true;
211 return RecursiveASTVisitor::TraverseFunctionDecl(functionDecl);
212 }
213
TraverseCXXConstructorDecl(CXXConstructorDecl * cxxMethodDecl)214 bool ManualRefCount::TraverseCXXConstructorDecl(CXXConstructorDecl* cxxMethodDecl)
215 {
216 if (ignoreCallerClass(cxxMethodDecl->getParent()))
217 return true;
218 return RecursiveASTVisitor::TraverseCXXMethodDecl(cxxMethodDecl);
219 }
220
TraverseCXXDestructorDecl(CXXDestructorDecl *)221 bool ManualRefCount::TraverseCXXDestructorDecl(CXXDestructorDecl*)
222 {
223 // just ignore destructors, tons of places like to call acquire() on themselves in their destructor
224 // supposedly to prevent recursively calling the destructor
225 return true;
226 }
TraverseCXXConversionDecl(CXXConversionDecl * cxxMethodDecl)227 bool ManualRefCount::TraverseCXXConversionDecl(CXXConversionDecl* cxxMethodDecl)
228 {
229 if (ignoreCallerClass(cxxMethodDecl->getParent()))
230 return true;
231 return RecursiveASTVisitor::TraverseCXXMethodDecl(cxxMethodDecl);
232 }
TraverseCXXRecordDecl(CXXRecordDecl * cxxRecordDecl)233 bool ManualRefCount::TraverseCXXRecordDecl(CXXRecordDecl* cxxRecordDecl)
234 {
235 if (ignoreCallerClass(cxxRecordDecl))
236 return true;
237 return RecursiveASTVisitor::TraverseCXXRecordDecl(cxxRecordDecl);
238 }
239
TraverseClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl * templateDecl)240 bool ManualRefCount::TraverseClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl* templateDecl)
241 {
242 if (ignoreCallerClass(templateDecl))
243 return true;
244 return RecursiveASTVisitor::TraverseClassTemplateSpecializationDecl(templateDecl);
245 }
246
TraverseLinkageSpecDecl(LinkageSpecDecl *)247 bool ManualRefCount::TraverseLinkageSpecDecl(LinkageSpecDecl *)
248 {
249 // ignore methods inside "extern ""C""" blocks, these are normally UNO constructors, which
250 // are required to raise the reference count before returning
251 return true;
252 }
253
ignoreCallerClass(CXXRecordDecl * cxxRecordDecl)254 bool ManualRefCount::ignoreCallerClass(CXXRecordDecl* cxxRecordDecl)
255 {
256 auto tc = loplugin::TypeCheck(cxxRecordDecl);
257 return
258 tc.Class("Reference").Namespace("rtl").GlobalNamespace()
259 || tc.Class("cow_wrapper").Namespace("o3tl").GlobalNamespace()
260 || tc.Class("Reference").Namespace("uno").Namespace("star").Namespace("sun").Namespace("com").GlobalNamespace()
261 || tc.Class("ShareGuard").Namespace("framework").GlobalNamespace()
262 || tc.Class("ControlModelLock").Namespace("frm").GlobalNamespace()
263 || tc.Struct("ReleaseFunc").Namespace("detail").Namespace("comphelper").GlobalNamespace()
264 // TODO no idea what this is up to
265 || tc.Class("SfxModelSubComponent").GlobalNamespace()
266 || tc.Class("OSubComponent").Namespace("mysqlc").Namespace("connectivity").GlobalNamespace()
267 || tc.Class("OSubComponent").Namespace("connectivity").GlobalNamespace()
268 // TODO do we really need this?
269 || tc.Class("ShareableMutex").Namespace("framework").GlobalNamespace()
270 || tc.Class("ObservableThread").GlobalNamespace()
271 ;
272 }
273
VisitCXXMemberCallExpr(const CXXMemberCallExpr * cxxMemberCallExpr)274 bool ManualRefCount::VisitCXXMemberCallExpr(const CXXMemberCallExpr* cxxMemberCallExpr)
275 {
276 if (ignoreLocation(cxxMemberCallExpr))
277 return true;
278 if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(cxxMemberCallExpr->getLocStart())))
279 return true;
280
281 // first, use some heuristics to find the right kind of acquire()/release() calls
282 CXXMethodDecl const * calleeMethodDecl = cxxMemberCallExpr->getMethodDecl();
283 if (!calleeMethodDecl || !calleeMethodDecl->getIdentifier())
284 return true;
285 if (calleeMethodDecl->getName() != "acquire" && calleeMethodDecl->getName() != "release")
286 return true;
287 if (calleeMethodDecl->getNumParams() != 0)
288 return true;
289 // std::unique_ptr::release() and similar methods
290 if (calleeMethodDecl->getName() == "release" && loplugin::TypeCheck(calleeMethodDecl->getReturnType()).Pointer())
291 return true;
292
293 // these are OK
294 auto calleeRecordTC = loplugin::TypeCheck(calleeMethodDecl->getParent());
295 if (calleeRecordTC.Struct("ResourceHolder").Namespace("store").GlobalNamespace())
296 return true;
297 if (calleeRecordTC.Class("Module").Namespace("osl").GlobalNamespace())
298 return true;
299 if (calleeRecordTC.Class("Mutex").Namespace("osl").GlobalNamespace())
300 return true;
301 if (calleeRecordTC.Class("multi_type_vector").Namespace("mdds").GlobalNamespace())
302 return true;
303
304 // while (calleeMethodDecl->size_overridden_methods() > 0)
305 // calleeMethodDecl = *calleeMethodDecl->begin_overridden_methods();
306 // auto tc2 = loplugin::TypeCheck(calleeMethodDecl->getParent());
307 // if (tc2.Class("XInterface").Namespace("uno").Namespace("star").Namespace("sun").Namespace("com").GlobalNamespace())
308 // return true;
309
310 std::cout << calleeMethodDecl->getParent()->getQualifiedNameAsString() << std::endl;
311 report(
312 DiagnosticsEngine::Warning, "call to acquire/release",
313 cxxMemberCallExpr->getLocStart())
314 << cxxMemberCallExpr->getSourceRange();
315 return true;
316 }
317
318
319 loplugin::Plugin::Registration< ManualRefCount > X("manualrefcount", true);
320
321 }
322
323 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
324