17

コード内のすべてのメッセージを書き直したいのですが、セレクターのみを置き換える必要がありますが、ネストされた式 fe を置き換えることができる必要があります。

[super foo:[someInstance someMessage:@""] foo2:[someInstance someMessage2]];

新しい文字列を生成しようとしましclang::Rewriter replaceTextたが、問題があります。ネストされたメッセージを古い位置に置き換えるため、セレクターの長さを変更すると機能しません。

だから、私は使用する必要があると仮定しましたclang::Rewriter ReplaceStmt(originalStatement, newStatement);

すべてのメッセージにアクセスするために使用RecursiveASTVisitorしています。これらのメッセージ オブジェクトをコピーして、セレクターを置き換えたいと考えています。

どうやってやるの?

使用してみましたが、非常に多くの引数があり、元のメッセージからパラメーターをObjCMessageExpr::Create取得する方法がわかりません。ASTContext &Context and ArrayRef<SourceLocation> SeLocsExpr *Receiver

clang ツール (clang ツール インターフェイス) を使用して、ネストされたメッセージのセレクターを置き換える適切な方法は何ですか?

アップデート:

ReplaceStmtWithStmtコールバックとを使用する必要がありASTMatchFinderますか?

アップデート:

次の関数を使用して、ファイル内のテキストを書き換えています。

void ReplaceText(SourceLocation start, unsigned originalLength, StringRef string) { 
    m_rewriter.ReplaceText(start, originalLength, string); 
    m_rewriter.overwriteChangedFiles(); 
} 

そして、コード内のすべての messageExpr を新しいセレクター fe: に置き換えたい:

[object someMessage:[object2 someMessage:obj3 calculate:obj4]]; 

それがどうあるべきか:

[object newSelector:[object2 newSelector:obj3 newSelector:obj4]]; 

ReqoursiveASTVisitor を使用しています:

bool VisitStmt(Stmt *statement) { 
    if (ObjCMessageExpr *messageExpr = dyn_cast<ObjCMessageExpr>(statement)) { 
         ReplaceMessage(*messageExpr) 
    } 
    return true; 
} 

新しいメッセージ 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); 
} 

次のように、ネストされたメッセージを置き換えようとすると、問題が発生します。

[simpleClass doSomeActionWithString:string3 andAnotherString:string4]; 
[simpleClass doSomeActionWithString:str andAnotherString:str2]; 
[simpleClass doSomeActionWithString:@"" andAnotherString:@"asdasdsad"]; 
[simpleClass setSimpleClassZAZAZAZAZAZAZAZA:[simpleClass getSimpleClassZAZAZAZAZAZAZAZA]];

結果は次のとおりです。

[simpleClass newSelector:string3 newSelector:string4 ]; 
[simpleClass newSelector:str newSelector:str2 ]; 
[simpleClass newSelector:@"" newSelector:@"asdasdsad" ]; 
[simpleClass newSelector:[simpleClass getSimp[simpleClass newSelector]]; 

messageExpression には「古い」値がgetLocStart();あり、getLocEnd();どうすれば修正できますか?

4

1 に答える 1

4

セレクター名の連続部分のみを置き換えることで、セレクター名を書き換えることができます。例えば、下線部のみ差し替え

[object someMessage:[object2 someMessage:obj3 calculate:obj4]];
        ^~~~~~~~~~~          ^~~~~~~~~~~      ^~~~~~~~~

これを達成するために必要なのは

  • セレクターパーツ数 -ObjCMessageExpr::getNumSelectorLocs()
  • 彼らの場所 -ObjCMessageExpr::getSelectorLoc(index)
  • それらの長さ - ObjCMessageExpr::getSelector().getNameForSlot(index).size().

全体として、次の RecursiveASTVisitor で ObjCMessageExpr を書き換えることができます。

#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
于 2014-02-07T16:51:09.340 に答える