LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / compilerplugins / clang / stringview.cxx
blobb3a0323d2923890fb4a8724e652214cc80907863
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 */
9 #ifndef LO_CLANG_SHARED_PLUGINS
11 #include <cassert>
12 #include <string>
13 #include <iostream>
14 #include <unordered_map>
15 #include <unordered_set>
17 #include "plugin.hxx"
18 #include "check.hxx"
19 #include "clang/AST/CXXInheritance.h"
20 #include "clang/AST/StmtVisitor.h"
22 /**
23 Look for places where we are making a substring copy of an OUString, and then passing it to a
24 function that takes a u16string_view, in which case it is more efficient to pass a view
25 of the OUString, rather than making a copy.
27 TODO currently does not check if there is some other visible overload of the callee, that can take
28 a string_view.
29 TODO handle OUStringBuffer/OStringBuffer similarly
32 namespace
34 class StringView : public loplugin::FilteringPlugin<StringView>
36 public:
37 explicit StringView(loplugin::InstantiationData const& data)
38 : FilteringPlugin(data)
42 bool preRun() override
44 auto const fn = handler.getMainFileName();
45 return !(
46 loplugin::isSamePathname(fn, SRCDIR "/sal/qa/OStringBuffer/rtl_OStringBuffer.cxx")
47 || loplugin::isSamePathname(fn, SRCDIR "/sal/qa/rtl/strings/test_ostring_concat.cxx")
48 || loplugin::isSamePathname(fn, SRCDIR "/sal/qa/rtl/strings/test_oustring_concat.cxx"));
51 virtual void run() override
53 if (!preRun())
54 return;
55 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
58 bool VisitFunctionDecl(FunctionDecl const*);
59 bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr const*);
60 bool VisitImplicitCastExpr(ImplicitCastExpr const*);
61 bool VisitCXXMemberCallExpr(CXXMemberCallExpr const*);
62 bool VisitCXXConstructExpr(CXXConstructExpr const*);
64 private:
65 void handleSubExprThatCouldBeView(Expr const* expr);
66 void handleCXXConstructExpr(CXXConstructExpr const* expr);
67 void handleCXXMemberCallExpr(CXXMemberCallExpr const* expr);
70 bool StringView::VisitCXXOperatorCallExpr(CXXOperatorCallExpr const* cxxOperatorCallExpr)
72 if (ignoreLocation(cxxOperatorCallExpr))
73 return true;
75 auto op = cxxOperatorCallExpr->getOperator();
76 if (op == OO_Plus && cxxOperatorCallExpr->getNumArgs() == 2)
78 handleSubExprThatCouldBeView(cxxOperatorCallExpr->getArg(0));
79 handleSubExprThatCouldBeView(cxxOperatorCallExpr->getArg(1));
81 if (compat::isComparisonOp(cxxOperatorCallExpr))
83 handleSubExprThatCouldBeView(cxxOperatorCallExpr->getArg(0));
84 handleSubExprThatCouldBeView(cxxOperatorCallExpr->getArg(1));
86 else if (op == OO_PlusEqual)
87 handleSubExprThatCouldBeView(cxxOperatorCallExpr->getArg(1));
88 else if (op == OO_Subscript)
89 handleSubExprThatCouldBeView(cxxOperatorCallExpr->getArg(0));
90 else if (op == OO_Equal)
92 if (loplugin::TypeCheck(cxxOperatorCallExpr->getType())
93 .Class("OUStringBuffer")
94 .Namespace("rtl")
95 .GlobalNamespace()
96 || loplugin::TypeCheck(cxxOperatorCallExpr->getType())
97 .Class("OStringBuffer")
98 .Namespace("rtl")
99 .GlobalNamespace())
101 handleSubExprThatCouldBeView(cxxOperatorCallExpr->getArg(1));
104 return true;
107 bool StringView::VisitFunctionDecl(FunctionDecl const* functionDecl)
109 if (ignoreLocation(functionDecl))
110 return true;
111 // debugging
112 // if (functionDecl->getIdentifier() && functionDecl->getName() == "f1")
113 // functionDecl->dump();
114 return true;
117 bool StringView::VisitImplicitCastExpr(ImplicitCastExpr const* expr)
119 if (ignoreLocation(expr))
121 return true;
123 if (!loplugin::TypeCheck(expr->getType()).ClassOrStruct("basic_string_view").StdNamespace())
125 return true;
127 handleSubExprThatCouldBeView(expr->getSubExprAsWritten());
128 return true;
131 void StringView::handleSubExprThatCouldBeView(Expr const* subExpr)
133 auto const e0 = compat::IgnoreImplicit(subExpr);
134 auto const e = e0->IgnoreParens();
135 auto const tc = loplugin::TypeCheck(e->getType());
136 if (!(tc.Class("OString").Namespace("rtl").GlobalNamespace()
137 || tc.Class("OUString").Namespace("rtl").GlobalNamespace()))
139 return;
141 if (auto const e1 = dyn_cast<CXXConstructExpr>(e))
143 if (e0 == subExpr)
145 handleCXXConstructExpr(e1);
148 else if (auto const e2 = dyn_cast<CXXFunctionalCastExpr>(e))
150 auto e3 = e2->getSubExpr();
151 if (auto const e4 = dyn_cast<CXXBindTemporaryExpr>(e3))
153 e3 = e4->getSubExpr();
155 if (auto const e4 = dyn_cast<CXXConstructExpr>(e3))
157 handleCXXConstructExpr(e4);
160 else if (auto const e3 = dyn_cast<CXXMemberCallExpr>(e))
162 handleCXXMemberCallExpr(e3);
166 void StringView::handleCXXConstructExpr(CXXConstructExpr const* expr)
168 QualType argType;
169 enum
171 None,
172 OrChar,
173 ViaConcatenation
174 } extra
175 = None;
176 auto const d = expr->getConstructor();
177 switch (d->getNumParams())
179 case 0:
180 break;
181 case 1:
183 auto const t = d->getParamDecl(0)->getType();
184 if (t->isAnyCharacterType())
186 argType = expr->getArg(0)->IgnoreImplicit()->getType();
187 extra = OrChar;
188 break;
190 loplugin::TypeCheck tc(t);
191 if (tc.LvalueReference()
192 .Const()
193 .Class("OStringLiteral")
194 .Namespace("rtl")
195 .GlobalNamespace()
196 || tc.LvalueReference()
197 .Const()
198 .Class("OUStringLiteral")
199 .Namespace("rtl")
200 .GlobalNamespace()
201 || tc.RvalueReference().Struct("OStringNumber").Namespace("rtl").GlobalNamespace()
202 || tc.RvalueReference().Struct("OUStringNumber").Namespace("rtl").GlobalNamespace()
203 || tc.ClassOrStruct("basic_string_view").StdNamespace())
205 argType = expr->getArg(0)->IgnoreImplicit()->getType();
206 break;
208 if (tc.RvalueReference().Struct("OStringConcat").Namespace("rtl").GlobalNamespace()
209 || tc.RvalueReference().Struct("OUStringConcat").Namespace("rtl").GlobalNamespace())
211 argType = expr->getArg(0)->IgnoreImplicit()->getType();
212 extra = ViaConcatenation;
213 break;
215 return;
217 case 2:
219 auto const t0 = d->getParamDecl(0)->getType();
220 if (t0->isPointerType() && t0->getPointeeType()->isAnyCharacterType())
222 auto const t = d->getParamDecl(1)->getType();
223 if (t->isIntegralType(compiler.getASTContext())
224 && !(t->isBooleanType() || t->isAnyCharacterType()))
226 auto const arg = expr->getArg(1);
227 if (!arg->isValueDependent())
229 if (auto const val
230 = compat::getIntegerConstantExpr(arg, compiler.getASTContext()))
232 if (val->getExtValue() == 1)
234 extra = OrChar;
238 argType = expr->getArg(0)->IgnoreImplicit()->getType();
239 break;
242 if (loplugin::TypeCheck(d->getParamDecl(1)->getType())
243 .Struct("Dummy")
244 .Namespace("libreoffice_internal")
245 .Namespace("rtl")
246 .GlobalNamespace())
248 argType = expr->getArg(0)->IgnoreImplicit()->getType();
249 break;
251 return;
253 default:
254 return;
256 report(DiagnosticsEngine::Warning,
257 "instead of an %0%select{| constructed from a %2}1, pass a"
258 " '%select{std::string_view|std::u16string_view}3'"
259 "%select{| (or an '%select{rtl::OStringChar|rtl::OUStringChar}3')|"
260 " via '%select{rtl::OStringConcatenation|rtl::OUStringConcatenation}3'}4",
261 expr->getExprLoc())
262 << expr->getType() << (argType.isNull() ? 0 : 1) << argType
263 << (loplugin::TypeCheck(expr->getType()).Class("OString").Namespace("rtl").GlobalNamespace()
265 : 1)
266 << extra << expr->getSourceRange();
269 void StringView::handleCXXMemberCallExpr(CXXMemberCallExpr const* expr)
271 auto const dc = loplugin::DeclCheck(expr->getMethodDecl()).Function("copy");
272 if (!dc)
274 return;
276 if (!(dc.Class("OString").Namespace("rtl").GlobalNamespace()
277 || dc.Class("OUString").Namespace("rtl").GlobalNamespace()))
279 return;
281 report(DiagnosticsEngine::Warning, "rather than copy, pass with a view using subView()",
282 expr->getExprLoc())
283 << expr->getSourceRange();
286 /** check for calls to O[U]StringBuffer::append that could be passed as a
287 std::u16string_view */
288 bool StringView::VisitCXXMemberCallExpr(CXXMemberCallExpr const* expr)
290 if (ignoreLocation(expr))
292 return true;
294 if (!loplugin::TypeCheck(expr->getType())
295 .Class("OUStringBuffer")
296 .Namespace("rtl")
297 .GlobalNamespace()
298 && !loplugin::TypeCheck(expr->getType())
299 .Class("OStringBuffer")
300 .Namespace("rtl")
301 .GlobalNamespace())
303 return true;
305 auto const dc = loplugin::DeclCheck(expr->getMethodDecl());
306 if (dc.Function("append") || dc.Function("indexOf") || dc.Function("lastIndexOf"))
308 handleSubExprThatCouldBeView(expr->getArg(0));
310 else if (dc.Function("insert"))
312 handleSubExprThatCouldBeView(expr->getArg(1));
314 return true;
317 /** check for calls to O[U]StringBuffer constructor that could be passed as a
318 std::u16string_view */
319 bool StringView::VisitCXXConstructExpr(CXXConstructExpr const* expr)
321 if (ignoreLocation(expr))
323 return true;
325 if (!loplugin::TypeCheck(expr->getType())
326 .Class("OUStringBuffer")
327 .Namespace("rtl")
328 .GlobalNamespace()
329 && !loplugin::TypeCheck(expr->getType())
330 .Class("OStringBuffer")
331 .Namespace("rtl")
332 .GlobalNamespace())
334 return true;
336 if (expr->getNumArgs() > 0)
337 handleSubExprThatCouldBeView(expr->getArg(0));
338 return true;
341 loplugin::Plugin::Registration<StringView> stringview("stringview");
344 #endif // LO_CLANG_SHARED_PLUGINS
346 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */