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>
22 #include "config_clang.h"
28 #if CLANG_VERSION >= 110000
29 #include "clang/AST/ParentMapContext.h"
33 Finds variables that are effectively write-only.
35 Largely the same as the unusedfields.cxx loplugin.
42 const VarDecl
* varDecl
;
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
;
59 * Wrap the different kinds of callable and callee objects in the clang AST so I can define methods that handle everything.
63 const CallExpr
* m_callExpr
;
64 const CXXConstructExpr
* m_cxxConstructExpr
;
67 CallerWrapper(const CallExpr
* callExpr
)
68 : m_callExpr(callExpr
)
69 , m_cxxConstructExpr(nullptr)
72 CallerWrapper(const CXXConstructExpr
* cxxConstructExpr
)
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
);
88 const FunctionDecl
* m_calleeFunctionDecl
= nullptr;
89 const CXXConstructorDecl
* m_cxxConstructorDecl
= nullptr;
90 const FunctionProtoType
* m_functionPrototype
= nullptr;
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
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();
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();
135 CXXMethodDecl
const* getAsCXXMethodDecl() const
137 if (m_calleeFunctionDecl
)
138 return dyn_cast
<CXXMethodDecl
>(m_calleeFunctionDecl
);
143 class WriteOnlyVars
: public loplugin::FilteringPlugin
<WriteOnlyVars
>
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
*);
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"))
185 // playing paging-in games with volatile
186 if (loplugin::isSamePathname(fn
, SRCDIR
"/desktop/unx/source/file_image_unx.c"))
189 if (loplugin::isSamePathname(fn
, SRCDIR
"/store/source/storpage.cxx"))
192 if (loplugin::isSamePathname(fn
, SRCDIR
"/idlc/source/idlccompile.cxx"))
194 if (fn
.contains("/qa/"))
196 if (fn
.contains("/vcl/workben/"))
199 if (loplugin::isSamePathname(fn
, SRCDIR
"/cppuhelper/source/servicemanager.cxx"))
201 // doing a "free items outside lock" thing
202 if (loplugin::isSamePathname(fn
, SRCDIR
"/unotools/source/config/itemholder1.cxx"))
204 if (loplugin::isSamePathname(fn
, SRCDIR
"/svl/source/config/itemholder2.cxx"))
206 if (loplugin::isSamePathname(fn
, SRCDIR
"/svtools/source/config/itemholder2.cxx"))
208 // doing a "keep objects alive" thing
209 if (loplugin::isSamePathname(fn
, SRCDIR
"/jvmfwk/source/framework.cxx"))
211 if (loplugin::isSamePathname(fn
, SRCDIR
"/jvmfwk/plugins/sunmajor/pluginlib/util.cxx"))
214 if (loplugin::isSamePathname(fn
, SRCDIR
"/svl/source/items/style.cxx"))
217 if (loplugin::isSamePathname(fn
, SRCDIR
"/stoc/source/inspect/introspection.cxx"))
219 if (loplugin::isSamePathname(fn
, SRCDIR
"/package/source/zippackage/ZipPackage.cxx"))
221 if (loplugin::isSamePathname(fn
, SRCDIR
"/hwpfilter/source/hwpreader.cxx"))
223 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/treelist/transfer.cxx"))
225 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/app/brand.cxx"))
227 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/filter/igif/gifread.cxx"))
229 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/gdi/metaact.cxx"))
231 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/fontsubset/sft.cxx"))
233 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/filter/ipdf/pdfdocument.cxx"))
235 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/filter/ipdf/pdfdocument2.cxx"))
237 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/unx/generic/app/sm.cxx"))
239 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/filter/jpeg/JpegWriter.cxx"))
241 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/unx/generic/dtrans/X11_selection.cxx"))
243 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/filter/jpeg/jpegc.cxx"))
245 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/unx/generic/window/FWS.cxx"))
247 if (loplugin::isSamePathname(fn
, SRCDIR
"/toolkit/source/awt/vclxspinbutton.cxx"))
249 if (loplugin::isSamePathname(fn
, SRCDIR
"/toolkit/source/controls/formattedcontrol.cxx"))
251 if (loplugin::isSamePathname(fn
, SRCDIR
"/svtools/source/config/helpopt.cxx"))
253 if (loplugin::isSamePathname(fn
, SRCDIR
"/svtools/source/filter/SvFilterOptionsDialog.cxx"))
255 if (loplugin::isSamePathname(fn
, SRCDIR
"/svtools/source/java/javainteractionhandler.cxx"))
257 if (loplugin::isSamePathname(fn
, SRCDIR
"/basic/source/classes/sbunoobj.cxx"))
259 if (loplugin::isSamePathname(fn
,
260 SRCDIR
"/accessibility/source/standard/vclxaccessiblebox.cxx"))
262 if (loplugin::isSamePathname(fn
, SRCDIR
"/cppcanvas/source/mtfrenderer/implrenderer.cxx"))
264 if (loplugin::isSamePathname(fn
, SRCDIR
"/sfx2/source/doc/guisaveas.cxx"))
266 if (loplugin::isSamePathname(fn
, SRCDIR
"/sfx2/source/appl/newhelp.cxx"))
268 if (loplugin::isSamePathname(fn
, SRCDIR
"/sfx2/source/control/thumbnailview.cxx"))
270 if (loplugin::isSamePathname(fn
, SRCDIR
"/sfx2/source/control/recentdocsview.cxx"))
272 if (loplugin::isSamePathname(fn
, SRCDIR
"/sfx2/source/view/viewfrm.cxx"))
274 if (loplugin::isSamePathname(fn
, SRCDIR
"/framework/source/services/desktop.cxx"))
276 if (loplugin::isSamePathname(fn
, SRCDIR
277 "/framework/source/uielement/generictoolbarcontroller.cxx"))
279 if (loplugin::isSamePathname(fn
, SRCDIR
280 "/framework/source/uielement/complextoolbarcontroller.cxx"))
282 if (loplugin::isSamePathname(fn
,
283 SRCDIR
"/framework/source/interaction/quietinteraction.cxx"))
285 if (loplugin::isSamePathname(fn
, SRCDIR
"/editeng/source/editeng/editdoc.cxx"))
287 if (loplugin::isSamePathname(fn
, SRCDIR
"/editeng/source/editeng/impedit4.cxx"))
289 if (loplugin::isSamePathname(fn
, SRCDIR
"/editeng/source/editeng/editobj.cxx"))
291 if (loplugin::isSamePathname(fn
, SRCDIR
"/editeng/source/items/frmitems.cxx"))
293 if (loplugin::isSamePathname(fn
, SRCDIR
"/binaryurp/source/bridge.cxx"))
295 if (loplugin::isSamePathname(fn
, SRCDIR
"/svx/source/tbxctrls/fontworkgallery.cxx"))
297 if (loplugin::isSamePathname(fn
, SRCDIR
"/basctl/source/basicide/moduldl2.cxx"))
299 if (loplugin::isSamePathname(fn
, SRCDIR
"/canvas/source/cairo/cairo_spritecanvas.cxx"))
301 if (loplugin::isSamePathname(fn
, SRCDIR
"/chart2/source/tools/DiagramHelper.cxx"))
303 if (loplugin::isSamePathname(fn
,
304 SRCDIR
"/chart2/source/tools/ExplicitCategoriesProvider.cxx"))
306 if (loplugin::isSamePathname(fn
, SRCDIR
"/chart2/source/tools/LegendHelper.cxx"))
308 if (loplugin::isSamePathname(fn
, SRCDIR
"/chart2/source/tools/OPropertySet.cxx"))
310 if (loplugin::isSamePathname(fn
, SRCDIR
"/chart2/source/tools/CommonConverters.cxx"))
312 if (loplugin::isSamePathname(
314 SRCDIR
"/chart2/source/controller/chartapiwrapper/WrappedNumberFormatProperty.cxx"))
316 if (loplugin::isSamePathname(fn
, SRCDIR
"/chart2/source/tools/DataSourceHelper.cxx"))
318 if (loplugin::isSamePathname(fn
, SRCDIR
"/oox/source/export/shapes.cxx"))
320 if (loplugin::isSamePathname(fn
, SRCDIR
"/oox/source/export/chartexport.cxx"))
322 if (loplugin::isSamePathname(fn
,
323 SRCDIR
"/filter/source/storagefilterdetect/filterdetect.cxx"))
325 if (loplugin::isSamePathname(fn
, SRCDIR
"/filter/source/pdf/pdfexport.cxx"))
327 if (loplugin::isSamePathname(fn
, SRCDIR
"/filter/source/svg/svgexport.cxx"))
329 if (loplugin::isSamePathname(fn
, SRCDIR
"/filter/source/msfilter/svdfppt.cxx"))
331 if (loplugin::isSamePathname(fn
, SRCDIR
332 "/dbaccess/source/core/recovery/subcomponentrecovery.cxx"))
334 if (loplugin::isSamePathname(fn
, SRCDIR
335 "/dbaccess/source/core/dataaccess/documentcontainer.cxx"))
337 if (loplugin::isSamePathname(fn
, SRCDIR
338 "/dbaccess/source/core/dataaccess/databasedocument.cxx"))
340 if (loplugin::isSamePathname(fn
,
341 SRCDIR
"/dbaccess/source/ui/browser/genericcontroller.cxx"))
343 if (loplugin::isSamePathname(fn
, SRCDIR
"/ucb/source/core/ucbcmds.cxx"))
345 if (loplugin::isSamePathname(fn
,
346 SRCDIR
"/desktop/source/deployment/manager/dp_manager.cxx"))
348 if (loplugin::isSamePathname(fn
, SRCDIR
349 "/desktop/source/deployment/registry/package/dp_package.cxx"))
351 if (loplugin::isSamePathname(fn
, SRCDIR
"/desktop/source/lib/init.cxx"))
353 if (loplugin::isSamePathname(fn
, SRCDIR
354 "/extensions/source/propctrlr/formcomponenthandler.cxx"))
356 if (loplugin::isSamePathname(fn
, SRCDIR
"/embeddedobj/source/general/docholder.cxx"))
358 if (loplugin::isSamePathname(fn
, SRCDIR
359 "/extensions/source/propctrlr/stringrepresentation.cxx"))
361 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwpcontent.cxx"))
363 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwpdivinfo.cxx"))
365 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwpdoc.cxx"))
367 if (loplugin::isSamePathname(fn
, SRCDIR
"/filter/source/pdf/impdialog.cxx"))
369 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwplayout.cxx"))
371 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwpoleobject.cxx"))
373 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwprowlayout.cxx"))
375 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwpfoundry.cxx"))
377 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwpparastyle.cxx"))
379 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwpnotes.cxx"))
381 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwpfont.cxx"))
383 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwptblcell.cxx"))
385 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwpusrdicts.cxx"))
387 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwpverdocument.cxx"))
389 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/lwptblformula.cxx"))
391 if (loplugin::isSamePathname(fn
, SRCDIR
"/vbahelper/source/vbahelper/vbafontbase.cxx"))
393 if (loplugin::isSamePathname(fn
, SRCDIR
"/vbahelper/source/vbahelper/vbadocumentbase.cxx"))
395 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/ui/docshell/docsh8.cxx"))
397 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/ui/docshell/docsh6.cxx"))
399 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/core/data/table3.cxx"))
401 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/ui/unoobj/cellsuno.cxx"))
403 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/filter/excel/xelink.cxx"))
405 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/filter/lotus/lotus.cxx"))
407 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/ui/vba/vbaworkbooks.cxx"))
409 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/ui/vba/vbaworksheets.cxx"))
411 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/ui/vba/vbarange.cxx"))
413 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/ui/view/drviews2.cxx"))
415 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/filter/ppt/pptin.cxx"))
417 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/ui/app/sdxfer.cxx"))
419 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/ui/view/drviewsf.cxx"))
421 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/filter/xml/sdxmlwrp.cxx"))
423 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/filter/html/pubdlg.cxx"))
425 if (loplugin::isSamePathname(fn
, SRCDIR
"/sw/source/core/txtnode/thints.cxx"))
427 if (loplugin::isSamePathname(fn
, SRCDIR
"/sw/source/core/doc/docbm.cxx"))
429 if (loplugin::isSamePathname(fn
, SRCDIR
"/sw/source/core/crsr/crsrsh.cxx"))
431 if (loplugin::isSamePathname(fn
, SRCDIR
"/sw/source/filter/xml/swxml.cxx"))
433 if (loplugin::isSamePathname(fn
, SRCDIR
"/sw/source/core/doc/docredln.cxx"))
435 if (loplugin::isSamePathname(fn
, SRCDIR
"/sw/source/filter/ww8/ww8par2.cxx"))
437 if (loplugin::isSamePathname(fn
, SRCDIR
"/sw/source/uibase/shells/drformsh.cxx"))
439 if (loplugin::isSamePathname(fn
, SRCDIR
"/sw/source/filter/ww8/ww8par6.cxx"))
441 if (loplugin::isSamePathname(fn
, SRCDIR
"/sw/source/ui/dbui/dbinsdlg.cxx"))
443 if (loplugin::isSamePathname(fn
, SRCDIR
"/sdext/source/minimizer/impoptimizer.cxx"))
445 if (loplugin::isSamePathname(fn
, SRCDIR
"/sdext/source/presenter/PresenterTheme.cxx"))
447 if (loplugin::isSamePathname(fn
, SRCDIR
"/sdext/source/pdfimport/wrapper/wrapper.cxx"))
449 if (loplugin::isSamePathname(fn
, SRCDIR
450 "/slideshow/source/engine/animationnodes/generateevent.cxx"))
452 if (loplugin::isSamePathname(fn
, SRCDIR
"/starmath/source/mathmlimport.cxx"))
454 if (loplugin::isSamePathname(fn
, SRCDIR
"/starmath/source/eqnolefilehdr.cxx"))
456 if (loplugin::isSamePathname(fn
, SRCDIR
"/svgio/source/svgreader/svgmarkernode.cxx"))
458 if (loplugin::isSamePathname(fn
, SRCDIR
"/uui/source/iahndl-locking.cxx"))
460 if (loplugin::isSamePathname(fn
, SRCDIR
461 "/shell/source/sessioninstall/SyncDbusSessionHelper.cxx"))
463 if (loplugin::isSamePathname(fn
,
464 SRCDIR
"/slideshow/source/engine/opengl/TransitionerImpl.cxx"))
466 if (loplugin::isSamePathname(fn
, SRCDIR
"/forms/source/component/FormattedField.cxx"))
468 if (loplugin::isSamePathname(fn
, SRCDIR
"/forms/source/component/DatabaseForm.cxx"))
470 if (loplugin::isSamePathname(fn
,
471 SRCDIR
"/reportdesign/source/ui/report/ReportController.cxx"))
473 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/test/"))
475 if (loplugin::isSamePathname(fn
, SRCDIR
"/i18npool/source/localedata/LocaleNode.cxx"))
479 if (loplugin::isSamePathname(fn
, SRCDIR
"/hwpfilter/source/grammar.cxx"))
482 for (MyVarInfo
const& v
: definitionSet
)
484 bool read
= readFromSet
.find(v
) != readFromSet
.end();
485 bool write
= writeToSet
.find(v
) != writeToSet
.end();
487 report(DiagnosticsEngine::Warning
, "write-only %0", compat::getBeginLoc(v
.varDecl
))
493 for (const MyVarInfo
& s
: readFromSet
)
494 report(DiagnosticsEngine::Warning
, "read %0", compat::getBeginLoc(s
.varDecl
))
496 for (const MyVarInfo
& s
: writeToSet
)
497 report(DiagnosticsEngine::Warning
, "write %0", compat::getBeginLoc(s
.varDecl
))
502 MyVarInfo
WriteOnlyVars::niceName(const VarDecl
* varDecl
)
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
);
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();
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
))
537 auto tc
= loplugin::TypeCheck(varDecl
->getType());
538 if (tc
.Pointer() || tc
.LvalueReference() || tc
.Class("shared_ptr").StdNamespace()
539 || tc
.Class("unique_ptr").StdNamespace())
541 if (tc
.Typedef("BitmapScopedWriteAccess"))
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"))
548 varDecl
= varDecl
->getCanonicalDecl();
549 if (!varDecl
->getLocation().isValid() || ignoreLocation(varDecl
))
551 if (!compiler
.getSourceManager().isInMainFile(varDecl
->getLocation()))
553 if (compiler
.getSourceManager().isMacroBodyExpansion(compat::getBeginLoc(varDecl
)))
555 if (compiler
.getSourceManager().isMacroArgExpansion(compat::getBeginLoc(varDecl
)))
557 // ignore stuff that forms part of the stable URE interface
558 if (isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(varDecl
->getLocation())))
561 definitionSet
.insert(niceName(varDecl
));
565 static char easytolower(char in
)
567 if (in
<= 'Z' && in
>= 'A')
568 return in
- ('Z' - 'z');
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
);
588 insideConditionalCheckOfMemberSet
.pop_back();
592 void WriteOnlyVars::checkIfReadFrom(const VarDecl
* varDecl
, const Expr
* memberExpr
)
594 auto parentsRange
= compiler
.getASTContext().getParents(*memberExpr
);
595 const Stmt
* child
= memberExpr
;
597 = parentsRange
.begin() == parentsRange
.end() ? nullptr : parentsRange
.begin()->get
<Stmt
>();
598 // walk up the tree until we find something interesting
599 bool bPotentiallyReadFrom
= false;
601 auto walkupUp
= [&]() {
603 auto parentsRange
= compiler
.getASTContext().getParents(*parent
);
604 parent
= parentsRange
.begin() == parentsRange
.end() ? nullptr
605 : parentsRange
.begin()->get
<Stmt
>();
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
)
623 if (isa
<CXXReinterpretCastExpr
>(parent
))
625 // once we see one of these, there is not much useful we can know
626 bPotentiallyReadFrom
= true;
629 else if (isa
<CastExpr
>(parent
) || isa
<MemberExpr
>(parent
) || isa
<ParenExpr
>(parent
)
630 || isa
<ParenListExpr
>(parent
) || isa
<ArrayInitLoopExpr
>(parent
)
631 || isa
<ExprWithCleanups
>(parent
))
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;
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.
655 else if (auto caseStmt
= dyn_cast
<CaseStmt
>(parent
))
657 bPotentiallyReadFrom
= caseStmt
->getLHS() == child
|| caseStmt
->getRHS() == child
;
660 else if (auto ifStmt
= dyn_cast
<IfStmt
>(parent
))
662 bPotentiallyReadFrom
= ifStmt
->getCond() == child
;
665 else if (auto doStmt
= dyn_cast
<DoStmt
>(parent
))
667 bPotentiallyReadFrom
= doStmt
->getCond() == child
;
670 else if (auto arraySubscriptExpr
= dyn_cast
<ArraySubscriptExpr
>(parent
))
672 if (arraySubscriptExpr
->getIdx() == child
)
674 bPotentiallyReadFrom
= true;
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
708 bPotentiallyReadFrom
= true;
711 bPotentiallyReadFrom
= true;
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
);
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
731 bPotentiallyReadFrom
= true;
734 bPotentiallyReadFrom
= true;
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;
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;
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
)
770 || isa
<CXXDefaultArgExpr
>(parent
) || isa
<LambdaExpr
>(parent
))
776 bPotentiallyReadFrom
= true;
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();
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
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())
811 auto parentsRange
= compiler
.getASTContext().getParents(*memberExpr
);
812 const Stmt
* child
= memberExpr
;
814 = parentsRange
.begin() == parentsRange
.end() ? nullptr : parentsRange
.begin()->get
<Stmt
>();
815 // walk up the tree until we find something interesting
816 bool bPotentiallyWrittenTo
= false;
818 auto walkupUp
= [&]() {
820 auto parentsRange
= compiler
.getASTContext().getParents(*parent
);
821 parent
= parentsRange
.begin() == parentsRange
.end() ? nullptr
822 : parentsRange
.begin()->get
<Stmt
>();
828 // check if we have an expression like
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;
844 if (isa
<CXXReinterpretCastExpr
>(parent
))
846 // once we see one of these, there is not much useful we can know
847 bPotentiallyWrittenTo
= true;
850 else if (isa
<CastExpr
>(parent
) || isa
<MemberExpr
>(parent
) || isa
<ParenExpr
>(parent
)
851 || isa
<ParenListExpr
>(parent
) || isa
<ArrayInitLoopExpr
>(parent
)
852 || isa
<ExprWithCleanups
>(parent
))
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
862 bPotentiallyWrittenTo
= true;
866 else if (auto arraySubscriptExpr
= dyn_cast
<ArraySubscriptExpr
>(parent
))
868 if (arraySubscriptExpr
->getIdx() == child
)
872 else if (auto operatorCallExpr
= dyn_cast
<CXXOperatorCallExpr
>(parent
))
874 auto callee
= getCallee(operatorCallExpr
);
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;
891 bPotentiallyWrittenTo
= true; // conservative, could improve
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
);
912 else if (IsPassedByNonConst(varDecl
, child
, cxxMemberCallExpr
,
913 CalleeWrapper(calleeMethodDecl
)))
914 bPotentiallyWrittenTo
= true;
917 bPotentiallyWrittenTo
= true; // can happen in templates
920 else if (auto cxxConstructExpr
= dyn_cast
<CXXConstructExpr
>(parent
))
922 if (IsPassedByNonConst(varDecl
, child
, cxxConstructExpr
,
923 CalleeWrapper(cxxConstructExpr
)))
924 bPotentiallyWrittenTo
= true;
927 else if (auto callExpr
= dyn_cast
<CallExpr
>(parent
))
929 auto callee
= getCallee(callExpr
);
932 if (IsPassedByNonConst(varDecl
, child
, callExpr
, *callee
))
933 bPotentiallyWrittenTo
= true;
936 bPotentiallyWrittenTo
= true; // conservative, could improve
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
;
949 if (binaryOp
->getLHS() == child
)
950 bPotentiallyWrittenTo
= true;
951 else if (loplugin::TypeCheck(binaryOp
->getLHS()->getType())
954 // if the LHS is a non-const reference, we could write to the var later on
955 bPotentiallyWrittenTo
= true;
959 else if (isa
<ReturnStmt
>(parent
))
961 if (insideFunctionDecl
)
963 auto tc
= loplugin::TypeCheck(insideFunctionDecl
->getReturnType());
964 if (tc
.LvalueReference().NonConst())
965 bPotentiallyWrittenTo
= true;
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
)
982 || isa
<GCCAsmStmt
>(parent
) || isa
<VAArgExpr
>(parent
)
983 || isa
<CXXDefaultArgExpr
>(parent
) || isa
<LambdaExpr
>(parent
))
989 bPotentiallyWrittenTo
= true;
997 report(DiagnosticsEngine::Warning
, "oh dear2, what can the matter be? writtenTo=%0",
998 compat::getBeginLoc(memberExpr
))
999 << bPotentiallyWrittenTo
<< memberExpr
->getSourceRange();
1002 report(DiagnosticsEngine::Note
, "parent over here", compat::getBeginLoc(parent
))
1003 << parent
->getSourceRange();
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())
1027 else if (tc
.Class("set").StdNamespace() || tc
.Class("unordered_set").StdNamespace())
1031 else if (tc
.Class("map").StdNamespace() || tc
.Class("unordered_map").StdNamespace())
1035 else if (tc
.Class("Sequence")
1047 if (calleeMethodDecl
->isOverloadedOperator())
1049 auto oo
= calleeMethodDecl
->getOverloadedOperator();
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
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")
1073 if (name
== "getArray" || name
== "begin" || name
== "end")
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())
1095 for (unsigned i
= 0; i
< len
; ++i
)
1096 if (callExpr
.getArg(i
) == child
)
1097 if (loplugin::TypeCheck(calleeFunctionDecl
.getParamType(i
))
1105 bool WriteOnlyVars::VisitDeclRefExpr(const DeclRefExpr
* declRefExpr
)
1107 const Decl
* decl
= declRefExpr
->getDecl();
1108 const VarDecl
* varDecl
= dyn_cast
<VarDecl
>(decl
);
1111 if (varDecl
->isImplicit() || isa
<ParmVarDecl
>(varDecl
))
1113 varDecl
= varDecl
->getCanonicalDecl();
1114 if (ignoreLocation(varDecl
))
1116 // ignore stuff that forms part of the stable URE interface
1117 if (isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(varDecl
->getLocation())))
1120 checkIfReadFrom(varDecl
, declRefExpr
);
1122 checkIfWrittenTo(varDecl
, declRefExpr
);
1127 llvm::Optional
<CalleeWrapper
> WriteOnlyVars::getCallee(CallExpr
const* callExpr
)
1129 FunctionDecl
const* functionDecl
= callExpr
->getDirectCallee();
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);
1153 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */