bump product version to 6.3.0.0.beta1
[LibreOffice.git] / compilerplugins / clang / stringloop.cxx
blobcb3ad327738fa0bc2882ea51b63950233ef0e903
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 #include "check.hxx"
11 #include "plugin.hxx"
12 #include <vector>
14 /** Look for OUString/OString being appended to inside a loop, where OUStringBuffer/OStringBuffer would be a better idea
16 namespace
18 class StringLoop : public clang::RecursiveASTVisitor<StringLoop>, public loplugin::Plugin
20 public:
21 explicit StringLoop(loplugin::InstantiationData const& rData)
22 : Plugin(rData)
26 void run() override;
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*);
34 private:
35 int m_insideLoop = 0;
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/"))
46 return;
47 if (loplugin::isSamePathname(fn, SRCDIR "/cppuhelper/source/shlib.cxx"))
48 return;
49 if (loplugin::isSamePathname(fn, SRCDIR "/registry/source/regimpl.cxx"))
50 return;
51 if (loplugin::isSamePathname(fn, SRCDIR "/l10ntools/source/lngmerge.cxx"))
52 return;
53 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/tools/qa/"))
54 return;
55 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/jvmfwk/"))
56 return;
57 if (loplugin::isSamePathname(fn, SRCDIR "/svl/source/passwordcontainer/passwordcontainer.cxx"))
58 return;
59 if (loplugin::isSamePathname(fn, SRCDIR "/svl/source/numbers/zformat.cxx"))
60 return;
61 if (loplugin::isSamePathname(fn, SRCDIR "/svl/source/numbers/zforscan.cxx"))
62 return;
63 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/control/combobox.cxx"))
64 return;
65 if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/gdi/pdfwriter_impl.cxx"))
66 return;
67 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/svtools/"))
68 return;
69 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/idl/"))
70 return;
71 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/framework/"))
72 return;
73 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/basic/"))
74 return;
75 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sfx2/"))
76 return;
77 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/avmedia/"))
78 return;
79 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/connectivity/"))
80 return;
81 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/editeng/"))
82 return;
83 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/svx/"))
84 return;
85 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/basctl/"))
86 return;
87 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/filter/"))
88 return;
89 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/chart2/"))
90 return;
91 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/cui/"))
92 return;
93 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/dbaccess/"))
94 return;
95 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/oox/"))
96 return;
97 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/writerfilter/"))
98 return;
99 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/desktop/"))
100 return;
101 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/extensions/"))
102 return;
103 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/dtrans/"))
104 return;
105 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/i18npool/"))
106 return;
107 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/embeddedobj/"))
108 return;
109 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sd/"))
110 return;
111 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/xmloff/"))
112 return;
113 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/xmlhelp/"))
114 return;
115 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/forms/"))
116 return;
117 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sc/source/core/tool/address.cxx"))
118 return;
119 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sc/source/core/tool/compiler.cxx"))
120 return;
121 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sc/source/ui/docshell/impex.cxx"))
122 return;
123 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sc/source/ui/miscdlgs/acredlin.cxx"))
124 return;
125 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sc/source/ui/pagedlg/areasdlg.cxx"))
126 return;
127 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sc/source/ui/view/gridwin2.cxx"))
128 return;
129 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sc/source/filter/html/htmlpars.cxx"))
130 return;
131 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/core/doc/doctxm.cxx"))
132 return;
133 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/core/edit/edattr.cxx"))
134 return;
135 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/core/layout/dbg_lay.cxx"))
136 return;
137 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/filter/ascii/ascatr.cxx"))
138 return;
139 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/filter/html/htmlforw.cxx"))
140 return;
141 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/core/unocore/unosect.cxx"))
142 return;
143 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/core/unocore/unochart.cxx"))
144 return;
145 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/core/unocore/unoobj.cxx"))
146 return;
147 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/filter/html/parcss1.cxx"))
148 return;
149 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/filter/html/svxcss1.cxx"))
150 return;
151 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/filter/html/swhtml.cxx"))
152 return;
153 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/uibase/utlui/gloslst.cxx"))
154 return;
155 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/uibase/utlui/content.cxx"))
156 return;
157 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/uibase/docvw/edtwin.cxx"))
158 return;
159 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/filter/ww8/ww8atr.cxx"))
160 return;
161 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/filter/ww8/ww8scan.cxx"))
162 return;
163 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/filter/ww8/ww8par5.cxx"))
164 return;
165 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/ui/fldui/fldfunc.cxx"))
166 return;
167 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/ui/misc/bookmark.cxx"))
168 return;
169 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/ui/dbui/mmlayoutpage.cxx"))
170 return;
171 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/ui/dbui/dbinsdlg.cxx"))
172 return;
173 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/ui/dbui/mmresultdialogs.cxx"))
174 return;
175 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/ui/index/cnttab.cxx"))
176 return;
177 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/ucb/source/ucp/file/bc.cxx"))
178 return;
180 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
183 bool StringLoop::TraverseForStmt(ForStmt* stmt)
185 ++m_insideLoop;
186 m_varsPerLoopLevel.push_back({});
187 auto const ret = RecursiveASTVisitor::TraverseForStmt(stmt);
188 m_varsPerLoopLevel.pop_back();
189 --m_insideLoop;
190 return ret;
193 bool StringLoop::TraverseCXXForRangeStmt(CXXForRangeStmt* stmt)
195 ++m_insideLoop;
196 m_varsPerLoopLevel.push_back({});
197 auto const ret = RecursiveASTVisitor::TraverseCXXForRangeStmt(stmt);
198 m_varsPerLoopLevel.pop_back();
199 --m_insideLoop;
200 return ret;
203 bool StringLoop::TraverseDoStmt(DoStmt* stmt)
205 ++m_insideLoop;
206 m_varsPerLoopLevel.push_back({});
207 auto const ret = RecursiveASTVisitor::TraverseDoStmt(stmt);
208 m_varsPerLoopLevel.pop_back();
209 --m_insideLoop;
210 return ret;
213 bool StringLoop::TraverseWhileStmt(WhileStmt* stmt)
215 ++m_insideLoop;
216 m_varsPerLoopLevel.push_back({});
217 auto const ret = RecursiveASTVisitor::TraverseWhileStmt(stmt);
218 m_varsPerLoopLevel.pop_back();
219 --m_insideLoop;
220 return ret;
223 bool StringLoop::VisitVarDecl(VarDecl const* varDecl)
225 if (ignoreLocation(varDecl))
226 return true;
227 if (!m_insideLoop)
228 return true;
229 m_varsPerLoopLevel.back().push_back(varDecl);
230 return true;
233 bool StringLoop::VisitCallExpr(CallExpr const* callExpr)
235 if (ignoreLocation(callExpr))
236 return true;
237 if (!m_insideLoop)
238 return true;
239 auto operatorCallExpr = dyn_cast<CXXOperatorCallExpr>(callExpr);
240 if (!operatorCallExpr)
241 return true;
242 if (operatorCallExpr->getOperator() != OO_PlusEqual)
243 return true;
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())
250 return true;
251 auto fieldDecl = dyn_cast<FieldDecl>(memberExpr->getMemberDecl());
252 if (isInUnoIncludeFile(
253 compiler.getSourceManager().getSpellingLoc(fieldDecl->getLocation())))
254 return true;
255 if (ignoreLocation(compiler.getSourceManager().getSpellingLoc(fieldDecl->getLocation())))
256 return true;
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())
271 return true;
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())
275 return true;
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();
284 return true;
287 loplugin::Plugin::Registration<StringLoop> X("stringloop", false);
289 } // namespace
291 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */