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/.
14 /** Look for OUString/OString being appended to inside a loop, where OUStringBuffer/OStringBuffer would be a better idea
18 class StringLoop
: public clang::RecursiveASTVisitor
<StringLoop
>, public loplugin::Plugin
21 explicit StringLoop(loplugin::InstantiationData
const& rData
)
27 bool TraverseForStmt(ForStmt
*);
28 bool TraverseCXXForRangeStmt(CXXForRangeStmt
*);
29 bool TraverseDoStmt(DoStmt
*);
30 bool TraverseWhileStmt(WhileStmt
*);
31 bool VisitVarDecl(VarDecl
const*);
32 bool VisitCallExpr(CallExpr
const*);
36 using VarDeclList
= std::vector
<VarDecl
const*>;
37 std::vector
<VarDeclList
> m_varsPerLoopLevel
;
40 void StringLoop::run()
42 // Various places are not worth changing, the code becomes too awkward
43 // Just exclude stuff as I go
44 StringRef
fn(handler
.getMainFileName());
45 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/bridges/"))
47 if (loplugin::isSamePathname(fn
, SRCDIR
"/cppuhelper/source/shlib.cxx"))
49 if (loplugin::isSamePathname(fn
, SRCDIR
"/registry/source/regimpl.cxx"))
51 if (loplugin::isSamePathname(fn
, SRCDIR
"/l10ntools/source/lngmerge.cxx"))
53 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/tools/qa/"))
55 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/jvmfwk/"))
57 if (loplugin::isSamePathname(fn
, SRCDIR
"/svl/source/passwordcontainer/passwordcontainer.cxx"))
59 if (loplugin::isSamePathname(fn
, SRCDIR
"/svl/source/numbers/zformat.cxx"))
61 if (loplugin::isSamePathname(fn
, SRCDIR
"/svl/source/numbers/zforscan.cxx"))
63 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/control/combobox.cxx"))
65 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/gdi/pdfwriter_impl.cxx"))
67 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/svtools/"))
69 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/idl/"))
71 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/framework/"))
73 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/basic/"))
75 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sfx2/"))
77 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/avmedia/"))
79 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/connectivity/"))
81 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/editeng/"))
83 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/svx/"))
85 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/basctl/"))
87 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/filter/"))
89 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/chart2/"))
91 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/cui/"))
93 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/dbaccess/"))
95 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/oox/"))
97 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/writerfilter/"))
99 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/desktop/"))
101 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/extensions/"))
103 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/dtrans/"))
105 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/i18npool/"))
107 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/embeddedobj/"))
109 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sd/"))
111 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/xmloff/"))
113 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/xmlhelp/"))
115 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/forms/"))
117 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sc/source/core/tool/address.cxx"))
119 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sc/source/core/tool/compiler.cxx"))
121 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sc/source/ui/docshell/impex.cxx"))
123 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sc/source/ui/miscdlgs/acredlin.cxx"))
125 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sc/source/ui/pagedlg/areasdlg.cxx"))
127 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sc/source/ui/view/gridwin2.cxx"))
129 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sc/source/filter/html/htmlpars.cxx"))
131 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/core/doc/doctxm.cxx"))
133 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/core/edit/edattr.cxx"))
135 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/core/layout/dbg_lay.cxx"))
137 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/filter/ascii/ascatr.cxx"))
139 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/filter/html/htmlforw.cxx"))
141 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/core/unocore/unosect.cxx"))
143 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/core/unocore/unochart.cxx"))
145 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/core/unocore/unoobj.cxx"))
147 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/filter/html/parcss1.cxx"))
149 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/filter/html/svxcss1.cxx"))
151 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/filter/html/swhtml.cxx"))
153 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/uibase/utlui/gloslst.cxx"))
155 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/uibase/utlui/content.cxx"))
157 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/uibase/docvw/edtwin.cxx"))
159 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/filter/ww8/ww8atr.cxx"))
161 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/filter/ww8/ww8scan.cxx"))
163 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/filter/ww8/ww8par5.cxx"))
165 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/ui/fldui/fldfunc.cxx"))
167 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/ui/misc/bookmark.cxx"))
169 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/ui/dbui/mmlayoutpage.cxx"))
171 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/ui/dbui/dbinsdlg.cxx"))
173 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/ui/dbui/mmresultdialogs.cxx"))
175 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/sw/source/ui/index/cnttab.cxx"))
177 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/ucb/source/ucp/file/bc.cxx"))
180 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
183 bool StringLoop::TraverseForStmt(ForStmt
* stmt
)
186 m_varsPerLoopLevel
.push_back({});
187 auto const ret
= RecursiveASTVisitor::TraverseForStmt(stmt
);
188 m_varsPerLoopLevel
.pop_back();
193 bool StringLoop::TraverseCXXForRangeStmt(CXXForRangeStmt
* stmt
)
196 m_varsPerLoopLevel
.push_back({});
197 auto const ret
= RecursiveASTVisitor::TraverseCXXForRangeStmt(stmt
);
198 m_varsPerLoopLevel
.pop_back();
203 bool StringLoop::TraverseDoStmt(DoStmt
* stmt
)
206 m_varsPerLoopLevel
.push_back({});
207 auto const ret
= RecursiveASTVisitor::TraverseDoStmt(stmt
);
208 m_varsPerLoopLevel
.pop_back();
213 bool StringLoop::TraverseWhileStmt(WhileStmt
* stmt
)
216 m_varsPerLoopLevel
.push_back({});
217 auto const ret
= RecursiveASTVisitor::TraverseWhileStmt(stmt
);
218 m_varsPerLoopLevel
.pop_back();
223 bool StringLoop::VisitVarDecl(VarDecl
const* varDecl
)
225 if (ignoreLocation(varDecl
))
229 m_varsPerLoopLevel
.back().push_back(varDecl
);
233 bool StringLoop::VisitCallExpr(CallExpr
const* callExpr
)
235 if (ignoreLocation(callExpr
))
239 auto operatorCallExpr
= dyn_cast
<CXXOperatorCallExpr
>(callExpr
);
240 if (!operatorCallExpr
)
242 if (operatorCallExpr
->getOperator() != OO_PlusEqual
)
245 if (auto memberExpr
= dyn_cast
<MemberExpr
>(callExpr
->getArg(0)))
247 auto tc
= loplugin::TypeCheck(memberExpr
->getType());
248 if (!tc
.Class("OUString").Namespace("rtl").GlobalNamespace()
249 && !tc
.Class("OString").Namespace("rtl").GlobalNamespace())
251 auto fieldDecl
= dyn_cast
<FieldDecl
>(memberExpr
->getMemberDecl());
252 if (isInUnoIncludeFile(
253 compiler
.getSourceManager().getSpellingLoc(fieldDecl
->getLocation())))
255 if (ignoreLocation(compiler
.getSourceManager().getSpellingLoc(fieldDecl
->getLocation())))
257 report(DiagnosticsEngine::Warning
,
258 "appending to OUString in loop, rather use OUStringBuffer",
259 compat::getBeginLoc(operatorCallExpr
))
260 << operatorCallExpr
->getSourceRange();
261 report(DiagnosticsEngine::Note
, "field here", compat::getBeginLoc(fieldDecl
))
262 << fieldDecl
->getSourceRange();
264 else if (auto declRefExpr
= dyn_cast
<DeclRefExpr
>(callExpr
->getArg(0)))
266 if (auto varDecl
= dyn_cast
<VarDecl
>(declRefExpr
->getDecl()))
268 auto tc
= loplugin::TypeCheck(varDecl
->getType());
269 if (!tc
.Class("OUString").Namespace("rtl").GlobalNamespace()
270 && !tc
.Class("OString").Namespace("rtl").GlobalNamespace())
272 // if the var is at the same block scope as the +=, not interesting
273 auto vars
= m_varsPerLoopLevel
.back();
274 if (std::find(vars
.begin(), vars
.end(), varDecl
) != vars
.end())
276 report(DiagnosticsEngine::Warning
,
277 "appending to OUString in loop, rather use OUStringBuffer",
278 compat::getBeginLoc(operatorCallExpr
))
279 << operatorCallExpr
->getSourceRange();
280 report(DiagnosticsEngine::Note
, "var here", compat::getBeginLoc(varDecl
))
281 << varDecl
->getSourceRange();
287 loplugin::Plugin::Registration
<StringLoop
> X("stringloop", false);
291 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */