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/.
10 #ifndef LO_CLANG_SHARED_PLUGINS
16 #include <unordered_set>
18 /** Look for static O*String and O*String[], they can be more efficiently declared as:
20 static constexpr OUStringLiteral our_aLBEntryMap[] = {u" ", u", "};
21 static constexpr OUStringLiteral sName(u"name");
23 which is more efficient at startup time.
28 : public loplugin::FilteringPlugin
<StringStatic
>
32 explicit StringStatic(loplugin::InstantiationData
const& rData
):
33 FilteringPlugin(rData
) {}
36 bool preRun() override
;
37 void postRun() override
;
38 bool VisitVarDecl(VarDecl
const*);
39 bool VisitReturnStmt(ReturnStmt
const*);
40 bool VisitDeclRefExpr(DeclRefExpr
const*);
41 bool VisitMemberExpr(MemberExpr
const*);
44 std::unordered_set
<VarDecl
const *> potentialVars
;
45 std::unordered_set
<VarDecl
const *> excludeVars
;
48 void StringStatic::run()
51 if( TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl()))
55 bool StringStatic::preRun()
57 StringRef
fn(handler
.getMainFileName());
58 // passing around pointers to global OUString
59 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/filter/source/svg/"))
61 // has a mix of literals and refs to external OUStrings
62 if (loplugin::isSamePathname(fn
, SRCDIR
"/ucb/source/ucp/webdav-neon/ContentProperties.cxx"))
67 void StringStatic::postRun()
69 for (auto const & pVarDecl
: excludeVars
) {
70 potentialVars
.erase(pVarDecl
);
72 for (auto const & varDecl
: potentialVars
) {
73 report(DiagnosticsEngine::Warning
,
74 "rather declare this using OUStringLiteral/OStringLiteral/char[]",
75 varDecl
->getLocation())
76 << varDecl
->getSourceRange();
80 bool StringStatic::VisitVarDecl(VarDecl
const* varDecl
)
82 if (ignoreLocation(varDecl
))
84 QualType qt
= varDecl
->getType();
85 if (!varDecl
->hasGlobalStorage())
87 if (varDecl
->hasGlobalStorage() && !varDecl
->isStaticLocal()) {
88 //TODO: For a non-public static member variable from an included file, we could still
89 // examine it further if all its uses must be seen in that included file:
90 if (!compiler
.getSourceManager().isInMainFile(varDecl
->getLocation())) {
94 if (!varDecl
->isThisDeclarationADefinition()
95 || !qt
.isConstQualified())
97 if (qt
->isArrayType())
98 qt
= qt
->getAsArrayTypeUnsafe()->getElementType();
100 auto tc
= loplugin::TypeCheck(qt
);
101 if (!tc
.Class("OUString").Namespace("rtl").GlobalNamespace()
102 && !tc
.Class("OString").Namespace("rtl").GlobalNamespace())
104 if (varDecl
->hasInit())
106 Expr
const * expr
= varDecl
->getInit();
108 if (ExprWithCleanups
const * exprWithCleanups
= dyn_cast
<ExprWithCleanups
>(expr
)) {
109 expr
= exprWithCleanups
->getSubExpr();
111 else if (CastExpr
const * castExpr
= dyn_cast
<CastExpr
>(expr
)) {
112 expr
= castExpr
->getSubExpr();
114 else if (MaterializeTemporaryExpr
const * materializeExpr
= dyn_cast
<MaterializeTemporaryExpr
>(expr
)) {
115 expr
= compat::getSubExpr(materializeExpr
);
117 else if (CXXBindTemporaryExpr
const * bindExpr
= dyn_cast
<CXXBindTemporaryExpr
>(expr
)) {
118 expr
= bindExpr
->getSubExpr();
120 else if (CXXConstructExpr
const * constructExpr
= dyn_cast
<CXXConstructExpr
>(expr
)) {
121 if (constructExpr
->getNumArgs() == 0) {
124 expr
= constructExpr
->getArg(0);
125 } else if (isa
<CallExpr
>(expr
)) {
132 potentialVars
.insert(varDecl
);
137 bool StringStatic::VisitReturnStmt(ReturnStmt
const * returnStmt
)
139 if (ignoreLocation(returnStmt
)) {
142 if (!returnStmt
->getRetValue()) {
145 DeclRefExpr
const * declRef
= dyn_cast
<DeclRefExpr
>(returnStmt
->getRetValue());
149 VarDecl
const * varDecl
= dyn_cast
<VarDecl
>(declRef
->getDecl());
151 excludeVars
.insert(varDecl
);
156 bool StringStatic::VisitDeclRefExpr(DeclRefExpr
const * declRef
)
158 if (ignoreLocation(declRef
))
160 VarDecl
const * varDecl
= dyn_cast
<VarDecl
>(declRef
->getDecl());
163 if (potentialVars
.count(varDecl
) == 0)
165 // ignore globals that are used in CPPUNIT_ASSERT expressions, otherwise we can end up
166 // trying to compare an OUStringLiteral and an OUString, and CPPUNIT can't handle that
167 auto loc
= compat::getBeginLoc(declRef
);
168 if (compiler
.getSourceManager().isMacroArgExpansion(loc
))
170 StringRef name
{ Lexer::getImmediateMacroName(loc
, compiler
.getSourceManager(), compiler
.getLangOpts()) };
171 if (name
.startswith("CPPUNIT_ASSERT"))
172 excludeVars
.insert(varDecl
);
177 bool StringStatic::VisitMemberExpr(MemberExpr
const * expr
)
179 if (ignoreLocation(expr
))
181 auto const declRef
= dyn_cast
<DeclRefExpr
>(expr
->getBase());
182 if (declRef
== nullptr) {
185 VarDecl
const * varDecl
= dyn_cast
<VarDecl
>(declRef
->getDecl());
188 if (potentialVars
.count(varDecl
) == 0)
190 auto const id
= expr
->getMemberDecl()->getIdentifier();
191 if (id
== nullptr || id
->getName() != "pData") {
194 excludeVars
.insert(varDecl
);
198 loplugin::Plugin::Registration
<StringStatic
> stringstatic("stringstatic");
202 #endif // LO_CLANG_SHARED_PLUGINS
204 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */