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/.
13 #include <unordered_map>
14 #include <unordered_set>
18 #include "clang/AST/CXXInheritance.h"
19 #include "clang/AST/StmtVisitor.h"
21 // Look for variables that either
22 // (a) could be statically initialised, without runtime code, and warn
23 // (b) variables that are statically declared, but require runtime initialisation, and warn
26 // static const OUString[] XXX { "xxx" };
27 // requires runtime initialisation, so should rather be declared as OUStringLiteral
29 // static int[] XXX { 1,2 };
30 // can be declared const since it does not require runtime initialisation.
34 class StaticVar
: public loplugin::FilteringPlugin
<StaticVar
>
37 explicit StaticVar(loplugin::InstantiationData
const& data
)
38 : FilteringPlugin(data
)
42 virtual void run() override
44 std::string
fn(handler
.getMainFileName());
45 loplugin::normalizeDotDotInFilePath(fn
);
48 // uses icu::UnicodeString
49 fn
== SRCDIR
"/l10ntools/source/xmlparse.cxx"
50 // contains mutable state
51 || fn
== SRCDIR
"/sal/osl/unx/signal.cxx"
52 || fn
== SRCDIR
"/sal/qa/rtl/digest/rtl_digest.cxx"
53 || fn
== SRCDIR
"/sal/qa/rtl/strings/test_oustring_endswith.cxx"
54 || fn
== SRCDIR
"/sal/qa/rtl/strings/test_oustring_convert.cxx"
55 || fn
== SRCDIR
"/svl/qa/unit/items/test_itempool.cxx"
56 // contains mutable state
57 || fn
== SRCDIR
"/vcl/unx/generic/dtrans/X11_selection.cxx"
58 || fn
== SRCDIR
"/sax/qa/cppunit/xmlimport.cxx"
59 || fn
== SRCDIR
"/pyuno/source/module/pyuno.cxx"
60 || fn
== SRCDIR
"/pyuno/source/module/pyuno_module.cxx"
61 || fn
== SRCDIR
"/pyuno/source/module/pyuno_struct.cxx"
62 // TODO for this one we need a static OUString
63 || fn
== SRCDIR
"/xmloff/source/core/xmltoken.cxx"
65 || fn
== SRCDIR
"/basic/source/runtime/stdobj.cxx"
66 // TODO this needs more extensive cleanup
67 || fn
== SRCDIR
"/connectivity/source/drivers/postgresql/pq_statics.cxx"
69 || fn
== SRCDIR
"/hwpfilter/source/hwpreader.cxx"
71 || fn
== SRCDIR
"/sw/source/filter/basflt/fltini.cxx"
73 || fn
== SRCDIR
"/sw/source/uibase/docvw/srcedtw.cxx"
75 || fn
== SRCDIR
"/forms/source/misc/limitedformats.cxx"
76 // aHTMLOptionTab is ordered by useful grouping, so let it sort at runtime
77 || fn
== SRCDIR
"/svtools/source/svhtml/htmlkywd.cxx"
78 // TODO sorting some of these tables will be a lot of work...
79 || fn
== SRCDIR
"/sw/source/filter/ww8/ww8par6.cxx"
80 // this only triggers on older versions of clang, not sure why
81 // in any case, it is actually about the array in vcl/inc/units.hrc, which we can't change
82 || fn
== SRCDIR
"/vcl/source/app/svdata.cxx"
83 // I tried doing this, but got very weird unit test failures, apparently sorting this table
84 // disturbs some code elsewhere
85 || fn
== SRCDIR
"/svx/source/unodraw/unoprov.cxx"
86 // aRTFTokenTab is ordered by useful grouping, so let it sort at runtime
87 || fn
== SRCDIR
"/svtools/source/svrtf/rtfkeywd.cxx")
89 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
92 bool VisitVarDecl(VarDecl
const*);
95 static bool containsNonLiteral(Expr
const* expr
)
97 expr
= expr
->IgnoreImplicit();
98 if (auto initList
= dyn_cast
<InitListExpr
>(expr
))
100 for (unsigned i
= 0; i
< initList
->getNumInits(); ++i
)
101 if (containsNonLiteral(initList
->getInit(i
)))
104 else if (auto constructExpr
= dyn_cast
<CXXConstructExpr
>(expr
))
106 for (Expr
const* arg
: constructExpr
->arguments())
107 if (containsNonLiteral(arg
))
110 else if (isa
<MemberExpr
>(expr
))
112 else if (auto declRefExpr
= dyn_cast
<DeclRefExpr
>(expr
))
114 auto varDecl
= dyn_cast_or_null
<VarDecl
>(declRefExpr
->getDecl());
115 return varDecl
&& varDecl
->isLocalVarDeclOrParm();
117 else if (isa
<CXXMemberCallExpr
>(expr
))
119 else if (auto castExpr
= dyn_cast
<CXXFunctionalCastExpr
>(expr
))
120 return containsNonLiteral(castExpr
->getSubExpr());
121 else if (auto unaryOp
= dyn_cast
<UnaryOperator
>(expr
))
122 return containsNonLiteral(unaryOp
->getSubExpr());
127 bool StaticVar::VisitVarDecl(VarDecl
const* varDecl
)
129 if (ignoreLocation(varDecl
))
131 if (!varDecl
->hasInit())
133 auto initList
= dyn_cast_or_null
<InitListExpr
>(varDecl
->getInit());
136 if (varDecl
->isExceptionVariable() || isa
<ParmVarDecl
>(varDecl
))
138 if (!varDecl
->getType()->isArrayType())
140 auto elementType
= varDecl
->getType()->getBaseElementTypeUnsafe();
141 if (!elementType
->isRecordType())
143 auto elementRecordDecl
144 = dyn_cast_or_null
<CXXRecordDecl
>(elementType
->getAs
<RecordType
>()->getDecl());
145 if (!elementRecordDecl
)
147 if (containsNonLiteral(initList
))
150 if (elementRecordDecl
->hasTrivialDestructor())
152 if (varDecl
->isLocalVarDecl())
154 if (varDecl
->getStorageDuration() == SD_Static
&& varDecl
->getType().isConstQualified())
159 if (varDecl
->getType().isConstQualified())
163 // TODO cannot figure out how to make the loplugin::TypeCheck stuff match this
164 // std::string typeName = varDecl->getType().getAsString();
165 // if (typeName == "std::va_list" || typeName == "va_list")
168 auto const tcElement
= loplugin::TypeCheck(elementRecordDecl
);
169 if (tcElement
.Struct("ContextID_Index_Pair").GlobalNamespace())
171 if (tcElement
.Class("SfxSlot").GlobalNamespace())
174 if (varDecl
->isLocalVarDecl())
175 report(DiagnosticsEngine::Warning
, "var should be static const, or whitelisted",
176 varDecl
->getLocation())
177 << varDecl
->getSourceRange();
179 report(DiagnosticsEngine::Warning
, "var should be const, or whitelisted",
180 varDecl
->getLocation())
181 << varDecl
->getSourceRange();
185 if (varDecl
->isLocalVarDecl())
187 if (varDecl
->getStorageDuration() != SD_Static
188 || !varDecl
->getType().isConstQualified())
193 if (!varDecl
->getType().isConstQualified())
197 if (varDecl
->isLocalVarDecl())
198 report(DiagnosticsEngine::Warning
, "static const var requires runtime initialization?",
199 varDecl
->getLocation())
200 << varDecl
->getSourceRange();
202 report(DiagnosticsEngine::Warning
, "static var requires runtime initialization?",
203 varDecl
->getLocation())
204 << varDecl
->getSourceRange();
209 loplugin::Plugin::Registration
<StaticVar
> X("staticvar", false);
212 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */