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

Firat Kasmis via cfe-dev cfe-dev at lists.llvm.org
Tue May 7 05:38:13 PDT 2019


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>:

> 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)
> > // 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
> >> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
> > _______________________________________________
> > cfe-dev mailing list
> > 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
> 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/20190507/e1a5ca5f/attachment.html>


More information about the cfe-dev mailing list