1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #if !defined _WIN32 //TODO, #include <sys/file.h>
16 #include <unordered_set>
26 Finds variables that are effectively write-only.
28 Largely the same as the unusedfields.cxx loplugin.
35 const VarDecl
* varDecl
;
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
;
52 * Wrap the different kinds of callable and callee objects in the clang AST so I can define methods that handle everything.
56 const CallExpr
* m_callExpr
;
57 const CXXConstructExpr
* m_cxxConstructExpr
;
60 CallerWrapper(const CallExpr
* callExpr
)
61 : m_callExpr(callExpr
)
62 , m_cxxConstructExpr(nullptr)
65 CallerWrapper(const CXXConstructExpr
* cxxConstructExpr
)
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
);
81 const FunctionDecl
* m_calleeFunctionDecl
= nullptr;
82 const CXXConstructorDecl
* m_cxxConstructorDecl
= nullptr;
83 const FunctionProtoType
* m_functionPrototype
= nullptr;
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
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();
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();
128 CXXMethodDecl
const* getAsCXXMethodDecl() const
130 if (m_calleeFunctionDecl
)
131 return dyn_cast
<CXXMethodDecl
>(m_calleeFunctionDecl
);
136 class WriteOnlyVars
: public loplugin::FilteringPlugin
<WriteOnlyVars
>
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
*);
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"))
178 // playing paging-in games with volatile
179 if (loplugin::isSamePathname(fn
, SRCDIR
"/desktop/unx/source/file_image_unx.c"))
182 if (loplugin::isSamePathname(fn
, SRCDIR
"/store/source/storpage.cxx"))
185 if (loplugin::isSamePathname(fn
, SRCDIR
"/idlc/source/idlccompile.cxx"))
187 if (fn
.contains("/qa/"))
189 if (fn
.contains("/vcl/workben/"))
192 if (loplugin::isSamePathname(fn
, SRCDIR
"/cppuhelper/source/servicemanager.cxx"))
194 // doing a "free items outside lock" thing
195 if (loplugin::isSamePathname(fn
, SRCDIR
"/unotools/source/config/itemholder1.cxx"))
197 if (loplugin::isSamePathname(fn
, SRCDIR
"/svl/source/config/itemholder2.cxx"))
199 if (loplugin::isSamePathname(fn
, SRCDIR
"/svtools/source/config/itemholder2.cxx"))
201 // doing a "keep objects alive" thing
202 if (loplugin::isSamePathname(fn
, SRCDIR
"/jvmfwk/source/framework.cxx"))
204 if (loplugin::isSamePathname(fn
, SRCDIR
"/jvmfwk/plugins/sunmajor/pluginlib/util.cxx"))
207 if (loplugin::isSamePathname(fn
, SRCDIR
"/svl/source/items/style.cxx"))
210 if (loplugin::isSamePathname(fn
, SRCDIR
"/stoc/source/inspect/introspection.cxx"))
212 if (loplugin::isSamePathname(fn
, SRCDIR
"/package/source/zippackage/ZipPackage.cxx"))
214 if (loplugin::isSamePathname(fn
, SRCDIR
"/hwpfilter/source/hwpreader.cxx"))
216 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/treelist/transfer.cxx"))
218 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/app/brand.cxx"))
220 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/filter/igif/gifread.cxx"))
222 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/gdi/metaact.cxx"))
224 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/fontsubset/sft.cxx"))
226 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/filter/ipdf/pdfdocument.cxx"))
228 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/filter/ipdf/pdfdocument2.cxx"))
230 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/unx/generic/app/sm.cxx"))
232 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/filter/jpeg/JpegWriter.cxx"))
234 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/unx/generic/dtrans/X11_selection.cxx"))
236 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/filter/jpeg/jpegc.cxx"))
238 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/unx/generic/window/FWS.cxx"))
240 if (loplugin::isSamePathname(fn
, SRCDIR
"/toolkit/source/awt/vclxspinbutton.cxx"))
242 if (loplugin::isSamePathname(fn
, SRCDIR
"/toolkit/source/controls/formattedcontrol.cxx"))
244 if (loplugin::isSamePathname(fn
, SRCDIR
"/svtools/source/config/helpopt.cxx"))
246 if (loplugin::isSamePathname(fn
, SRCDIR
"/svtools/source/filter/SvFilterOptionsDialog.cxx"))
248 if (loplugin::isSamePathname(fn
, SRCDIR
"/svtools/source/uno/generictoolboxcontroller.cxx"))
250 if (loplugin::isSamePathname(fn
, SRCDIR
"/svtools/source/java/javainteractionhandler.cxx"))
252 if (loplugin::isSamePathname(fn
, SRCDIR
"/basic/source/classes/sbunoobj.cxx"))
254 if (loplugin::isSamePathname(fn
,
255 SRCDIR
"/accessibility/source/standard/vclxaccessiblebox.cxx"))
257 if (loplugin::isSamePathname(fn
, SRCDIR
"/cppcanvas/source/mtfrenderer/implrenderer.cxx"))
259 if (loplugin::isSamePathname(fn
, SRCDIR
"/sfx2/source/doc/guisaveas.cxx"))
261 if (loplugin::isSamePathname(fn
, SRCDIR
"/sfx2/source/appl/newhelp.cxx"))
263 if (loplugin::isSamePathname(fn
, SRCDIR
"/sfx2/source/control/thumbnailview.cxx"))
265 if (loplugin::isSamePathname(fn
, SRCDIR
"/sfx2/source/control/recentdocsview.cxx"))
267 if (loplugin::isSamePathname(fn
, SRCDIR
"/sfx2/source/view/viewfrm.cxx"))
269 if (loplugin::isSamePathname(fn
, SRCDIR
"/framework/source/services/desktop.cxx"))
271 if (loplugin::isSamePathname(fn
, SRCDIR
272 "/framework/source/uielement/generictoolbarcontroller.cxx"))
274 if (loplugin::isSamePathname(fn
, SRCDIR
275 "/framework/source/uielement/complextoolbarcontroller.cxx"))
277 if (loplugin::isSamePathname(fn
,
278 SRCDIR
"/framework/source/interaction/quietinteraction.cxx"))
280 if (loplugin::isSamePathname(fn
, SRCDIR
"/editeng/source/editeng/editdoc.cxx"))
282 if (loplugin::isSamePathname(fn
, SRCDIR
"/editeng/source/editeng/impedit4.cxx"))
284 if (loplugin::isSamePathname(fn
, SRCDIR
"/editeng/source/editeng/editobj.cxx"))
286 if (loplugin::isSamePathname(fn
, SRCDIR
"/editeng/source/items/frmitems.cxx"))
288 if (loplugin::isSamePathname(fn
, SRCDIR
"/binaryurp/source/bridge.cxx"))
290 if (loplugin::isSamePathname(fn
, SRCDIR
"/svx/source/tbxctrls/fontworkgallery.cxx"))
292 if (loplugin::isSamePathname(fn
, SRCDIR
"/basctl/source/basicide/moduldl2.cxx"))
294 if (loplugin::isSamePathname(fn
, SRCDIR
"/canvas/source/cairo/cairo_spritecanvas.cxx"))
296 if (loplugin::isSamePathname(fn
, SRCDIR
"/chart2/source/tools/DiagramHelper.cxx"))
298 if (loplugin::isSamePathname(fn
,
299 SRCDIR
"/chart2/source/tools/ExplicitCategoriesProvider.cxx"))
301 if (loplugin::isSamePathname(fn
, SRCDIR
"/chart2/source/tools/LegendHelper.cxx"))
303 if (loplugin::isSamePathname(fn
, SRCDIR
"/chart2/source/tools/OPropertySet.cxx"))
305 if (loplugin::isSamePathname(fn
, SRCDIR
"/chart2/source/tools/CommonConverters.cxx"))
307 if (loplugin::isSamePathname(
309 SRCDIR
"/chart2/source/controller/chartapiwrapper/WrappedNumberFormatProperty.cxx"))
311 if (loplugin::isSamePathname(fn
, SRCDIR
"/chart2/source/tools/DataSourceHelper.cxx"))
313 if (loplugin::isSamePathname(fn
, SRCDIR
"/oox/source/export/shapes.cxx"))
315 if (loplugin::isSamePathname(fn
, SRCDIR
"/oox/source/export/chartexport.cxx"))
317 if (loplugin::isSamePathname(fn
,
318 SRCDIR
"/filter/source/storagefilterdetect/filterdetect.cxx"))
320 if (loplugin::isSamePathname(fn
, SRCDIR
"/filter/source/pdf/pdfexport.cxx"))
322 if (loplugin::isSamePathname(fn
, SRCDIR
"/filter/source/svg/svgexport.cxx"))
324 if (loplugin::isSamePathname(fn
, SRCDIR
"/filter/source/msfilter/svdfppt.cxx"))
326 if (loplugin::isSamePathname(fn
, SRCDIR
327 "/dbaccess/source/core/recovery/subcomponentrecovery.cxx"))
329 if (loplugin::isSamePathname(fn
, SRCDIR
330 "/dbaccess/source/core/dataaccess/documentcontainer.cxx"))
332 if (loplugin::isSamePathname(fn
, SRCDIR
333 "/dbaccess/source/core/dataaccess/databasedocument.cxx"))
335 if (loplugin::isSamePathname(fn
,
336 SRCDIR
"/dbaccess/source/ui/browser/genericcontroller.cxx"))
338 if (loplugin::isSamePathname(fn
, SRCDIR
"/ucb/source/core/ucbcmds.cxx"))
340 if (loplugin::isSamePathname(fn
,
341 SRCDIR
"/desktop/source/deployment/manager/dp_manager.cxx"))
343 if (loplugin::isSamePathname(fn
, SRCDIR
344 "/desktop/source/deployment/registry/package/dp_package.cxx"))
346 if (loplugin::isSamePathname(fn
, SRCDIR
"/desktop/source/lib/init.cxx"))
348 if (loplugin::isSamePathname(fn
, SRCDIR
349 "/extensions/source/propctrlr/formcomponenthandler.cxx"))
351 if (loplugin::isSamePathname(fn
, SRCDIR
"/embeddedobj/source/general/docholder.cxx"))
353 if (loplugin::isSamePathname(fn
, SRCDIR
354 "/extensions/source/propctrlr/stringrepresentation.cxx"))
356 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwpcontent.cxx"))
358 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwpdivinfo.cxx"))
360 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwpdoc.cxx"))
362 if (loplugin::isSamePathname(fn
, SRCDIR
"/filter/source/pdf/impdialog.cxx"))
364 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwplayout.cxx"))
366 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwpoleobject.cxx"))
368 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwprowlayout.cxx"))
370 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwpfoundry.cxx"))
372 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwpparastyle.cxx"))
374 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwpnotes.cxx"))
376 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwpfont.cxx"))
378 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwptblcell.cxx"))
380 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwpusrdicts.cxx"))
382 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwpverdocument.cxx"))
384 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwptblformula.cxx"))
386 if (loplugin::isSamePathname(fn
, SRCDIR
"/vbahelper/source/vbahelper/vbafontbase.cxx"))
388 if (loplugin::isSamePathname(fn
, SRCDIR
"/vbahelper/source/vbahelper/vbadocumentbase.cxx"))
390 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/ui/docshell/docsh8.cxx"))
392 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/ui/docshell/docsh6.cxx"))
394 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/core/data/table3.cxx"))
396 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/ui/unoobj/cellsuno.cxx"))
398 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/filter/excel/xelink.cxx"))
400 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/filter/lotus/lotus.cxx"))
402 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/ui/vba/vbaworkbooks.cxx"))
404 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/ui/vba/vbaworksheets.cxx"))
406 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/ui/vba/vbarange.cxx"))
408 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/ui/view/drviews2.cxx"))
410 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/filter/ppt/pptin.cxx"))
412 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/ui/app/sdxfer.cxx"))
414 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/ui/view/drviewsf.cxx"))
416 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/filter/xml/sdxmlwrp.cxx"))
418 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/filter/html/pubdlg.cxx"))
420 if (loplugin::isSamePathname(fn
, SRCDIR
"/sw/source/core/txtnode/thints.cxx"))
422 if (loplugin::isSamePathname(fn
, SRCDIR
"/sw/source/core/doc/docbm.cxx"))
424 if (loplugin::isSamePathname(fn
, SRCDIR
"/sw/source/core/crsr/crsrsh.cxx"))
426 if (loplugin::isSamePathname(fn
, SRCDIR
"/sw/source/filter/xml/swxml.cxx"))
428 if (loplugin::isSamePathname(fn
, SRCDIR
"/sw/source/core/doc/docredln.cxx"))
430 if (loplugin::isSamePathname(fn
, SRCDIR
"/sw/source/filter/ww8/ww8par2.cxx"))
432 if (loplugin::isSamePathname(fn
, SRCDIR
"/sw/source/uibase/shells/drformsh.cxx"))
434 if (loplugin::isSamePathname(fn
, SRCDIR
"/sw/source/filter/ww8/ww8par6.cxx"))
436 if (loplugin::isSamePathname(fn
, SRCDIR
"/sw/source/ui/dbui/dbinsdlg.cxx"))
438 if (loplugin::isSamePathname(fn
, SRCDIR
"/sdext/source/minimizer/impoptimizer.cxx"))
440 if (loplugin::isSamePathname(fn
, SRCDIR
"/sdext/source/presenter/PresenterTheme.cxx"))
442 if (loplugin::isSamePathname(fn
, SRCDIR
"/sdext/source/pdfimport/wrapper/wrapper.cxx"))
444 if (loplugin::isSamePathname(fn
, SRCDIR
445 "/slideshow/source/engine/animationnodes/generateevent.cxx"))
447 if (loplugin::isSamePathname(fn
, SRCDIR
"/starmath/source/mathmlimport.cxx"))
449 if (loplugin::isSamePathname(fn
, SRCDIR
"/starmath/source/eqnolefilehdr.cxx"))
451 if (loplugin::isSamePathname(fn
, SRCDIR
"/svgio/source/svgreader/svgmarkernode.cxx"))
453 if (loplugin::isSamePathname(fn
, SRCDIR
"/uui/source/iahndl-locking.cxx"))
455 if (loplugin::isSamePathname(fn
, SRCDIR
456 "/shell/source/sessioninstall/SyncDbusSessionHelper.cxx"))
458 if (loplugin::isSamePathname(fn
,
459 SRCDIR
"/slideshow/source/engine/opengl/TransitionerImpl.cxx"))
461 if (loplugin::isSamePathname(fn
, SRCDIR
"/forms/source/component/FormattedField.cxx"))
463 if (loplugin::isSamePathname(fn
, SRCDIR
"/forms/source/component/DatabaseForm.cxx"))
465 if (loplugin::isSamePathname(fn
,
466 SRCDIR
"/reportdesign/source/ui/report/ReportController.cxx"))
468 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/test/"))
470 if (loplugin::isSamePathname(fn
, SRCDIR
"/i18npool/source/localedata/LocaleNode.cxx"))
474 if (loplugin::isSamePathname(fn
, SRCDIR
"/hwpfilter/source/grammar.cxx"))
477 for (MyVarInfo
const& v
: definitionSet
)
479 bool read
= readFromSet
.find(v
) != readFromSet
.end();
480 bool write
= writeToSet
.find(v
) != writeToSet
.end();
482 report(DiagnosticsEngine::Warning
, "write-only %0", compat::getBeginLoc(v
.varDecl
))
488 for (const MyVarInfo
& s
: readFromSet
)
489 report(DiagnosticsEngine::Warning
, "read %0", compat::getBeginLoc(s
.varDecl
))
491 for (const MyVarInfo
& s
: writeToSet
)
492 report(DiagnosticsEngine::Warning
, "write %0", compat::getBeginLoc(s
.varDecl
))
497 MyVarInfo
WriteOnlyVars::niceName(const VarDecl
* varDecl
)
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
);
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
;
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
))
532 auto tc
= loplugin::TypeCheck(varDecl
->getType());
533 if (tc
.Pointer() || tc
.LvalueReference() || tc
.Class("shared_ptr").StdNamespace()
534 || tc
.Class("unique_ptr").StdNamespace())
536 if (tc
.Typedef("BitmapScopedWriteAccess"))
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"))
543 varDecl
= varDecl
->getCanonicalDecl();
544 if (!varDecl
->getLocation().isValid() || ignoreLocation(varDecl
))
546 if (!compiler
.getSourceManager().isInMainFile(varDecl
->getLocation()))
548 if (compiler
.getSourceManager().isMacroBodyExpansion(compat::getBeginLoc(varDecl
)))
550 if (compiler
.getSourceManager().isMacroArgExpansion(compat::getBeginLoc(varDecl
)))
552 // ignore stuff that forms part of the stable URE interface
553 if (isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(varDecl
->getLocation())))
556 definitionSet
.insert(niceName(varDecl
));
560 static char easytolower(char in
)
562 if (in
<= 'Z' && in
>= 'A')
563 return in
- ('Z' - 'z');
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
);
583 insideConditionalCheckOfMemberSet
.pop_back();
587 void WriteOnlyVars::checkIfReadFrom(const VarDecl
* varDecl
, const Expr
* memberExpr
)
589 auto parentsRange
= compiler
.getASTContext().getParents(*memberExpr
);
590 const Stmt
* child
= memberExpr
;
592 = parentsRange
.begin() == parentsRange
.end() ? nullptr : parentsRange
.begin()->get
<Stmt
>();
593 // walk up the tree until we find something interesting
594 bool bPotentiallyReadFrom
= false;
596 auto walkupUp
= [&]() {
598 auto parentsRange
= compiler
.getASTContext().getParents(*parent
);
599 parent
= parentsRange
.begin() == parentsRange
.end() ? nullptr
600 : parentsRange
.begin()->get
<Stmt
>();
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
)
618 if (isa
<CXXReinterpretCastExpr
>(parent
))
620 // once we see one of these, there is not much useful we can know
621 bPotentiallyReadFrom
= true;
624 else if (isa
<CastExpr
>(parent
) || isa
<MemberExpr
>(parent
) || isa
<ParenExpr
>(parent
)
625 || isa
<ParenListExpr
>(parent
) || isa
<ArrayInitLoopExpr
>(parent
)
626 || isa
<ExprWithCleanups
>(parent
))
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;
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.
650 else if (auto caseStmt
= dyn_cast
<CaseStmt
>(parent
))
652 bPotentiallyReadFrom
= caseStmt
->getLHS() == child
|| caseStmt
->getRHS() == child
;
655 else if (auto ifStmt
= dyn_cast
<IfStmt
>(parent
))
657 bPotentiallyReadFrom
= ifStmt
->getCond() == child
;
660 else if (auto doStmt
= dyn_cast
<DoStmt
>(parent
))
662 bPotentiallyReadFrom
= doStmt
->getCond() == child
;
665 else if (auto arraySubscriptExpr
= dyn_cast
<ArraySubscriptExpr
>(parent
))
667 if (arraySubscriptExpr
->getIdx() == child
)
669 bPotentiallyReadFrom
= true;
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
703 bPotentiallyReadFrom
= true;
706 bPotentiallyReadFrom
= true;
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
);
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
726 bPotentiallyReadFrom
= true;
729 bPotentiallyReadFrom
= true;
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;
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;
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
)
765 || isa
<CXXDefaultArgExpr
>(parent
) || isa
<LambdaExpr
>(parent
))
771 bPotentiallyReadFrom
= true;
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();
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
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())
806 auto parentsRange
= compiler
.getASTContext().getParents(*memberExpr
);
807 const Stmt
* child
= memberExpr
;
809 = parentsRange
.begin() == parentsRange
.end() ? nullptr : parentsRange
.begin()->get
<Stmt
>();
810 // walk up the tree until we find something interesting
811 bool bPotentiallyWrittenTo
= false;
813 auto walkupUp
= [&]() {
815 auto parentsRange
= compiler
.getASTContext().getParents(*parent
);
816 parent
= parentsRange
.begin() == parentsRange
.end() ? nullptr
817 : parentsRange
.begin()->get
<Stmt
>();
823 // check if we have an expression like
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;
839 if (isa
<CXXReinterpretCastExpr
>(parent
))
841 // once we see one of these, there is not much useful we can know
842 bPotentiallyWrittenTo
= true;
845 else if (isa
<CastExpr
>(parent
) || isa
<MemberExpr
>(parent
) || isa
<ParenExpr
>(parent
)
846 || isa
<ParenListExpr
>(parent
) || isa
<ArrayInitLoopExpr
>(parent
)
847 || isa
<ExprWithCleanups
>(parent
))
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
857 bPotentiallyWrittenTo
= true;
861 else if (auto arraySubscriptExpr
= dyn_cast
<ArraySubscriptExpr
>(parent
))
863 if (arraySubscriptExpr
->getIdx() == child
)
867 else if (auto operatorCallExpr
= dyn_cast
<CXXOperatorCallExpr
>(parent
))
869 auto callee
= getCallee(operatorCallExpr
);
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;
886 bPotentiallyWrittenTo
= true; // conservative, could improve
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
);
907 else if (IsPassedByNonConst(varDecl
, child
, cxxMemberCallExpr
,
908 CalleeWrapper(calleeMethodDecl
)))
909 bPotentiallyWrittenTo
= true;
912 bPotentiallyWrittenTo
= true; // can happen in templates
915 else if (auto cxxConstructExpr
= dyn_cast
<CXXConstructExpr
>(parent
))
917 if (IsPassedByNonConst(varDecl
, child
, cxxConstructExpr
,
918 CalleeWrapper(cxxConstructExpr
)))
919 bPotentiallyWrittenTo
= true;
922 else if (auto callExpr
= dyn_cast
<CallExpr
>(parent
))
924 auto callee
= getCallee(callExpr
);
927 if (IsPassedByNonConst(varDecl
, child
, callExpr
, *callee
))
928 bPotentiallyWrittenTo
= true;
931 bPotentiallyWrittenTo
= true; // conservative, could improve
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
;
944 if (binaryOp
->getLHS() == child
)
945 bPotentiallyWrittenTo
= true;
946 else if (loplugin::TypeCheck(binaryOp
->getLHS()->getType())
949 // if the LHS is a non-const reference, we could write to the var later on
950 bPotentiallyWrittenTo
= true;
954 else if (isa
<ReturnStmt
>(parent
))
956 if (insideFunctionDecl
)
958 auto tc
= loplugin::TypeCheck(insideFunctionDecl
->getReturnType());
959 if (tc
.LvalueReference().NonConst())
960 bPotentiallyWrittenTo
= true;
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
)
977 || isa
<GCCAsmStmt
>(parent
) || isa
<VAArgExpr
>(parent
)
978 || isa
<CXXDefaultArgExpr
>(parent
) || isa
<LambdaExpr
>(parent
))
984 bPotentiallyWrittenTo
= true;
992 report(DiagnosticsEngine::Warning
, "oh dear2, what can the matter be? writtenTo=%0",
993 compat::getBeginLoc(memberExpr
))
994 << bPotentiallyWrittenTo
<< memberExpr
->getSourceRange();
997 report(DiagnosticsEngine::Note
, "parent over here", compat::getBeginLoc(parent
))
998 << parent
->getSourceRange();
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())
1022 else if (tc
.Class("set").StdNamespace() || tc
.Class("unordered_set").StdNamespace())
1026 else if (tc
.Class("map").StdNamespace() || tc
.Class("unordered_map").StdNamespace())
1030 else if (tc
.Class("Sequence")
1042 if (calleeMethodDecl
->isOverloadedOperator())
1044 auto oo
= calleeMethodDecl
->getOverloadedOperator();
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
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")
1068 if (name
== "getArray" || name
== "begin" || name
== "end")
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())
1090 for (unsigned i
= 0; i
< len
; ++i
)
1091 if (callExpr
.getArg(i
) == child
)
1092 if (loplugin::TypeCheck(calleeFunctionDecl
.getParamType(i
))
1100 bool WriteOnlyVars::VisitDeclRefExpr(const DeclRefExpr
* declRefExpr
)
1102 const Decl
* decl
= declRefExpr
->getDecl();
1103 const VarDecl
* varDecl
= dyn_cast
<VarDecl
>(decl
);
1106 if (varDecl
->isImplicit() || isa
<ParmVarDecl
>(varDecl
))
1108 varDecl
= varDecl
->getCanonicalDecl();
1109 if (ignoreLocation(varDecl
))
1111 // ignore stuff that forms part of the stable URE interface
1112 if (isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(varDecl
->getLocation())))
1115 checkIfReadFrom(varDecl
, declRefExpr
);
1117 checkIfWrittenTo(varDecl
, declRefExpr
);
1122 llvm::Optional
<CalleeWrapper
> WriteOnlyVars::getCallee(CallExpr
const* callExpr
)
1124 FunctionDecl
const* functionDecl
= callExpr
->getDirectCallee();
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);
1148 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */