diff options
author | Stephan Bergmann <sbergman@redhat.com> | 2019-10-14 11:42:20 +0200 |
---|---|---|
committer | Stephan Bergmann <sbergman@redhat.com> | 2019-10-14 15:48:38 +0200 |
commit | 77d083f2cbe496274cdab38a3a34497d1b742d86 (patch) | |
tree | 7092c7dbd5d746deb30565d8670dd79246327b58 /compilerplugins | |
parent | de2cb96d5f9bb3c831fd136062582b4c11a003d1 (diff) |
New loplugin:getstr
...to find matches of
... << s.getStr()
(for the rtl string classes) that can be written as just
... << s
Some notes:
* The OUStringToOString(..., RTL_TEXTENCODING_UTF8) is left explicit in
desktop/source/app/crashreport.cxx (even though that would also be done
internally by the "<< OUString" operator) to clarify that these values are
written out as UTF-8 (and not as what that operator << happens to use, which
just also happens to be UTF-8).
* OUSTRING_TO_CSTR (include/oox/helper/helper.hxx) is no longer used now.
* Just don't bother to use osl_getThreadTextEncoding() in the SAL_WARN in
lingucomponent/source/hyphenator/hyphen/hyphenimp.cxx.
* The toUtf8() in the SAL_DEBUG in pyuno/source/module/pyuno_module.cxx can just
go, too.
Change-Id: I4602f0379ef816bff310f1e51b57c56b7e3f0136
Reviewed-on: https://gerrit.libreoffice.org/80762
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
Diffstat (limited to 'compilerplugins')
-rw-r--r-- | compilerplugins/clang/check.cxx | 16 | ||||
-rw-r--r-- | compilerplugins/clang/check.hxx | 10 | ||||
-rw-r--r-- | compilerplugins/clang/compat.hxx | 13 | ||||
-rw-r--r-- | compilerplugins/clang/getstr.cxx | 172 | ||||
-rw-r--r-- | compilerplugins/clang/test/getstr.cxx | 41 |
5 files changed, 251 insertions, 1 deletions
diff --git a/compilerplugins/clang/check.cxx b/compilerplugins/clang/check.cxx index d53bfabbe9cf..be1b1f764eb1 100644 --- a/compilerplugins/clang/check.cxx +++ b/compilerplugins/clang/check.cxx @@ -10,6 +10,7 @@ #include <cassert> #include <clang/AST/DeclCXX.h> +#include <clang/AST/DeclTemplate.h> #include "check.hxx" @@ -131,6 +132,21 @@ TypeCheck TypeCheck::Typedef() const { return TypeCheck(); } +DeclCheck TypeCheck::TemplateSpecializationClass() const { + if (!type_.isNull()) { + if (auto const t = type_->getAs<clang::TemplateSpecializationType>()) { + if (!t->isTypeAlias()) { + if (auto const d = llvm::dyn_cast_or_null<clang::ClassTemplateDecl>( + t->getTemplateName().getAsTemplateDecl())) + { + return DeclCheck(d->getTemplatedDecl()); + } + } + } + } + return DeclCheck(); +} + TypeCheck TypeCheck::NotSubstTemplateTypeParmType() const { return (!type_.isNull() diff --git a/compilerplugins/clang/check.hxx b/compilerplugins/clang/check.hxx index 7aa56cb8523d..8e835b8b6a96 100644 --- a/compilerplugins/clang/check.hxx +++ b/compilerplugins/clang/check.hxx @@ -29,6 +29,8 @@ inline ContextCheck checkRecordDecl( } +class DeclCheck; + class TypeCheck { public: explicit TypeCheck(clang::QualType type): type_(type) {} @@ -71,6 +73,8 @@ public: inline ContextCheck Typedef(llvm::StringRef id) const; + DeclCheck TemplateSpecializationClass() const; + TypeCheck NotSubstTemplateTypeParmType() const; private: @@ -80,6 +84,8 @@ private: }; class DeclCheck { + friend TypeCheck; + public: explicit DeclCheck(clang::Decl const * decl): decl_(decl) {} @@ -102,7 +108,9 @@ public: ContextCheck MemberFunction() const; private: - clang::Decl const * const decl_; + DeclCheck() = default; + + clang::Decl const * const decl_ = nullptr; }; class ContextCheck { diff --git a/compilerplugins/clang/compat.hxx b/compilerplugins/clang/compat.hxx index cb13f44cfa66..c091c51601f7 100644 --- a/compilerplugins/clang/compat.hxx +++ b/compilerplugins/clang/compat.hxx @@ -240,6 +240,19 @@ inline const clang::Expr *getSubExprAsWritten(const clang::CastExpr *This) { return getSubExprAsWritten(const_cast<clang::CastExpr *>(This)); } +inline clang::QualType getObjectType(clang::CXXMemberCallExpr const * expr) { +#if CLANG_VERSION >= 100000 + return expr->getObjectType(); +#else + // <https://github.com/llvm/llvm-project/commit/88559637641e993895337e1047a0bd787fecc647> + // "[OpenCL] Improve destructor support in C++ for OpenCL": + clang::QualType Ty = expr->getImplicitObjectArgument()->getType(); + if (Ty->isPointerType()) + Ty = Ty->getPointeeType(); + return Ty; +#endif +} + inline bool isExplicitSpecified(clang::CXXConstructorDecl const * decl) { #if CLANG_VERSION >= 90000 return decl->getExplicitSpecifier().isExplicit(); diff --git a/compilerplugins/clang/getstr.cxx b/compilerplugins/clang/getstr.cxx new file mode 100644 index 000000000000..15f340296bea --- /dev/null +++ b/compilerplugins/clang/getstr.cxx @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef LO_CLANG_SHARED_PLUGINS + +#include <cassert> +#include <stack> + +#include "check.hxx" +#include "compat.hxx" +#include "plugin.hxx" + +// Find matches of +// +// ... << s.getStr() +// +// (for the rtl string classes) that can be written as just +// +// ... << s + +namespace +{ +class GetStr final : public loplugin::FilteringPlugin<GetStr> +{ +public: + explicit GetStr(loplugin::InstantiationData const& data) + : FilteringPlugin(data) + { + } + + bool PreTraverseFunctionDecl(FunctionDecl* decl) + { + functions_.push(decl); + return true; + } + + bool PostTraverseFunctionDecl(FunctionDecl*, bool) + { + assert(!functions_.empty()); + functions_.pop(); + return true; + } + + bool TraverseFunctionDecl(FunctionDecl* decl) + { + bool ret = true; + if (PreTraverseFunctionDecl(decl)) + { + ret = FilteringPlugin::TraverseFunctionDecl(decl); + PostTraverseFunctionDecl(decl, ret); + } + return ret; + } + + bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr* expr) + { + if (ignoreLocation(expr)) + { + return true; + } + if (expr->getOperator() != OO_LessLess) + { + return true; + } + assert(expr->getNumArgs() == 2); + if (!loplugin::TypeCheck(expr->getArg(0)->getType()) + .TemplateSpecializationClass() + .ClassOrStruct("basic_ostream") + .StdNamespace()) //TODO: check template args + { + return true; + } + auto const arg1 = expr->getArg(1); + auto const e = dyn_cast<CXXMemberCallExpr>(arg1->IgnoreParenImpCasts()); + if (e == nullptr) + { + return true; + } + bool castToVoid = false; + if (auto const ic = dyn_cast<ImplicitCastExpr>(arg1)) + { + if (loplugin::TypeCheck(arg1->getType()).Pointer().Void()) + { + castToVoid = true; + } + } + auto const t = compat::getObjectType(e); + auto const tc = loplugin::TypeCheck(t); + if (!(tc.Class("OString").Namespace("rtl").GlobalNamespace() + || tc.Class("OUString").Namespace("rtl").GlobalNamespace() + || (castToVoid + && (tc.Class("OStringBuffer").Namespace("rtl").GlobalNamespace() + || tc.Class("OUStringBuffer").Namespace("rtl").GlobalNamespace())))) + { + return true; + } + if (!loplugin::DeclCheck(e->getMethodDecl()).Function("getStr")) + { + return true; + } + if (castToVoid) + { + report(DiagnosticsEngine::Warning, + ("suspicious use of 'getStr' on an object of type %0; the result is implicitly" + " cast to a void pointer in a call of 'operator <<'"), + e->getExprLoc()) + << t.getLocalUnqualifiedType() << expr->getSourceRange(); + return true; + } + if (!functions_.empty()) + { + // Filter out occurrences of `s << t.getStr()` in the implementation of + // `operator <<(std::basic_ostream<...> & s, T const & t)`: + auto const fd = functions_.top(); + if (fd->getOverloadedOperator() == OO_LessLess) + { + assert(fd->getNumParams() == 2); + if (loplugin::TypeCheck(fd->getParamDecl(0)->getType()) + .LvalueReference() + .NonConstVolatile() + .TemplateSpecializationClass() + .ClassOrStruct("basic_ostream") + .StdNamespace()) //TODO: check template args + { + if (auto const t2 + = fd->getParamDecl(1)->getType()->getAs<LValueReferenceType>()) + { + auto const t3 = t2->getPointeeType(); + if (t3.isConstQualified() && !t3.isVolatileQualified() + && (t3.getCanonicalType().getTypePtr() + == t.getCanonicalType().getTypePtr())) + { + return true; + } + } + } + } + } + report(DiagnosticsEngine::Warning, + ("directly use object of type %0 in a call of 'operator <<', instead of calling" + " 'getStr' first"), + e->getExprLoc()) + << t.getLocalUnqualifiedType() << expr->getSourceRange(); + return true; + } + + bool preRun() override { return compiler.getLangOpts().CPlusPlus; } + +private: + std::stack<FunctionDecl*> functions_; + + void run() override + { + if (preRun()) + { + TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); + } + } +}; + +loplugin::Plugin::Registration<GetStr> getstr("getstr"); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/compilerplugins/clang/test/getstr.cxx b/compilerplugins/clang/test/getstr.cxx new file mode 100644 index 000000000000..358f2e2b1e7d --- /dev/null +++ b/compilerplugins/clang/test/getstr.cxx @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/config.h> + +#include <ostream> + +#include <rtl/strbuf.hxx> +#include <rtl/string.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> + +using S = OString; + +void f(std::ostream& st, OString const& s1, OStringBuffer const& s2, OUString const& s3, + OUStringBuffer const& s4, S const& s5, OString* p1, OStringBuffer* p2, OUString* p3, + OUStringBuffer* p4, S* p5, char const* (OString::*pf)() const) +{ + st << s1.getStr() // expected-error {{directly use object of type 'rtl::OString' in a call of 'operator <<', instead of calling 'getStr' first [loplugin:getstr]}} + << s2.getStr() + << s3.getStr() // expected-error {{suspicious use of 'getStr' on an object of type 'rtl::OUString'; the result is implicitly cast to a void pointer in a call of 'operator <<' [loplugin:getstr]}} + << s4.getStr() // expected-error {{suspicious use of 'getStr' on an object of type 'rtl::OUStringBuffer'; the result is implicitly cast to a void pointer in a call of 'operator <<' [loplugin:getstr]}} + << s5.getStr() // expected-error {{directly use object of type 'S' (aka 'rtl::OString') in a call of 'operator <<', instead of calling 'getStr' first [loplugin:getstr]}} + << p1->getStr() // expected-error {{directly use object of type 'rtl::OString' in a call of 'operator <<', instead of calling 'getStr' first [loplugin:getstr]}} + << p2->getStr() + << p3->getStr() // expected-error {{suspicious use of 'getStr' on an object of type 'rtl::OUString'; the result is implicitly cast to a void pointer in a call of 'operator <<' [loplugin:getstr]}} + << p4->getStr() // expected-error {{suspicious use of 'getStr' on an object of type 'rtl::OUStringBuffer'; the result is implicitly cast to a void pointer in a call of 'operator <<' [loplugin:getstr]}} + << p5->getStr() // expected-error {{directly use object of type 'rtl::OString' in a call of 'operator <<', instead of calling 'getStr' first [loplugin:getstr]}} + << (s1.*pf)(); + SAL_INFO( // expected-error 1+ {{directly use object of type 'rtl::OString' in a call of 'operator <<', instead of calling 'getStr' first [loplugin:getstr]}} + "test", s1.getStr()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ |