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 #include "clang/AST/ParentMapContext.h"
31 Finds variables that are effectively write-only.
33 Largely the same as the unusedfields.cxx loplugin.
40 const VarDecl
* varDecl
;
44 std::string sourceLocation
;
46 bool operator<(const MyVarInfo
& lhs
, const MyVarInfo
& rhs
)
48 return std::tie(lhs
.parent
, lhs
.varName
) < std::tie(rhs
.parent
, rhs
.varName
);
51 // try to limit the voluminous output a little
52 static std::set
<MyVarInfo
> readFromSet
;
53 static std::set
<MyVarInfo
> writeToSet
;
54 static std::set
<MyVarInfo
> definitionSet
;
57 * Wrap the different kinds of callable and callee objects in the clang AST so I can define methods that handle everything.
61 const CallExpr
* m_callExpr
;
62 const CXXConstructExpr
* m_cxxConstructExpr
;
65 CallerWrapper(const CallExpr
* callExpr
)
66 : m_callExpr(callExpr
)
67 , m_cxxConstructExpr(nullptr)
70 CallerWrapper(const CXXConstructExpr
* cxxConstructExpr
)
72 , m_cxxConstructExpr(cxxConstructExpr
)
75 unsigned getNumArgs() const
77 return m_callExpr
? m_callExpr
->getNumArgs() : m_cxxConstructExpr
->getNumArgs();
79 const Expr
* getArg(unsigned i
) const
81 return m_callExpr
? m_callExpr
->getArg(i
) : m_cxxConstructExpr
->getArg(i
);
86 const FunctionDecl
* m_calleeFunctionDecl
= nullptr;
87 const CXXConstructorDecl
* m_cxxConstructorDecl
= nullptr;
88 const FunctionProtoType
* m_functionPrototype
= nullptr;
91 explicit CalleeWrapper(const FunctionDecl
* calleeFunctionDecl
)
92 : m_calleeFunctionDecl(calleeFunctionDecl
)
95 explicit CalleeWrapper(const CXXConstructExpr
* cxxConstructExpr
)
96 : m_cxxConstructorDecl(cxxConstructExpr
->getConstructor())
99 explicit CalleeWrapper(const FunctionProtoType
* functionPrototype
)
100 : m_functionPrototype(functionPrototype
)
103 unsigned getNumParams() const
105 if (m_calleeFunctionDecl
)
106 return m_calleeFunctionDecl
->getNumParams();
107 else if (m_cxxConstructorDecl
)
108 return m_cxxConstructorDecl
->getNumParams();
109 else if (m_functionPrototype
->param_type_begin() == m_functionPrototype
->param_type_end())
110 // FunctionProtoType will assert if we call getParamTypes() and it has no params
113 return m_functionPrototype
->getParamTypes().size();
115 const QualType
getParamType(unsigned i
) const
117 if (m_calleeFunctionDecl
)
118 return m_calleeFunctionDecl
->getParamDecl(i
)->getType();
119 else if (m_cxxConstructorDecl
)
120 return m_cxxConstructorDecl
->getParamDecl(i
)->getType();
122 return m_functionPrototype
->getParamTypes()[i
];
124 std::string
getNameAsString() const
126 if (m_calleeFunctionDecl
)
127 return m_calleeFunctionDecl
->getNameAsString();
128 else if (m_cxxConstructorDecl
)
129 return m_cxxConstructorDecl
->getNameAsString();
133 CXXMethodDecl
const* getAsCXXMethodDecl() const
135 if (m_calleeFunctionDecl
)
136 return dyn_cast
<CXXMethodDecl
>(m_calleeFunctionDecl
);
141 class WriteOnlyVars
: public loplugin::FilteringPlugin
<WriteOnlyVars
>
144 explicit WriteOnlyVars(loplugin::InstantiationData
const& data
)
145 : FilteringPlugin(data
)
149 virtual void run() override
;
151 bool shouldVisitTemplateInstantiations() const { return true; }
152 bool shouldVisitImplicitCode() const { return true; }
154 bool VisitVarDecl(const VarDecl
*);
155 bool VisitDeclRefExpr(const DeclRefExpr
*);
156 bool TraverseIfStmt(IfStmt
*);
159 MyVarInfo
niceName(const VarDecl
*);
160 void checkIfReadFrom(const VarDecl
* varDecl
, const Expr
* memberExpr
);
161 void checkIfWrittenTo(const VarDecl
* varDecl
, const Expr
* memberExpr
);
162 bool checkForWriteWhenUsingCollectionType(const CXXMethodDecl
* calleeMethodDecl
);
163 bool IsPassedByNonConst(const VarDecl
* varDecl
, const Stmt
* child
, CallerWrapper callExpr
,
164 CalleeWrapper calleeFunctionDecl
);
165 compat::optional
<CalleeWrapper
> getCallee(CallExpr
const*);
167 // For reasons I do not understand, parentFunctionDecl() is not reliable, so
168 // we store the parent function on the way down the AST.
169 FunctionDecl
* insideFunctionDecl
= nullptr;
170 std::vector
<VarDecl
const*> insideConditionalCheckOfMemberSet
;
173 void WriteOnlyVars::run()
175 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
177 if (!isUnitTestMode())
179 StringRef
fn(handler
.getMainFileName());
180 // playing paging-in games with volatile
181 if (loplugin::isSamePathname(fn
, SRCDIR
"/sal/osl/unx/file.cxx"))
183 // playing paging-in games with volatile
184 if (loplugin::isSamePathname(fn
, SRCDIR
"/desktop/unx/source/file_image_unx.c"))
187 if (loplugin::isSamePathname(fn
, SRCDIR
"/store/source/storpage.cxx"))
189 if (fn
.contains("/qa/"))
191 if (fn
.contains("/vcl/workben/"))
194 if (loplugin::isSamePathname(fn
, SRCDIR
"/cppuhelper/source/servicemanager.cxx"))
196 // doing a "free items outside lock" thing
197 if (loplugin::isSamePathname(fn
, SRCDIR
"/unotools/source/config/itemholder1.cxx"))
199 if (loplugin::isSamePathname(fn
, SRCDIR
"/svl/source/config/itemholder2.cxx"))
201 if (loplugin::isSamePathname(fn
, SRCDIR
"/svtools/source/config/itemholder2.cxx"))
203 // doing a "keep objects alive" thing
204 if (loplugin::isSamePathname(fn
, SRCDIR
"/jvmfwk/source/framework.cxx"))
206 if (loplugin::isSamePathname(fn
, SRCDIR
"/jvmfwk/plugins/sunmajor/pluginlib/util.cxx"))
209 if (loplugin::isSamePathname(fn
, SRCDIR
"/svl/source/items/style.cxx"))
212 if (loplugin::isSamePathname(fn
, SRCDIR
"/stoc/source/inspect/introspection.cxx"))
214 if (loplugin::isSamePathname(fn
, SRCDIR
"/package/source/zippackage/ZipPackage.cxx"))
216 if (loplugin::isSamePathname(fn
, SRCDIR
"/hwpfilter/source/hwpreader.cxx"))
218 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/treelist/transfer.cxx"))
220 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/app/brand.cxx"))
222 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/filter/igif/gifread.cxx"))
224 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/gdi/metaact.cxx"))
226 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/fontsubset/sft.cxx"))
228 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/filter/ipdf/pdfdocument.cxx"))
230 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/filter/ipdf/pdfdocument2.cxx"))
232 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/unx/generic/app/sm.cxx"))
234 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/filter/jpeg/JpegWriter.cxx"))
236 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/unx/generic/dtrans/X11_selection.cxx"))
238 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/filter/jpeg/jpegc.cxx"))
240 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/unx/generic/window/FWS.cxx"))
242 if (loplugin::isSamePathname(fn
, SRCDIR
"/toolkit/source/awt/vclxspinbutton.cxx"))
244 if (loplugin::isSamePathname(fn
, SRCDIR
"/toolkit/source/controls/formattedcontrol.cxx"))
246 if (loplugin::isSamePathname(fn
, SRCDIR
"/svtools/source/config/helpopt.cxx"))
248 if (loplugin::isSamePathname(fn
, SRCDIR
"/svtools/source/filter/SvFilterOptionsDialog.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", v
.varDecl
->getBeginLoc())
488 for (const MyVarInfo
& s
: readFromSet
)
489 report(DiagnosticsEngine::Warning
, "read %0", s
.varDecl
->getBeginLoc()) << s
.varName
;
490 for (const MyVarInfo
& s
: writeToSet
)
491 report(DiagnosticsEngine::Warning
, "write %0", s
.varDecl
->getBeginLoc()) << s
.varName
;
495 MyVarInfo
WriteOnlyVars::niceName(const VarDecl
* varDecl
)
499 aInfo
.varDecl
= varDecl
->getCanonicalDecl();
500 aInfo
.varName
= varDecl
->getNameAsString();
501 // sometimes the name (if it's an anonymous thing) contains the full path of the build folder, which we don't need
502 size_t idx
= aInfo
.varName
.find(SRCDIR
);
503 if (idx
!= std::string::npos
)
505 aInfo
.varName
= aInfo
.varName
.replace(idx
, strlen(SRCDIR
), "");
507 aInfo
.varType
= varDecl
->getType().getAsString();
509 SourceLocation expansionLoc
510 = compiler
.getSourceManager().getExpansionLoc(varDecl
->getLocation());
511 StringRef filename
= getFilenameOfLocation(expansionLoc
);
513 = std::string(filename
.substr(strlen(SRCDIR
) + 1)) + ":"
514 + std::to_string(compiler
.getSourceManager().getSpellingLineNumber(expansionLoc
));
515 loplugin::normalizeDotDotInFilePath(aInfo
.sourceLocation
);
516 aInfo
.parent
= filename
.str();
521 static bool contains(std::string
const& s
, std::string
const& needle
)
523 return s
.find(needle
) != std::string::npos
;
526 bool WriteOnlyVars::VisitVarDecl(const VarDecl
* varDecl
)
528 if (varDecl
->isImplicit() || varDecl
->isExternC() || isa
<ParmVarDecl
>(varDecl
))
530 auto tc
= loplugin::TypeCheck(varDecl
->getType());
531 if (tc
.Pointer() || tc
.LvalueReference() || tc
.Class("shared_ptr").StdNamespace()
532 || tc
.Class("unique_ptr").StdNamespace())
534 if (tc
.Typedef("BitmapScopedWriteAccess"))
536 std::string typeName
= varDecl
->getType().getAsString();
537 if (contains(typeName
, "Guard") || contains(typeName
, "Reader") || contains(typeName
, "Stream")
538 || contains(typeName
, "Parser") || contains(typeName
, "Codec")
539 || contains(typeName
, "Exception"))
541 varDecl
= varDecl
->getCanonicalDecl();
542 if (!varDecl
->getLocation().isValid() || ignoreLocation(varDecl
))
544 if (!compiler
.getSourceManager().isInMainFile(varDecl
->getLocation()))
546 if (compiler
.getSourceManager().isMacroBodyExpansion(varDecl
->getBeginLoc()))
548 if (compiler
.getSourceManager().isMacroArgExpansion(varDecl
->getBeginLoc()))
550 // ignore stuff that forms part of the stable URE interface
551 if (isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(varDecl
->getLocation())))
554 definitionSet
.insert(niceName(varDecl
));
558 static char easytolower(char in
)
560 if (in
<= 'Z' && in
>= 'A')
561 return in
- ('Z' - 'z');
565 bool startswith(const std::string
& rStr
, const char* pSubStr
)
567 return rStr
.compare(0, strlen(pSubStr
), pSubStr
) == 0;
570 bool WriteOnlyVars::TraverseIfStmt(IfStmt
* ifStmt
)
572 VarDecl
const* varDecl
= nullptr;
573 Expr
const* cond
= ifStmt
->getCond()->IgnoreParenImpCasts();
574 if (auto declRefExpr
= dyn_cast
<DeclRefExpr
>(cond
))
576 if ((varDecl
= dyn_cast
<VarDecl
>(declRefExpr
->getDecl())))
577 insideConditionalCheckOfMemberSet
.push_back(varDecl
);
579 bool ret
= RecursiveASTVisitor::TraverseIfStmt(ifStmt
);
581 insideConditionalCheckOfMemberSet
.pop_back();
585 void WriteOnlyVars::checkIfReadFrom(const VarDecl
* varDecl
, const Expr
* memberExpr
)
587 auto parentsRange
= compiler
.getASTContext().getParents(*memberExpr
);
588 const Stmt
* child
= memberExpr
;
590 = parentsRange
.begin() == parentsRange
.end() ? nullptr : parentsRange
.begin()->get
<Stmt
>();
591 // walk up the tree until we find something interesting
592 bool bPotentiallyReadFrom
= false;
594 auto walkupUp
= [&]() {
596 auto parentsRange
= compiler
.getASTContext().getParents(*parent
);
597 parent
= parentsRange
.begin() == parentsRange
.end() ? nullptr
598 : parentsRange
.begin()->get
<Stmt
>();
604 // check if we're inside a CXXCtorInitializer or a VarDecl
605 auto parentsRange
= compiler
.getASTContext().getParents(*child
);
606 if (parentsRange
.begin() != parentsRange
.end())
608 const Decl
* decl
= parentsRange
.begin()->get
<Decl
>();
609 if (decl
&& (isa
<CXXConstructorDecl
>(decl
) || isa
<VarDecl
>(decl
)))
610 bPotentiallyReadFrom
= true;
612 if (!bPotentiallyReadFrom
)
616 if (isa
<CXXReinterpretCastExpr
>(parent
))
618 // once we see one of these, there is not much useful we can know
619 bPotentiallyReadFrom
= true;
622 else if (isa
<CastExpr
>(parent
) || isa
<MemberExpr
>(parent
) || isa
<ParenExpr
>(parent
)
623 || isa
<ParenListExpr
>(parent
) || isa
<ArrayInitLoopExpr
>(parent
)
624 || isa
<ExprWithCleanups
>(parent
))
628 else if (auto unaryOperator
= dyn_cast
<UnaryOperator
>(parent
))
630 UnaryOperator::Opcode op
= unaryOperator
->getOpcode();
631 if (memberExpr
->getType()->isArrayType() && op
== UO_Deref
)
633 // ignore, deref'ing an array does not count as a read
635 else if (op
== UO_AddrOf
|| op
== UO_Deref
|| op
== UO_Plus
|| op
== UO_Minus
636 || op
== UO_Not
|| op
== UO_LNot
)
638 bPotentiallyReadFrom
= true;
641 /* The following are technically reads, but from a code-sense they're more of a write/modify, so
642 ignore them to find interesting fields that only modified, not usefully read:
643 UO_PreInc / UO_PostInc / UO_PreDec / UO_PostDec
644 But we still walk up in case the result of the expression is used in a read sense.
648 else if (auto caseStmt
= dyn_cast
<CaseStmt
>(parent
))
650 bPotentiallyReadFrom
= caseStmt
->getLHS() == child
|| caseStmt
->getRHS() == child
;
653 else if (auto ifStmt
= dyn_cast
<IfStmt
>(parent
))
655 bPotentiallyReadFrom
= ifStmt
->getCond() == child
;
658 else if (auto doStmt
= dyn_cast
<DoStmt
>(parent
))
660 bPotentiallyReadFrom
= doStmt
->getCond() == child
;
663 else if (auto arraySubscriptExpr
= dyn_cast
<ArraySubscriptExpr
>(parent
))
665 if (arraySubscriptExpr
->getIdx() == child
)
667 bPotentiallyReadFrom
= true;
672 else if (auto callExpr
= dyn_cast
<CXXMemberCallExpr
>(parent
))
674 // check for calls to ReadXXX() type methods and the operator>>= methods on Any.
675 auto callee
= getCallee(callExpr
);
676 if (callee
&& *callExpr
->child_begin() == child
)
678 // FIXME perhaps a better solution here would be some kind of SAL_PARAM_WRITEONLY attribute
679 // which we could scatter around.
680 std::string name
= callee
->getNameAsString();
681 std::transform(name
.begin(), name
.end(), name
.begin(), easytolower
);
682 if (startswith(name
, "read"))
683 // this is a write-only call
685 else if (startswith(name
, "emplace") || name
== "insert" || name
== "erase"
686 || name
== "remove" || name
== "remove_if" || name
== "sort"
687 || name
== "push_back" || name
== "pop_back" || name
== "push_front"
688 || name
== "pop_front" || name
== "reserve" || name
== "resize"
689 || name
== "clear" || name
== "fill")
690 // write-only modifications to collections
692 else if (name
.find(">>=") != std::string::npos
&& callExpr
->getArg(1) == child
)
693 // this is a write-only call
695 else if (name
== "dispose" || name
== "disposeAndClear" || name
== "swap")
696 // we're abusing the write-only analysis here to look for vars which don't have anything useful
697 // being done to them, so we're ignoring things like std::vector::clear, std::vector::swap,
698 // and VclPtr::disposeAndClear
701 bPotentiallyReadFrom
= true;
704 bPotentiallyReadFrom
= true;
707 else if (auto callExpr
= dyn_cast
<CallExpr
>(parent
))
709 // check for calls to ReadXXX() type methods and the operator>>= methods on Any.
710 auto callee
= getCallee(callExpr
);
713 // FIXME perhaps a better solution here would be some kind of SAL_PARAM_WRITEONLY attribute
714 // which we could scatter around.
715 std::string name
= callee
->getNameAsString();
716 std::transform(name
.begin(), name
.end(), name
.begin(), easytolower
);
717 if (startswith(name
, "read"))
718 // this is a write-only call
720 else if (name
.find(">>=") != std::string::npos
&& callExpr
->getArg(1) == child
)
721 // this is a write-only call
724 bPotentiallyReadFrom
= true;
727 bPotentiallyReadFrom
= true;
730 else if (auto binaryOp
= dyn_cast
<BinaryOperator
>(parent
))
732 BinaryOperator::Opcode op
= binaryOp
->getOpcode();
733 // If the child is on the LHS and it is an assignment op, we are obviously not reading from it
734 const bool assignmentOp
= op
== BO_Assign
|| op
== BO_MulAssign
|| op
== BO_DivAssign
735 || op
== BO_RemAssign
|| op
== BO_AddAssign
736 || op
== BO_SubAssign
|| op
== BO_ShlAssign
737 || op
== BO_ShrAssign
|| op
== BO_AndAssign
738 || op
== BO_XorAssign
|| op
== BO_OrAssign
;
739 if (!(binaryOp
->getLHS() == child
&& assignmentOp
))
741 bPotentiallyReadFrom
= true;
745 else if (isa
<ReturnStmt
>(parent
) || isa
<CXXConstructExpr
>(parent
)
746 || isa
<ConditionalOperator
>(parent
) || isa
<SwitchStmt
>(parent
)
747 || isa
<DeclStmt
>(parent
) || isa
<WhileStmt
>(parent
) || isa
<CXXNewExpr
>(parent
)
748 || isa
<ForStmt
>(parent
) || isa
<InitListExpr
>(parent
)
749 || isa
<CXXDependentScopeMemberExpr
>(parent
) || isa
<UnresolvedMemberExpr
>(parent
)
750 || isa
<MaterializeTemporaryExpr
>(parent
))
752 bPotentiallyReadFrom
= true;
755 else if (isa
<CXXDeleteExpr
>(parent
) || isa
<UnaryExprOrTypeTraitExpr
>(parent
)
756 || isa
<CXXUnresolvedConstructExpr
>(parent
) || isa
<CompoundStmt
>(parent
)
757 || isa
<LabelStmt
>(parent
) || isa
<CXXForRangeStmt
>(parent
)
758 || isa
<CXXTypeidExpr
>(parent
) || isa
<DefaultStmt
>(parent
)
759 || isa
<GCCAsmStmt
>(parent
) || isa
<VAArgExpr
>(parent
) || isa
<ConstantExpr
>(parent
)
760 || isa
<CXXDefaultArgExpr
>(parent
) || isa
<LambdaExpr
>(parent
))
766 bPotentiallyReadFrom
= true;
774 report(DiagnosticsEngine::Warning
, "oh dear, what can the matter be?",
775 memberExpr
->getBeginLoc())
776 << memberExpr
->getSourceRange();
777 report(DiagnosticsEngine::Note
, "parent over here", parent
->getBeginLoc())
778 << parent
->getSourceRange();
783 MyVarInfo varInfo
= niceName(varDecl
);
784 if (bPotentiallyReadFrom
)
786 readFromSet
.insert(varInfo
);
790 void WriteOnlyVars::checkIfWrittenTo(const VarDecl
* varDecl
, const Expr
* memberExpr
)
792 // if we're inside a block that looks like
795 // then writes to this var don't matter, because unless we find another write to this var, this var is dead
796 if (std::find(insideConditionalCheckOfMemberSet
.begin(),
797 insideConditionalCheckOfMemberSet
.end(), varDecl
)
798 != insideConditionalCheckOfMemberSet
.end())
801 auto parentsRange
= compiler
.getASTContext().getParents(*memberExpr
);
802 const Stmt
* child
= memberExpr
;
804 = parentsRange
.begin() == parentsRange
.end() ? nullptr : parentsRange
.begin()->get
<Stmt
>();
805 // walk up the tree until we find something interesting
806 bool bPotentiallyWrittenTo
= false;
808 auto walkupUp
= [&]() {
810 auto parentsRange
= compiler
.getASTContext().getParents(*parent
);
811 parent
= parentsRange
.begin() == parentsRange
.end() ? nullptr
812 : parentsRange
.begin()->get
<Stmt
>();
818 // check if we have an expression like
820 auto parentsRange
= compiler
.getASTContext().getParents(*child
);
821 if (parentsRange
.begin() != parentsRange
.end())
823 auto varDecl
= dyn_cast_or_null
<VarDecl
>(parentsRange
.begin()->get
<Decl
>());
824 // The isImplicit() call is to avoid triggering when we see the vardecl which is part of a for-range statement,
825 // which is of type 'T&&' and also an l-value-ref ?
826 if (varDecl
&& !varDecl
->isImplicit()
827 && loplugin::TypeCheck(varDecl
->getType()).LvalueReference().NonConst())
829 bPotentiallyWrittenTo
= true;
834 if (isa
<CXXReinterpretCastExpr
>(parent
))
836 // once we see one of these, there is not much useful we can know
837 bPotentiallyWrittenTo
= true;
840 else if (isa
<CastExpr
>(parent
) || isa
<MemberExpr
>(parent
) || isa
<ParenExpr
>(parent
)
841 || isa
<ParenListExpr
>(parent
) || isa
<ArrayInitLoopExpr
>(parent
)
842 || isa
<ExprWithCleanups
>(parent
))
846 else if (auto unaryOperator
= dyn_cast
<UnaryOperator
>(parent
))
848 UnaryOperator::Opcode op
= unaryOperator
->getOpcode();
849 if (op
== UO_AddrOf
|| op
== UO_PostInc
|| op
== UO_PostDec
|| op
== UO_PreInc
852 bPotentiallyWrittenTo
= true;
856 else if (auto arraySubscriptExpr
= dyn_cast
<ArraySubscriptExpr
>(parent
))
858 if (arraySubscriptExpr
->getIdx() == child
)
862 else if (auto operatorCallExpr
= dyn_cast
<CXXOperatorCallExpr
>(parent
))
864 auto callee
= getCallee(operatorCallExpr
);
867 // if calling a non-const operator on the var
868 auto calleeMethodDecl
= callee
->getAsCXXMethodDecl();
869 if (calleeMethodDecl
&& operatorCallExpr
->getArg(0) == child
)
871 if (!calleeMethodDecl
->isConst())
872 bPotentiallyWrittenTo
873 = checkForWriteWhenUsingCollectionType(calleeMethodDecl
);
875 else if (IsPassedByNonConst(varDecl
, child
, operatorCallExpr
, *callee
))
877 bPotentiallyWrittenTo
= true;
881 bPotentiallyWrittenTo
= true; // conservative, could improve
884 else if (auto cxxMemberCallExpr
= dyn_cast
<CXXMemberCallExpr
>(parent
))
886 const CXXMethodDecl
* calleeMethodDecl
= cxxMemberCallExpr
->getMethodDecl();
887 if (calleeMethodDecl
)
889 // if calling a non-const method on the var
890 const Expr
* tmp
= dyn_cast
<Expr
>(child
);
891 if (tmp
->isBoundMemberFunction(compiler
.getASTContext()))
893 tmp
= dyn_cast
<MemberExpr
>(tmp
)->getBase();
895 if (cxxMemberCallExpr
->getImplicitObjectArgument() == tmp
)
897 if (!calleeMethodDecl
->isConst())
898 bPotentiallyWrittenTo
899 = checkForWriteWhenUsingCollectionType(calleeMethodDecl
);
902 else if (IsPassedByNonConst(varDecl
, child
, cxxMemberCallExpr
,
903 CalleeWrapper(calleeMethodDecl
)))
904 bPotentiallyWrittenTo
= true;
907 bPotentiallyWrittenTo
= true; // can happen in templates
910 else if (auto cxxConstructExpr
= dyn_cast
<CXXConstructExpr
>(parent
))
912 if (IsPassedByNonConst(varDecl
, child
, cxxConstructExpr
,
913 CalleeWrapper(cxxConstructExpr
)))
914 bPotentiallyWrittenTo
= true;
917 else if (auto callExpr
= dyn_cast
<CallExpr
>(parent
))
919 auto callee
= getCallee(callExpr
);
922 if (IsPassedByNonConst(varDecl
, child
, callExpr
, *callee
))
923 bPotentiallyWrittenTo
= true;
926 bPotentiallyWrittenTo
= true; // conservative, could improve
929 else if (auto binaryOp
= dyn_cast
<BinaryOperator
>(parent
))
931 BinaryOperator::Opcode op
= binaryOp
->getOpcode();
932 const bool assignmentOp
= op
== BO_Assign
|| op
== BO_MulAssign
|| op
== BO_DivAssign
933 || op
== BO_RemAssign
|| op
== BO_AddAssign
934 || op
== BO_SubAssign
|| op
== BO_ShlAssign
935 || op
== BO_ShrAssign
|| op
== BO_AndAssign
936 || op
== BO_XorAssign
|| op
== BO_OrAssign
;
939 if (binaryOp
->getLHS() == child
)
940 bPotentiallyWrittenTo
= true;
941 else if (loplugin::TypeCheck(binaryOp
->getLHS()->getType())
944 // if the LHS is a non-const reference, we could write to the var later on
945 bPotentiallyWrittenTo
= true;
949 else if (isa
<ReturnStmt
>(parent
))
951 if (insideFunctionDecl
)
953 auto tc
= loplugin::TypeCheck(insideFunctionDecl
->getReturnType());
954 if (tc
.LvalueReference().NonConst())
955 bPotentiallyWrittenTo
= true;
959 else if (isa
<ConditionalOperator
>(parent
) || isa
<SwitchStmt
>(parent
)
960 || isa
<DeclStmt
>(parent
) || isa
<WhileStmt
>(parent
) || isa
<CXXNewExpr
>(parent
)
961 || isa
<ForStmt
>(parent
) || isa
<InitListExpr
>(parent
)
962 || isa
<CXXDependentScopeMemberExpr
>(parent
) || isa
<UnresolvedMemberExpr
>(parent
)
963 || isa
<MaterializeTemporaryExpr
>(parent
) || isa
<IfStmt
>(parent
)
964 || isa
<DoStmt
>(parent
) || isa
<CXXDeleteExpr
>(parent
)
965 || isa
<UnaryExprOrTypeTraitExpr
>(parent
) || isa
<CXXUnresolvedConstructExpr
>(parent
)
966 || isa
<CompoundStmt
>(parent
) || isa
<LabelStmt
>(parent
)
967 || isa
<CXXForRangeStmt
>(parent
) || isa
<CXXTypeidExpr
>(parent
)
968 || isa
<DefaultStmt
>(parent
) || isa
<ConstantExpr
>(parent
) || isa
<GCCAsmStmt
>(parent
)
969 || isa
<VAArgExpr
>(parent
) || isa
<CXXDefaultArgExpr
>(parent
)
970 || isa
<LambdaExpr
>(parent
))
976 bPotentiallyWrittenTo
= true;
984 report(DiagnosticsEngine::Warning
, "oh dear2, what can the matter be? writtenTo=%0",
985 memberExpr
->getBeginLoc())
986 << bPotentiallyWrittenTo
<< memberExpr
->getSourceRange();
989 report(DiagnosticsEngine::Note
, "parent over here", parent
->getBeginLoc())
990 << parent
->getSourceRange();
994 varDecl
->getType()->dump();
997 MyVarInfo varInfo
= niceName(varDecl
);
998 if (bPotentiallyWrittenTo
)
1000 writeToSet
.insert(varInfo
);
1004 // return true if this not a collection type, or if it is a collection type, and we might be writing to it
1005 bool WriteOnlyVars::checkForWriteWhenUsingCollectionType(const CXXMethodDecl
* calleeMethodDecl
)
1007 auto const tc
= loplugin::TypeCheck(calleeMethodDecl
->getParent());
1008 bool listLike
= false, setLike
= false, mapLike
= false, cssSequence
= false;
1009 if (tc
.Class("deque").StdNamespace() || tc
.Class("list").StdNamespace()
1010 || tc
.Class("queue").StdNamespace() || tc
.Class("vector").StdNamespace())
1014 else if (tc
.Class("set").StdNamespace() || tc
.Class("unordered_set").StdNamespace())
1018 else if (tc
.Class("map").StdNamespace() || tc
.Class("unordered_map").StdNamespace())
1022 else if (tc
.Class("Sequence")
1034 if (calleeMethodDecl
->isOverloadedOperator())
1036 auto oo
= calleeMethodDecl
->getOverloadedOperator();
1039 // This is operator[]. We only care about things that add elements to the collection.
1040 // if nothing modifies the size of the collection, then nothing useful
1047 auto name
= calleeMethodDecl
->getName();
1048 if (listLike
|| setLike
|| mapLike
)
1050 if (name
== "reserve" || name
== "shrink_to_fit" || name
== "clear" || name
== "erase"
1051 || name
== "pop_back" || name
== "pop_front" || name
== "front" || name
== "back"
1052 || name
== "data" || name
== "remove" || name
== "remove_if" || name
== "unique"
1053 || name
== "sort" || name
== "begin" || name
== "end" || name
== "rbegin"
1054 || name
== "rend" || name
== "at" || name
== "find" || name
== "equal_range"
1055 || name
== "lower_bound" || name
== "upper_bound")
1060 if (name
== "getArray" || name
== "begin" || name
== "end")
1067 bool WriteOnlyVars::IsPassedByNonConst(const VarDecl
* varDecl
, const Stmt
* child
,
1068 CallerWrapper callExpr
, CalleeWrapper calleeFunctionDecl
)
1070 unsigned len
= std::min(callExpr
.getNumArgs(), calleeFunctionDecl
.getNumParams());
1071 // if it's an array, passing it by value to a method typically means the
1072 // callee takes a pointer and can modify the array
1073 if (varDecl
->getType()->isConstantArrayType())
1075 for (unsigned i
= 0; i
< len
; ++i
)
1076 if (callExpr
.getArg(i
) == child
)
1077 if (loplugin::TypeCheck(calleeFunctionDecl
.getParamType(i
)).Pointer().NonConst())
1082 for (unsigned i
= 0; i
< len
; ++i
)
1083 if (callExpr
.getArg(i
) == child
)
1084 if (loplugin::TypeCheck(calleeFunctionDecl
.getParamType(i
))
1092 bool WriteOnlyVars::VisitDeclRefExpr(const DeclRefExpr
* declRefExpr
)
1094 const Decl
* decl
= declRefExpr
->getDecl();
1095 const VarDecl
* varDecl
= dyn_cast
<VarDecl
>(decl
);
1098 if (varDecl
->isImplicit() || isa
<ParmVarDecl
>(varDecl
))
1100 varDecl
= varDecl
->getCanonicalDecl();
1101 if (ignoreLocation(varDecl
))
1103 // ignore stuff that forms part of the stable URE interface
1104 if (isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(varDecl
->getLocation())))
1107 checkIfReadFrom(varDecl
, declRefExpr
);
1109 checkIfWrittenTo(varDecl
, declRefExpr
);
1114 compat::optional
<CalleeWrapper
> WriteOnlyVars::getCallee(CallExpr
const* callExpr
)
1116 FunctionDecl
const* functionDecl
= callExpr
->getDirectCallee();
1118 return CalleeWrapper(functionDecl
);
1120 // Extract the functionprototype from a type
1121 clang::Type
const* calleeType
= callExpr
->getCallee()->getType().getTypePtr();
1122 if (auto pointerType
= calleeType
->getUnqualifiedDesugaredType()->getAs
<clang::PointerType
>())
1124 if (auto prototype
= pointerType
->getPointeeType()
1125 ->getUnqualifiedDesugaredType()
1126 ->getAs
<FunctionProtoType
>())
1128 return CalleeWrapper(prototype
);
1132 return compat::optional
<CalleeWrapper
>();
1135 loplugin::Plugin::Registration
<WriteOnlyVars
> X("writeonlyvars", false);
1140 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */