lok: getSlideShowInfo: interactions: check that properties are available
[LibreOffice.git] / compilerplugins / clang / elidestringvar.cxx
blob097120463ea061e7d33f4898e1e4ac2423a0fa73
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>
15 #include <stack>
17 #include "check.hxx"
18 #include "compat.hxx"
19 #include "plugin.hxx"
21 // Find cases where a variable of a OString/OUString type is initialized
22 // with a literal value (incl. as an empty string) and used only once. Conservatively this only
23 // covers local non-static variables that are not defined outside of the loop (if any) in which they
24 // are used, as other cases may deliberately use the variable for performance (or even correctness,
25 // if addresses are taken and compared) reasons.
27 // For one, the historically heavy syntax for such uses of string literals
28 // (RTL_CONSTASCII_USTRINGPARAM etc.) probably explains many of these redundant variables, which can
29 // now be considered cargo-cult baggage. For another, some of those variables are used as arguments
30 // to functions which also have more efficient overloads directly taking string literals. And for
31 // yet another, some cases with default-initialized variables turned out to be effectively unused
32 // code that could be removed completely (d073cca5f7c04de3e1bcedda334d864e98ac7835 "Clean up dead
33 // code", 91345e7dde6100496a7c9e815b72b2821ae82bc2 "Clean up dead code",
34 // 868b0763ac47f765cb48c277897274a595b831d0 "Upcoming loplugin:elidestringvar: dbaccess" in
35 // dbaccess/source/ui/app/AppController.cxx, bde0aac4ccf7b830b5ef21d5b9e75e62aee6aaf9 "Clean up dead
36 // code", 354aefec42de856b4ab6201ada54a3a3c630b4bd "Upcoming loplugin:elidestringvar: cui" in
37 // cui/source/dialogs/SpellDialog.cxx).
39 namespace
41 bool isStringType(QualType type)
43 loplugin::TypeCheck const c(type);
44 return c.Class("OString").Namespace("rtl").GlobalNamespace()
45 || c.Class("OUString").Namespace("rtl").GlobalNamespace();
48 class ElideStringVar : public loplugin::FilteringPlugin<ElideStringVar>
50 public:
51 explicit ElideStringVar(loplugin::InstantiationData const& data)
52 : FilteringPlugin(data)
56 bool preRun() override { return compiler.getLangOpts().CPlusPlus; }
58 void postRun() override
60 for (auto const& var : vars_)
62 if (!var.second.singleUse || *var.second.singleUse == nullptr)
64 continue;
66 if (containsPreprocessingConditionalInclusion(
67 SourceRange(var.first->getBeginLoc(), (*var.second.singleUse)->getEndLoc())))
69 // This is not perfect, as additional uses can be hidden in conditional blocks that
70 // only start after the (would-be) single use (as was the case in
71 // 3bc5057f9689e024957cfa898a221ee2c4c4afe7 "Upcoming loplugin:elidestringvar:
72 // testtools" when built with --enable-debug, but where also fixing the hidden
73 // additional use was trivial). If this ever becomes a real problem, we can extend
74 // the above check to cover more of the current function body's remainder.
75 continue;
77 report(DiagnosticsEngine::Warning,
78 "replace single use of literal %0 variable with a literal",
79 (*var.second.singleUse)->getExprLoc())
80 << var.first->getType() << (*var.second.singleUse)->getSourceRange();
81 report(DiagnosticsEngine::Note, "literal %0 variable defined here",
82 var.first->getLocation())
83 << var.first->getType() << var.first->getSourceRange();
87 bool VisitVarDecl(VarDecl const* decl)
89 if (ignoreLocation(decl))
91 return true;
93 if (!decl->isThisDeclarationADefinition())
95 return true;
97 if (isa<ParmVarDecl>(decl))
99 return true;
101 if (decl->getStorageDuration() != SD_Automatic)
103 return true;
105 if (!isStringType(decl->getType()))
107 return true;
109 if (!decl->hasInit())
111 return true;
113 auto const e1 = dyn_cast<CXXConstructExpr>(decl->getInit()->IgnoreParenImpCasts());
114 if (e1 == nullptr)
116 return true;
118 if (!isStringType(e1->getType()))
120 return true;
122 switch (e1->getNumArgs())
124 case 0:
125 break;
126 case 1:
128 auto const e2 = e1->getArg(0);
129 loplugin::TypeCheck const c(e2->getType());
130 if (c.Class("OStringLiteral").Namespace("rtl").GlobalNamespace()
131 || c.Class("OUStringLiteral").Namespace("rtl").GlobalNamespace())
133 break;
135 if (!e2->isValueDependent() && e2->isIntegerConstantExpr(compiler.getASTContext()))
137 break;
139 return true;
141 case 2:
143 auto const e2 = e1->getArg(0);
144 auto const t = e2->getType();
145 if (!(t.isConstQualified() && t->isConstantArrayType()))
147 return true;
149 if (isa<AbstractConditionalOperator>(e2->IgnoreParenImpCasts()))
151 return true;
153 auto const e3 = e1->getArg(1);
154 if (!(isa<CXXDefaultArgExpr>(e3)
155 && loplugin::TypeCheck(e3->getType())
156 .Struct("Dummy")
157 .Namespace("libreoffice_internal")
158 .Namespace("rtl")
159 .GlobalNamespace()))
161 return true;
163 break;
165 default:
166 return true;
168 auto const ok = vars_.emplace(decl, Data(getInnermostLoop()));
169 assert(ok.second);
170 (void)ok;
171 return true;
174 bool VisitDeclRefExpr(DeclRefExpr const* expr)
176 if (ignoreLocation(expr))
178 return true;
180 auto const var = dyn_cast<VarDecl>(expr->getDecl());
181 if (var == nullptr)
183 return true;
185 auto const i = vars_.find(var);
186 if (i == vars_.end())
188 return true;
190 i->second.singleUse
191 = i->second.singleUse || i->second.innermostLoop != getInnermostLoop() ? nullptr : expr;
192 return true;
195 bool VisitMemberExpr(MemberExpr const* expr)
197 if (ignoreLocation(expr))
199 return true;
201 auto const e = dyn_cast<DeclRefExpr>(expr->getBase()->IgnoreParenImpCasts());
202 if (e == nullptr)
204 return true;
206 auto const var = dyn_cast<VarDecl>(e->getDecl());
207 if (var == nullptr)
209 return true;
211 auto const i = vars_.find(var);
212 if (i == vars_.end())
214 return true;
216 i->second.singleUse = nullptr;
217 return true;
220 bool VisitUnaryOperator(UnaryOperator const* expr)
222 if (ignoreLocation(expr))
224 return true;
226 if (expr->getOpcode() != UO_AddrOf)
228 return true;
230 auto const e = dyn_cast<DeclRefExpr>(expr->getSubExpr()->IgnoreParenImpCasts());
231 if (e == nullptr)
233 return true;
235 auto const var = dyn_cast<VarDecl>(e->getDecl());
236 if (var == nullptr)
238 return true;
240 auto const i = vars_.find(var);
241 if (i == vars_.end())
243 return true;
245 i->second.singleUse = nullptr;
246 return true;
249 bool VisitCallExpr(CallExpr const* expr)
251 if (ignoreLocation(expr))
253 return true;
255 auto const fun = expr->getDirectCallee();
256 if (fun == nullptr)
258 return true;
260 unsigned const n = std::min(fun->getNumParams(), expr->getNumArgs());
261 for (unsigned i = 0; i != n; ++i)
263 if (!loplugin::TypeCheck(fun->getParamDecl(i)->getType())
264 .LvalueReference()
265 .NonConstVolatile())
267 continue;
269 auto const e = dyn_cast<DeclRefExpr>(expr->getArg(i)->IgnoreParenImpCasts());
270 if (e == nullptr)
272 continue;
274 auto const var = dyn_cast<VarDecl>(e->getDecl());
275 if (var == nullptr)
277 continue;
279 auto const j = vars_.find(var);
280 if (j == vars_.end())
282 continue;
284 j->second.singleUse = nullptr;
286 return true;
289 bool VisitCXXConstructExpr(CXXConstructExpr const* expr)
291 if (ignoreLocation(expr))
293 return true;
295 auto const ctor = expr->getConstructor();
296 unsigned const n = std::min(ctor->getNumParams(), expr->getNumArgs());
297 for (unsigned i = 0; i != n; ++i)
299 if (!loplugin::TypeCheck(ctor->getParamDecl(i)->getType())
300 .LvalueReference()
301 .NonConstVolatile())
303 continue;
305 auto const e = dyn_cast<DeclRefExpr>(expr->getArg(i)->IgnoreParenImpCasts());
306 if (e == nullptr)
308 continue;
310 auto const var = dyn_cast<VarDecl>(e->getDecl());
311 if (var == nullptr)
313 continue;
315 auto const j = vars_.find(var);
316 if (j == vars_.end())
318 continue;
320 j->second.singleUse = nullptr;
322 return true;
325 bool TraverseWhileStmt(WhileStmt* stmt)
327 bool ret = true;
328 if (PreTraverseWhileStmt(stmt))
330 ret = FilteringPlugin::TraverseWhileStmt(stmt);
331 PostTraverseWhileStmt(stmt, ret);
333 return ret;
336 bool PreTraverseWhileStmt(WhileStmt* stmt)
338 innermostLoop_.push(stmt);
339 return true;
342 bool PostTraverseWhileStmt(WhileStmt* stmt, bool)
344 assert(!innermostLoop_.empty());
345 assert(innermostLoop_.top() == stmt);
346 (void)stmt;
347 innermostLoop_.pop();
348 return true;
351 bool TraverseDoStmt(DoStmt* stmt)
353 bool ret = true;
354 if (PreTraverseDoStmt(stmt))
356 ret = FilteringPlugin::TraverseDoStmt(stmt);
357 PostTraverseDoStmt(stmt, ret);
359 return ret;
362 bool PreTraverseDoStmt(DoStmt* stmt)
364 innermostLoop_.push(stmt);
365 return true;
368 bool PostTraverseDoStmt(DoStmt* stmt, bool)
370 assert(!innermostLoop_.empty());
371 assert(innermostLoop_.top() == stmt);
372 (void)stmt;
373 innermostLoop_.pop();
374 return true;
377 bool TraverseForStmt(ForStmt* stmt)
379 bool ret = true;
380 if (PreTraverseForStmt(stmt))
382 ret = FilteringPlugin::TraverseForStmt(stmt);
383 PostTraverseForStmt(stmt, ret);
385 return ret;
388 bool PreTraverseForStmt(ForStmt* stmt)
390 innermostLoop_.push(stmt);
391 return true;
394 bool PostTraverseForStmt(ForStmt* stmt, bool)
396 assert(!innermostLoop_.empty());
397 assert(innermostLoop_.top() == stmt);
398 (void)stmt;
399 innermostLoop_.pop();
400 return true;
403 bool TraverseCXXForRangeStmt(CXXForRangeStmt* stmt)
405 bool ret = true;
406 if (PreTraverseCXXForRangeStmt(stmt))
408 ret = FilteringPlugin::TraverseCXXForRangeStmt(stmt);
409 PostTraverseCXXForRangeStmt(stmt, ret);
411 return ret;
414 bool PreTraverseCXXForRangeStmt(CXXForRangeStmt* stmt)
416 innermostLoop_.push(stmt);
417 return true;
420 bool PostTraverseCXXForRangeStmt(CXXForRangeStmt* stmt, bool)
422 assert(!innermostLoop_.empty());
423 assert(innermostLoop_.top() == stmt);
424 (void)stmt;
425 innermostLoop_.pop();
426 return true;
429 private:
430 void run() override
432 if (preRun() && TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()))
434 postRun();
438 Stmt const* getInnermostLoop() const
440 return innermostLoop_.empty() ? nullptr : innermostLoop_.top();
443 struct Data
445 Data(Stmt const* theInnermostLoop)
446 : innermostLoop(theInnermostLoop)
449 Stmt const* innermostLoop;
450 compat::optional<Expr const*> singleUse;
453 std::stack<Stmt const*> innermostLoop_;
454 std::map<VarDecl const*, Data> vars_;
457 loplugin::Plugin::Registration<ElideStringVar> elidestringvar("elidestringvar");
460 #endif
462 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */