Remove exec bits from docx
[LibreOffice.git] / compilerplugins / clang / elidestringvar.cxx
blobc14eaf027b0ffee3e28873039c9517d8ec154ad5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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 #ifndef LO_CLANG_SHARED_PLUGINS
12 #include <algorithm>
13 #include <cassert>
14 #include <map>
16 #include "check.hxx"
17 #include "compat.hxx"
18 #include "plugin.hxx"
20 // Find cases where a variable of a OString/OUString type is initialized
21 // with a literal value (incl. as an empty string) and used only once. Conservatively this only
22 // covers local non-static variables that are not defined outside of the loop (if any) in which they
23 // are used, as other cases may deliberately use the variable for performance (or even correctness,
24 // if addresses are taken and compared) reasons.
26 // For one, the historically heavy syntax for such uses of string literals
27 // (RTL_CONSTASCII_USTRINGPARAM etc.) probably explains many of these redundant variables, which can
28 // now be considered cargo-cult baggage. For another, some of those variables are used as arguments
29 // to functions which also have more efficient overloads directly taking string literals. And for
30 // yet another, some cases with default-initialized variables turned out to be effectively unused
31 // code that could be removed completely (d073cca5f7c04de3e1bcedda334d864e98ac7835 "Clean up dead
32 // code", 91345e7dde6100496a7c9e815b72b2821ae82bc2 "Clean up dead code",
33 // 868b0763ac47f765cb48c277897274a595b831d0 "Upcoming loplugin:elidestringvar: dbaccess" in
34 // dbaccess/source/ui/app/AppController.cxx, bde0aac4ccf7b830b5ef21d5b9e75e62aee6aaf9 "Clean up dead
35 // code", 354aefec42de856b4ab6201ada54a3a3c630b4bd "Upcoming loplugin:elidestringvar: cui" in
36 // cui/source/dialogs/SpellDialog.cxx).
38 namespace
40 bool isStringType(QualType type)
42 loplugin::TypeCheck const c(type);
43 return c.Class("OString").Namespace("rtl").GlobalNamespace()
44 || c.Class("OUString").Namespace("rtl").GlobalNamespace();
47 class ElideStringVar : public loplugin::FilteringPlugin<ElideStringVar>
49 public:
50 explicit ElideStringVar(loplugin::InstantiationData const& data)
51 : FilteringPlugin(data)
55 bool preRun() override { return compiler.getLangOpts().CPlusPlus; }
57 void postRun() override
59 for (auto const& var : vars_)
61 if (!var.second.singleUse || *var.second.singleUse == nullptr)
63 continue;
65 if (containsPreprocessingConditionalInclusion(
66 SourceRange(var.first->getBeginLoc(), (*var.second.singleUse)->getEndLoc())))
68 // This is not perfect, as additional uses can be hidden in conditional blocks that
69 // only start after the (would-be) single use (as was the case in
70 // 3bc5057f9689e024957cfa898a221ee2c4c4afe7 "Upcoming loplugin:elidestringvar:
71 // testtools" when built with --enable-debug, but where also fixing the hidden
72 // additional use was trivial). If this ever becomes a real problem, we can extend
73 // the above check to cover more of the current function body's remainder.
74 continue;
76 report(DiagnosticsEngine::Warning,
77 "replace single use of literal %0 variable with a literal",
78 (*var.second.singleUse)->getExprLoc())
79 << var.first->getType() << (*var.second.singleUse)->getSourceRange();
80 report(DiagnosticsEngine::Note, "literal %0 variable defined here",
81 var.first->getLocation())
82 << var.first->getType() << var.first->getSourceRange();
86 bool VisitVarDecl(VarDecl const* decl)
88 if (ignoreLocation(decl))
90 return true;
92 if (!decl->isThisDeclarationADefinition())
94 return true;
96 if (isa<ParmVarDecl>(decl))
98 return true;
100 if (decl->getStorageDuration() != SD_Automatic)
102 return true;
104 if (!isStringType(decl->getType()))
106 return true;
108 if (!decl->hasInit())
110 return true;
112 auto const e1 = dyn_cast<CXXConstructExpr>(decl->getInit()->IgnoreParenImpCasts());
113 if (e1 == nullptr)
115 return true;
117 if (!isStringType(e1->getType()))
119 return true;
121 switch (e1->getNumArgs())
123 case 0:
124 break;
125 case 1:
127 auto const e2 = e1->getArg(0);
128 loplugin::TypeCheck const c(e2->getType());
129 if (c.Class("OStringLiteral").Namespace("rtl").GlobalNamespace()
130 || c.Class("OUStringLiteral").Namespace("rtl").GlobalNamespace())
132 break;
134 if (!e2->isValueDependent() && e2->isIntegerConstantExpr(compiler.getASTContext()))
136 break;
138 return true;
140 case 2:
142 auto const e2 = e1->getArg(0);
143 auto const t = e2->getType();
144 if (!(t.isConstQualified() && t->isConstantArrayType()))
146 return true;
148 if (isa<AbstractConditionalOperator>(e2->IgnoreParenImpCasts()))
150 return true;
152 auto const e3 = e1->getArg(1);
153 if (!(isa<CXXDefaultArgExpr>(e3)
154 && loplugin::TypeCheck(e3->getType())
155 .Struct("Dummy")
156 .Namespace("libreoffice_internal")
157 .Namespace("rtl")
158 .GlobalNamespace()))
160 return true;
162 break;
164 default:
165 return true;
167 auto const ok = vars_.emplace(decl, Data(getInnermostLoop()));
168 assert(ok.second);
169 (void)ok;
170 return true;
173 bool VisitDeclRefExpr(DeclRefExpr const* expr)
175 if (ignoreLocation(expr))
177 return true;
179 auto const var = dyn_cast<VarDecl>(expr->getDecl());
180 if (var == nullptr)
182 return true;
184 auto const i = vars_.find(var);
185 if (i == vars_.end())
187 return true;
189 i->second.singleUse
190 = i->second.singleUse || i->second.innermostLoop != getInnermostLoop() ? nullptr : expr;
191 return true;
194 bool VisitMemberExpr(MemberExpr const* expr)
196 if (ignoreLocation(expr))
198 return true;
200 auto const e = dyn_cast<DeclRefExpr>(expr->getBase()->IgnoreParenImpCasts());
201 if (e == nullptr)
203 return true;
205 auto const var = dyn_cast<VarDecl>(e->getDecl());
206 if (var == nullptr)
208 return true;
210 auto const i = vars_.find(var);
211 if (i == vars_.end())
213 return true;
215 i->second.singleUse = nullptr;
216 return true;
219 bool VisitUnaryOperator(UnaryOperator const* expr)
221 if (ignoreLocation(expr))
223 return true;
225 if (expr->getOpcode() != UO_AddrOf)
227 return true;
229 auto const e = dyn_cast<DeclRefExpr>(expr->getSubExpr()->IgnoreParenImpCasts());
230 if (e == nullptr)
232 return true;
234 auto const var = dyn_cast<VarDecl>(e->getDecl());
235 if (var == nullptr)
237 return true;
239 auto const i = vars_.find(var);
240 if (i == vars_.end())
242 return true;
244 i->second.singleUse = nullptr;
245 return true;
248 bool VisitCallExpr(CallExpr const* expr)
250 if (ignoreLocation(expr))
252 return true;
254 auto const fun = expr->getDirectCallee();
255 if (fun == nullptr)
257 return true;
259 unsigned const n = std::min(fun->getNumParams(), expr->getNumArgs());
260 for (unsigned i = 0; i != n; ++i)
262 if (!loplugin::TypeCheck(fun->getParamDecl(i)->getType())
263 .LvalueReference()
264 .NonConstVolatile())
266 continue;
268 auto const e = dyn_cast<DeclRefExpr>(expr->getArg(i)->IgnoreParenImpCasts());
269 if (e == nullptr)
271 continue;
273 auto const var = dyn_cast<VarDecl>(e->getDecl());
274 if (var == nullptr)
276 continue;
278 auto const j = vars_.find(var);
279 if (j == vars_.end())
281 continue;
283 j->second.singleUse = nullptr;
285 return true;
288 bool VisitCXXConstructExpr(CXXConstructExpr const* expr)
290 if (ignoreLocation(expr))
292 return true;
294 auto const ctor = expr->getConstructor();
295 unsigned const n = std::min(ctor->getNumParams(), expr->getNumArgs());
296 for (unsigned i = 0; i != n; ++i)
298 if (!loplugin::TypeCheck(ctor->getParamDecl(i)->getType())
299 .LvalueReference()
300 .NonConstVolatile())
302 continue;
304 auto const e = dyn_cast<DeclRefExpr>(expr->getArg(i)->IgnoreParenImpCasts());
305 if (e == nullptr)
307 continue;
309 auto const var = dyn_cast<VarDecl>(e->getDecl());
310 if (var == nullptr)
312 continue;
314 auto const j = vars_.find(var);
315 if (j == vars_.end())
317 continue;
319 j->second.singleUse = nullptr;
321 return true;
324 bool TraverseWhileStmt(WhileStmt* stmt)
326 bool ret = true;
327 if (PreTraverseWhileStmt(stmt))
329 ret = FilteringPlugin::TraverseWhileStmt(stmt);
330 PostTraverseWhileStmt(stmt, ret);
332 return ret;
335 bool PreTraverseWhileStmt(WhileStmt* stmt)
337 innermostLoop_.push(stmt);
338 return true;
341 bool PostTraverseWhileStmt(WhileStmt* stmt, bool)
343 assert(!innermostLoop_.empty());
344 assert(innermostLoop_.top() == stmt);
345 (void)stmt;
346 innermostLoop_.pop();
347 return true;
350 bool TraverseDoStmt(DoStmt* stmt)
352 bool ret = true;
353 if (PreTraverseDoStmt(stmt))
355 ret = FilteringPlugin::TraverseDoStmt(stmt);
356 PostTraverseDoStmt(stmt, ret);
358 return ret;
361 bool PreTraverseDoStmt(DoStmt* stmt)
363 innermostLoop_.push(stmt);
364 return true;
367 bool PostTraverseDoStmt(DoStmt* stmt, bool)
369 assert(!innermostLoop_.empty());
370 assert(innermostLoop_.top() == stmt);
371 (void)stmt;
372 innermostLoop_.pop();
373 return true;
376 bool TraverseForStmt(ForStmt* stmt)
378 bool ret = true;
379 if (PreTraverseForStmt(stmt))
381 ret = FilteringPlugin::TraverseForStmt(stmt);
382 PostTraverseForStmt(stmt, ret);
384 return ret;
387 bool PreTraverseForStmt(ForStmt* stmt)
389 innermostLoop_.push(stmt);
390 return true;
393 bool PostTraverseForStmt(ForStmt* stmt, bool)
395 assert(!innermostLoop_.empty());
396 assert(innermostLoop_.top() == stmt);
397 (void)stmt;
398 innermostLoop_.pop();
399 return true;
402 bool TraverseCXXForRangeStmt(CXXForRangeStmt* stmt)
404 bool ret = true;
405 if (PreTraverseCXXForRangeStmt(stmt))
407 ret = FilteringPlugin::TraverseCXXForRangeStmt(stmt);
408 PostTraverseCXXForRangeStmt(stmt, ret);
410 return ret;
413 bool PreTraverseCXXForRangeStmt(CXXForRangeStmt* stmt)
415 innermostLoop_.push(stmt);
416 return true;
419 bool PostTraverseCXXForRangeStmt(CXXForRangeStmt* stmt, bool)
421 assert(!innermostLoop_.empty());
422 assert(innermostLoop_.top() == stmt);
423 (void)stmt;
424 innermostLoop_.pop();
425 return true;
428 private:
429 void run() override
431 if (preRun() && TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()))
433 postRun();
437 Stmt const* getInnermostLoop() const
439 return innermostLoop_.empty() ? nullptr : innermostLoop_.top();
442 struct Data
444 Data(Stmt const* theInnermostLoop)
445 : innermostLoop(theInnermostLoop)
448 Stmt const* innermostLoop;
449 compat::optional<Expr const*> singleUse;
452 std::stack<Stmt const*> innermostLoop_;
453 std::map<VarDecl const*, Data> vars_;
456 loplugin::Plugin::Registration<ElideStringVar> elidestringvar("elidestringvar");
459 #endif
461 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */