/* -*- 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/.
*/
// Find functions that take rtl::O[U]String parameters that can be generalized to take // std::[u16]string_view instead.
//TODO: At least theoretically, there are issues with replacing parameters that are being assigned // to, as in // // void f(OUString s) { // { // OUString t = ...; // s = t; // } // ... use s ... // if s is now std::u16string_view, it points into destroyed contents of t // }
DeclRefExpr const* relevantCXXMemberCallExpr(CXXMemberCallExpr const* expr)
{
StringType t = relevantStringType(expr->getObjectType()); if (t == StringType::None)
{ return nullptr;
} bool good = false; autoconst d = expr->getMethodDecl(); if (d->getOverloadedOperator() == OO_Subscript)
{
good = true;
} elseif (autoconst i = d->getIdentifier())
{ autoconst n = i->getName(); if (!(n == "getLength" || n == "getStr" || n == "convertToString" || n == "replace"
|| n == "replaceAll" || n == "replaceAt" || n == "replaceFirst"
|| n == "toAsciiLowerCase" || n == "toAsciiUpperCase" || n == "toUtf8"
|| n == "startsWithIgnoreAsciiCase" || n == "toUInt64" || n == "toFloat"
|| n == "toBoolean"))
{
good = true;
} #if 0 //TODO: rtl::O[U]String::getLength would be awkward to replace with // std::[u16]string_view::length/size due to the sal_Int32 vs. std::size_t return type // mismatch (C++20 ssize might make that easier, though); and while rtl::OString::getStr is // documented to be NUL-terminated (so not eligible for replacement with // std::string_view::data in general), rtl::OUString::getStr is not (so should be eligible // for replacement with std::u16string_view::data, but some call sites might nevertheless // incorrectly rely on NUL termination, so any replacement would need careful review): if (n == "getLength" || (t == StringType::RtlOustring && n == "getStr"))
{
good = true;
} #endif
} if (!good)
{ return nullptr;
} return relevantDeclRefExpr(expr->getImplicitObjectArgument());
}
SmallVector<DeclRefExpr const*, 2> relevantCXXOperatorCallExpr(CXXOperatorCallExpr const* expr)
{ autoconst op = expr->getOperator(); if (op == OO_Subscript)
{ autoconst e = expr->getArg(0); if (relevantStringType(e->getType()) == StringType::None)
{ return {};
} return wrap(relevantDeclRefExpr(e));
} if (expr->isComparisonOp() || (op == OO_Plus && expr->getNumArgs() == 2))
{
SmallVector<DeclRefExpr const*, 2> v; if (autoconst e = relevantDeclRefExpr(expr->getArg(0)))
{
v.push_back(e);
} if (autoconst e = relevantDeclRefExpr(expr->getArg(1)))
{
v.push_back(e);
} return v;
} if (op == OO_PlusEqual)
{ if (relevantStringType(expr->getArg(0)->getType()) != StringType::RtlOustring)
{ return {};
} return wrap(relevantDeclRefExpr(expr->getArg(1)));
} return {};
}
//TODO: current implementation is not at all general, just tests what we encounter in practice: bool hasStringViewOverload(ParmVarDecl const* decl)
{ autoconst d1 = cast<FunctionDecl>(decl->getDeclContext()); autoconst ctx = d1->getDeclContext(); if (!ctx->isLookupContext())
{ returnfalse;
} autoconst res = ctx->lookup(d1->getDeclName()); autoconst idx = decl->getFunctionScopeIndex(); autoconst n = d1->getNumParams();
assert(n > idx); for (auto i = res.begin(); i != res.end(); ++i)
{ autoconst d2 = dyn_cast<FunctionDecl>(*i); if (d2 == nullptr)
{ continue;
} if (d2->getNumParams() != n)
{ continue;
} auto match = true; for (unsigned j = 0; j != n; ++j)
{ if (j == idx)
{ //TODO: check for exactly std::string_view or std::u16string_view: if (!isStringView(d2->getParamDecl(j)->getType()))
{
match = false; break;
}
} elseif (d1->getParamDecl(j)->getType().getCanonicalType()
!= d2->getParamDecl(j)->getType().getCanonicalType())
{
match = false; break;
}
} if (match)
{ returntrue;
}
} returnfalse;
}
class StringViewParam final
: public loplugin::FunctionAddress<loplugin::FilteringPlugin<StringViewParam>>
{ public: explicit StringViewParam(loplugin::InstantiationData const& data)
: FunctionAddress(data)
{
}
//TODO: Also check lambdas bool TraverseFunctionDecl(FunctionDecl* decl)
{ if (ignoreLocation(decl))
{ returntrue;
} if (!relevantFunctionDecl(decl))
{ return FunctionAddress::TraverseFunctionDecl(decl);
} autoconst oldParams = currentParams_; autoconst n = decl->getNumParams(); for (unsigned i = 0; i != n; ++i)
{ autoconst d = decl->getParamDecl(i); if (relevantParmVarDecl(d))
{
currentParams_.insert(d);
}
} autoconst ret = FunctionAddress::TraverseFunctionDecl(decl); if (ret)
{ for (unsigned i = 0; i != n; ++i)
{ autoconst d1 = decl->getParamDecl(i); if (currentParams_.find(d1) == currentParams_.end())
{ continue;
} if (containsPreprocessingConditionalInclusion(decl->getSourceRange()))
{ break;
}
badParams_.push_back(d1);
}
}
currentParams_ = oldParams; return ret;
}
bool TraverseCXXMethodDecl(CXXMethodDecl* decl)
{ if (ignoreLocation(decl))
{ returntrue;
} if (!relevantFunctionDecl(decl))
{ return FunctionAddress::TraverseCXXMethodDecl(decl);
} autoconst oldParams = currentParams_; autoconst n = decl->getNumParams(); for (unsigned i = 0; i != n; ++i)
{ autoconst d = decl->getParamDecl(i); if (relevantParmVarDecl(d))
{
currentParams_.insert(d);
}
} autoconst ret = FunctionAddress::TraverseCXXMethodDecl(decl); if (ret)
{ for (unsigned i = 0; i != n; ++i)
{ autoconst d1 = decl->getParamDecl(i); if (currentParams_.find(d1) == currentParams_.end())
{ continue;
} if (containsPreprocessingConditionalInclusion(decl->getSourceRange()))
{ break;
}
badParams_.push_back(d1);
}
}
currentParams_ = oldParams; return ret;
}
bool TraverseCXXConstructorDecl(CXXConstructorDecl* decl)
{ if (ignoreLocation(decl))
{ returntrue;
} if (!relevantFunctionDecl(decl))
{ return FunctionAddress::TraverseCXXConstructorDecl(decl);
} autoconst oldParams = currentParams_; autoconst n = decl->getNumParams(); for (unsigned i = 0; i != n; ++i)
{ autoconst d = decl->getParamDecl(i); if (relevantParmVarDecl(d))
{
currentParams_.insert(d);
}
} autoconst ret = FunctionAddress::TraverseCXXConstructorDecl(decl); if (ret)
{ for (unsigned i = 0; i != n; ++i)
{ autoconst d1 = decl->getParamDecl(i); if (currentParams_.find(d1) == currentParams_.end())
{ continue;
} if (containsPreprocessingConditionalInclusion(decl->getSourceRange()))
{ break;
}
badParams_.push_back(d1);
}
}
currentParams_ = oldParams; return ret;
}
bool TraverseImplicitCastExpr(ImplicitCastExpr* expr)
{ if (ignoreLocation(expr))
{ returntrue;
} autoconst e = relevantImplicitCastExpr(expr); if (e == nullptr)
{ return FunctionAddress::TraverseImplicitCastExpr(expr);
}
currentGoodUses_.insert(e); autoconst ret = FunctionAddress::TraverseImplicitCastExpr(expr);
currentGoodUses_.erase(e); return ret;
}
bool TraverseCXXMemberCallExpr(CXXMemberCallExpr* expr)
{ if (ignoreLocation(expr))
{ returntrue;
} autoconst e = relevantCXXMemberCallExpr(expr); if (e == nullptr)
{ return FunctionAddress::TraverseCXXMemberCallExpr(expr);
}
currentGoodUses_.insert(e); autoconst ret = FunctionAddress::TraverseCXXMemberCallExpr(expr);
currentGoodUses_.erase(e); return ret;
}
bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr* expr)
{ if (ignoreLocation(expr))
{ returntrue;
} autoconst es = relevantCXXOperatorCallExpr(expr); if (es.empty())
{ return FunctionAddress::TraverseCXXOperatorCallExpr(expr);
}
currentGoodUses_.insert(es.begin(), es.end()); autoconst ret = FunctionAddress::TraverseCXXOperatorCallExpr(expr); for (autoconst i : es)
{
currentGoodUses_.erase(i);
} return ret;
}
bool VisitDeclRefExpr(DeclRefExpr* expr)
{ if (!FunctionAddress::VisitDeclRefExpr(expr))
{ returnfalse;
} if (ignoreLocation(expr))
{ returntrue;
} if (currentGoodUses_.find(expr) != currentGoodUses_.end())
{ returntrue;
} if (autoconst d = dyn_cast<ParmVarDecl>(expr->getDecl()))
{
currentParams_.erase(d);
} returntrue;
}
private: void run() override
{ if (!compiler.getLangOpts().CPlusPlus)
{ return;
} if (compiler.getPreprocessor().getIdentifierInfo("NDEBUG")->hasMacroDefinition())
{ return;
}
StringRef fn(handler.getMainFileName()); // leave the string QA tests alone if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sal/qa/"))
{ return;
} if (!TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()))
{ return;
} autoconst ignoredFns = getFunctionsWithAddressTaken(); for (autoconst i : badParams_)
{ autoconst d1 = cast<FunctionDecl>(i->getDeclContext()); if (ignoredFns.find(d1) != ignoredFns.end())
{ continue;
} if (hasStringViewOverload(i))
{ continue;
} autoconst t = relevantStringType(i->getType().getNonReferenceType());
assert(t != StringType::None);
report(DiagnosticsEngine::Warning, "replace function parameter of type %0 with " "'%select{std::string_view|std::u16string_view}1'",
i->getLocation())
<< i->getType() << (int(t) - 1) << i->getSourceRange(); for (auto d2 = d1;;)
{
d2 = d2->getPreviousDecl(); if (d2 == nullptr)
{ break;
} autoconst d3 = d2->getParamDecl(i->getFunctionScopeIndex());
report(DiagnosticsEngine::Note, "previous declaration is here", d3->getLocation())
<< d3->getSourceRange();
}
}
}
bool relevantFunctionDecl(FunctionDecl const* decl)
{ if (!decl->doesThisDeclarationHaveABody())
{ returnfalse;
} if (decl->getBody() == nullptr) // unparsed template
{ returnfalse;
} if (autoconst d = dyn_cast<CXXMethodDecl>(decl))
{ if (d->isVirtual())
{ returnfalse;
}
} if (decl->isFunctionTemplateSpecialization())
{ returnfalse;
} if (decl->getLocation().isMacroID())
{ returnfalse;
} // Filter out functions that are presumably meant to be called dynamically (e.g., via // dlopen, or backwards compatibility stubs in cppuhelper/cppu/sal compat.cxx): if (decl->getPreviousDecl() == nullptr && !decl->isInlined()
&& hasSalDllpublicExportAttr(decl)
&& compiler.getSourceManager().isInMainFile(decl->getLocation()))
{ returnfalse;
} if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(decl->getLocation())))
{ returnfalse;
} returntrue;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.