c++ - Herramienta de Clang: reescribir ObjCMessageExpr
objective-c selector (1)
Quiero reescribir todos los mensajes en mi código, necesito reemplazar solo los selectores, pero necesito poder reemplazar las expresiones anidadas fe:
[super foo:[someInstance someMessage:@""] foo2:[someInstance someMessage2]];
Intenté hacerlo con clang::Rewriter replaceText
y solo generar una nueva cadena, pero hay un problema: no funcionaría si cambio la longitud de los selectores, porque sustituyo los mensajes anidados con esas posiciones antiguas.
Entonces, asumí que necesito usar clang::Rewriter ReplaceStmt(originalStatement, newStatement);
Estoy usando RecursiveASTVisitor
para visitar todos los mensajes, y quiero copiar esos objetos de mensajes y reemplazar los selectores:
¿Cómo puedo hacer eso?
Intenté usar ObjCMessageExpr::Create
pero hay tantos argumentos, no sé cómo obtener los ASTContext &Context
and ArrayRef<SourceLocation> SeLocs
y Expr *Receiver
ASTContext &Context
and ArrayRef<SourceLocation> SeLocs
del mensaje original.
¿Cuál es la forma correcta de reemplazar los selectores en los mensajes anidados usando la herramienta de Clang (interfaz de herramientas de Clang)?
Actualizar:
¿Debo usar la ReplaceStmtWithStmt
llamada ASTMatchFinder
y ASTMatchFinder
?
Actualizar:
Estoy usando la siguiente función para reescribir el texto en el archivo:
void ReplaceText(SourceLocation start, unsigned originalLength, StringRef string) {
m_rewriter.ReplaceText(start, originalLength, string);
m_rewriter.overwriteChangedFiles();
}
Y quiero reemplazar todos los mensajes EXPR en código con el nuevo selector fe: cómo fue:
[object someMessage:[object2 someMessage:obj3 calculate:obj4]];
como debería ser:
[object newSelector:[object2 newSelector:obj3 newSelector:obj4]];
Estoy usando ReqoursiveASTVisitor:
bool VisitStmt(Stmt *statement) {
if (ObjCMessageExpr *messageExpr = dyn_cast<ObjCMessageExpr>(statement)) {
ReplaceMessage(*messageExpr)
}
return true;
}
Creé un método para generar un nuevo mensaje de cadena expr:
string StringFromObjCMessageExpr(ObjCMessageExpr& messageExpression) {
std::ostringstream stringStream;
const string selectorString = messageExpression.getSelector().getAsString();
cout << selectorString << endl;
vector<string> methodParts;
split(selectorString, ParametersDelimiter, methodParts);
stringStream << "[" ;
const string receiver = GetStringFromLocations(m_compiler, messageExpression.getReceiverRange().getBegin(), messageExpression.getSelectorStartLoc());
stringStream << receiver;
clang::ObjCMessageExpr::arg_iterator argIterator = messageExpression.arg_begin();
for (vector<string>::const_iterator partsIterator = methodParts.begin();
partsIterator != methodParts.end();
++partsIterator) {
stringStream << "newSelector";
if (messageExpression.getNumArgs() != 0) {
const clang::Stmt *argument = *argIterator;
stringStream << ":" << GetStatementString(*argument) << " ";
++argIterator;
}
}
stringStream << "]";
return stringStream.str();
}
void ReplaceMessage(ObjCMessageExpr& messageExpression) {
SourceLocation locStart = messageExpression.getLocStart();
SourceLocation locEnd = messageExpression.getLocEnd();
string newExpr = StringFromObjCMessageExpr(messageExpression);
const int exprStringLegth = m_rewriter.getRangeSize(SourceRange(locStart, locEnd));
ReplaceText(locStart, exprStringLegth, newExpr);
}
El problema ocurre cuando trato de reemplazar los mensajes anidados, de esa manera:
[simpleClass doSomeActionWithString:string3 andAnotherString:string4];
[simpleClass doSomeActionWithString:str andAnotherString:str2];
[simpleClass doSomeActionWithString:@"" andAnotherString:@"asdasdsad"];
[simpleClass setSimpleClassZAZAZAZAZAZAZAZA:[simpleClass getSimpleClassZAZAZAZAZAZAZAZA]];
el resultado es:
[simpleClass newSelector:string3 newSelector:string4 ];
[simpleClass newSelector:str newSelector:str2 ];
[simpleClass newSelector:@"" newSelector:@"asdasdsad" ];
[simpleClass newSelector:[simpleClass getSimp[simpleClass newSelector]];
porque messageExpression tiene el valor "antiguo" de getLocStart();
y getLocEnd();
¿Cómo puedo arreglarlo?
Puede volver a escribir el nombre del selector reemplazando solo partes continuas del nombre del selector. Por ejemplo, reemplazar solo las partes subrayadas.
[object someMessage:[object2 someMessage:obj3 calculate:obj4]];
^~~~~~~~~~~ ^~~~~~~~~~~ ^~~~~~~~~
Para lograrlo solo necesitas
- de piezas de selector -
ObjCMessageExpr::getNumSelectorLocs()
- sus ubicaciones -
ObjCMessageExpr::getSelectorLoc(index)
- sus longitudes -
ObjCMessageExpr::getSelector().getNameForSlot(index).size()
.
En general, puede volver a escribir ObjCMessageExpr con el siguiente RecursiveASTVisitor:
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Rewrite/Core/Rewriter.h"
namespace clang_tooling
{
using clang::SourceLocation;
class RewritingVisitor : public clang::ASTConsumer,
public clang::RecursiveASTVisitor<RewritingVisitor>
{
public:
// You can obtain SourceManager and LangOptions from CompilerInstance when
// you are creating visitor (which is also ASTConsumer) in
// clang::ASTFrontendAction::CreateASTConsumer.
RewritingVisitor(clang::SourceManager &sourceManager,
const clang::LangOptions &langOptions)
: _sourceManager(sourceManager), _rewriter(sourceManager, langOptions)
{}
virtual void HandleTranslationUnit(clang::ASTContext &context)
{
TraverseDecl(context.getTranslationUnitDecl());
_rewriter.overwriteChangedFiles();
}
bool VisitObjCMessageExpr(clang::ObjCMessageExpr *messageExpr)
{
if (_sourceManager.isInMainFile(messageExpr->getLocStart()))
{
clang::Selector selector = messageExpr->getSelector();
for (unsigned i = 0, end = messageExpr->getNumSelectorLocs();
i < end; ++i)
{
SourceLocation selectorLoc = messageExpr->getSelectorLoc(i);
_rewriter.ReplaceText(selectorLoc,
selector.getNameForSlot(i).size(),
"newSelector");
}
}
return Base::VisitObjCMessageExpr(messageExpr);
}
private:
typedef clang::RecursiveASTVisitor<RewritingVisitor> Base;
clang::SourceManager &_sourceManager;
clang::Rewriter _rewriter;
};
} // end namespace clang_tooling