/* -*- 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: */
Messung V0.5 C=90 H=98 G=94
¤ Dauer der Verarbeitung: 0.1 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland