From c07b913cc6f2ad29863c0c9a50bd3b6bdd849569 Mon Sep 17 00:00:00 2001 From: Noel Grandin Date: Thu, 8 Feb 2018 15:51:39 +0200 Subject: add setter methods to tools::Rectangle intended to replace the existing methods that return mutable ref And add the clang rewriter I will use to do most of the work. Change-Id: If00be13da5c16c07671cedbcfea21528bac33c8a Reviewed-on: https://gerrit.libreoffice.org/49430 Tested-by: Jenkins Reviewed-by: Noel Grandin --- compilerplugins/clang/changerectanglegetref.cxx | 268 ++++++++++++++++++++++++ 1 file changed, 268 insertions(+) create mode 100644 compilerplugins/clang/changerectanglegetref.cxx (limited to 'compilerplugins') diff --git a/compilerplugins/clang/changerectanglegetref.cxx b/compilerplugins/clang/changerectanglegetref.cxx new file mode 100644 index 000000000000..b943e9f4516c --- /dev/null +++ b/compilerplugins/clang/changerectanglegetref.cxx @@ -0,0 +1,268 @@ +/* -*- 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 + +/** + * Changes calls to tools::Rectangle methods that return a ref to instead call the setter methods. + * + * run as: + * make COMPILER_PLUGIN_TOOL=changerectanglegetref UPDATE_FILES=all FORCE_COMPILE_ALL=1 + * or + * make COMPILER_PLUGIN_TOOL=changerectanglegetref FORCE_COMPILE_ALL=1 + */ + +namespace +{ +class ChangeRectangleGetRef : public RecursiveASTVisitor, + public loplugin::RewritePlugin +{ +public: + explicit ChangeRectangleGetRef(loplugin::InstantiationData const& data) + : RewritePlugin(data) + { + } + virtual void run() override; + bool VisitCXXMemberCallExpr(CXXMemberCallExpr const* call); + +private: + bool ChangeAssignment(Stmt const* parent, std::string const& methodName); + bool ChangeBinaryOperator(BinaryOperator const* parent, CXXMemberCallExpr const* call, + std::string const& methodName); + bool ChangeUnaryOperator(UnaryOperator const* parent, CXXMemberCallExpr const* call, + std::string const& methodName); + std::string extractCode(SourceLocation startLoc, SourceLocation endLoc); +}; + +void ChangeRectangleGetRef::run() +{ + TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); +} + +bool ChangeRectangleGetRef::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; + if (dc.Function("Top").Class("Rectangle").Namespace("tools").GlobalNamespace()) + methodName = "Top"; + else if (dc.Function("Bottom").Class("Rectangle").Namespace("tools").GlobalNamespace()) + methodName = "Bottom"; + else if (dc.Function("Left").Class("Rectangle").Namespace("tools").GlobalNamespace()) + methodName = "Left"; + else if (dc.Function("Right").Class("Rectangle").Namespace("tools").GlobalNamespace()) + methodName = "Right"; + else + return true; + if (!loplugin::TypeCheck(func->getReturnType()).LvalueReference()) + return true; + + auto parent = getParentStmt(call); + if (!parent) + return true; + if (auto unaryOp = dyn_cast(parent)) + { + if (!ChangeUnaryOperator(unaryOp, call, methodName)) + report(DiagnosticsEngine::Warning, "Could not fix this one1", call->getLocStart()); + return true; + } + auto binaryOp = dyn_cast(parent); + if (!binaryOp) + { + // parent->dump(); + // report(DiagnosticsEngine::Warning, "Could not fix this one3", call->getLocStart()); + return true; + } + auto opcode = binaryOp->getOpcode(); + if (opcode == BO_Assign) + { + if (!ChangeAssignment(parent, methodName)) + report(DiagnosticsEngine::Warning, "Could not fix this one4", call->getLocStart()); + return true; + } + if (opcode == BO_RemAssign || opcode == BO_AddAssign || opcode == BO_SubAssign + || opcode == BO_MulAssign || opcode == BO_DivAssign) + { + if (!ChangeBinaryOperator(binaryOp, call, methodName)) + report(DiagnosticsEngine::Warning, "Could not fix this one5", call->getLocStart()); + return true; + } + return true; +} + +bool ChangeRectangleGetRef::ChangeAssignment(Stmt const* parent, std::string const& methodName) +{ + // Look for expressions like + // aRect.Left() = ...; + // and replace with + // aRect.SetLeft( ... ); + SourceManager& SM = compiler.getSourceManager(); + SourceLocation startLoc = parent->getLocStart(); + SourceLocation endLoc = parent->getLocEnd(); + 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 + "\\(\\) *="), + "Set" + methodName + "("); + if (newText == callText) + return false; + newText += " )"; + + return replaceText(startLoc, originalLength, newText); +} + +bool ChangeRectangleGetRef::ChangeBinaryOperator(BinaryOperator const* binaryOp, + CXXMemberCallExpr const* call, + std::string const& methodName) +{ + // Look for expressions like + // aRect.Left() += ...; + // and replace with + // aRect.SetLeft( aRect.GetLeft() + ... ); + SourceManager& SM = compiler.getSourceManager(); + SourceLocation startLoc = binaryOp->getLocStart(); + SourceLocation endLoc = binaryOp->getLocEnd(); + 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_AddAssign: + regexOpname = "\\+="; + replaceOpname = "+"; + break; + case BO_SubAssign: + 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()); + auto newText = std::regex_replace(callText, std::regex(methodName + "\\(\\) *" + regexOpname), + "Set" + methodName + "( " + implicitObjectText + ".Get" + + methodName + "() " + replaceOpname + " "); + if (newText == callText) + return false; + // sometimes we end up with duplicate spaces after the opname + newText = std::regex_replace( + newText, std::regex("Get" + methodName + "\\(\\) \\" + replaceOpname + " "), + "Get" + methodName + "() " + replaceOpname + " "); + newText += " )"; + + return replaceText(startLoc, originalLength, newText); +} + +bool ChangeRectangleGetRef::ChangeUnaryOperator(UnaryOperator const* unaryOp, + CXXMemberCallExpr const* call, + std::string const& methodName) +{ + // Look for expressions like + // aRect.Left()++; + // and replace with + // aRect.SetLeft( ++aRect.GetLeft() ); + SourceManager& SM = compiler.getSourceManager(); + SourceLocation startLoc = unaryOp->getLocStart(); + SourceLocation endLoc = unaryOp->getLocEnd(); + 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 replaceOpname; + switch (op) + { + case UO_PostInc: + case UO_PreInc: + replaceOpname = "++"; + regexOpname = "\\+\\+"; + break; + case UO_PostDec: + case UO_PreDec: + replaceOpname = "--"; + regexOpname = "\\-\\-"; + break; + default: + assert(false); + } + if (op == UO_PostInc || op == UO_PostDec) + { + auto newText + = std::regex_replace(callText, std::regex(methodName + "\\(\\) *" + regexOpname), + "Set" + methodName + "( " + replaceOpname + implicitObjectText + + ".Get" + methodName + "()"); + return replaceText(startLoc, originalLength, newText); + } + else + { + auto newText + = std::regex_replace(callText, std::regex(regexOpname + " *" + methodName + "\\(\\)"), + "Set" + methodName + "( " + replaceOpname + implicitObjectText + + ".Get" + methodName + "()"); + return replaceText(startLoc, originalLength, newText); + } +} + +std::string ChangeRectangleGetRef::extractCode(SourceLocation startLoc, SourceLocation endLoc) +{ + SourceManager& SM = compiler.getSourceManager(); + const char* p1 = SM.getCharacterData(startLoc); + const char* p2 = SM.getCharacterData(endLoc); + unsigned n = Lexer::MeasureTokenLength(endLoc, SM, compiler.getLangOpts()); + return std::string(p1, p2 - p1 + n); +} + +static loplugin::Plugin::Registration X("changerectanglegetref", false); + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit