Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / compilerplugins / clang / stringstatic.cxx
bloba0b83c46523505025e768aed9799594c48e7dfe1
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 "check.hxx"
13 #include "plugin.hxx"
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.
26 namespace {
28 class StringStatic
29 : public loplugin::FilteringPlugin<StringStatic>
32 public:
33 explicit StringStatic(loplugin::InstantiationData const& rData):
34 FilteringPlugin(rData) {}
36 void run() override;
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*);
44 private:
45 std::unordered_set<VarDecl const *> potentialVars;
46 std::unordered_set<VarDecl const *> excludeVars;
49 void StringStatic::run()
51 if( preRun())
52 if( TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()))
53 postRun();
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/"))
61 return false;
62 return true;
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))
81 return true;
82 QualType qt = varDecl->getType();
83 if (!varDecl->hasGlobalStorage())
84 return true;
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())) {
89 return true;
92 if (!varDecl->isThisDeclarationADefinition()
93 || !qt.isConstQualified())
94 return true;
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())
101 return true;
102 if (varDecl->hasInit())
104 Expr const * expr = varDecl->getInit();
105 while (true) {
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) {
120 return true;
122 expr = constructExpr->getArg(0);
123 } else if (isa<CallExpr>(expr)) {
124 return true;
125 } else {
126 break;
130 potentialVars.insert(varDecl);
132 return true;
135 bool StringStatic::VisitReturnStmt(ReturnStmt const * returnStmt)
137 if (ignoreLocation(returnStmt)) {
138 return true;
140 if (!returnStmt->getRetValue()) {
141 return true;
143 DeclRefExpr const * declRef = dyn_cast<DeclRefExpr>(returnStmt->getRetValue());
144 if (!declRef) {
145 return true;
147 VarDecl const * varDecl = dyn_cast<VarDecl>(declRef->getDecl());
148 if (varDecl) {
149 excludeVars.insert(varDecl);
151 return true;
154 bool StringStatic::VisitDeclRefExpr(DeclRefExpr const * declRef)
156 if (ignoreLocation(declRef))
157 return true;
158 VarDecl const * varDecl = dyn_cast<VarDecl>(declRef->getDecl());
159 if (!varDecl)
160 return true;
161 if (potentialVars.count(varDecl) == 0)
162 return true;
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);
172 return true;
175 bool StringStatic::VisitMemberExpr(MemberExpr const * expr)
177 if (ignoreLocation(expr))
178 return true;
179 auto const declRef = dyn_cast<DeclRefExpr>(expr->getBase());
180 if (declRef == nullptr) {
181 return true;
183 VarDecl const * varDecl = dyn_cast<VarDecl>(declRef->getDecl());
184 if (!varDecl)
185 return true;
186 if (potentialVars.count(varDecl) == 0)
187 return true;
188 auto const id = expr->getMemberDecl()->getIdentifier();
189 if (id == nullptr || id->getName() != "pData") {
190 return true;
192 excludeVars.insert(varDecl);
193 return true;
196 loplugin::Plugin::Registration<StringStatic> stringstatic("stringstatic");
198 } // namespace
200 #endif // LO_CLANG_SHARED_PLUGINS
202 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */