/* -*- 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 <algorithm>
#include <sstream>
#include <tuple>
#include <vector>
#include "check.hxx"
#include "plugin.hxx"
namespace {
bool gap(APSInt n1, APSInt n2) { return n1 < n2 && n2 - n1 > 1 ; }
class Visitor final:
public loplugin::FilteringRewritePlugin<Visitor>
{
public :
explicit Visitor(InstantiationData const & data): FilteringRewritePlugin(data) {}
bool VisitCXXConstructExpr(CXXConstructExpr const * expr) {
if (ignoreLocation(expr)) {
return true ;
}
auto const ctor = expr->getConstructor();
if (!loplugin::DeclCheck(ctor->getParent()).Class ("SfxItemSet" )
.GlobalNamespace())
{
return true ;
}
auto const numParams = ctor->getNumParams();
auto const variadic = ctor->isVariadic();
if (!(((numParams == 3 && !variadic) || (numParams == 4 && variadic))
&& (loplugin::TypeCheck(ctor->getParamDecl(0 )->getType())
.LvalueReference().Class ("SfxItemPool" ).GlobalNamespace())))
{
return true ;
}
auto const numArgs = expr->getNumArgs();
if (numArgs < 3 ) {
report(
DiagnosticsEngine::Warning,
("unexpected SfxItemPool constructor call with less than three"
" arguments" ),
expr->getExprLoc())
<< expr->getSourceRange();
return true ;
}
rewrite(expr, variadic, false , numArgs, expr->getArgs());
return true ;
}
bool VisitCallExpr(CallExpr const * expr) {
if (ignoreLocation(expr)) {
return true ;
}
auto const dre = dyn_cast<DeclRefExpr>(
expr->getCallee()->IgnoreParenImpCasts());
if (dre == nullptr
|| !(loplugin::DeclCheck(dre->getDecl()).Function("make_unique" )
.Namespace ("o3tl" ).GlobalNamespace()))
{
return true ;
}
auto const numTArgs = dre->getNumTemplateArgs();
if (numTArgs == 0 ) {
report(
DiagnosticsEngine::Warning,
"unexpected o3tl::make_unique call without template arguments" ,
expr->getExprLoc())
<< expr->getSourceRange();
return true ;
}
auto const tArg0 = dre->getTemplateArgs()[0 ].getArgument();
if (tArg0.getKind() != TemplateArgument::Type) {
report(
DiagnosticsEngine::Warning,
("unexpected o3tl::make_unique call with non-type first"
" template argument" ),
expr->getExprLoc())
<< expr->getSourceRange();
return true ;
}
if (!loplugin::TypeCheck(tArg0.getAsType()).Class ("SfxItemSet" )
.GlobalNamespace())
{
return true ;
}
auto const numArgs = expr->getNumArgs();
if (numArgs < 3 ) {
return true ;
}
rewrite(expr, numArgs > 3 , true , numArgs, expr->getArgs());
return true ;
}
bool VisitTemplateSpecializationTypeLoc(
TemplateSpecializationTypeLoc typeLoc)
{
auto const loc = typeLoc.getBeginLoc();
if (loc.isInvalid() || ignoreLocation(loc)) {
return true ;
}
if (!loplugin::TypeCheck(typeLoc.getType()).Struct ("Items" )
.Namespace ("svl" ).GlobalNamespace())
{
return true ;
}
unsigned const numArgs = typeLoc.getNumArgs();
if (numArgs == 0 ) {
report(
DiagnosticsEngine::Warning,
("unexpected svl::Items specialization with zero template"
" arguments" ),
loc)
<< typeLoc.getSourceRange();
return true ;
}
if (numArgs % 2 == 1 ) {
report(
DiagnosticsEngine::Warning,
("unexpected svl::Items specialization with odd number of"
" template arguments" ),
loc)
<< typeLoc.getSourceRange();
return true ;
}
std::vector<Range> ranges;
auto good = true ;
APSInt prev;
for (unsigned i = 0 ; i != numArgs; ++i) {
auto const argLoc = typeLoc.getArgLoc(i);
auto const & arg = argLoc.getArgument();
APSInt v;
switch (arg.getKind()) {
case TemplateArgument::Integral:
v = arg.getAsIntegral();
break ;
case TemplateArgument::Expression:
if (arg.getAsExpr()->EvaluateAsInt(v, compiler.getASTContext()))
{
break ;
}
// [[fallthrough]];
default :
report(
DiagnosticsEngine::Warning,
("unexpected svl::Items specialization with non-integral"
" template argument %0" ),
argLoc.getLocation())
<< (i + 1 )
<< typeLoc.getSourceRange();
return true ;
}
if (i % 2 == 0 ) {
good = good && (i == 0 || gap(prev, v));
} else {
if (v < prev) {
report(
DiagnosticsEngine::Warning,
("unexpected svl::Items specialization with template"
" argument %0 smaller than previous one, %1 < %2" ),
argLoc.getLocation())
<< (i + 1 ) << v.toString(10 ) << prev.toString(10 )
<< typeLoc.getSourceRange();
return true ;
}
ranges.emplace_back(prev, v, (i / 2 ) + 1 );
}
prev = v;
}
if (good) {
return true ;
}
std::ostringstream buf1;
for (auto const i: ranges) {
buf1 << "\n " ;
printBegin(buf1, typeLoc, i);
buf1 << " ... " ;
printEnd(buf1, typeLoc, i);
}
std::sort(ranges.begin(), ranges.end());
std::ostringstream buf2;
for (auto i = ranges.begin(); i != ranges.end();) {
buf2 << "\n " ;
printBegin(buf2, typeLoc, *i);
buf2 << " ... " ;
auto end = *i;
for (;;) {
auto j = i + 1 ;
if (j == ranges.end() || gap(get<1 >(end), get<0 >(*j))) {
printEnd(buf2, typeLoc, end);
i = j;
break ;
}
if (get<1 >(*j) >= get<1 >(end)) {
end = *j;
}
i = j;
}
}
report(
DiagnosticsEngine::Warning,
("reorder svl::Items specialization template arguments from:%0\nto:"
"%1" ),
loc)
<< buf1.str() << buf2.str() << typeLoc.getSourceRange();
return true ;
}
private :
void run() override {
if (compiler.getLangOpts().CPlusPlus) {
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
}
}
SourceLocation nextToken(SourceLocation loc) {
return loc.getLocWithOffset(
Lexer::MeasureTokenLength(
loc, compiler.getSourceManager(), compiler.getLangOpts()));
}
SourceLocation atMacroExpansionStart(SourceLocation loc) {
while (loc.isMacroID()
&& (compiler.getSourceManager()
.isAtStartOfImmediateMacroExpansion(loc, &loc)))
{}
return loc;
}
SourceLocation atMacroExpansionEnd(SourceLocation loc) {
while (compiler.getSourceManager().isMacroBodyExpansion(loc)) {
loc = compiler.getSourceManager().getImmediateExpansionRange(loc)
.second;
}
return loc;
}
using Range = std::tuple<APSInt, APSInt, unsigned >;
void printSource(
std::ostringstream & s, TemplateSpecializationTypeLoc typeLoc, Range r,
bool end)
{
auto const argLoc = typeLoc.getArgLoc(
2 * (get<2 >(r) - 1 ) + (end ? 1 : 0 ));
auto const src1 = argLoc.getSourceRange();
auto const src2 = SourceRange(
atMacroExpansionStart(src1.getBegin()),
Lexer::getLocForEndOfToken(
compiler.getSourceManager().getExpansionLoc(
atMacroExpansionEnd(src1.getEnd())),
0 , compiler.getSourceManager(), compiler.getLangOpts()));
s << " '" << Lexer::getSourceText(
Lexer::getAsCharRange(
src2, compiler.getSourceManager(), compiler.getLangOpts()),
compiler.getSourceManager(), compiler.getLangOpts()).str()
<< "'" ;
}
void printBegin(
std::ostringstream & s, TemplateSpecializationTypeLoc typeLoc, Range r)
{
s << get<2 >(r) << "B: " << get<0 >(r).toString(10 );
printSource(s, typeLoc, r, false );
}
void printEnd(
std::ostringstream & s, TemplateSpecializationTypeLoc typeLoc, Range r)
{
s << get<2 >(r) << "E: " << get<1 >(r).toString(10 );
printSource(s, typeLoc, r, true );
}
void rewrite(
Expr const * expr, bool variadic, bool forward, unsigned numArgs,
Expr const * const * args)
{
bool constant = true ;
unsigned firstZero = 0 ;
for (unsigned i = 1 ; i != numArgs; ++i) {
auto const arg = args[i];
constant = constant
&& arg->isCXX11ConstantExpr(compiler.getASTContext());
APSInt v;
auto const zero
= ((arg->EvaluateAsInt(v, compiler.getASTContext())
&& v == 0 )
|| (variadic && i > 4
&& arg->isNullPointerConstant(
compiler.getASTContext(),
Expr::NPC_ValueDependentIsNotNull)));
if (variadic) {
if (zero) {
if (firstZero == 0 ) {
if (i == 1 ) {
report(
DiagnosticsEngine::Warning,
("unexpected missing non-zero arguments before"
" first zero argument in SfxItemPool"
" constructor call" ),
arg->getExprLoc())
<< expr->getSourceRange();
return ;
}
if (i % 2 == 0 ) {
report(
DiagnosticsEngine::Warning,
("unexpected odd number of potentially non-zero"
" arguments before first definitely zero"
" argument in SfxItemPool constructor call" ),
arg->getExprLoc())
<< expr->getSourceRange();
return ;
}
firstZero = i;
}
} else if (firstZero != 0 ) {
report(
DiagnosticsEngine::Warning,
("unexpected potentially non-zero argument in"
" SfxItemPool constructor call, following zero"
" argument" ),
arg->getExprLoc())
<< expr->getSourceRange();
return ;
}
} else if (zero) {
report(
DiagnosticsEngine::Warning,
"unexpected zero argument in SfxItemPool constructor call" ,
arg->getExprLoc())
<< expr->getSourceRange();
return ;
}
}
if (variadic && firstZero == 0 ) {
report(
DiagnosticsEngine::Warning,
("unexpected SfxItemPool constructor call with no detectable"
" zero arguments" ),
expr->getExprLoc())
<< expr->getSourceRange();
return ;
}
if (rewriter != nullptr) {
if (!insertTextBefore(
atMacroExpansionStart(args[1 ]->getLocStart()),
(constant
? StringRef("svl::Items<" )
: (forward
? StringRef("std::initializer_list<SfxItemSet::Pair>{{" )
: StringRef("{{" )))))
{
goto failed;
}
auto const postLoc = atMacroExpansionEnd(
args[numArgs - 1 ]->getLocEnd());
auto const postStr = constant ? StringRef(">{}" ) : StringRef("}}" );
if (variadic) {
//TODO: the replaced range can contain relevant comments:
if (!replaceText(
SourceRange(
nextToken(
atMacroExpansionEnd(
args[firstZero - 1 ]->getLocEnd())),
postLoc),
postStr))
{
goto failed;
}
} else {
if (!insertTextAfterToken(postLoc, postStr)) {
goto failed;
}
}
if (!constant && variadic) {
for (unsigned i = 2 ; i != firstZero - 1 ; ++i) {
auto const arg = args[i];
if (!(i % 2 == 0
? insertTextAfterToken(
atMacroExpansionEnd(arg->getLocEnd()), "}" )
: insertTextBefore(
atMacroExpansionStart(arg->getLocStart()), "{" )))
{
goto failed;
}
}
}
return ;
}
failed: //TODO: undo partial changes
report(
DiagnosticsEngine::Warning,
("rewrite SfxItemPool constructor call with"
" %select{%select{|std::initializer_list<SfxItemSet::Pair>}1"
"%{%{..., ...%}, ..., %{..., ...%}%}|svl::Items<...>%{%}}0 around"
" the %select{|leading }2%3 WID arguments%select{| and remove the"
" remaining %4 zero %plural{1:argument|:arguments}4}2" ),
expr->getExprLoc())
<< constant << forward << variadic
<< (variadic ? firstZero - 1 : numArgs - 1 ) << (numArgs - firstZero)
<< expr->getSourceRange();
}
};
static loplugin::Plugin::Registration<Visitor> reg("sfxitemsetrewrite" ,true );
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
Messung V0.5 in Prozent C=99 H=89 G=94
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-10-01)
¤
*© Formatika GbR, Deutschland