nss: upgrade to release 3.73
[LibreOffice.git] / compilerplugins / clang / writeonlyvars.cxx
blob51a967dac8a0fc568e025bbce8713343e11ef083
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 #if !defined _WIN32 //TODO, #include <sys/file.h>
12 #include <cassert>
13 #include <string>
14 #include <iostream>
15 #include <fstream>
16 #include <unordered_set>
17 #include <vector>
18 #include <algorithm>
19 #include <sys/file.h>
20 #include <unistd.h>
22 #include "config_clang.h"
24 #include "plugin.hxx"
25 #include "compat.hxx"
26 #include "check.hxx"
28 #if CLANG_VERSION >= 110000
29 #include "clang/AST/ParentMapContext.h"
30 #endif
32 /**
33 Finds variables that are effectively write-only.
35 Largely the same as the unusedfields.cxx loplugin.
38 namespace
40 struct MyVarInfo
42 const VarDecl* varDecl;
43 std::string parent;
44 std::string varName;
45 std::string varType;
46 std::string sourceLocation;
48 bool operator<(const MyVarInfo& lhs, const MyVarInfo& rhs)
50 return std::tie(lhs.parent, lhs.varName) < std::tie(rhs.parent, rhs.varName);
53 // try to limit the voluminous output a little
54 static std::set<MyVarInfo> readFromSet;
55 static std::set<MyVarInfo> writeToSet;
56 static std::set<MyVarInfo> definitionSet;
58 /**
59 * Wrap the different kinds of callable and callee objects in the clang AST so I can define methods that handle everything.
61 class CallerWrapper
63 const CallExpr* m_callExpr;
64 const CXXConstructExpr* m_cxxConstructExpr;
66 public:
67 CallerWrapper(const CallExpr* callExpr)
68 : m_callExpr(callExpr)
69 , m_cxxConstructExpr(nullptr)
72 CallerWrapper(const CXXConstructExpr* cxxConstructExpr)
73 : m_callExpr(nullptr)
74 , m_cxxConstructExpr(cxxConstructExpr)
77 unsigned getNumArgs() const
79 return m_callExpr ? m_callExpr->getNumArgs() : m_cxxConstructExpr->getNumArgs();
81 const Expr* getArg(unsigned i) const
83 return m_callExpr ? m_callExpr->getArg(i) : m_cxxConstructExpr->getArg(i);
86 class CalleeWrapper
88 const FunctionDecl* m_calleeFunctionDecl = nullptr;
89 const CXXConstructorDecl* m_cxxConstructorDecl = nullptr;
90 const FunctionProtoType* m_functionPrototype = nullptr;
92 public:
93 explicit CalleeWrapper(const FunctionDecl* calleeFunctionDecl)
94 : m_calleeFunctionDecl(calleeFunctionDecl)
97 explicit CalleeWrapper(const CXXConstructExpr* cxxConstructExpr)
98 : m_cxxConstructorDecl(cxxConstructExpr->getConstructor())
101 explicit CalleeWrapper(const FunctionProtoType* functionPrototype)
102 : m_functionPrototype(functionPrototype)
105 unsigned getNumParams() const
107 if (m_calleeFunctionDecl)
108 return m_calleeFunctionDecl->getNumParams();
109 else if (m_cxxConstructorDecl)
110 return m_cxxConstructorDecl->getNumParams();
111 else if (m_functionPrototype->param_type_begin() == m_functionPrototype->param_type_end())
112 // FunctionProtoType will assert if we call getParamTypes() and it has no params
113 return 0;
114 else
115 return m_functionPrototype->getParamTypes().size();
117 const QualType getParamType(unsigned i) const
119 if (m_calleeFunctionDecl)
120 return m_calleeFunctionDecl->getParamDecl(i)->getType();
121 else if (m_cxxConstructorDecl)
122 return m_cxxConstructorDecl->getParamDecl(i)->getType();
123 else
124 return m_functionPrototype->getParamTypes()[i];
126 std::string getNameAsString() const
128 if (m_calleeFunctionDecl)
129 return m_calleeFunctionDecl->getNameAsString();
130 else if (m_cxxConstructorDecl)
131 return m_cxxConstructorDecl->getNameAsString();
132 else
133 return "";
135 CXXMethodDecl const* getAsCXXMethodDecl() const
137 if (m_calleeFunctionDecl)
138 return dyn_cast<CXXMethodDecl>(m_calleeFunctionDecl);
139 return nullptr;
143 class WriteOnlyVars : public loplugin::FilteringPlugin<WriteOnlyVars>
145 public:
146 explicit WriteOnlyVars(loplugin::InstantiationData const& data)
147 : FilteringPlugin(data)
151 virtual void run() override;
153 bool shouldVisitTemplateInstantiations() const { return true; }
154 bool shouldVisitImplicitCode() const { return true; }
156 bool VisitVarDecl(const VarDecl*);
157 bool VisitDeclRefExpr(const DeclRefExpr*);
158 bool TraverseIfStmt(IfStmt*);
160 private:
161 MyVarInfo niceName(const VarDecl*);
162 void checkIfReadFrom(const VarDecl* varDecl, const Expr* memberExpr);
163 void checkIfWrittenTo(const VarDecl* varDecl, const Expr* memberExpr);
164 bool checkForWriteWhenUsingCollectionType(const CXXMethodDecl* calleeMethodDecl);
165 bool IsPassedByNonConst(const VarDecl* varDecl, const Stmt* child, CallerWrapper callExpr,
166 CalleeWrapper calleeFunctionDecl);
167 llvm::Optional<CalleeWrapper> getCallee(CallExpr const*);
169 // For reasons I do not understand, parentFunctionDecl() is not reliable, so
170 // we store the parent function on the way down the AST.
171 FunctionDecl* insideFunctionDecl = nullptr;
172 std::vector<VarDecl const*> insideConditionalCheckOfMemberSet;
175 void WriteOnlyVars::run()
177 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
179 if (!isUnitTestMode())
181 StringRef fn(handler.getMainFileName());
182 // playing paging-in games with volatile
183 if (loplugin::isSamePathname(fn, SRCDIR "/sal/osl/unx/file.cxx"))
184 return;
185 // playing paging-in games with volatile
186 if (loplugin::isSamePathname(fn, SRCDIR "/desktop/unx/source/file_image_unx.c"))
187 return;
188 // false+
189 if (loplugin::isSamePathname(fn, SRCDIR "/store/source/storpage.cxx"))
190 return;
191 // yydebug?
192 if (loplugin::isSamePathname(fn, SRCDIR "/idlc/source/idlccompile.cxx"))
193 return;
194 if (fn.contains("/qa/"))
195 return;
196 if (fn.contains("/vcl/workben/"))
197 return;
198 // preload
199 if (loplugin::isSamePathname(fn, SRCDIR "/cppuhelper/source/servicemanager.cxx"))
200 return;
201 // doing a "free items outside lock" thing
202 if (loplugin::isSamePathname(fn, SRCDIR "/unotools/source/config/itemholder1.cxx"))
203 return;
204 if (loplugin::isSamePathname(fn, SRCDIR "/svl/source/config/itemholder2.cxx"))
205 return;
206 if (loplugin::isSamePathname(fn, SRCDIR "/svtools/source/config/itemholder2.cxx"))
207 return;
208 // doing a "keep objects alive" thing
209 if (loplugin::isSamePathname(fn, SRCDIR "/jvmfwk/source/framework.cxx"))
210 return;
211 if (loplugin::isSamePathname(fn, SRCDIR "/jvmfwk/plugins/sunmajor/pluginlib/util.cxx"))
212 return;
213 // debug code
214 if (loplugin::isSamePathname(fn, SRCDIR "/svl/source/items/style.cxx"))
215 return;
216 // ok
217 if (loplugin::isSamePathname(fn, SRCDIR "/stoc/source/inspect/introspection.cxx"))
218 return;
219 if (loplugin::isSamePathname(fn, SRCDIR "/package/source/zippackage/ZipPackage.cxx"))
220 return;
221 if (loplugin::isSamePathname(fn, SRCDIR "/hwpfilter/source/hwpreader.cxx"))
222 return;
223 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/treelist/transfer.cxx"))
224 return;
225 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/app/brand.cxx"))
226 return;
227 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/filter/igif/gifread.cxx"))
228 return;
229 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/gdi/metaact.cxx"))
230 return;
231 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/fontsubset/sft.cxx"))
232 return;
233 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/filter/ipdf/pdfdocument.cxx"))
234 return;
235 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/filter/ipdf/pdfdocument2.cxx"))
236 return;
237 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/unx/generic/app/sm.cxx"))
238 return;
239 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/filter/jpeg/JpegWriter.cxx"))
240 return;
241 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/unx/generic/dtrans/X11_selection.cxx"))
242 return;
243 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/filter/jpeg/jpegc.cxx"))
244 return;
245 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/unx/generic/window/FWS.cxx"))
246 return;
247 if (loplugin::isSamePathname(fn, SRCDIR "/toolkit/source/awt/vclxspinbutton.cxx"))
248 return;
249 if (loplugin::isSamePathname(fn, SRCDIR "/toolkit/source/controls/formattedcontrol.cxx"))
250 return;
251 if (loplugin::isSamePathname(fn, SRCDIR "/svtools/source/config/helpopt.cxx"))
252 return;
253 if (loplugin::isSamePathname(fn, SRCDIR "/svtools/source/filter/SvFilterOptionsDialog.cxx"))
254 return;
255 if (loplugin::isSamePathname(fn, SRCDIR "/svtools/source/java/javainteractionhandler.cxx"))
256 return;
257 if (loplugin::isSamePathname(fn, SRCDIR "/basic/source/classes/sbunoobj.cxx"))
258 return;
259 if (loplugin::isSamePathname(fn,
260 SRCDIR "/accessibility/source/standard/vclxaccessiblebox.cxx"))
261 return;
262 if (loplugin::isSamePathname(fn, SRCDIR "/cppcanvas/source/mtfrenderer/implrenderer.cxx"))
263 return;
264 if (loplugin::isSamePathname(fn, SRCDIR "/sfx2/source/doc/guisaveas.cxx"))
265 return;
266 if (loplugin::isSamePathname(fn, SRCDIR "/sfx2/source/appl/newhelp.cxx"))
267 return;
268 if (loplugin::isSamePathname(fn, SRCDIR "/sfx2/source/control/thumbnailview.cxx"))
269 return;
270 if (loplugin::isSamePathname(fn, SRCDIR "/sfx2/source/control/recentdocsview.cxx"))
271 return;
272 if (loplugin::isSamePathname(fn, SRCDIR "/sfx2/source/view/viewfrm.cxx"))
273 return;
274 if (loplugin::isSamePathname(fn, SRCDIR "/framework/source/services/desktop.cxx"))
275 return;
276 if (loplugin::isSamePathname(fn, SRCDIR
277 "/framework/source/uielement/generictoolbarcontroller.cxx"))
278 return;
279 if (loplugin::isSamePathname(fn, SRCDIR
280 "/framework/source/uielement/complextoolbarcontroller.cxx"))
281 return;
282 if (loplugin::isSamePathname(fn,
283 SRCDIR "/framework/source/interaction/quietinteraction.cxx"))
284 return;
285 if (loplugin::isSamePathname(fn, SRCDIR "/editeng/source/editeng/editdoc.cxx"))
286 return;
287 if (loplugin::isSamePathname(fn, SRCDIR "/editeng/source/editeng/impedit4.cxx"))
288 return;
289 if (loplugin::isSamePathname(fn, SRCDIR "/editeng/source/editeng/editobj.cxx"))
290 return;
291 if (loplugin::isSamePathname(fn, SRCDIR "/editeng/source/items/frmitems.cxx"))
292 return;
293 if (loplugin::isSamePathname(fn, SRCDIR "/binaryurp/source/bridge.cxx"))
294 return;
295 if (loplugin::isSamePathname(fn, SRCDIR "/svx/source/tbxctrls/fontworkgallery.cxx"))
296 return;
297 if (loplugin::isSamePathname(fn, SRCDIR "/basctl/source/basicide/moduldl2.cxx"))
298 return;
299 if (loplugin::isSamePathname(fn, SRCDIR "/canvas/source/cairo/cairo_spritecanvas.cxx"))
300 return;
301 if (loplugin::isSamePathname(fn, SRCDIR "/chart2/source/tools/DiagramHelper.cxx"))
302 return;
303 if (loplugin::isSamePathname(fn,
304 SRCDIR "/chart2/source/tools/ExplicitCategoriesProvider.cxx"))
305 return;
306 if (loplugin::isSamePathname(fn, SRCDIR "/chart2/source/tools/LegendHelper.cxx"))
307 return;
308 if (loplugin::isSamePathname(fn, SRCDIR "/chart2/source/tools/OPropertySet.cxx"))
309 return;
310 if (loplugin::isSamePathname(fn, SRCDIR "/chart2/source/tools/CommonConverters.cxx"))
311 return;
312 if (loplugin::isSamePathname(
314 SRCDIR "/chart2/source/controller/chartapiwrapper/WrappedNumberFormatProperty.cxx"))
315 return;
316 if (loplugin::isSamePathname(fn, SRCDIR "/chart2/source/tools/DataSourceHelper.cxx"))
317 return;
318 if (loplugin::isSamePathname(fn, SRCDIR "/oox/source/export/shapes.cxx"))
319 return;
320 if (loplugin::isSamePathname(fn, SRCDIR "/oox/source/export/chartexport.cxx"))
321 return;
322 if (loplugin::isSamePathname(fn,
323 SRCDIR "/filter/source/storagefilterdetect/filterdetect.cxx"))
324 return;
325 if (loplugin::isSamePathname(fn, SRCDIR "/filter/source/pdf/pdfexport.cxx"))
326 return;
327 if (loplugin::isSamePathname(fn, SRCDIR "/filter/source/svg/svgexport.cxx"))
328 return;
329 if (loplugin::isSamePathname(fn, SRCDIR "/filter/source/msfilter/svdfppt.cxx"))
330 return;
331 if (loplugin::isSamePathname(fn, SRCDIR
332 "/dbaccess/source/core/recovery/subcomponentrecovery.cxx"))
333 return;
334 if (loplugin::isSamePathname(fn, SRCDIR
335 "/dbaccess/source/core/dataaccess/documentcontainer.cxx"))
336 return;
337 if (loplugin::isSamePathname(fn, SRCDIR
338 "/dbaccess/source/core/dataaccess/databasedocument.cxx"))
339 return;
340 if (loplugin::isSamePathname(fn,
341 SRCDIR "/dbaccess/source/ui/browser/genericcontroller.cxx"))
342 return;
343 if (loplugin::isSamePathname(fn, SRCDIR "/ucb/source/core/ucbcmds.cxx"))
344 return;
345 if (loplugin::isSamePathname(fn,
346 SRCDIR "/desktop/source/deployment/manager/dp_manager.cxx"))
347 return;
348 if (loplugin::isSamePathname(fn, SRCDIR
349 "/desktop/source/deployment/registry/package/dp_package.cxx"))
350 return;
351 if (loplugin::isSamePathname(fn, SRCDIR "/desktop/source/lib/init.cxx"))
352 return;
353 if (loplugin::isSamePathname(fn, SRCDIR
354 "/extensions/source/propctrlr/formcomponenthandler.cxx"))
355 return;
356 if (loplugin::isSamePathname(fn, SRCDIR "/embeddedobj/source/general/docholder.cxx"))
357 return;
358 if (loplugin::isSamePathname(fn, SRCDIR
359 "/extensions/source/propctrlr/stringrepresentation.cxx"))
360 return;
361 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwpcontent.cxx"))
362 return;
363 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwpdivinfo.cxx"))
364 return;
365 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwpdoc.cxx"))
366 return;
367 if (loplugin::isSamePathname(fn, SRCDIR "/filter/source/pdf/impdialog.cxx"))
368 return;
369 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwplayout.cxx"))
370 return;
371 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwpoleobject.cxx"))
372 return;
373 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwprowlayout.cxx"))
374 return;
375 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwpfoundry.cxx"))
376 return;
377 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwpparastyle.cxx"))
378 return;
379 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwpnotes.cxx"))
380 return;
381 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwpfont.cxx"))
382 return;
383 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwptblcell.cxx"))
384 return;
385 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwpusrdicts.cxx"))
386 return;
387 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwpverdocument.cxx"))
388 return;
389 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwptblformula.cxx"))
390 return;
391 if (loplugin::isSamePathname(fn, SRCDIR "/vbahelper/source/vbahelper/vbafontbase.cxx"))
392 return;
393 if (loplugin::isSamePathname(fn, SRCDIR "/vbahelper/source/vbahelper/vbadocumentbase.cxx"))
394 return;
395 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/ui/docshell/docsh8.cxx"))
396 return;
397 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/ui/docshell/docsh6.cxx"))
398 return;
399 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/core/data/table3.cxx"))
400 return;
401 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/ui/unoobj/cellsuno.cxx"))
402 return;
403 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/filter/excel/xelink.cxx"))
404 return;
405 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/filter/lotus/lotus.cxx"))
406 return;
407 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/ui/vba/vbaworkbooks.cxx"))
408 return;
409 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/ui/vba/vbaworksheets.cxx"))
410 return;
411 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/ui/vba/vbarange.cxx"))
412 return;
413 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/view/drviews2.cxx"))
414 return;
415 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/filter/ppt/pptin.cxx"))
416 return;
417 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/app/sdxfer.cxx"))
418 return;
419 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/view/drviewsf.cxx"))
420 return;
421 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/filter/xml/sdxmlwrp.cxx"))
422 return;
423 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/filter/html/pubdlg.cxx"))
424 return;
425 if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/core/txtnode/thints.cxx"))
426 return;
427 if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/core/doc/docbm.cxx"))
428 return;
429 if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/core/crsr/crsrsh.cxx"))
430 return;
431 if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/filter/xml/swxml.cxx"))
432 return;
433 if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/core/doc/docredln.cxx"))
434 return;
435 if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/filter/ww8/ww8par2.cxx"))
436 return;
437 if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/uibase/shells/drformsh.cxx"))
438 return;
439 if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/filter/ww8/ww8par6.cxx"))
440 return;
441 if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/ui/dbui/dbinsdlg.cxx"))
442 return;
443 if (loplugin::isSamePathname(fn, SRCDIR "/sdext/source/minimizer/impoptimizer.cxx"))
444 return;
445 if (loplugin::isSamePathname(fn, SRCDIR "/sdext/source/presenter/PresenterTheme.cxx"))
446 return;
447 if (loplugin::isSamePathname(fn, SRCDIR "/sdext/source/pdfimport/wrapper/wrapper.cxx"))
448 return;
449 if (loplugin::isSamePathname(fn, SRCDIR
450 "/slideshow/source/engine/animationnodes/generateevent.cxx"))
451 return;
452 if (loplugin::isSamePathname(fn, SRCDIR "/starmath/source/mathmlimport.cxx"))
453 return;
454 if (loplugin::isSamePathname(fn, SRCDIR "/starmath/source/eqnolefilehdr.cxx"))
455 return;
456 if (loplugin::isSamePathname(fn, SRCDIR "/svgio/source/svgreader/svgmarkernode.cxx"))
457 return;
458 if (loplugin::isSamePathname(fn, SRCDIR "/uui/source/iahndl-locking.cxx"))
459 return;
460 if (loplugin::isSamePathname(fn, SRCDIR
461 "/shell/source/sessioninstall/SyncDbusSessionHelper.cxx"))
462 return;
463 if (loplugin::isSamePathname(fn,
464 SRCDIR "/slideshow/source/engine/opengl/TransitionerImpl.cxx"))
465 return;
466 if (loplugin::isSamePathname(fn, SRCDIR "/forms/source/component/FormattedField.cxx"))
467 return;
468 if (loplugin::isSamePathname(fn, SRCDIR "/forms/source/component/DatabaseForm.cxx"))
469 return;
470 if (loplugin::isSamePathname(fn,
471 SRCDIR "/reportdesign/source/ui/report/ReportController.cxx"))
472 return;
473 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/test/"))
474 return;
475 if (loplugin::isSamePathname(fn, SRCDIR "/i18npool/source/localedata/LocaleNode.cxx"))
476 return;
478 // yynerrs?
479 if (loplugin::isSamePathname(fn, SRCDIR "/hwpfilter/source/grammar.cxx"))
480 return;
482 for (MyVarInfo const& v : definitionSet)
484 bool read = readFromSet.find(v) != readFromSet.end();
485 bool write = writeToSet.find(v) != writeToSet.end();
486 if (!read && write)
487 report(DiagnosticsEngine::Warning, "write-only %0", compat::getBeginLoc(v.varDecl))
488 << v.varName;
491 else
493 for (const MyVarInfo& s : readFromSet)
494 report(DiagnosticsEngine::Warning, "read %0", compat::getBeginLoc(s.varDecl))
495 << s.varName;
496 for (const MyVarInfo& s : writeToSet)
497 report(DiagnosticsEngine::Warning, "write %0", compat::getBeginLoc(s.varDecl))
498 << s.varName;
502 MyVarInfo WriteOnlyVars::niceName(const VarDecl* varDecl)
504 MyVarInfo aInfo;
506 aInfo.varDecl = varDecl->getCanonicalDecl();
507 aInfo.varName = varDecl->getNameAsString();
508 // sometimes the name (if it's an anonymous thing) contains the full path of the build folder, which we don't need
509 size_t idx = aInfo.varName.find(SRCDIR);
510 if (idx != std::string::npos)
512 aInfo.varName = aInfo.varName.replace(idx, strlen(SRCDIR), "");
514 aInfo.varType = varDecl->getType().getAsString();
516 SourceLocation expansionLoc
517 = compiler.getSourceManager().getExpansionLoc(varDecl->getLocation());
518 StringRef filename = getFilenameOfLocation(expansionLoc);
519 aInfo.sourceLocation
520 = std::string(filename.substr(strlen(SRCDIR) + 1)) + ":"
521 + std::to_string(compiler.getSourceManager().getSpellingLineNumber(expansionLoc));
522 loplugin::normalizeDotDotInFilePath(aInfo.sourceLocation);
523 aInfo.parent = filename.str();
525 return aInfo;
528 static bool contains(std::string const& s, std::string const& needle)
530 return s.find(needle) != std::string::npos;
533 bool WriteOnlyVars::VisitVarDecl(const VarDecl* varDecl)
535 if (varDecl->isImplicit() || varDecl->isExternC() || isa<ParmVarDecl>(varDecl))
536 return true;
537 auto tc = loplugin::TypeCheck(varDecl->getType());
538 if (tc.Pointer() || tc.LvalueReference() || tc.Class("shared_ptr").StdNamespace()
539 || tc.Class("unique_ptr").StdNamespace())
540 return true;
541 if (tc.Typedef("BitmapScopedWriteAccess"))
542 return true;
543 std::string typeName = varDecl->getType().getAsString();
544 if (contains(typeName, "Guard") || contains(typeName, "Reader") || contains(typeName, "Stream")
545 || contains(typeName, "Parser") || contains(typeName, "Codec")
546 || contains(typeName, "Exception"))
547 return true;
548 varDecl = varDecl->getCanonicalDecl();
549 if (!varDecl->getLocation().isValid() || ignoreLocation(varDecl))
550 return true;
551 if (!compiler.getSourceManager().isInMainFile(varDecl->getLocation()))
552 return true;
553 if (compiler.getSourceManager().isMacroBodyExpansion(compat::getBeginLoc(varDecl)))
554 return true;
555 if (compiler.getSourceManager().isMacroArgExpansion(compat::getBeginLoc(varDecl)))
556 return true;
557 // ignore stuff that forms part of the stable URE interface
558 if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(varDecl->getLocation())))
559 return true;
561 definitionSet.insert(niceName(varDecl));
562 return true;
565 static char easytolower(char in)
567 if (in <= 'Z' && in >= 'A')
568 return in - ('Z' - 'z');
569 return in;
572 bool startswith(const std::string& rStr, const char* pSubStr)
574 return rStr.compare(0, strlen(pSubStr), pSubStr) == 0;
577 bool WriteOnlyVars::TraverseIfStmt(IfStmt* ifStmt)
579 VarDecl const* varDecl = nullptr;
580 Expr const* cond = ifStmt->getCond()->IgnoreParenImpCasts();
581 if (auto declRefExpr = dyn_cast<DeclRefExpr>(cond))
583 if ((varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl())))
584 insideConditionalCheckOfMemberSet.push_back(varDecl);
586 bool ret = RecursiveASTVisitor::TraverseIfStmt(ifStmt);
587 if (varDecl)
588 insideConditionalCheckOfMemberSet.pop_back();
589 return ret;
592 void WriteOnlyVars::checkIfReadFrom(const VarDecl* varDecl, const Expr* memberExpr)
594 auto parentsRange = compiler.getASTContext().getParents(*memberExpr);
595 const Stmt* child = memberExpr;
596 const Stmt* parent
597 = parentsRange.begin() == parentsRange.end() ? nullptr : parentsRange.begin()->get<Stmt>();
598 // walk up the tree until we find something interesting
599 bool bPotentiallyReadFrom = false;
600 bool bDump = false;
601 auto walkupUp = [&]() {
602 child = parent;
603 auto parentsRange = compiler.getASTContext().getParents(*parent);
604 parent = parentsRange.begin() == parentsRange.end() ? nullptr
605 : parentsRange.begin()->get<Stmt>();
609 if (!parent)
611 // check if we're inside a CXXCtorInitializer or a VarDecl
612 auto parentsRange = compiler.getASTContext().getParents(*child);
613 if (parentsRange.begin() != parentsRange.end())
615 const Decl* decl = parentsRange.begin()->get<Decl>();
616 if (decl && (isa<CXXConstructorDecl>(decl) || isa<VarDecl>(decl)))
617 bPotentiallyReadFrom = true;
619 if (!bPotentiallyReadFrom)
620 return;
621 break;
623 if (isa<CXXReinterpretCastExpr>(parent))
625 // once we see one of these, there is not much useful we can know
626 bPotentiallyReadFrom = true;
627 break;
629 else if (isa<CastExpr>(parent) || isa<MemberExpr>(parent) || isa<ParenExpr>(parent)
630 || isa<ParenListExpr>(parent) || isa<ArrayInitLoopExpr>(parent)
631 || isa<ExprWithCleanups>(parent))
633 walkupUp();
635 else if (auto unaryOperator = dyn_cast<UnaryOperator>(parent))
637 UnaryOperator::Opcode op = unaryOperator->getOpcode();
638 if (memberExpr->getType()->isArrayType() && op == UO_Deref)
640 // ignore, deref'ing an array does not count as a read
642 else if (op == UO_AddrOf || op == UO_Deref || op == UO_Plus || op == UO_Minus
643 || op == UO_Not || op == UO_LNot)
645 bPotentiallyReadFrom = true;
646 break;
648 /* The following are technically reads, but from a code-sense they're more of a write/modify, so
649 ignore them to find interesting fields that only modified, not usefully read:
650 UO_PreInc / UO_PostInc / UO_PreDec / UO_PostDec
651 But we still walk up in case the result of the expression is used in a read sense.
653 walkupUp();
655 else if (auto caseStmt = dyn_cast<CaseStmt>(parent))
657 bPotentiallyReadFrom = caseStmt->getLHS() == child || caseStmt->getRHS() == child;
658 break;
660 else if (auto ifStmt = dyn_cast<IfStmt>(parent))
662 bPotentiallyReadFrom = ifStmt->getCond() == child;
663 break;
665 else if (auto doStmt = dyn_cast<DoStmt>(parent))
667 bPotentiallyReadFrom = doStmt->getCond() == child;
668 break;
670 else if (auto arraySubscriptExpr = dyn_cast<ArraySubscriptExpr>(parent))
672 if (arraySubscriptExpr->getIdx() == child)
674 bPotentiallyReadFrom = true;
675 break;
677 walkupUp();
679 else if (auto callExpr = dyn_cast<CXXMemberCallExpr>(parent))
681 // check for calls to ReadXXX() type methods and the operator>>= methods on Any.
682 auto callee = getCallee(callExpr);
683 if (callee && *callExpr->child_begin() == child)
685 // FIXME perhaps a better solution here would be some kind of SAL_PARAM_WRITEONLY attribute
686 // which we could scatter around.
687 std::string name = callee->getNameAsString();
688 std::transform(name.begin(), name.end(), name.begin(), easytolower);
689 if (startswith(name, "read"))
690 // this is a write-only call
692 else if (startswith(name, "emplace") || name == "insert" || name == "erase"
693 || name == "remove" || name == "remove_if" || name == "sort"
694 || name == "push_back" || name == "pop_back" || name == "push_front"
695 || name == "pop_front" || name == "reserve" || name == "resize"
696 || name == "clear" || name == "fill")
697 // write-only modifications to collections
699 else if (name.find(">>=") != std::string::npos && callExpr->getArg(1) == child)
700 // this is a write-only call
702 else if (name == "dispose" || name == "disposeAndClear" || name == "swap")
703 // we're abusing the write-only analysis here to look for vars which don't have anything useful
704 // being done to them, so we're ignoring things like std::vector::clear, std::vector::swap,
705 // and VclPtr::disposeAndClear
707 else
708 bPotentiallyReadFrom = true;
710 else
711 bPotentiallyReadFrom = true;
712 break;
714 else if (auto callExpr = dyn_cast<CallExpr>(parent))
716 // check for calls to ReadXXX() type methods and the operator>>= methods on Any.
717 auto callee = getCallee(callExpr);
718 if (callee)
720 // FIXME perhaps a better solution here would be some kind of SAL_PARAM_WRITEONLY attribute
721 // which we could scatter around.
722 std::string name = callee->getNameAsString();
723 std::transform(name.begin(), name.end(), name.begin(), easytolower);
724 if (startswith(name, "read"))
725 // this is a write-only call
727 else if (name.find(">>=") != std::string::npos && callExpr->getArg(1) == child)
728 // this is a write-only call
730 else
731 bPotentiallyReadFrom = true;
733 else
734 bPotentiallyReadFrom = true;
735 break;
737 else if (auto binaryOp = dyn_cast<BinaryOperator>(parent))
739 BinaryOperator::Opcode op = binaryOp->getOpcode();
740 // If the child is on the LHS and it is an assignment op, we are obviously not reading from it
741 const bool assignmentOp = op == BO_Assign || op == BO_MulAssign || op == BO_DivAssign
742 || op == BO_RemAssign || op == BO_AddAssign
743 || op == BO_SubAssign || op == BO_ShlAssign
744 || op == BO_ShrAssign || op == BO_AndAssign
745 || op == BO_XorAssign || op == BO_OrAssign;
746 if (!(binaryOp->getLHS() == child && assignmentOp))
748 bPotentiallyReadFrom = true;
750 break;
752 else if (isa<ReturnStmt>(parent) || isa<CXXConstructExpr>(parent)
753 || isa<ConditionalOperator>(parent) || isa<SwitchStmt>(parent)
754 || isa<DeclStmt>(parent) || isa<WhileStmt>(parent) || isa<CXXNewExpr>(parent)
755 || isa<ForStmt>(parent) || isa<InitListExpr>(parent)
756 || isa<CXXDependentScopeMemberExpr>(parent) || isa<UnresolvedMemberExpr>(parent)
757 || isa<MaterializeTemporaryExpr>(parent))
759 bPotentiallyReadFrom = true;
760 break;
762 else if (isa<CXXDeleteExpr>(parent) || isa<UnaryExprOrTypeTraitExpr>(parent)
763 || isa<CXXUnresolvedConstructExpr>(parent) || isa<CompoundStmt>(parent)
764 || isa<LabelStmt>(parent) || isa<CXXForRangeStmt>(parent)
765 || isa<CXXTypeidExpr>(parent) || isa<DefaultStmt>(parent)
766 || isa<GCCAsmStmt>(parent) || isa<VAArgExpr>(parent)
767 #if CLANG_VERSION >= 80000
768 || isa<ConstantExpr>(parent)
769 #endif
770 || isa<CXXDefaultArgExpr>(parent) || isa<LambdaExpr>(parent))
772 break;
774 else
776 bPotentiallyReadFrom = true;
777 bDump = true;
778 break;
780 } while (true);
782 if (bDump)
784 report(DiagnosticsEngine::Warning, "oh dear, what can the matter be?",
785 compat::getBeginLoc(memberExpr))
786 << memberExpr->getSourceRange();
787 report(DiagnosticsEngine::Note, "parent over here", compat::getBeginLoc(parent))
788 << parent->getSourceRange();
789 parent->dump();
790 memberExpr->dump();
793 MyVarInfo varInfo = niceName(varDecl);
794 if (bPotentiallyReadFrom)
796 readFromSet.insert(varInfo);
800 void WriteOnlyVars::checkIfWrittenTo(const VarDecl* varDecl, const Expr* memberExpr)
802 // if we're inside a block that looks like
803 // if (varDecl)
804 // ...
805 // then writes to this var don't matter, because unless we find another write to this var, this var is dead
806 if (std::find(insideConditionalCheckOfMemberSet.begin(),
807 insideConditionalCheckOfMemberSet.end(), varDecl)
808 != insideConditionalCheckOfMemberSet.end())
809 return;
811 auto parentsRange = compiler.getASTContext().getParents(*memberExpr);
812 const Stmt* child = memberExpr;
813 const Stmt* parent
814 = parentsRange.begin() == parentsRange.end() ? nullptr : parentsRange.begin()->get<Stmt>();
815 // walk up the tree until we find something interesting
816 bool bPotentiallyWrittenTo = false;
817 bool bDump = false;
818 auto walkupUp = [&]() {
819 child = parent;
820 auto parentsRange = compiler.getASTContext().getParents(*parent);
821 parent = parentsRange.begin() == parentsRange.end() ? nullptr
822 : parentsRange.begin()->get<Stmt>();
826 if (!parent)
828 // check if we have an expression like
829 // int& r = var;
830 auto parentsRange = compiler.getASTContext().getParents(*child);
831 if (parentsRange.begin() != parentsRange.end())
833 auto varDecl = dyn_cast_or_null<VarDecl>(parentsRange.begin()->get<Decl>());
834 // The isImplicit() call is to avoid triggering when we see the vardecl which is part of a for-range statement,
835 // which is of type 'T&&' and also an l-value-ref ?
836 if (varDecl && !varDecl->isImplicit()
837 && loplugin::TypeCheck(varDecl->getType()).LvalueReference().NonConst())
839 bPotentiallyWrittenTo = true;
842 break;
844 if (isa<CXXReinterpretCastExpr>(parent))
846 // once we see one of these, there is not much useful we can know
847 bPotentiallyWrittenTo = true;
848 break;
850 else if (isa<CastExpr>(parent) || isa<MemberExpr>(parent) || isa<ParenExpr>(parent)
851 || isa<ParenListExpr>(parent) || isa<ArrayInitLoopExpr>(parent)
852 || isa<ExprWithCleanups>(parent))
854 walkupUp();
856 else if (auto unaryOperator = dyn_cast<UnaryOperator>(parent))
858 UnaryOperator::Opcode op = unaryOperator->getOpcode();
859 if (op == UO_AddrOf || op == UO_PostInc || op == UO_PostDec || op == UO_PreInc
860 || op == UO_PreDec)
862 bPotentiallyWrittenTo = true;
864 break;
866 else if (auto arraySubscriptExpr = dyn_cast<ArraySubscriptExpr>(parent))
868 if (arraySubscriptExpr->getIdx() == child)
869 break;
870 walkupUp();
872 else if (auto operatorCallExpr = dyn_cast<CXXOperatorCallExpr>(parent))
874 auto callee = getCallee(operatorCallExpr);
875 if (callee)
877 // if calling a non-const operator on the var
878 auto calleeMethodDecl = callee->getAsCXXMethodDecl();
879 if (calleeMethodDecl && operatorCallExpr->getArg(0) == child)
881 if (!calleeMethodDecl->isConst())
882 bPotentiallyWrittenTo
883 = checkForWriteWhenUsingCollectionType(calleeMethodDecl);
885 else if (IsPassedByNonConst(varDecl, child, operatorCallExpr, *callee))
887 bPotentiallyWrittenTo = true;
890 else
891 bPotentiallyWrittenTo = true; // conservative, could improve
892 break;
894 else if (auto cxxMemberCallExpr = dyn_cast<CXXMemberCallExpr>(parent))
896 const CXXMethodDecl* calleeMethodDecl = cxxMemberCallExpr->getMethodDecl();
897 if (calleeMethodDecl)
899 // if calling a non-const method on the var
900 const Expr* tmp = dyn_cast<Expr>(child);
901 if (tmp->isBoundMemberFunction(compiler.getASTContext()))
903 tmp = dyn_cast<MemberExpr>(tmp)->getBase();
905 if (cxxMemberCallExpr->getImplicitObjectArgument() == tmp)
907 if (!calleeMethodDecl->isConst())
908 bPotentiallyWrittenTo
909 = checkForWriteWhenUsingCollectionType(calleeMethodDecl);
910 break;
912 else if (IsPassedByNonConst(varDecl, child, cxxMemberCallExpr,
913 CalleeWrapper(calleeMethodDecl)))
914 bPotentiallyWrittenTo = true;
916 else
917 bPotentiallyWrittenTo = true; // can happen in templates
918 break;
920 else if (auto cxxConstructExpr = dyn_cast<CXXConstructExpr>(parent))
922 if (IsPassedByNonConst(varDecl, child, cxxConstructExpr,
923 CalleeWrapper(cxxConstructExpr)))
924 bPotentiallyWrittenTo = true;
925 break;
927 else if (auto callExpr = dyn_cast<CallExpr>(parent))
929 auto callee = getCallee(callExpr);
930 if (callee)
932 if (IsPassedByNonConst(varDecl, child, callExpr, *callee))
933 bPotentiallyWrittenTo = true;
935 else
936 bPotentiallyWrittenTo = true; // conservative, could improve
937 break;
939 else if (auto binaryOp = dyn_cast<BinaryOperator>(parent))
941 BinaryOperator::Opcode op = binaryOp->getOpcode();
942 const bool assignmentOp = op == BO_Assign || op == BO_MulAssign || op == BO_DivAssign
943 || op == BO_RemAssign || op == BO_AddAssign
944 || op == BO_SubAssign || op == BO_ShlAssign
945 || op == BO_ShrAssign || op == BO_AndAssign
946 || op == BO_XorAssign || op == BO_OrAssign;
947 if (assignmentOp)
949 if (binaryOp->getLHS() == child)
950 bPotentiallyWrittenTo = true;
951 else if (loplugin::TypeCheck(binaryOp->getLHS()->getType())
952 .LvalueReference()
953 .NonConst())
954 // if the LHS is a non-const reference, we could write to the var later on
955 bPotentiallyWrittenTo = true;
957 break;
959 else if (isa<ReturnStmt>(parent))
961 if (insideFunctionDecl)
963 auto tc = loplugin::TypeCheck(insideFunctionDecl->getReturnType());
964 if (tc.LvalueReference().NonConst())
965 bPotentiallyWrittenTo = true;
967 break;
969 else if (isa<ConditionalOperator>(parent) || isa<SwitchStmt>(parent)
970 || isa<DeclStmt>(parent) || isa<WhileStmt>(parent) || isa<CXXNewExpr>(parent)
971 || isa<ForStmt>(parent) || isa<InitListExpr>(parent)
972 || isa<CXXDependentScopeMemberExpr>(parent) || isa<UnresolvedMemberExpr>(parent)
973 || isa<MaterializeTemporaryExpr>(parent) || isa<IfStmt>(parent)
974 || isa<DoStmt>(parent) || isa<CXXDeleteExpr>(parent)
975 || isa<UnaryExprOrTypeTraitExpr>(parent) || isa<CXXUnresolvedConstructExpr>(parent)
976 || isa<CompoundStmt>(parent) || isa<LabelStmt>(parent)
977 || isa<CXXForRangeStmt>(parent) || isa<CXXTypeidExpr>(parent)
978 || isa<DefaultStmt>(parent)
979 #if CLANG_VERSION >= 80000
980 || isa<ConstantExpr>(parent)
981 #endif
982 || isa<GCCAsmStmt>(parent) || isa<VAArgExpr>(parent)
983 || isa<CXXDefaultArgExpr>(parent) || isa<LambdaExpr>(parent))
985 break;
987 else
989 bPotentiallyWrittenTo = true;
990 bDump = true;
991 break;
993 } while (true);
995 if (bDump)
997 report(DiagnosticsEngine::Warning, "oh dear2, what can the matter be? writtenTo=%0",
998 compat::getBeginLoc(memberExpr))
999 << bPotentiallyWrittenTo << memberExpr->getSourceRange();
1000 if (parent)
1002 report(DiagnosticsEngine::Note, "parent over here", compat::getBeginLoc(parent))
1003 << parent->getSourceRange();
1004 parent->dump();
1006 memberExpr->dump();
1007 varDecl->getType()->dump();
1010 MyVarInfo varInfo = niceName(varDecl);
1011 if (bPotentiallyWrittenTo)
1013 writeToSet.insert(varInfo);
1017 // return true if this not a collection type, or if it is a collection type, and we might be writing to it
1018 bool WriteOnlyVars::checkForWriteWhenUsingCollectionType(const CXXMethodDecl* calleeMethodDecl)
1020 auto const tc = loplugin::TypeCheck(calleeMethodDecl->getParent());
1021 bool listLike = false, setLike = false, mapLike = false, cssSequence = false;
1022 if (tc.Class("deque").StdNamespace() || tc.Class("list").StdNamespace()
1023 || tc.Class("queue").StdNamespace() || tc.Class("vector").StdNamespace())
1025 listLike = true;
1027 else if (tc.Class("set").StdNamespace() || tc.Class("unordered_set").StdNamespace())
1029 setLike = true;
1031 else if (tc.Class("map").StdNamespace() || tc.Class("unordered_map").StdNamespace())
1033 mapLike = true;
1035 else if (tc.Class("Sequence")
1036 .Namespace("uno")
1037 .Namespace("star")
1038 .Namespace("sun")
1039 .Namespace("com")
1040 .GlobalNamespace())
1042 cssSequence = true;
1044 else
1045 return true;
1047 if (calleeMethodDecl->isOverloadedOperator())
1049 auto oo = calleeMethodDecl->getOverloadedOperator();
1050 if (oo == OO_Equal)
1051 return true;
1052 // This is operator[]. We only care about things that add elements to the collection.
1053 // if nothing modifies the size of the collection, then nothing useful
1054 // is stored in it.
1055 if (listLike)
1056 return false;
1057 return true;
1060 auto name = calleeMethodDecl->getName();
1061 if (listLike || setLike || mapLike)
1063 if (name == "reserve" || name == "shrink_to_fit" || name == "clear" || name == "erase"
1064 || name == "pop_back" || name == "pop_front" || name == "front" || name == "back"
1065 || name == "data" || name == "remove" || name == "remove_if" || name == "unique"
1066 || name == "sort" || name == "begin" || name == "end" || name == "rbegin"
1067 || name == "rend" || name == "at" || name == "find" || name == "equal_range"
1068 || name == "lower_bound" || name == "upper_bound")
1069 return false;
1071 if (cssSequence)
1073 if (name == "getArray" || name == "begin" || name == "end")
1074 return false;
1077 return true;
1080 bool WriteOnlyVars::IsPassedByNonConst(const VarDecl* varDecl, const Stmt* child,
1081 CallerWrapper callExpr, CalleeWrapper calleeFunctionDecl)
1083 unsigned len = std::min(callExpr.getNumArgs(), calleeFunctionDecl.getNumParams());
1084 // if it's an array, passing it by value to a method typically means the
1085 // callee takes a pointer and can modify the array
1086 if (varDecl->getType()->isConstantArrayType())
1088 for (unsigned i = 0; i < len; ++i)
1089 if (callExpr.getArg(i) == child)
1090 if (loplugin::TypeCheck(calleeFunctionDecl.getParamType(i)).Pointer().NonConst())
1091 return true;
1093 else
1095 for (unsigned i = 0; i < len; ++i)
1096 if (callExpr.getArg(i) == child)
1097 if (loplugin::TypeCheck(calleeFunctionDecl.getParamType(i))
1098 .LvalueReference()
1099 .NonConst())
1100 return true;
1102 return false;
1105 bool WriteOnlyVars::VisitDeclRefExpr(const DeclRefExpr* declRefExpr)
1107 const Decl* decl = declRefExpr->getDecl();
1108 const VarDecl* varDecl = dyn_cast<VarDecl>(decl);
1109 if (!varDecl)
1110 return true;
1111 if (varDecl->isImplicit() || isa<ParmVarDecl>(varDecl))
1112 return true;
1113 varDecl = varDecl->getCanonicalDecl();
1114 if (ignoreLocation(varDecl))
1115 return true;
1116 // ignore stuff that forms part of the stable URE interface
1117 if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(varDecl->getLocation())))
1118 return true;
1120 checkIfReadFrom(varDecl, declRefExpr);
1122 checkIfWrittenTo(varDecl, declRefExpr);
1124 return true;
1127 llvm::Optional<CalleeWrapper> WriteOnlyVars::getCallee(CallExpr const* callExpr)
1129 FunctionDecl const* functionDecl = callExpr->getDirectCallee();
1130 if (functionDecl)
1131 return CalleeWrapper(functionDecl);
1133 // Extract the functionprototype from a type
1134 clang::Type const* calleeType = callExpr->getCallee()->getType().getTypePtr();
1135 if (auto pointerType = calleeType->getUnqualifiedDesugaredType()->getAs<clang::PointerType>())
1137 if (auto prototype = pointerType->getPointeeType()
1138 ->getUnqualifiedDesugaredType()
1139 ->getAs<FunctionProtoType>())
1141 return CalleeWrapper(prototype);
1145 return llvm::Optional<CalleeWrapper>();
1148 loplugin::Plugin::Registration<WriteOnlyVars> X("writeonlyvars", false);
1151 #endif
1153 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */