[cfe-dev] Problem with retrieving the BinaryOperator RHS end location

Nat! via cfe-dev cfe-dev at lists.llvm.org
Tue May 7 15:15:23 PDT 2019


I copied your code, replacing ReplaceText with InsertText and RemoveText 
to make my function read now:


```

   bool VisitMemberExpr(MemberExpr *Expr) {
     TheRewriter.InsertText(Expr->getSourceRange().getBegin(), "getX(");
     TheRewriter.InsertText(Expr->getOperatorLoc(), ")");
     TheRewriter.RemoveText(SourceRange(Expr->getOperatorLoc(), 
Expr->getSourceRange().getEnd().getLocWithOffset(-1)));
     return true;
   }
```


and it works.  Thanks a lot Firat!

Nat!



On 07.05.19 14:38, Firat Kasmis wrote:
> I just made a branch on my tooling repo to attack your issue. See 
> https://github.com/firolino/clang-tool/blob/nat-example/src/transformer/nattransformer.cc 
>
>
> Just execute "bin/clang-tool ../examples/simple.cc --". It transforms
>
> int getXXY(struct _x *p)
> {
>     p->x->y;
>     return( p->x->x->y);
> }
>
> to
>
> int getXXY(struct _x *p)
> {
>     getX(p)->y;
>     return( getX(getX(p))->y);
> }
>
> Hopefully it helps you. I had the same issues as you have in the past 
> as well. Mostly cause of ReplaceText. So I stopped using it :)
>
>
> Am Di., 7. Mai 2019 um 13:18 Uhr schrieb Nat! via cfe-dev 
> <cfe-dev at lists.llvm.org <mailto:cfe-dev at lists.llvm.org>>:
>
>     For ease of reproduction I put everything together into a small
>     github
>     project.
>
>
>     https://github.com/mulle-nat-stash/cfe-mailinglist
>
>
>     Assuming I didn't goof completely, I can think of three ways this
>     could
>     be fixable:
>
>     * abort visiting after the first substitution and reparse
>     everything (I
>     don't know how to do this, but I can probably find out)
>
>     * hack the AST on the fly to represent the change from MemberExpr
>     to a
>     FunctionCall (I don't know if this would help at all)
>
>     * get a second SourceManager on the RewriteBuffer and calculate
>     extents
>     with it (I don't know how and if this is possible either)
>
>
>     Ciao
>
>         Nat!
>
>
>     On 05.05.19 13:33, Nat! via cfe-dev wrote:
>     > I think this is the simplest code, that exhibits my problem.
>     >
>     > This code rewrites direct struct access like p->x
>     >
>     > into a C function-call getX( p).
>     >
>     >
>     > ```
>     >
>     >
>     //------------------------------------------------------------------------------
>
>     >
>     > // Tooling sample. Demonstrates:
>     > //
>     > // * How to write a simple source tool using libTooling.
>     > // * How to use RecursiveASTVisitor to find interesting AST nodes.
>     > // * How to use the Rewriter API to rewrite the source code.
>     > //
>     > // Eli Bendersky (eliben at gmail.com <mailto:eliben at gmail.com>)
>     > // This code is in the public domain
>     >
>     //------------------------------------------------------------------------------
>
>     >
>     > #include <sstream>
>     > #include <string>
>     >
>     > #include "clang/AST/AST.h"
>     > #include "clang/AST/ASTConsumer.h"
>     > #include "clang/AST/RecursiveASTVisitor.h"
>     > #include "clang/Frontend/ASTConsumers.h"
>     > #include "clang/Frontend/CompilerInstance.h"
>     > #include "clang/Frontend/FrontendActions.h"
>     > #include "clang/Rewrite/Core/Rewriter.h"
>     > #include "clang/Tooling/CommonOptionsParser.h"
>     > #include "clang/Tooling/Tooling.h"
>     > #include "llvm/Support/raw_ostream.h"
>     >
>     > using namespace clang;
>     > using namespace clang::driver;
>     > using namespace clang::tooling;
>     >
>     > static llvm::cl::OptionCategory ToolingSampleCategory("Tooling
>     Sample");
>     >
>     > // By implementing RecursiveASTVisitor, we can specify which AST
>     nodes
>     > // we're interested in by overriding relevant methods.
>     > class MyASTVisitor : public RecursiveASTVisitor<MyASTVisitor> {
>     > public:
>     >   MyASTVisitor(Rewriter &R) : TheRewriter(R) {}
>     >
>     >   bool VisitMemberExpr(MemberExpr *Expr) {
>     >     SourceManager   *SM;
>     >     int             length;
>     >     const char      *startBuf;
>     >     const char      *endBuf;
>     >     const char      *opBuf;
>     >
>     >     SM       = &TheRewriter.getSourceMgr();
>     >     startBuf = SM->getCharacterData( Expr->getBeginLoc());
>     >     opBuf    = SM->getCharacterData( Expr->getOperatorLoc());
>     >     endBuf   = SM->getCharacterData( Expr->getEndLoc());
>     >     length   = endBuf - startBuf;
>     >
>     >     std::string  front( startBuf, opBuf - startBuf);
>     >
>     >     length  += Lexer::MeasureTokenLength( Expr->getEndLoc(), *SM,
>     > TheRewriter.getLangOpts());
>     >
>     >     std::string  origin( startBuf, length);
>     >     std::string  replace;
>     >
>     >     replace = std::string( "getX(") + front + std::string( ")");
>     >
>     >     fprintf( stderr, "rewrite: \"%s\" -> \"%s\"\n", origin.c_str(),
>     > replace.c_str());
>     >     TheRewriter.ReplaceText( Expr->getBeginLoc(), length, replace);
>     >
>     >     return true;
>     >   }
>     >
>     > private:
>     >   Rewriter &TheRewriter;
>     > };
>     >
>     >
>     > // Implementation of the ASTConsumer interface for reading an AST
>     > produced
>     > // by the Clang parser.
>     > class MyASTConsumer : public ASTConsumer {
>     > public:
>     >   MyASTConsumer(Rewriter &R) : Visitor(R) {}
>     >
>     >   // Override the method that gets called for each parsed top-level
>     >   // declaration.
>     >   bool HandleTopLevelDecl(DeclGroupRef DR) override {
>     >     for (DeclGroupRef::iterator b = DR.begin(), e = DR.end(); b
>     != e;
>     > ++b) {
>     >       // Traverse the declaration using our AST visitor.
>     >       Visitor.TraverseDecl(*b);
>     >       (*b)->dump();
>     >     }
>     >     return true;
>     >   }
>     >
>     > private:
>     >   MyASTVisitor Visitor;
>     > };I changed my theory is,
>     >
>     > // For each source file provided to the tool, a new
>     FrontendAction is
>     > created.
>     > class MyFrontendAction : public ASTFrontendAction {
>     > public:
>     >   MyFrontendAction() {}
>     >   void EndSourceFileAction() override {
>     >     SourceManager &SM = TheRewriter.getSourceMgr();
>     >     llvm::errs() << "** EndSourceFileAction for: "
>     >                  <<
>     > SM.getFileEntryForID(SM.getMainFileID())->getName() << "\n";
>     >
>     >     // Now emit the rewritten buffer.
>     > TheRewriter.getEditBuffer(SM.getMainFileID()).write(llvm::outs());
>     >   }
>     >
>     >   std::unique_ptr<ASTConsumer>
>     CreateASTConsumer(CompilerInstance &CI,
>     > StringRef file)
>     > override {
>     >     llvm::errs() << "** Creating AST consumer for: " << file <<
>     "\n";
>     >     TheRewriter.setSourceMgr(CI.getSourceManager(),
>     CI.getLangOpts());
>     >     return llvm::make_unique<MyASTConsumer>(TheRewriter);
>     >   }
>     >
>     > private:
>     >   Rewriter TheRewriter;
>     > };
>     >
>     > int main(int argc, const char **argv) {
>     >   CommonOptionsParser op(argc, argv, ToolingSampleCategory);
>     >   ClangTool Tool(op.getCompilations(), op.getSourcePathList());
>     >
>     >   return
>     Tool.run(newFrontendActionFactory<MyFrontendAction>().get());
>     > }
>     > ```
>     >
>     > With input
>     >
>     >
>     > ```
>     >
>     > struct _x
>     > {
>     >    struct _x  *x;
>     >    int        y;
>     > };
>     >
>     >
>     > int   getXXY( struct _x *p)
>     > {
>     >    return( p->x->x->y);
>     > }
>     >
>     > ```
>     >
>     > it produces
>     >
>     > ```
>     >
>     > RecordDecl 0x27cdad8
>     </usr/local/llvm/srcL/llvm-clang-samples/z.c:1:1,
>     > line:5:1> line:1:8 struct _x definition
>     > |-FieldDecl 0x282a620 <line:3:4, col:16> col:16 x 'struct _x *'
>     > `-FieldDecl 0x282a680 <line:4:4, col:15> col:15 y 'int'
>     > rewrite: "p->x->x->y" -> "getX(p->x->x)"
>     > rewrite: "p->x->x" -> "getX(p->x)"
>     > rewrite: "p->x" -> "getX(p)"
>     > FunctionDecl 0x282a7e0
>     > </usr/local/llvm/srcL/llvm-clang-samples/z.c:8:1, line:11:1>
>     line:8:7
>     > getXXY 'int (struct _x *)'
>     > |-ParmVarDecl 0x282a6f0 <col:15, col:26> col:26 used p 'struct _x *'
>     > `-CompoundStmt 0x282aa00 <line:9:1, line:11:1>
>     >   `-ReturnStmt 0x282a9f0 <line:10:4, col:22>
>     >     `-ImplicitCastExpr 0x282a9d8 <col:10, col:22> 'int'
>     <LValueToRValue>
>     >       `-ParenExpr 0x282a9b8 <col:10, col:22> 'int' lvalue
>     >         `-MemberExpr 0x282a988 <col:12, col:21> 'int' lvalue ->y
>     > 0x282a680
>     >           `-ImplicitCastExpr 0x282a970 <col:12, col:18> 'struct
>     _x *'
>     > <LValueToRValue>
>     >             `-MemberExpr 0x282a940 <col:12, col:18> 'struct _x *'
>     > lvalue ->x 0x282a620
>     >               `-ImplicitCastExpr 0x282a928 <col:12, col:15>
>     'struct _x
>     > *' <LValueToRValue>
>     >                 `-MemberExpr 0x282a8f8 <col:12, col:15> 'struct
>     _x *'
>     > lvalue ->x 0x282a620
>     >                   `-ImplicitCastExpr 0x282a8e0 <col:12> 'struct
>     _x *'
>     > <LValueToRValue>
>     >                     `-DeclRefExpr 0x282a8c0 <col:12> 'struct _x *'
>     > lvalue ParmVar 0x282a6f0 'p' 'struct _x *'
>     > ** EndSourceFileAction for:
>     /usr/local/llvm/srcL/llvm-clang-samples/z.c
>     > struct _x
>     > {
>     >    struct _x  *x;
>     >    int        y;
>     > };
>     >
>     >
>     > int   getXXY( struct _x *p)
>     > {
>     >    return( getX(p)(p->x)>x->x));
>     > }
>     >
>     > ```
>     >
>     > From a few more tests along the way, my current pet theory is, that
>     > the change from member access to a function call is tripping
>     something
>     > up. If I rewrite just the righthand side of the expression, it
>     works OK.
>     >
>     >
>     > Ciao
>     >
>     >    Nat!
>     >
>     >
>     > On 04.05.19 14:28, Stephen Kelly via cfe-dev wrote:
>     >> On 03/05/2019 14:28, Nat! via cfe-dev wrote:
>     >>> It turns out the fix suggestion - at least as I implemented it -
>     >>> works for simple cases, but not in general.
>     >>>
>     >>> ```
>     >>> endBuf  = SM->getCharacterData( Stmt->getRHS()->getEndLoc());
>     >>> endBuf += Lexer::MeasureTokenLength( Stmt->getEndLoc(), *SM,
>     >>> TheRewriter.getLangOpts());
>     >>> ```
>     >>>
>     >>> I believe the problem is that the SourceManager I am using
>     >>> to "measure" the length is looking at the unmodified
>     sourcecode. But
>     >>> the RewriteBuffer may already contain changes.
>     >>>
>     >>> So in my example if I am looking at `x = yyy` at the source but
>     >>> in the rewrite buffer its now `x=yyy_renamed` it will not take the
>     >>> extra characters into account.
>     >>>
>     >>> I should be measuring the contents of the RewriteBuffer instead,
>     >>> with likely another SourceManager, but I lack the expertise to set
>     >>> this up.
>     >>
>     >> Can you post a more-complete sscce example of what you are
>     trying to do?
>     >>
>     >> Thanks,
>     >>
>     >> Stephen
>     >>
>     >> _______________________________________________
>     >> cfe-dev mailing list
>     >> cfe-dev at lists.llvm.org <mailto:cfe-dev at lists.llvm.org>
>     >> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
>     > _______________________________________________
>     > cfe-dev mailing list
>     > cfe-dev at lists.llvm.org <mailto:cfe-dev at lists.llvm.org>
>     > https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
>     _______________________________________________
>     cfe-dev mailing list
>     cfe-dev at lists.llvm.org <mailto:cfe-dev at lists.llvm.org>
>     https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20190508/bde4b933/attachment.html>


More information about the cfe-dev mailing list