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
15 #include "config_clang.h"
17 #include <unordered_set>
19 /** Look for static O*String and O*String[], they can be more efficiently declared as:
21 static constexpr OUStringLiteral our_aLBEntryMap[] = {u" ", u", "};
22 static constexpr OUStringLiteral sName(u"name");
24 which is more efficient at startup time.
29 : public loplugin::FilteringPlugin
<StringStatic
>
33 explicit StringStatic(loplugin::InstantiationData
const& rData
):
34 FilteringPlugin(rData
) {}
37 bool preRun() override
;
38 void postRun() override
;
39 bool VisitVarDecl(VarDecl
const*);
40 bool VisitReturnStmt(ReturnStmt
const*);
41 bool VisitDeclRefExpr(DeclRefExpr
const*);
42 bool VisitMemberExpr(MemberExpr
const*);
45 std::unordered_set
<VarDecl
const *> potentialVars
;
46 std::unordered_set
<VarDecl
const *> excludeVars
;
49 void StringStatic::run()
52 if( TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl()))
56 bool StringStatic::preRun()
58 StringRef
fn(handler
.getMainFileName());
59 // passing around pointers to global OUString
60 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/filter/source/svg/"))
65 void StringStatic::postRun()
67 for (auto const & pVarDecl
: excludeVars
) {
68 potentialVars
.erase(pVarDecl
);
70 for (auto const & varDecl
: potentialVars
) {
71 report(DiagnosticsEngine::Warning
,
72 "rather declare this using OUStringLiteral/OStringLiteral/char[]",
73 varDecl
->getLocation())
74 << varDecl
->getSourceRange();
78 bool StringStatic::VisitVarDecl(VarDecl
const* varDecl
)
80 if (ignoreLocation(varDecl
))
82 QualType qt
= varDecl
->getType();
83 if (!varDecl
->hasGlobalStorage())
85 if (varDecl
->hasGlobalStorage() && !varDecl
->isStaticLocal()) {
86 //TODO: For a non-public static member variable from an included file, we could still
87 // examine it further if all its uses must be seen in that included file:
88 if (!compiler
.getSourceManager().isInMainFile(varDecl
->getLocation())) {
92 if (!varDecl
->isThisDeclarationADefinition()
93 || !qt
.isConstQualified())
95 if (qt
->isArrayType())
96 qt
= qt
->getAsArrayTypeUnsafe()->getElementType();
98 auto tc
= loplugin::TypeCheck(qt
);
99 if (!tc
.Class("OUString").Namespace("rtl").GlobalNamespace()
100 && !tc
.Class("OString").Namespace("rtl").GlobalNamespace())
102 if (varDecl
->hasInit())
104 Expr
const * expr
= varDecl
->getInit();
106 if (ExprWithCleanups
const * exprWithCleanups
= dyn_cast
<ExprWithCleanups
>(expr
)) {
107 expr
= exprWithCleanups
->getSubExpr();
109 else if (CastExpr
const * castExpr
= dyn_cast
<CastExpr
>(expr
)) {
110 expr
= castExpr
->getSubExpr();
112 else if (MaterializeTemporaryExpr
const * materializeExpr
= dyn_cast
<MaterializeTemporaryExpr
>(expr
)) {
113 expr
= materializeExpr
->getSubExpr();
115 else if (CXXBindTemporaryExpr
const * bindExpr
= dyn_cast
<CXXBindTemporaryExpr
>(expr
)) {
116 expr
= bindExpr
->getSubExpr();
118 else if (CXXConstructExpr
const * constructExpr
= dyn_cast
<CXXConstructExpr
>(expr
)) {
119 if (constructExpr
->getNumArgs() == 0) {
122 expr
= constructExpr
->getArg(0);
123 } else if (isa
<CallExpr
>(expr
)) {
130 potentialVars
.insert(varDecl
);
135 bool StringStatic::VisitReturnStmt(ReturnStmt
const * returnStmt
)
137 if (ignoreLocation(returnStmt
)) {
140 if (!returnStmt
->getRetValue()) {
143 DeclRefExpr
const * declRef
= dyn_cast
<DeclRefExpr
>(returnStmt
->getRetValue());
147 VarDecl
const * varDecl
= dyn_cast
<VarDecl
>(declRef
->getDecl());
149 excludeVars
.insert(varDecl
);
154 bool StringStatic::VisitDeclRefExpr(DeclRefExpr
const * declRef
)
156 if (ignoreLocation(declRef
))
158 VarDecl
const * varDecl
= dyn_cast
<VarDecl
>(declRef
->getDecl());
161 if (potentialVars
.count(varDecl
) == 0)
163 // ignore globals that are used in CPPUNIT_ASSERT expressions, otherwise we can end up
164 // trying to compare an OUStringLiteral and an OUString, and CPPUNIT can't handle that
165 auto loc
= declRef
->getBeginLoc();
166 if (compiler
.getSourceManager().isMacroArgExpansion(loc
))
168 StringRef name
{ Lexer::getImmediateMacroName(loc
, compiler
.getSourceManager(), compiler
.getLangOpts()) };
169 if (name
.startswith("CPPUNIT_ASSERT"))
170 excludeVars
.insert(varDecl
);
175 bool StringStatic::VisitMemberExpr(MemberExpr
const * expr
)
177 if (ignoreLocation(expr
))
179 auto const declRef
= dyn_cast
<DeclRefExpr
>(expr
->getBase());
180 if (declRef
== nullptr) {
183 VarDecl
const * varDecl
= dyn_cast
<VarDecl
>(declRef
->getDecl());
186 if (potentialVars
.count(varDecl
) == 0)
188 auto const id
= expr
->getMemberDecl()->getIdentifier();
189 if (id
== nullptr || id
->getName() != "pData") {
192 excludeVars
.insert(varDecl
);
196 loplugin::Plugin::Registration
<StringStatic
> stringstatic("stringstatic");
200 #endif // LO_CLANG_SHARED_PLUGINS
202 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */