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/.
12 #include "config_clang.h"
15 /** Look for OUString/OString being appended to inside a loop, where OUStringBuffer/OStringBuffer would be a better idea
19 class StringLoop
: public clang::RecursiveASTVisitor
<StringLoop
>, public loplugin::Plugin
22 explicit StringLoop(loplugin::InstantiationData
const& rData
)
28 bool TraverseForStmt(ForStmt
*);
29 bool TraverseCXXForRangeStmt(CXXForRangeStmt
*);
30 bool TraverseDoStmt(DoStmt
*);
31 bool TraverseWhileStmt(WhileStmt
*);
32 bool VisitVarDecl(VarDecl
const*);
33 bool VisitCallExpr(CallExpr
const*);
37 using VarDeclList
= std::vector
<VarDecl
const*>;
38 std::vector
<VarDeclList
> m_varsPerLoopLevel
;
41 void StringLoop::run()
43 // Various places are not worth changing, the code becomes too awkward
44 // Just exclude stuff as I go
45 StringRef
fn(handler
.getMainFileName());
46 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/bridges/"))
48 if (loplugin::isSamePathname(fn
, SRCDIR
"/cppuhelper/source/shlib.cxx"))
50 if (loplugin::isSamePathname(fn
, SRCDIR
"/registry/source/regimpl.cxx"))
52 if (loplugin::isSamePathname(fn
, SRCDIR
"/l10ntools/source/lngmerge.cxx"))
54 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/tools/qa/"))
56 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/jvmfwk/"))
58 if (loplugin::isSamePathname(fn
, SRCDIR
"/svl/source/passwordcontainer/passwordcontainer.cxx"))
60 if (loplugin::isSamePathname(fn
, SRCDIR
"/svl/source/numbers/zformat.cxx"))
62 if (loplugin::isSamePathname(fn
, SRCDIR
"/svl/source/numbers/zforscan.cxx"))
64 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/control/combobox.cxx"))
66 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/gdi/pdfwriter_impl.cxx"))
68 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/svtools/"))
70 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/idl/"))
72 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/framework/"))
74 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/basic/"))
76 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sfx2/"))
78 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/avmedia/"))
80 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/connectivity/"))
82 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/editeng/"))
84 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/svx/"))
86 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/basctl/"))
88 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/filter/"))
90 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/chart2/"))
92 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/cui/"))
94 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/dbaccess/"))
96 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/oox/"))
98 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/writerfilter/"))
100 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/desktop/"))
102 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/extensions/"))
104 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/dtrans/"))
106 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/i18npool/"))
108 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/embeddedobj/"))
110 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sd/"))
112 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/xmloff/"))
114 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/xmlhelp/"))
116 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/forms/"))
118 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sc/source/core/tool/address.cxx"))
120 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sc/source/core/tool/compiler.cxx"))
122 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sc/source/ui/docshell/impex.cxx"))
124 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sc/source/ui/miscdlgs/acredlin.cxx"))
126 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sc/source/ui/pagedlg/areasdlg.cxx"))
128 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sc/source/ui/view/gridwin2.cxx"))
130 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sc/source/filter/html/htmlpars.cxx"))
132 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/core/doc/doctxm.cxx"))
134 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/core/edit/edattr.cxx"))
136 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/core/layout/dbg_lay.cxx"))
138 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/filter/ascii/ascatr.cxx"))
140 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/filter/html/htmlforw.cxx"))
142 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/core/unocore/unosect.cxx"))
144 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/core/unocore/unochart.cxx"))
146 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/core/unocore/unoobj.cxx"))
148 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/filter/html/parcss1.cxx"))
150 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/filter/html/svxcss1.cxx"))
152 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/filter/html/swhtml.cxx"))
154 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/uibase/utlui/gloslst.cxx"))
156 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/uibase/utlui/content.cxx"))
158 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/uibase/docvw/edtwin.cxx"))
160 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/filter/ww8/ww8atr.cxx"))
162 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/filter/ww8/ww8scan.cxx"))
164 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/filter/ww8/ww8par5.cxx"))
166 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/ui/fldui/fldfunc.cxx"))
168 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/ui/misc/bookmark.cxx"))
170 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/ui/dbui/mmlayoutpage.cxx"))
172 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/ui/dbui/dbinsdlg.cxx"))
174 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/ui/dbui/mmresultdialogs.cxx"))
176 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/ui/index/cnttab.cxx"))
178 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/ucb/source/ucp/file/bc.cxx"))
181 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
184 bool StringLoop::TraverseForStmt(ForStmt
* stmt
)
187 m_varsPerLoopLevel
.push_back({});
188 auto const ret
= RecursiveASTVisitor::TraverseForStmt(stmt
);
189 m_varsPerLoopLevel
.pop_back();
194 bool StringLoop::TraverseCXXForRangeStmt(CXXForRangeStmt
* stmt
)
197 m_varsPerLoopLevel
.push_back({});
198 auto const ret
= RecursiveASTVisitor::TraverseCXXForRangeStmt(stmt
);
199 m_varsPerLoopLevel
.pop_back();
204 bool StringLoop::TraverseDoStmt(DoStmt
* stmt
)
207 m_varsPerLoopLevel
.push_back({});
208 auto const ret
= RecursiveASTVisitor::TraverseDoStmt(stmt
);
209 m_varsPerLoopLevel
.pop_back();
214 bool StringLoop::TraverseWhileStmt(WhileStmt
* stmt
)
217 m_varsPerLoopLevel
.push_back({});
218 auto const ret
= RecursiveASTVisitor::TraverseWhileStmt(stmt
);
219 m_varsPerLoopLevel
.pop_back();
224 bool StringLoop::VisitVarDecl(VarDecl
const* varDecl
)
226 if (ignoreLocation(varDecl
))
230 m_varsPerLoopLevel
.back().push_back(varDecl
);
234 bool StringLoop::VisitCallExpr(CallExpr
const* callExpr
)
236 if (ignoreLocation(callExpr
))
240 auto operatorCallExpr
= dyn_cast
<CXXOperatorCallExpr
>(callExpr
);
241 if (!operatorCallExpr
)
243 if (operatorCallExpr
->getOperator() != OO_PlusEqual
)
246 if (auto memberExpr
= dyn_cast
<MemberExpr
>(callExpr
->getArg(0)))
248 auto tc
= loplugin::TypeCheck(memberExpr
->getType());
249 if (!tc
.Class("OUString").Namespace("rtl").GlobalNamespace()
250 && !tc
.Class("OString").Namespace("rtl").GlobalNamespace())
252 auto fieldDecl
= dyn_cast
<FieldDecl
>(memberExpr
->getMemberDecl());
253 if (isInUnoIncludeFile(
254 compiler
.getSourceManager().getSpellingLoc(fieldDecl
->getLocation())))
256 if (ignoreLocation(compiler
.getSourceManager().getSpellingLoc(fieldDecl
->getLocation())))
258 report(DiagnosticsEngine::Warning
,
259 "appending to OUString in loop, rather use OUStringBuffer",
260 operatorCallExpr
->getBeginLoc())
261 << operatorCallExpr
->getSourceRange();
262 report(DiagnosticsEngine::Note
, "field here", fieldDecl
->getBeginLoc())
263 << fieldDecl
->getSourceRange();
265 else if (auto declRefExpr
= dyn_cast
<DeclRefExpr
>(callExpr
->getArg(0)))
267 if (auto varDecl
= dyn_cast
<VarDecl
>(declRefExpr
->getDecl()))
269 auto tc
= loplugin::TypeCheck(varDecl
->getType());
270 if (!tc
.Class("OUString").Namespace("rtl").GlobalNamespace()
271 && !tc
.Class("OString").Namespace("rtl").GlobalNamespace())
273 // if the var is at the same block scope as the +=, not interesting
274 auto vars
= m_varsPerLoopLevel
.back();
275 if (std::find(vars
.begin(), vars
.end(), varDecl
) != vars
.end())
277 report(DiagnosticsEngine::Warning
,
278 "appending to OUString in loop, rather use OUStringBuffer",
279 operatorCallExpr
->getBeginLoc())
280 << operatorCallExpr
->getSourceRange();
281 report(DiagnosticsEngine::Note
, "var here", varDecl
->getBeginLoc())
282 << varDecl
->getSourceRange();
288 loplugin::Plugin::Registration
<StringLoop
> X("stringloop", false);
292 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */