LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / compilerplugins / clang / stringstatic.cxx
blob364bc566aa97cdc929ff38b5cf8d5685ad75c1d2
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 "compat.hxx"
14 #include "plugin.hxx"
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.
25 namespace {
27 class StringStatic
28 : public loplugin::FilteringPlugin<StringStatic>
31 public:
32 explicit StringStatic(loplugin::InstantiationData const& rData):
33 FilteringPlugin(rData) {}
35 void run() override;
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*);
43 private:
44 std::unordered_set<VarDecl const *> potentialVars;
45 std::unordered_set<VarDecl const *> excludeVars;
48 void StringStatic::run()
50 if( preRun())
51 if( TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()))
52 postRun();
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/"))
60 return false;
61 // has a mix of literals and refs to external OUStrings
62 if (loplugin::isSamePathname(fn, SRCDIR "/ucb/source/ucp/webdav-neon/ContentProperties.cxx"))
63 return false;
64 return true;
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))
83 return true;
84 QualType qt = varDecl->getType();
85 if (!varDecl->hasGlobalStorage())
86 return true;
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())) {
91 return true;
94 if (!varDecl->isThisDeclarationADefinition()
95 || !qt.isConstQualified())
96 return true;
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())
103 return true;
104 if (varDecl->hasInit())
106 Expr const * expr = varDecl->getInit();
107 while (true) {
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) {
122 return true;
124 expr = constructExpr->getArg(0);
125 } else if (isa<CallExpr>(expr)) {
126 return true;
127 } else {
128 break;
132 potentialVars.insert(varDecl);
134 return true;
137 bool StringStatic::VisitReturnStmt(ReturnStmt const * returnStmt)
139 if (ignoreLocation(returnStmt)) {
140 return true;
142 if (!returnStmt->getRetValue()) {
143 return true;
145 DeclRefExpr const * declRef = dyn_cast<DeclRefExpr>(returnStmt->getRetValue());
146 if (!declRef) {
147 return true;
149 VarDecl const * varDecl = dyn_cast<VarDecl>(declRef->getDecl());
150 if (varDecl) {
151 excludeVars.insert(varDecl);
153 return true;
156 bool StringStatic::VisitDeclRefExpr(DeclRefExpr const * declRef)
158 if (ignoreLocation(declRef))
159 return true;
160 VarDecl const * varDecl = dyn_cast<VarDecl>(declRef->getDecl());
161 if (!varDecl)
162 return true;
163 if (potentialVars.count(varDecl) == 0)
164 return true;
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);
174 return true;
177 bool StringStatic::VisitMemberExpr(MemberExpr const * expr)
179 if (ignoreLocation(expr))
180 return true;
181 auto const declRef = dyn_cast<DeclRefExpr>(expr->getBase());
182 if (declRef == nullptr) {
183 return true;
185 VarDecl const * varDecl = dyn_cast<VarDecl>(declRef->getDecl());
186 if (!varDecl)
187 return true;
188 if (potentialVars.count(varDecl) == 0)
189 return true;
190 auto const id = expr->getMemberDecl()->getIdentifier();
191 if (id == nullptr || id->getName() != "pData") {
192 return true;
194 excludeVars.insert(varDecl);
195 return true;
198 loplugin::Plugin::Registration<StringStatic> stringstatic("stringstatic");
200 } // namespace
202 #endif // LO_CLANG_SHARED_PLUGINS
204 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */