compilerplugins/clang/stringviewdangle.cxx | 117 ++++++++++++++++++++++++ compilerplugins/clang/test/stringviewdangle.cxx | 37 +++++++ solenv/CompilerTest_compilerplugins_clang.mk | 1 svx/source/tbxctrls/StylesPreviewWindow.cxx | 4 vcl/jsdialog/executor.cxx | 10 +- 5 files changed, 162 insertions(+), 7 deletions(-)
New commits: commit 40077fe30919494f0ecd04c4620cac2334d3d382 Author: Noel Grandin <noel.gran...@collabora.co.uk> AuthorDate: Sat Apr 30 09:10:48 2022 +0200 Commit: Noel Grandin <noel.gran...@collabora.co.uk> CommitDate: Sat Apr 30 21:20:55 2022 +0200 new loplugin:stringviewdangle to find places where string_view is pointing into a temporary String Change-Id: Ib530b36f441e95d83d8f687d40a97516a0806721 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133656 Tested-by: Jenkins Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk> diff --git a/compilerplugins/clang/stringviewdangle.cxx b/compilerplugins/clang/stringviewdangle.cxx new file mode 100644 index 000000000000..99cb852d03b6 --- /dev/null +++ b/compilerplugins/clang/stringviewdangle.cxx @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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 <string> +#include <iostream> +#include <unordered_map> +#include <unordered_set> + +#include "plugin.hxx" +#include "check.hxx" +#include "compat.hxx" +#include "config_clang.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/StmtVisitor.h" + +/** +Look for places where we are assigning a temporary O[U]String to a std::*string_view, which leads +to a view pointing to freed memory. +*/ + +namespace +{ +class StringViewDangle : public loplugin::FilteringPlugin<StringViewDangle> +{ +public: + explicit StringViewDangle(loplugin::InstantiationData const& data) + : FilteringPlugin(data) + { + } + + bool preRun() override { return true; } + + virtual void run() override + { + if (!preRun()) + return; + TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); + } + + bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr const*); + bool VisitVarDecl(VarDecl const*); +}; + +static const Expr* IgnoreImplicitAndConversionOperator(const Expr* expr) +{ + expr = expr->IgnoreImplicit(); + if (auto memberCall = dyn_cast<CXXMemberCallExpr>(expr)) + { + if (auto conversionDecl = dyn_cast_or_null<CXXConversionDecl>(memberCall->getMethodDecl())) + { + if (!conversionDecl->isExplicit()) + expr = memberCall->getImplicitObjectArgument()->IgnoreImpCasts(); + } + } + return expr; +} + +bool StringViewDangle::VisitCXXOperatorCallExpr(CXXOperatorCallExpr const* cxxOperatorCallExpr) +{ + if (ignoreLocation(cxxOperatorCallExpr)) + return true; + + auto op = cxxOperatorCallExpr->getOperator(); + if (op != OO_Equal) + return true; + if (!loplugin::TypeCheck(cxxOperatorCallExpr->getType()) + .ClassOrStruct("basic_string_view") + .StdNamespace()) + return true; + auto expr = IgnoreImplicitAndConversionOperator(cxxOperatorCallExpr->getArg(1)); + auto tc = loplugin::TypeCheck(expr->getType()); + if (!tc.Class("OUString").Namespace("rtl").GlobalNamespace() + && !tc.Class("OString").Namespace("rtl").GlobalNamespace()) + return true; + if (!isa<MaterializeTemporaryExpr>(expr)) + return true; + report(DiagnosticsEngine::Warning, "view pointing into temporary i.e. dangling", + cxxOperatorCallExpr->getExprLoc()) + << cxxOperatorCallExpr->getSourceRange(); + return true; +} + +bool StringViewDangle::VisitVarDecl(VarDecl const* varDecl) +{ + if (ignoreLocation(varDecl)) + return true; + if (!loplugin::TypeCheck(varDecl->getType()).ClassOrStruct("basic_string_view").StdNamespace()) + return true; + if (!varDecl->hasInit()) + return true; + auto expr = IgnoreImplicitAndConversionOperator(varDecl->getInit()); + auto tc = loplugin::TypeCheck(expr->getType()); + if (!tc.Class("OUString").Namespace("rtl").GlobalNamespace() + && !tc.Class("OString").Namespace("rtl").GlobalNamespace()) + return true; + if (!isa<MaterializeTemporaryExpr>(expr)) + return true; + report(DiagnosticsEngine::Warning, "view pointing into temporary i.e. dangling", + varDecl->getLocation()) + << varDecl->getSourceRange(); + return true; +} + +loplugin::Plugin::Registration<StringViewDangle> stringviewdangle("stringviewdangle"); +} + +#endif // LO_CLANG_SHARED_PLUGINS + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/compilerplugins/clang/test/stringviewdangle.cxx b/compilerplugins/clang/test/stringviewdangle.cxx new file mode 100644 index 000000000000..0a8d2aa54b44 --- /dev/null +++ b/compilerplugins/clang/test/stringviewdangle.cxx @@ -0,0 +1,37 @@ +/* -*- 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 <string_view> +#include <utility> + +#include <rtl/strbuf.hxx> +#include <rtl/string.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> + +namespace test1 +{ +OUString foo1(); +OUString& foo2(); +void f1() +{ + // expected-error@+1 {{view pointing into temporary i.e. dangling [loplugin:stringviewdangle]}} + std::u16string_view v = foo1(); + // expected-error@+1 {{view pointing into temporary i.e. dangling [loplugin:stringviewdangle]}} + v = foo1(); + + // no warning expected + std::u16string_view v2 = foo2(); + v2 = foo2(); +} +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/solenv/CompilerTest_compilerplugins_clang.mk b/solenv/CompilerTest_compilerplugins_clang.mk index 123e6951edb3..fc50ca7a672e 100644 --- a/solenv/CompilerTest_compilerplugins_clang.mk +++ b/solenv/CompilerTest_compilerplugins_clang.mk @@ -93,6 +93,7 @@ $(eval $(call gb_CompilerTest_add_exception_objects,compilerplugins_clang, \ compilerplugins/clang/test/stringloop \ compilerplugins/clang/test/stringstatic \ compilerplugins/clang/test/stringview \ + compilerplugins/clang/test/stringviewdangle \ compilerplugins/clang/test/stringviewparam \ compilerplugins/clang/test/stringviewvar \ compilerplugins/clang/test/trivialconstructor \ diff --git a/svx/source/tbxctrls/StylesPreviewWindow.cxx b/svx/source/tbxctrls/StylesPreviewWindow.cxx index ffd6d1e9b0ed..e786c7b60744 100644 --- a/svx/source/tbxctrls/StylesPreviewWindow.cxx +++ b/svx/source/tbxctrls/StylesPreviewWindow.cxx @@ -129,8 +129,8 @@ bool StylesPreviewWindow_Base::Command(const CommandEvent& rEvent) std::unique_ptr<weld::Builder> xBuilder( Application::CreateBuilder(m_xStylesView.get(), "svx/ui/stylemenu.ui")); std::unique_ptr<weld::Menu> xMenu(xBuilder->weld_menu("menu")); - std::string_view rIdent = xMenu->popup_at_rect( - m_xStylesView.get(), tools::Rectangle(rEvent.GetMousePosPixel(), Size(1, 1))); + OString rIdent = xMenu->popup_at_rect(m_xStylesView.get(), + tools::Rectangle(rEvent.GetMousePosPixel(), Size(1, 1))); if (rIdent == "update" || rIdent == "edit") { css::uno::Sequence<css::beans::PropertyValue> aArgs(0); diff --git a/vcl/jsdialog/executor.cxx b/vcl/jsdialog/executor.cxx index 87183f504c67..cda6a05a52d6 100644 --- a/vcl/jsdialog/executor.cxx +++ b/vcl/jsdialog/executor.cxx @@ -170,14 +170,14 @@ bool ExecuteAction(const std::string& nWindowId, const OString& rWidget, StringM if (separatorPos > 0) { // x;y - std::string_view clickPosX = OUStringToOString( + OString clickPosX = OUStringToOString( rData["data"].subView(0, separatorPos), RTL_TEXTENCODING_ASCII_US); - std::string_view clickPosY = OUStringToOString( + OString clickPosY = OUStringToOString( rData["data"].subView(separatorPos + 1), RTL_TEXTENCODING_ASCII_US); - if (!clickPosX.empty() && !clickPosY.empty()) + if (!clickPosX.isEmpty() && !clickPosY.isEmpty()) { - double posX = std::atof(clickPosX.data()); - double posY = std::atof(clickPosY.data()); + double posX = std::atof(clickPosX.getStr()); + double posY = std::atof(clickPosY.getStr()); OutputDevice& rRefDevice = pArea->get_ref_device(); // We send OutPutSize for the drawing area bitmap // get_size_request is not necessarily updated