office-gobmx/compilerplugins/clang/changetoolsgen.cxx
Stephan Bergmann d1a2b80b9d Bump compiler plugins Clang baseline to 12.0.1
...as discussed in the mail thread starting at
<https://lists.freedesktop.org/archives/libreoffice/2020-November/086234.html>
"Bump --enable-compiler-plugins Clang baseline?" (and now picked up again at
<https://lists.freedesktop.org/archives/libreoffice/2022-February/088459.html>
"Re: Bump --enable-compiler-plugins Clang baseline?"), and clean up
compilerplugins/clang/ accordingly

Change-Id: I5e81c6fdcc363aeefd6227606225b526fdf7ac16
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/129989
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2022-02-17 21:45:06 +01:00

376 lines
14 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* Based on LLVM/Clang.
*
* This file is distributed under the University of Illinois Open Source
* License. See LICENSE.TXT for details.
*
*/
#include "plugin.hxx"
#include "check.hxx"
#include <regex>
/**
* Changes calls to tools::Rectangle/Point/Size methods that return a ref to instead call the setter methods.
*
* run as:
* make COMPILER_PLUGIN_TOOL=changetoolsgen UPDATE_FILES=all FORCE_COMPILE=all
* or
* make <module> COMPILER_PLUGIN_TOOL=changetoolsgen FORCE_COMPILE=all
*/
namespace
{
class ChangeToolsGen : public loplugin::FilteringRewritePlugin<ChangeToolsGen>
{
public:
explicit ChangeToolsGen(loplugin::InstantiationData const& data)
: FilteringRewritePlugin(data)
{
}
virtual void run() override;
bool VisitCXXMemberCallExpr(CXXMemberCallExpr const* call);
private:
bool ChangeAssignment(Stmt const* parent, std::string const& methodName,
std::string const& setPrefix);
bool ChangeBinaryOperatorPlusMinus(BinaryOperator const* parent, CXXMemberCallExpr const* call,
std::string const& methodName);
bool ChangeBinaryOperatorOther(BinaryOperator const* parent, CXXMemberCallExpr const* call,
std::string const& methodName, std::string const& setPrefix);
bool ChangeUnaryOperator(UnaryOperator const* parent, CXXMemberCallExpr const* call,
std::string const& methodName);
std::string extractCode(SourceLocation startLoc, SourceLocation endLoc);
};
void ChangeToolsGen::run() { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
bool ChangeToolsGen::VisitCXXMemberCallExpr(CXXMemberCallExpr const* call)
{
if (ignoreLocation(call))
return true;
const CXXMethodDecl* func = call->getMethodDecl();
if (!func)
return true;
if (func->isConst())
return true;
auto dc = loplugin::DeclCheck(func);
std::string methodName;
std::string setPrefix;
if (dc.Function("Top").Class("Rectangle").Namespace("tools").GlobalNamespace())
{
methodName = "Top";
setPrefix = "Set";
}
else if (dc.Function("Bottom").Class("Rectangle").Namespace("tools").GlobalNamespace())
{
methodName = "Bottom";
setPrefix = "Set";
}
else if (dc.Function("Left").Class("Rectangle").Namespace("tools").GlobalNamespace())
{
methodName = "Left";
setPrefix = "Set";
}
else if (dc.Function("Right").Class("Rectangle").Namespace("tools").GlobalNamespace())
{
methodName = "Right";
setPrefix = "Set";
}
else if (dc.Function("X").Class("Point").GlobalNamespace())
{
methodName = "X";
setPrefix = "set";
}
else if (dc.Function("Y").Class("Point").GlobalNamespace())
{
methodName = "Y";
setPrefix = "set";
}
else if (dc.Function("Width").Class("Size").GlobalNamespace())
{
methodName = "Width";
setPrefix = "set";
}
else if (dc.Function("Height").Class("Size").GlobalNamespace())
{
methodName = "Height";
setPrefix = "set";
}
else
return true;
if (!loplugin::TypeCheck(func->getReturnType()).LvalueReference())
return true;
auto parent = getParentStmt(call);
if (!parent)
return true;
if (auto unaryOp = dyn_cast<UnaryOperator>(parent))
{
if (!ChangeUnaryOperator(unaryOp, call, methodName))
report(DiagnosticsEngine::Warning, "Could not fix, unary", call->getBeginLoc());
return true;
}
auto binaryOp = dyn_cast<BinaryOperator>(parent);
if (!binaryOp)
{
// normal getter
return true;
}
auto opcode = binaryOp->getOpcode();
if (opcode == BO_Assign)
{
// Check for assignments embedded inside other expressions
auto parent2 = getParentStmt(parent);
if (dyn_cast_or_null<ExprWithCleanups>(parent2))
parent2 = getParentStmt(parent2);
if (parent2 && isa<Expr>(parent2))
{
report(DiagnosticsEngine::Warning, "Could not fix, embedded assign",
call->getBeginLoc());
return true;
}
// Check for
// X.Width() = X.Height() = 1;
if (auto rhs = dyn_cast<BinaryOperator>(binaryOp->getRHS()->IgnoreParenImpCasts()))
if (rhs->getOpcode() == BO_Assign)
{
report(DiagnosticsEngine::Warning, "Could not fix, double assign",
call->getBeginLoc());
return true;
}
if (!ChangeAssignment(parent, methodName, setPrefix))
report(DiagnosticsEngine::Warning, "Could not fix, assign", call->getBeginLoc());
return true;
}
if (opcode == BO_AddAssign || opcode == BO_SubAssign)
{
if (!ChangeBinaryOperatorPlusMinus(binaryOp, call, methodName))
report(DiagnosticsEngine::Warning, "Could not fix, assign-and-change",
call->getBeginLoc());
return true;
}
else if (opcode == BO_RemAssign || opcode == BO_MulAssign || opcode == BO_DivAssign)
{
if (!ChangeBinaryOperatorOther(binaryOp, call, methodName, setPrefix))
report(DiagnosticsEngine::Warning, "Could not fix, assign-and-change",
call->getBeginLoc());
return true;
}
else
assert(false);
return true;
}
bool ChangeToolsGen::ChangeAssignment(Stmt const* parent, std::string const& methodName,
std::string const& setPrefix)
{
// Look for expressions like
// aRect.Left() = ...;
// and replace with
// aRect.SetLeft( ... );
SourceManager& SM = compiler.getSourceManager();
SourceLocation startLoc = SM.getExpansionLoc(parent->getBeginLoc());
SourceLocation endLoc = SM.getExpansionLoc(parent->getEndLoc());
const char* p1 = SM.getCharacterData(startLoc);
const char* p2 = SM.getCharacterData(endLoc);
unsigned n = Lexer::MeasureTokenLength(endLoc, SM, compiler.getLangOpts());
if (p2 < p1) // clang is misbehaving, appears to be macro constant related
return false;
std::string callText(p1, p2 - p1 + n);
auto originalLength = callText.size();
auto newText = std::regex_replace(callText, std::regex(methodName + " *\\( *\\) *="),
setPrefix + methodName + "(");
if (newText == callText)
return false;
newText += " )";
return replaceText(startLoc, originalLength, newText);
}
bool ChangeToolsGen::ChangeBinaryOperatorPlusMinus(BinaryOperator const* binaryOp,
CXXMemberCallExpr const* call,
std::string const& methodName)
{
// Look for expressions like
// aRect.Left() += ...;
// and replace with
// aRect.MoveLeft( ... );
SourceManager& SM = compiler.getSourceManager();
SourceLocation startLoc = SM.getExpansionLoc(binaryOp->getBeginLoc());
SourceLocation endLoc = SM.getExpansionLoc(binaryOp->getEndLoc());
const char* p1 = SM.getCharacterData(startLoc);
const char* p2 = SM.getCharacterData(endLoc);
if (p2 < p1) // clang is misbehaving, appears to be macro constant related
return false;
unsigned n = Lexer::MeasureTokenLength(endLoc, SM, compiler.getLangOpts());
std::string callText(p1, p2 - p1 + n);
auto originalLength = callText.size();
std::string newText;
if (binaryOp->getOpcode() == BO_AddAssign)
{
newText = std::regex_replace(callText, std::regex(methodName + " *\\( *\\) *\\+= *"),
"Adjust" + methodName + "(");
newText += " )";
}
else
{
newText = std::regex_replace(callText, std::regex(methodName + " *\\( *\\) *\\-= *"),
"Adjust" + methodName + "( -(");
newText += ") )";
}
if (newText == callText)
{
report(DiagnosticsEngine::Warning, "binaryop-plusminus regex match failed",
call->getBeginLoc());
return false;
}
return replaceText(startLoc, originalLength, newText);
}
bool ChangeToolsGen::ChangeBinaryOperatorOther(BinaryOperator const* binaryOp,
CXXMemberCallExpr const* call,
std::string const& methodName,
std::string const& setPrefix)
{
// Look for expressions like
// aRect.Left() += ...;
// and replace with
// aRect.SetLeft( aRect.GetLeft() + ... );
SourceManager& SM = compiler.getSourceManager();
SourceLocation startLoc = SM.getExpansionLoc(binaryOp->getBeginLoc());
SourceLocation endLoc = SM.getExpansionLoc(binaryOp->getEndLoc());
const char* p1 = SM.getCharacterData(startLoc);
const char* p2 = SM.getCharacterData(endLoc);
if (p2 < p1) // clang is misbehaving, appears to be macro constant related
return false;
unsigned n = Lexer::MeasureTokenLength(endLoc, SM, compiler.getLangOpts());
std::string callText(p1, p2 - p1 + n);
auto originalLength = callText.size();
std::string regexOpname;
std::string replaceOpname;
switch (binaryOp->getOpcode())
{
case BO_RemAssign:
regexOpname = "\\%=";
replaceOpname = "%";
break;
case BO_MulAssign:
regexOpname = "\\*=";
replaceOpname = "*";
break;
case BO_DivAssign:
regexOpname = "\\/=";
replaceOpname = "/";
break;
default:
assert(false);
}
auto implicitObjectText = extractCode(call->getImplicitObjectArgument()->getExprLoc(),
call->getImplicitObjectArgument()->getExprLoc());
std::string reString(methodName + " *\\( *\\) *" + regexOpname);
auto newText = std::regex_replace(callText, std::regex(reString),
setPrefix + methodName + "( " + implicitObjectText + "."
+ methodName + "() " + replaceOpname + " (");
if (newText == callText)
{
report(DiagnosticsEngine::Warning, "binaryop-other regex match failed %0",
call->getBeginLoc())
<< reString;
return false;
}
// sometimes we end up with duplicate spaces after the opname
newText
= std::regex_replace(newText, std::regex(methodName + "\\(\\) \\" + replaceOpname + " "),
methodName + "() " + replaceOpname + " ");
newText += ") )";
return replaceText(startLoc, originalLength, newText);
}
bool ChangeToolsGen::ChangeUnaryOperator(UnaryOperator const* unaryOp,
CXXMemberCallExpr const* call,
std::string const& methodName)
{
// Look for expressions like
// aRect.Left()++;
// ++aRect.Left();
// and replace with
// aRect.MoveLeft( 1 );
SourceManager& SM = compiler.getSourceManager();
SourceLocation startLoc = SM.getExpansionLoc(unaryOp->getBeginLoc());
SourceLocation endLoc = SM.getExpansionLoc(unaryOp->getEndLoc());
const char* p1 = SM.getCharacterData(startLoc);
const char* p2 = SM.getCharacterData(endLoc);
if (p2 < p1) // clang is misbehaving, appears to be macro constant related
return false;
unsigned n = Lexer::MeasureTokenLength(endLoc, SM, compiler.getLangOpts());
std::string callText(p1, p2 - p1 + n);
auto originalLength = callText.size();
auto implicitObjectText = extractCode(call->getImplicitObjectArgument()->getExprLoc(),
call->getImplicitObjectArgument()->getExprLoc());
auto op = unaryOp->getOpcode();
std::string regexOpname;
std::string replaceOp;
switch (op)
{
case UO_PostInc:
case UO_PreInc:
replaceOp = "1";
regexOpname = "\\+\\+";
break;
case UO_PostDec:
case UO_PreDec:
replaceOp = "-1";
regexOpname = "\\-\\-";
break;
default:
assert(false);
}
std::string newText;
std::string reString;
if (op == UO_PostInc || op == UO_PostDec)
{
reString = methodName + " *\\( *\\) *" + regexOpname;
newText = std::regex_replace(callText, std::regex(reString),
"Adjust" + methodName + "( " + replaceOp);
}
else
{
newText = implicitObjectText + "." + "Adjust" + methodName + "( " + replaceOp;
}
if (newText == callText)
{
report(DiagnosticsEngine::Warning, "unaryop regex match failed %0", call->getBeginLoc())
<< reString;
return false;
}
newText += " )";
return replaceText(startLoc, originalLength, newText);
}
std::string ChangeToolsGen::extractCode(SourceLocation startLoc, SourceLocation endLoc)
{
SourceManager& SM = compiler.getSourceManager();
const char* p1 = SM.getCharacterData(SM.getExpansionLoc(startLoc));
const char* p2 = SM.getCharacterData(SM.getExpansionLoc(endLoc));
unsigned n = Lexer::MeasureTokenLength(endLoc, SM, compiler.getLangOpts());
return std::string(p1, p2 - p1 + n);
}
static loplugin::Plugin::Registration<ChangeToolsGen> X("changetoolsgen", false);
} // namespace
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */