bump product version to 6.3.0.0.beta1
[LibreOffice.git] / compilerplugins / clang / writeonlyvars.cxx
blob95d6ac3ad0a27e52e02cdb23a72910697a22b73c
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>
21 #include "plugin.hxx"
22 #include "compat.hxx"
23 #include "check.hxx"
25 /**
26 Finds variables that are effectively write-only.
28 Largely the same as the unusedfields.cxx loplugin.
31 namespace
33 struct MyVarInfo
35 const VarDecl* varDecl;
36 std::string parent;
37 std::string varName;
38 std::string varType;
39 std::string sourceLocation;
41 bool operator<(const MyVarInfo& lhs, const MyVarInfo& rhs)
43 return std::tie(lhs.parent, lhs.varName) < std::tie(rhs.parent, rhs.varName);
46 // try to limit the voluminous output a little
47 static std::set<MyVarInfo> readFromSet;
48 static std::set<MyVarInfo> writeToSet;
49 static std::set<MyVarInfo> definitionSet;
51 /**
52 * Wrap the different kinds of callable and callee objects in the clang AST so I can define methods that handle everything.
54 class CallerWrapper
56 const CallExpr* m_callExpr;
57 const CXXConstructExpr* m_cxxConstructExpr;
59 public:
60 CallerWrapper(const CallExpr* callExpr)
61 : m_callExpr(callExpr)
62 , m_cxxConstructExpr(nullptr)
65 CallerWrapper(const CXXConstructExpr* cxxConstructExpr)
66 : m_callExpr(nullptr)
67 , m_cxxConstructExpr(cxxConstructExpr)
70 unsigned getNumArgs() const
72 return m_callExpr ? m_callExpr->getNumArgs() : m_cxxConstructExpr->getNumArgs();
74 const Expr* getArg(unsigned i) const
76 return m_callExpr ? m_callExpr->getArg(i) : m_cxxConstructExpr->getArg(i);
79 class CalleeWrapper
81 const FunctionDecl* m_calleeFunctionDecl = nullptr;
82 const CXXConstructorDecl* m_cxxConstructorDecl = nullptr;
83 const FunctionProtoType* m_functionPrototype = nullptr;
85 public:
86 explicit CalleeWrapper(const FunctionDecl* calleeFunctionDecl)
87 : m_calleeFunctionDecl(calleeFunctionDecl)
90 explicit CalleeWrapper(const CXXConstructExpr* cxxConstructExpr)
91 : m_cxxConstructorDecl(cxxConstructExpr->getConstructor())
94 explicit CalleeWrapper(const FunctionProtoType* functionPrototype)
95 : m_functionPrototype(functionPrototype)
98 unsigned getNumParams() const
100 if (m_calleeFunctionDecl)
101 return m_calleeFunctionDecl->getNumParams();
102 else if (m_cxxConstructorDecl)
103 return m_cxxConstructorDecl->getNumParams();
104 else if (m_functionPrototype->param_type_begin() == m_functionPrototype->param_type_end())
105 // FunctionProtoType will assert if we call getParamTypes() and it has no params
106 return 0;
107 else
108 return m_functionPrototype->getParamTypes().size();
110 const QualType getParamType(unsigned i) const
112 if (m_calleeFunctionDecl)
113 return m_calleeFunctionDecl->getParamDecl(i)->getType();
114 else if (m_cxxConstructorDecl)
115 return m_cxxConstructorDecl->getParamDecl(i)->getType();
116 else
117 return m_functionPrototype->getParamTypes()[i];
119 std::string getNameAsString() const
121 if (m_calleeFunctionDecl)
122 return m_calleeFunctionDecl->getNameAsString();
123 else if (m_cxxConstructorDecl)
124 return m_cxxConstructorDecl->getNameAsString();
125 else
126 return "";
128 CXXMethodDecl const* getAsCXXMethodDecl() const
130 if (m_calleeFunctionDecl)
131 return dyn_cast<CXXMethodDecl>(m_calleeFunctionDecl);
132 return nullptr;
136 class WriteOnlyVars : public loplugin::FilteringPlugin<WriteOnlyVars>
138 public:
139 explicit WriteOnlyVars(loplugin::InstantiationData const& data)
140 : FilteringPlugin(data)
144 virtual void run() override;
146 bool shouldVisitTemplateInstantiations() const { return true; }
147 bool shouldVisitImplicitCode() const { return true; }
149 bool VisitVarDecl(const VarDecl*);
150 bool VisitDeclRefExpr(const DeclRefExpr*);
151 bool TraverseIfStmt(IfStmt*);
153 private:
154 MyVarInfo niceName(const VarDecl*);
155 void checkIfReadFrom(const VarDecl* varDecl, const Expr* memberExpr);
156 void checkIfWrittenTo(const VarDecl* varDecl, const Expr* memberExpr);
157 bool checkForWriteWhenUsingCollectionType(const CXXMethodDecl* calleeMethodDecl);
158 bool IsPassedByNonConst(const VarDecl* varDecl, const Stmt* child, CallerWrapper callExpr,
159 CalleeWrapper calleeFunctionDecl);
160 llvm::Optional<CalleeWrapper> getCallee(CallExpr const*);
162 // For reasons I do not understand, parentFunctionDecl() is not reliable, so
163 // we store the parent function on the way down the AST.
164 FunctionDecl* insideFunctionDecl = nullptr;
165 std::vector<VarDecl const*> insideConditionalCheckOfMemberSet;
168 void WriteOnlyVars::run()
170 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
172 if (!isUnitTestMode())
174 StringRef fn(handler.getMainFileName());
175 // playing paging-in games with volatile
176 if (loplugin::isSamePathname(fn, SRCDIR "/sal/osl/unx/file.cxx"))
177 return;
178 // playing paging-in games with volatile
179 if (loplugin::isSamePathname(fn, SRCDIR "/desktop/unx/source/file_image_unx.c"))
180 return;
181 // false+
182 if (loplugin::isSamePathname(fn, SRCDIR "/store/source/storpage.cxx"))
183 return;
184 // yydebug?
185 if (loplugin::isSamePathname(fn, SRCDIR "/idlc/source/idlccompile.cxx"))
186 return;
187 if (fn.contains("/qa/"))
188 return;
189 if (fn.contains("/vcl/workben/"))
190 return;
191 // preload
192 if (loplugin::isSamePathname(fn, SRCDIR "/cppuhelper/source/servicemanager.cxx"))
193 return;
194 // doing a "free items outside lock" thing
195 if (loplugin::isSamePathname(fn, SRCDIR "/unotools/source/config/itemholder1.cxx"))
196 return;
197 if (loplugin::isSamePathname(fn, SRCDIR "/svl/source/config/itemholder2.cxx"))
198 return;
199 if (loplugin::isSamePathname(fn, SRCDIR "/svtools/source/config/itemholder2.cxx"))
200 return;
201 // doing a "keep objects alive" thing
202 if (loplugin::isSamePathname(fn, SRCDIR "/jvmfwk/source/framework.cxx"))
203 return;
204 if (loplugin::isSamePathname(fn, SRCDIR "/jvmfwk/plugins/sunmajor/pluginlib/util.cxx"))
205 return;
206 // debug code
207 if (loplugin::isSamePathname(fn, SRCDIR "/svl/source/items/style.cxx"))
208 return;
209 // ok
210 if (loplugin::isSamePathname(fn, SRCDIR "/stoc/source/inspect/introspection.cxx"))
211 return;
212 if (loplugin::isSamePathname(fn, SRCDIR "/package/source/zippackage/ZipPackage.cxx"))
213 return;
214 if (loplugin::isSamePathname(fn, SRCDIR "/hwpfilter/source/hwpreader.cxx"))
215 return;
216 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/treelist/transfer.cxx"))
217 return;
218 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/app/brand.cxx"))
219 return;
220 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/filter/igif/gifread.cxx"))
221 return;
222 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/gdi/metaact.cxx"))
223 return;
224 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/fontsubset/sft.cxx"))
225 return;
226 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/filter/ipdf/pdfdocument.cxx"))
227 return;
228 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/filter/ipdf/pdfdocument2.cxx"))
229 return;
230 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/unx/generic/app/sm.cxx"))
231 return;
232 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/filter/jpeg/JpegWriter.cxx"))
233 return;
234 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/unx/generic/dtrans/X11_selection.cxx"))
235 return;
236 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/filter/jpeg/jpegc.cxx"))
237 return;
238 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/unx/generic/window/FWS.cxx"))
239 return;
240 if (loplugin::isSamePathname(fn, SRCDIR "/toolkit/source/awt/vclxspinbutton.cxx"))
241 return;
242 if (loplugin::isSamePathname(fn, SRCDIR "/toolkit/source/controls/formattedcontrol.cxx"))
243 return;
244 if (loplugin::isSamePathname(fn, SRCDIR "/svtools/source/config/helpopt.cxx"))
245 return;
246 if (loplugin::isSamePathname(fn, SRCDIR "/svtools/source/filter/SvFilterOptionsDialog.cxx"))
247 return;
248 if (loplugin::isSamePathname(fn, SRCDIR "/svtools/source/uno/generictoolboxcontroller.cxx"))
249 return;
250 if (loplugin::isSamePathname(fn, SRCDIR "/svtools/source/java/javainteractionhandler.cxx"))
251 return;
252 if (loplugin::isSamePathname(fn, SRCDIR "/basic/source/classes/sbunoobj.cxx"))
253 return;
254 if (loplugin::isSamePathname(fn,
255 SRCDIR "/accessibility/source/standard/vclxaccessiblebox.cxx"))
256 return;
257 if (loplugin::isSamePathname(fn, SRCDIR "/cppcanvas/source/mtfrenderer/implrenderer.cxx"))
258 return;
259 if (loplugin::isSamePathname(fn, SRCDIR "/sfx2/source/doc/guisaveas.cxx"))
260 return;
261 if (loplugin::isSamePathname(fn, SRCDIR "/sfx2/source/appl/newhelp.cxx"))
262 return;
263 if (loplugin::isSamePathname(fn, SRCDIR "/sfx2/source/control/thumbnailview.cxx"))
264 return;
265 if (loplugin::isSamePathname(fn, SRCDIR "/sfx2/source/control/recentdocsview.cxx"))
266 return;
267 if (loplugin::isSamePathname(fn, SRCDIR "/sfx2/source/view/viewfrm.cxx"))
268 return;
269 if (loplugin::isSamePathname(fn, SRCDIR "/framework/source/services/desktop.cxx"))
270 return;
271 if (loplugin::isSamePathname(fn, SRCDIR
272 "/framework/source/uielement/generictoolbarcontroller.cxx"))
273 return;
274 if (loplugin::isSamePathname(fn, SRCDIR
275 "/framework/source/uielement/complextoolbarcontroller.cxx"))
276 return;
277 if (loplugin::isSamePathname(fn,
278 SRCDIR "/framework/source/interaction/quietinteraction.cxx"))
279 return;
280 if (loplugin::isSamePathname(fn, SRCDIR "/editeng/source/editeng/editdoc.cxx"))
281 return;
282 if (loplugin::isSamePathname(fn, SRCDIR "/editeng/source/editeng/impedit4.cxx"))
283 return;
284 if (loplugin::isSamePathname(fn, SRCDIR "/editeng/source/editeng/editobj.cxx"))
285 return;
286 if (loplugin::isSamePathname(fn, SRCDIR "/editeng/source/items/frmitems.cxx"))
287 return;
288 if (loplugin::isSamePathname(fn, SRCDIR "/binaryurp/source/bridge.cxx"))
289 return;
290 if (loplugin::isSamePathname(fn, SRCDIR "/svx/source/tbxctrls/fontworkgallery.cxx"))
291 return;
292 if (loplugin::isSamePathname(fn, SRCDIR "/basctl/source/basicide/moduldl2.cxx"))
293 return;
294 if (loplugin::isSamePathname(fn, SRCDIR "/canvas/source/cairo/cairo_spritecanvas.cxx"))
295 return;
296 if (loplugin::isSamePathname(fn, SRCDIR "/chart2/source/tools/DiagramHelper.cxx"))
297 return;
298 if (loplugin::isSamePathname(fn,
299 SRCDIR "/chart2/source/tools/ExplicitCategoriesProvider.cxx"))
300 return;
301 if (loplugin::isSamePathname(fn, SRCDIR "/chart2/source/tools/LegendHelper.cxx"))
302 return;
303 if (loplugin::isSamePathname(fn, SRCDIR "/chart2/source/tools/OPropertySet.cxx"))
304 return;
305 if (loplugin::isSamePathname(fn, SRCDIR "/chart2/source/tools/CommonConverters.cxx"))
306 return;
307 if (loplugin::isSamePathname(
309 SRCDIR "/chart2/source/controller/chartapiwrapper/WrappedNumberFormatProperty.cxx"))
310 return;
311 if (loplugin::isSamePathname(fn, SRCDIR "/chart2/source/tools/DataSourceHelper.cxx"))
312 return;
313 if (loplugin::isSamePathname(fn, SRCDIR "/oox/source/export/shapes.cxx"))
314 return;
315 if (loplugin::isSamePathname(fn, SRCDIR "/oox/source/export/chartexport.cxx"))
316 return;
317 if (loplugin::isSamePathname(fn,
318 SRCDIR "/filter/source/storagefilterdetect/filterdetect.cxx"))
319 return;
320 if (loplugin::isSamePathname(fn, SRCDIR "/filter/source/pdf/pdfexport.cxx"))
321 return;
322 if (loplugin::isSamePathname(fn, SRCDIR "/filter/source/svg/svgexport.cxx"))
323 return;
324 if (loplugin::isSamePathname(fn, SRCDIR "/filter/source/msfilter/svdfppt.cxx"))
325 return;
326 if (loplugin::isSamePathname(fn, SRCDIR
327 "/dbaccess/source/core/recovery/subcomponentrecovery.cxx"))
328 return;
329 if (loplugin::isSamePathname(fn, SRCDIR
330 "/dbaccess/source/core/dataaccess/documentcontainer.cxx"))
331 return;
332 if (loplugin::isSamePathname(fn, SRCDIR
333 "/dbaccess/source/core/dataaccess/databasedocument.cxx"))
334 return;
335 if (loplugin::isSamePathname(fn,
336 SRCDIR "/dbaccess/source/ui/browser/genericcontroller.cxx"))
337 return;
338 if (loplugin::isSamePathname(fn, SRCDIR "/ucb/source/core/ucbcmds.cxx"))
339 return;
340 if (loplugin::isSamePathname(fn,
341 SRCDIR "/desktop/source/deployment/manager/dp_manager.cxx"))
342 return;
343 if (loplugin::isSamePathname(fn, SRCDIR
344 "/desktop/source/deployment/registry/package/dp_package.cxx"))
345 return;
346 if (loplugin::isSamePathname(fn, SRCDIR "/desktop/source/lib/init.cxx"))
347 return;
348 if (loplugin::isSamePathname(fn, SRCDIR
349 "/extensions/source/propctrlr/formcomponenthandler.cxx"))
350 return;
351 if (loplugin::isSamePathname(fn, SRCDIR "/embeddedobj/source/general/docholder.cxx"))
352 return;
353 if (loplugin::isSamePathname(fn, SRCDIR
354 "/extensions/source/propctrlr/stringrepresentation.cxx"))
355 return;
356 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwpcontent.cxx"))
357 return;
358 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwpdivinfo.cxx"))
359 return;
360 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwpdoc.cxx"))
361 return;
362 if (loplugin::isSamePathname(fn, SRCDIR "/filter/source/pdf/impdialog.cxx"))
363 return;
364 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwplayout.cxx"))
365 return;
366 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwpoleobject.cxx"))
367 return;
368 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwprowlayout.cxx"))
369 return;
370 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwpfoundry.cxx"))
371 return;
372 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwpparastyle.cxx"))
373 return;
374 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwpnotes.cxx"))
375 return;
376 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwpfont.cxx"))
377 return;
378 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwptblcell.cxx"))
379 return;
380 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwpusrdicts.cxx"))
381 return;
382 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwpverdocument.cxx"))
383 return;
384 if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/lwptblformula.cxx"))
385 return;
386 if (loplugin::isSamePathname(fn, SRCDIR "/vbahelper/source/vbahelper/vbafontbase.cxx"))
387 return;
388 if (loplugin::isSamePathname(fn, SRCDIR "/vbahelper/source/vbahelper/vbadocumentbase.cxx"))
389 return;
390 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/ui/docshell/docsh8.cxx"))
391 return;
392 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/ui/docshell/docsh6.cxx"))
393 return;
394 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/core/data/table3.cxx"))
395 return;
396 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/ui/unoobj/cellsuno.cxx"))
397 return;
398 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/filter/excel/xelink.cxx"))
399 return;
400 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/filter/lotus/lotus.cxx"))
401 return;
402 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/ui/vba/vbaworkbooks.cxx"))
403 return;
404 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/ui/vba/vbaworksheets.cxx"))
405 return;
406 if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/ui/vba/vbarange.cxx"))
407 return;
408 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/view/drviews2.cxx"))
409 return;
410 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/filter/ppt/pptin.cxx"))
411 return;
412 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/app/sdxfer.cxx"))
413 return;
414 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/view/drviewsf.cxx"))
415 return;
416 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/filter/xml/sdxmlwrp.cxx"))
417 return;
418 if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/filter/html/pubdlg.cxx"))
419 return;
420 if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/core/txtnode/thints.cxx"))
421 return;
422 if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/core/doc/docbm.cxx"))
423 return;
424 if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/core/crsr/crsrsh.cxx"))
425 return;
426 if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/filter/xml/swxml.cxx"))
427 return;
428 if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/core/doc/docredln.cxx"))
429 return;
430 if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/filter/ww8/ww8par2.cxx"))
431 return;
432 if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/uibase/shells/drformsh.cxx"))
433 return;
434 if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/filter/ww8/ww8par6.cxx"))
435 return;
436 if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/ui/dbui/dbinsdlg.cxx"))
437 return;
438 if (loplugin::isSamePathname(fn, SRCDIR "/sdext/source/minimizer/impoptimizer.cxx"))
439 return;
440 if (loplugin::isSamePathname(fn, SRCDIR "/sdext/source/presenter/PresenterTheme.cxx"))
441 return;
442 if (loplugin::isSamePathname(fn, SRCDIR "/sdext/source/pdfimport/wrapper/wrapper.cxx"))
443 return;
444 if (loplugin::isSamePathname(fn, SRCDIR
445 "/slideshow/source/engine/animationnodes/generateevent.cxx"))
446 return;
447 if (loplugin::isSamePathname(fn, SRCDIR "/starmath/source/mathmlimport.cxx"))
448 return;
449 if (loplugin::isSamePathname(fn, SRCDIR "/starmath/source/eqnolefilehdr.cxx"))
450 return;
451 if (loplugin::isSamePathname(fn, SRCDIR "/svgio/source/svgreader/svgmarkernode.cxx"))
452 return;
453 if (loplugin::isSamePathname(fn, SRCDIR "/uui/source/iahndl-locking.cxx"))
454 return;
455 if (loplugin::isSamePathname(fn, SRCDIR
456 "/shell/source/sessioninstall/SyncDbusSessionHelper.cxx"))
457 return;
458 if (loplugin::isSamePathname(fn,
459 SRCDIR "/slideshow/source/engine/opengl/TransitionerImpl.cxx"))
460 return;
461 if (loplugin::isSamePathname(fn, SRCDIR "/forms/source/component/FormattedField.cxx"))
462 return;
463 if (loplugin::isSamePathname(fn, SRCDIR "/forms/source/component/DatabaseForm.cxx"))
464 return;
465 if (loplugin::isSamePathname(fn,
466 SRCDIR "/reportdesign/source/ui/report/ReportController.cxx"))
467 return;
468 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/test/"))
469 return;
470 if (loplugin::isSamePathname(fn, SRCDIR "/i18npool/source/localedata/LocaleNode.cxx"))
471 return;
473 // yynerrs?
474 if (loplugin::isSamePathname(fn, SRCDIR "/hwpfilter/source/grammar.cxx"))
475 return;
477 for (MyVarInfo const& v : definitionSet)
479 bool read = readFromSet.find(v) != readFromSet.end();
480 bool write = writeToSet.find(v) != writeToSet.end();
481 if (!read && write)
482 report(DiagnosticsEngine::Warning, "write-only %0", compat::getBeginLoc(v.varDecl))
483 << v.varName;
486 else
488 for (const MyVarInfo& s : readFromSet)
489 report(DiagnosticsEngine::Warning, "read %0", compat::getBeginLoc(s.varDecl))
490 << s.varName;
491 for (const MyVarInfo& s : writeToSet)
492 report(DiagnosticsEngine::Warning, "write %0", compat::getBeginLoc(s.varDecl))
493 << s.varName;
497 MyVarInfo WriteOnlyVars::niceName(const VarDecl* varDecl)
499 MyVarInfo aInfo;
501 aInfo.varDecl = varDecl->getCanonicalDecl();
502 aInfo.varName = varDecl->getNameAsString();
503 // sometimes the name (if it's an anonymous thing) contains the full path of the build folder, which we don't need
504 size_t idx = aInfo.varName.find(SRCDIR);
505 if (idx != std::string::npos)
507 aInfo.varName = aInfo.varName.replace(idx, strlen(SRCDIR), "");
509 aInfo.varType = varDecl->getType().getAsString();
511 SourceLocation expansionLoc
512 = compiler.getSourceManager().getExpansionLoc(varDecl->getLocation());
513 StringRef filename = compiler.getSourceManager().getFilename(expansionLoc);
514 aInfo.sourceLocation
515 = std::string(filename.substr(strlen(SRCDIR) + 1)) + ":"
516 + std::to_string(compiler.getSourceManager().getSpellingLineNumber(expansionLoc));
517 loplugin::normalizeDotDotInFilePath(aInfo.sourceLocation);
518 aInfo.parent = filename;
520 return aInfo;
523 static bool contains(std::string const& s, std::string const& needle)
525 return s.find(needle) != std::string::npos;
528 bool WriteOnlyVars::VisitVarDecl(const VarDecl* varDecl)
530 if (varDecl->isImplicit() || varDecl->isExternC() || isa<ParmVarDecl>(varDecl))
531 return true;
532 auto tc = loplugin::TypeCheck(varDecl->getType());
533 if (tc.Pointer() || tc.LvalueReference() || tc.Class("shared_ptr").StdNamespace()
534 || tc.Class("unique_ptr").StdNamespace())
535 return true;
536 if (tc.Typedef("BitmapScopedWriteAccess"))
537 return true;
538 std::string typeName = varDecl->getType().getAsString();
539 if (contains(typeName, "Guard") || contains(typeName, "Reader") || contains(typeName, "Stream")
540 || contains(typeName, "Parser") || contains(typeName, "Codec")
541 || contains(typeName, "Exception"))
542 return true;
543 varDecl = varDecl->getCanonicalDecl();
544 if (!varDecl->getLocation().isValid() || ignoreLocation(varDecl))
545 return true;
546 if (!compiler.getSourceManager().isInMainFile(varDecl->getLocation()))
547 return true;
548 if (compiler.getSourceManager().isMacroBodyExpansion(compat::getBeginLoc(varDecl)))
549 return true;
550 if (compiler.getSourceManager().isMacroArgExpansion(compat::getBeginLoc(varDecl)))
551 return true;
552 // ignore stuff that forms part of the stable URE interface
553 if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(varDecl->getLocation())))
554 return true;
556 definitionSet.insert(niceName(varDecl));
557 return true;
560 static char easytolower(char in)
562 if (in <= 'Z' && in >= 'A')
563 return in - ('Z' - 'z');
564 return in;
567 bool startswith(const std::string& rStr, const char* pSubStr)
569 return rStr.compare(0, strlen(pSubStr), pSubStr) == 0;
572 bool WriteOnlyVars::TraverseIfStmt(IfStmt* ifStmt)
574 VarDecl const* varDecl = nullptr;
575 Expr const* cond = ifStmt->getCond()->IgnoreParenImpCasts();
576 if (auto declRefExpr = dyn_cast<DeclRefExpr>(cond))
578 if ((varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl())))
579 insideConditionalCheckOfMemberSet.push_back(varDecl);
581 bool ret = RecursiveASTVisitor::TraverseIfStmt(ifStmt);
582 if (varDecl)
583 insideConditionalCheckOfMemberSet.pop_back();
584 return ret;
587 void WriteOnlyVars::checkIfReadFrom(const VarDecl* varDecl, const Expr* memberExpr)
589 auto parentsRange = compiler.getASTContext().getParents(*memberExpr);
590 const Stmt* child = memberExpr;
591 const Stmt* parent
592 = parentsRange.begin() == parentsRange.end() ? nullptr : parentsRange.begin()->get<Stmt>();
593 // walk up the tree until we find something interesting
594 bool bPotentiallyReadFrom = false;
595 bool bDump = false;
596 auto walkupUp = [&]() {
597 child = parent;
598 auto parentsRange = compiler.getASTContext().getParents(*parent);
599 parent = parentsRange.begin() == parentsRange.end() ? nullptr
600 : parentsRange.begin()->get<Stmt>();
604 if (!parent)
606 // check if we're inside a CXXCtorInitializer or a VarDecl
607 auto parentsRange = compiler.getASTContext().getParents(*child);
608 if (parentsRange.begin() != parentsRange.end())
610 const Decl* decl = parentsRange.begin()->get<Decl>();
611 if (decl && (isa<CXXConstructorDecl>(decl) || isa<VarDecl>(decl)))
612 bPotentiallyReadFrom = true;
614 if (!bPotentiallyReadFrom)
615 return;
616 break;
618 if (isa<CXXReinterpretCastExpr>(parent))
620 // once we see one of these, there is not much useful we can know
621 bPotentiallyReadFrom = true;
622 break;
624 else if (isa<CastExpr>(parent) || isa<MemberExpr>(parent) || isa<ParenExpr>(parent)
625 || isa<ParenListExpr>(parent) || isa<ArrayInitLoopExpr>(parent)
626 || isa<ExprWithCleanups>(parent))
628 walkupUp();
630 else if (auto unaryOperator = dyn_cast<UnaryOperator>(parent))
632 UnaryOperator::Opcode op = unaryOperator->getOpcode();
633 if (memberExpr->getType()->isArrayType() && op == UO_Deref)
635 // ignore, deref'ing an array does not count as a read
637 else if (op == UO_AddrOf || op == UO_Deref || op == UO_Plus || op == UO_Minus
638 || op == UO_Not || op == UO_LNot)
640 bPotentiallyReadFrom = true;
641 break;
643 /* The following are technically reads, but from a code-sense they're more of a write/modify, so
644 ignore them to find interesting fields that only modified, not usefully read:
645 UO_PreInc / UO_PostInc / UO_PreDec / UO_PostDec
646 But we still walk up in case the result of the expression is used in a read sense.
648 walkupUp();
650 else if (auto caseStmt = dyn_cast<CaseStmt>(parent))
652 bPotentiallyReadFrom = caseStmt->getLHS() == child || caseStmt->getRHS() == child;
653 break;
655 else if (auto ifStmt = dyn_cast<IfStmt>(parent))
657 bPotentiallyReadFrom = ifStmt->getCond() == child;
658 break;
660 else if (auto doStmt = dyn_cast<DoStmt>(parent))
662 bPotentiallyReadFrom = doStmt->getCond() == child;
663 break;
665 else if (auto arraySubscriptExpr = dyn_cast<ArraySubscriptExpr>(parent))
667 if (arraySubscriptExpr->getIdx() == child)
669 bPotentiallyReadFrom = true;
670 break;
672 walkupUp();
674 else if (auto callExpr = dyn_cast<CXXMemberCallExpr>(parent))
676 // check for calls to ReadXXX() type methods and the operator>>= methods on Any.
677 auto callee = getCallee(callExpr);
678 if (callee && *callExpr->child_begin() == child)
680 // FIXME perhaps a better solution here would be some kind of SAL_PARAM_WRITEONLY attribute
681 // which we could scatter around.
682 std::string name = callee->getNameAsString();
683 std::transform(name.begin(), name.end(), name.begin(), easytolower);
684 if (startswith(name, "read"))
685 // this is a write-only call
687 else if (startswith(name, "emplace") || name == "insert" || name == "erase"
688 || name == "remove" || name == "remove_if" || name == "sort"
689 || name == "push_back" || name == "pop_back" || name == "push_front"
690 || name == "pop_front" || name == "reserve" || name == "resize"
691 || name == "clear" || name == "fill")
692 // write-only modifications to collections
694 else if (name.find(">>=") != std::string::npos && callExpr->getArg(1) == child)
695 // this is a write-only call
697 else if (name == "dispose" || name == "disposeAndClear" || name == "swap")
698 // we're abusing the write-only analysis here to look for vars which don't have anything useful
699 // being done to them, so we're ignoring things like std::vector::clear, std::vector::swap,
700 // and VclPtr::disposeAndClear
702 else
703 bPotentiallyReadFrom = true;
705 else
706 bPotentiallyReadFrom = true;
707 break;
709 else if (auto callExpr = dyn_cast<CallExpr>(parent))
711 // check for calls to ReadXXX() type methods and the operator>>= methods on Any.
712 auto callee = getCallee(callExpr);
713 if (callee)
715 // FIXME perhaps a better solution here would be some kind of SAL_PARAM_WRITEONLY attribute
716 // which we could scatter around.
717 std::string name = callee->getNameAsString();
718 std::transform(name.begin(), name.end(), name.begin(), easytolower);
719 if (startswith(name, "read"))
720 // this is a write-only call
722 else if (name.find(">>=") != std::string::npos && callExpr->getArg(1) == child)
723 // this is a write-only call
725 else
726 bPotentiallyReadFrom = true;
728 else
729 bPotentiallyReadFrom = true;
730 break;
732 else if (auto binaryOp = dyn_cast<BinaryOperator>(parent))
734 BinaryOperator::Opcode op = binaryOp->getOpcode();
735 // If the child is on the LHS and it is an assignment op, we are obviously not reading from it
736 const bool assignmentOp = op == BO_Assign || op == BO_MulAssign || op == BO_DivAssign
737 || op == BO_RemAssign || op == BO_AddAssign
738 || op == BO_SubAssign || op == BO_ShlAssign
739 || op == BO_ShrAssign || op == BO_AndAssign
740 || op == BO_XorAssign || op == BO_OrAssign;
741 if (!(binaryOp->getLHS() == child && assignmentOp))
743 bPotentiallyReadFrom = true;
745 break;
747 else if (isa<ReturnStmt>(parent) || isa<CXXConstructExpr>(parent)
748 || isa<ConditionalOperator>(parent) || isa<SwitchStmt>(parent)
749 || isa<DeclStmt>(parent) || isa<WhileStmt>(parent) || isa<CXXNewExpr>(parent)
750 || isa<ForStmt>(parent) || isa<InitListExpr>(parent)
751 || isa<CXXDependentScopeMemberExpr>(parent) || isa<UnresolvedMemberExpr>(parent)
752 || isa<MaterializeTemporaryExpr>(parent))
754 bPotentiallyReadFrom = true;
755 break;
757 else if (isa<CXXDeleteExpr>(parent) || isa<UnaryExprOrTypeTraitExpr>(parent)
758 || isa<CXXUnresolvedConstructExpr>(parent) || isa<CompoundStmt>(parent)
759 || isa<LabelStmt>(parent) || isa<CXXForRangeStmt>(parent)
760 || isa<CXXTypeidExpr>(parent) || isa<DefaultStmt>(parent)
761 || isa<GCCAsmStmt>(parent) || isa<VAArgExpr>(parent)
762 #if CLANG_VERSION >= 80000
763 || isa<ConstantExpr>(parent)
764 #endif
765 || isa<CXXDefaultArgExpr>(parent) || isa<LambdaExpr>(parent))
767 break;
769 else
771 bPotentiallyReadFrom = true;
772 bDump = true;
773 break;
775 } while (true);
777 if (bDump)
779 report(DiagnosticsEngine::Warning, "oh dear, what can the matter be?",
780 compat::getBeginLoc(memberExpr))
781 << memberExpr->getSourceRange();
782 report(DiagnosticsEngine::Note, "parent over here", compat::getBeginLoc(parent))
783 << parent->getSourceRange();
784 parent->dump();
785 memberExpr->dump();
788 MyVarInfo varInfo = niceName(varDecl);
789 if (bPotentiallyReadFrom)
791 readFromSet.insert(varInfo);
795 void WriteOnlyVars::checkIfWrittenTo(const VarDecl* varDecl, const Expr* memberExpr)
797 // if we're inside a block that looks like
798 // if (varDecl)
799 // ....
800 // then writes to this var don't matter, because unless we find another write to this var, this var is dead
801 if (std::find(insideConditionalCheckOfMemberSet.begin(),
802 insideConditionalCheckOfMemberSet.end(), varDecl)
803 != insideConditionalCheckOfMemberSet.end())
804 return;
806 auto parentsRange = compiler.getASTContext().getParents(*memberExpr);
807 const Stmt* child = memberExpr;
808 const Stmt* parent
809 = parentsRange.begin() == parentsRange.end() ? nullptr : parentsRange.begin()->get<Stmt>();
810 // walk up the tree until we find something interesting
811 bool bPotentiallyWrittenTo = false;
812 bool bDump = false;
813 auto walkupUp = [&]() {
814 child = parent;
815 auto parentsRange = compiler.getASTContext().getParents(*parent);
816 parent = parentsRange.begin() == parentsRange.end() ? nullptr
817 : parentsRange.begin()->get<Stmt>();
821 if (!parent)
823 // check if we have an expression like
824 // int& r = var;
825 auto parentsRange = compiler.getASTContext().getParents(*child);
826 if (parentsRange.begin() != parentsRange.end())
828 auto varDecl = dyn_cast_or_null<VarDecl>(parentsRange.begin()->get<Decl>());
829 // The isImplicit() call is to avoid triggering when we see the vardecl which is part of a for-range statement,
830 // which is of type 'T&&' and also an l-value-ref ?
831 if (varDecl && !varDecl->isImplicit()
832 && loplugin::TypeCheck(varDecl->getType()).LvalueReference().NonConst())
834 bPotentiallyWrittenTo = true;
837 break;
839 if (isa<CXXReinterpretCastExpr>(parent))
841 // once we see one of these, there is not much useful we can know
842 bPotentiallyWrittenTo = true;
843 break;
845 else if (isa<CastExpr>(parent) || isa<MemberExpr>(parent) || isa<ParenExpr>(parent)
846 || isa<ParenListExpr>(parent) || isa<ArrayInitLoopExpr>(parent)
847 || isa<ExprWithCleanups>(parent))
849 walkupUp();
851 else if (auto unaryOperator = dyn_cast<UnaryOperator>(parent))
853 UnaryOperator::Opcode op = unaryOperator->getOpcode();
854 if (op == UO_AddrOf || op == UO_PostInc || op == UO_PostDec || op == UO_PreInc
855 || op == UO_PreDec)
857 bPotentiallyWrittenTo = true;
859 break;
861 else if (auto arraySubscriptExpr = dyn_cast<ArraySubscriptExpr>(parent))
863 if (arraySubscriptExpr->getIdx() == child)
864 break;
865 walkupUp();
867 else if (auto operatorCallExpr = dyn_cast<CXXOperatorCallExpr>(parent))
869 auto callee = getCallee(operatorCallExpr);
870 if (callee)
872 // if calling a non-const operator on the var
873 auto calleeMethodDecl = callee->getAsCXXMethodDecl();
874 if (calleeMethodDecl && operatorCallExpr->getArg(0) == child)
876 if (!calleeMethodDecl->isConst())
877 bPotentiallyWrittenTo
878 = checkForWriteWhenUsingCollectionType(calleeMethodDecl);
880 else if (IsPassedByNonConst(varDecl, child, operatorCallExpr, *callee))
882 bPotentiallyWrittenTo = true;
885 else
886 bPotentiallyWrittenTo = true; // conservative, could improve
887 break;
889 else if (auto cxxMemberCallExpr = dyn_cast<CXXMemberCallExpr>(parent))
891 const CXXMethodDecl* calleeMethodDecl = cxxMemberCallExpr->getMethodDecl();
892 if (calleeMethodDecl)
894 // if calling a non-const method on the var
895 const Expr* tmp = dyn_cast<Expr>(child);
896 if (tmp->isBoundMemberFunction(compiler.getASTContext()))
898 tmp = dyn_cast<MemberExpr>(tmp)->getBase();
900 if (cxxMemberCallExpr->getImplicitObjectArgument() == tmp)
902 if (!calleeMethodDecl->isConst())
903 bPotentiallyWrittenTo
904 = checkForWriteWhenUsingCollectionType(calleeMethodDecl);
905 break;
907 else if (IsPassedByNonConst(varDecl, child, cxxMemberCallExpr,
908 CalleeWrapper(calleeMethodDecl)))
909 bPotentiallyWrittenTo = true;
911 else
912 bPotentiallyWrittenTo = true; // can happen in templates
913 break;
915 else if (auto cxxConstructExpr = dyn_cast<CXXConstructExpr>(parent))
917 if (IsPassedByNonConst(varDecl, child, cxxConstructExpr,
918 CalleeWrapper(cxxConstructExpr)))
919 bPotentiallyWrittenTo = true;
920 break;
922 else if (auto callExpr = dyn_cast<CallExpr>(parent))
924 auto callee = getCallee(callExpr);
925 if (callee)
927 if (IsPassedByNonConst(varDecl, child, callExpr, *callee))
928 bPotentiallyWrittenTo = true;
930 else
931 bPotentiallyWrittenTo = true; // conservative, could improve
932 break;
934 else if (auto binaryOp = dyn_cast<BinaryOperator>(parent))
936 BinaryOperator::Opcode op = binaryOp->getOpcode();
937 const bool assignmentOp = op == BO_Assign || op == BO_MulAssign || op == BO_DivAssign
938 || op == BO_RemAssign || op == BO_AddAssign
939 || op == BO_SubAssign || op == BO_ShlAssign
940 || op == BO_ShrAssign || op == BO_AndAssign
941 || op == BO_XorAssign || op == BO_OrAssign;
942 if (assignmentOp)
944 if (binaryOp->getLHS() == child)
945 bPotentiallyWrittenTo = true;
946 else if (loplugin::TypeCheck(binaryOp->getLHS()->getType())
947 .LvalueReference()
948 .NonConst())
949 // if the LHS is a non-const reference, we could write to the var later on
950 bPotentiallyWrittenTo = true;
952 break;
954 else if (isa<ReturnStmt>(parent))
956 if (insideFunctionDecl)
958 auto tc = loplugin::TypeCheck(insideFunctionDecl->getReturnType());
959 if (tc.LvalueReference().NonConst())
960 bPotentiallyWrittenTo = true;
962 break;
964 else if (isa<ConditionalOperator>(parent) || isa<SwitchStmt>(parent)
965 || isa<DeclStmt>(parent) || isa<WhileStmt>(parent) || isa<CXXNewExpr>(parent)
966 || isa<ForStmt>(parent) || isa<InitListExpr>(parent)
967 || isa<CXXDependentScopeMemberExpr>(parent) || isa<UnresolvedMemberExpr>(parent)
968 || isa<MaterializeTemporaryExpr>(parent) || isa<IfStmt>(parent)
969 || isa<DoStmt>(parent) || isa<CXXDeleteExpr>(parent)
970 || isa<UnaryExprOrTypeTraitExpr>(parent) || isa<CXXUnresolvedConstructExpr>(parent)
971 || isa<CompoundStmt>(parent) || isa<LabelStmt>(parent)
972 || isa<CXXForRangeStmt>(parent) || isa<CXXTypeidExpr>(parent)
973 || isa<DefaultStmt>(parent)
974 #if CLANG_VERSION >= 80000
975 || isa<ConstantExpr>(parent)
976 #endif
977 || isa<GCCAsmStmt>(parent) || isa<VAArgExpr>(parent)
978 || isa<CXXDefaultArgExpr>(parent) || isa<LambdaExpr>(parent))
980 break;
982 else
984 bPotentiallyWrittenTo = true;
985 bDump = true;
986 break;
988 } while (true);
990 if (bDump)
992 report(DiagnosticsEngine::Warning, "oh dear2, what can the matter be? writtenTo=%0",
993 compat::getBeginLoc(memberExpr))
994 << bPotentiallyWrittenTo << memberExpr->getSourceRange();
995 if (parent)
997 report(DiagnosticsEngine::Note, "parent over here", compat::getBeginLoc(parent))
998 << parent->getSourceRange();
999 parent->dump();
1001 memberExpr->dump();
1002 varDecl->getType()->dump();
1005 MyVarInfo varInfo = niceName(varDecl);
1006 if (bPotentiallyWrittenTo)
1008 writeToSet.insert(varInfo);
1012 // return true if this not a collection type, or if it is a collection type, and we might be writing to it
1013 bool WriteOnlyVars::checkForWriteWhenUsingCollectionType(const CXXMethodDecl* calleeMethodDecl)
1015 auto const tc = loplugin::TypeCheck(calleeMethodDecl->getParent());
1016 bool listLike = false, setLike = false, mapLike = false, cssSequence = false;
1017 if (tc.Class("deque").StdNamespace() || tc.Class("list").StdNamespace()
1018 || tc.Class("queue").StdNamespace() || tc.Class("vector").StdNamespace())
1020 listLike = true;
1022 else if (tc.Class("set").StdNamespace() || tc.Class("unordered_set").StdNamespace())
1024 setLike = true;
1026 else if (tc.Class("map").StdNamespace() || tc.Class("unordered_map").StdNamespace())
1028 mapLike = true;
1030 else if (tc.Class("Sequence")
1031 .Namespace("uno")
1032 .Namespace("star")
1033 .Namespace("sun")
1034 .Namespace("com")
1035 .GlobalNamespace())
1037 cssSequence = true;
1039 else
1040 return true;
1042 if (calleeMethodDecl->isOverloadedOperator())
1044 auto oo = calleeMethodDecl->getOverloadedOperator();
1045 if (oo == OO_Equal)
1046 return true;
1047 // This is operator[]. We only care about things that add elements to the collection.
1048 // if nothing modifies the size of the collection, then nothing useful
1049 // is stored in it.
1050 if (listLike)
1051 return false;
1052 return true;
1055 auto name = calleeMethodDecl->getName();
1056 if (listLike || setLike || mapLike)
1058 if (name == "reserve" || name == "shrink_to_fit" || name == "clear" || name == "erase"
1059 || name == "pop_back" || name == "pop_front" || name == "front" || name == "back"
1060 || name == "data" || name == "remove" || name == "remove_if" || name == "unique"
1061 || name == "sort" || name == "begin" || name == "end" || name == "rbegin"
1062 || name == "rend" || name == "at" || name == "find" || name == "equal_range"
1063 || name == "lower_bound" || name == "upper_bound")
1064 return false;
1066 if (cssSequence)
1068 if (name == "getArray" || name == "begin" || name == "end")
1069 return false;
1072 return true;
1075 bool WriteOnlyVars::IsPassedByNonConst(const VarDecl* varDecl, const Stmt* child,
1076 CallerWrapper callExpr, CalleeWrapper calleeFunctionDecl)
1078 unsigned len = std::min(callExpr.getNumArgs(), calleeFunctionDecl.getNumParams());
1079 // if it's an array, passing it by value to a method typically means the
1080 // callee takes a pointer and can modify the array
1081 if (varDecl->getType()->isConstantArrayType())
1083 for (unsigned i = 0; i < len; ++i)
1084 if (callExpr.getArg(i) == child)
1085 if (loplugin::TypeCheck(calleeFunctionDecl.getParamType(i)).Pointer().NonConst())
1086 return true;
1088 else
1090 for (unsigned i = 0; i < len; ++i)
1091 if (callExpr.getArg(i) == child)
1092 if (loplugin::TypeCheck(calleeFunctionDecl.getParamType(i))
1093 .LvalueReference()
1094 .NonConst())
1095 return true;
1097 return false;
1100 bool WriteOnlyVars::VisitDeclRefExpr(const DeclRefExpr* declRefExpr)
1102 const Decl* decl = declRefExpr->getDecl();
1103 const VarDecl* varDecl = dyn_cast<VarDecl>(decl);
1104 if (!varDecl)
1105 return true;
1106 if (varDecl->isImplicit() || isa<ParmVarDecl>(varDecl))
1107 return true;
1108 varDecl = varDecl->getCanonicalDecl();
1109 if (ignoreLocation(varDecl))
1110 return true;
1111 // ignore stuff that forms part of the stable URE interface
1112 if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(varDecl->getLocation())))
1113 return true;
1115 checkIfReadFrom(varDecl, declRefExpr);
1117 checkIfWrittenTo(varDecl, declRefExpr);
1119 return true;
1122 llvm::Optional<CalleeWrapper> WriteOnlyVars::getCallee(CallExpr const* callExpr)
1124 FunctionDecl const* functionDecl = callExpr->getDirectCallee();
1125 if (functionDecl)
1126 return CalleeWrapper(functionDecl);
1128 // Extract the functionprototype from a type
1129 clang::Type const* calleeType = callExpr->getCallee()->getType().getTypePtr();
1130 if (auto pointerType = calleeType->getUnqualifiedDesugaredType()->getAs<clang::PointerType>())
1132 if (auto prototype = pointerType->getPointeeType()
1133 ->getUnqualifiedDesugaredType()
1134 ->getAs<FunctionProtoType>())
1136 return CalleeWrapper(prototype);
1140 return llvm::Optional<CalleeWrapper>();
1143 loplugin::Plugin::Registration<WriteOnlyVars> X("writeonlyvars", false);
1146 #endif
1148 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */