Index: lib/Rewrite/RewriteScopedRefptr.cpp |
=================================================================== |
--- lib/Rewrite/RewriteScopedRefptr.cpp (revision 0) |
+++ lib/Rewrite/RewriteScopedRefptr.cpp (revision 0) |
@@ -0,0 +1,415 @@ |
+//===--- RewriteScopedRefptr.cpp - Playground for the code rewriter ---------------===// |
+// |
+// The LLVM Compiler Infrastructure |
+// |
+// This file is distributed under the University of Illinois Open Source |
+// License. See LICENSE.TXT for details. |
+// |
+//===----------------------------------------------------------------------===// |
+// |
+// Hacks and fun related to the code rewriter. |
+// |
+//===----------------------------------------------------------------------===// |
+ |
+#include "clang/Rewrite/ASTConsumers.h" |
+#include "clang/Rewrite/Rewriter.h" |
+#include "clang/AST/AST.h" |
+#include "clang/AST/ASTConsumer.h" |
+#include "clang/AST/ParentMap.h" |
+#include "clang/Basic/SourceManager.h" |
+#include "clang/Basic/IdentifierTable.h" |
+#include "clang/Basic/Diagnostic.h" |
+#include "clang/Lex/Lexer.h" |
+#include "llvm/Support/MemoryBuffer.h" |
+#include "llvm/Support/raw_ostream.h" |
+#include "llvm/ADT/StringExtras.h" |
+#include "llvm/ADT/SmallPtrSet.h" |
+#include "llvm/ADT/OwningPtr.h" |
+#include "llvm/ADT/DenseSet.h" |
+ |
+#include <set> |
+ |
+using namespace clang; |
+ |
+namespace { |
+ class RewriteScopedRefptr : public ASTConsumer { |
+ |
+ Rewriter Rewrite; |
+ Diagnostic &Diags; |
+ const LangOptions &LangOpts; |
+ unsigned RewriteFailedDiag; |
+ |
+ ASTContext *Context; |
+ SourceManager *SM; |
+ TranslationUnitDecl *TUDecl; |
+ FileID MainFileID; |
+ const char *MainFileStart, *MainFileEnd; |
+ |
+ std::string InFileName; |
+ llvm::raw_ostream* OutFile; |
+ |
+ // Rarely, the same function gets processed twice (I think because clang |
+ // accidentally assigns the declaration a body too under certain |
+ // circumstances). These set makes sure we process nothing twice. |
+ std::set<VarDecl*> RewrittenDecls; |
+ std::set<Expr*> RewrittenExprs; |
+ |
+ bool SilenceRewriteMacroWarning; |
+ |
+ bool DisableReplaceStmt; |
+ |
+ std::string Result; |
+ public: |
+ // Top Level Driver code. |
+ virtual void Initialize(ASTContext &context); |
+ virtual void HandleTopLevelDecl(DeclGroupRef D) { |
+ for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) |
+ HandleTopLevelSingleDecl(*I); |
+ } |
+ |
+ void HandleTopLevelSingleDecl(Decl *D); |
+ void HandleDeclInMainFile(Decl *D); |
+ RewriteScopedRefptr(std::string inFile, llvm::raw_ostream *OS, |
+ Diagnostic &D, const LangOptions &LOpts, |
+ bool silenceMacroWarn); |
+ |
+ ~RewriteScopedRefptr() {} |
+ |
+ virtual void HandleTranslationUnit(ASTContext &C); |
+ |
+ void InsertText(SourceLocation Loc, llvm::StringRef Str, |
+ bool InsertAfter = true) { |
+ // If insertion succeeded or warning disabled return with no warning. |
+ if (!Rewrite.InsertText(Loc, Str, InsertAfter) || |
+ SilenceRewriteMacroWarning) |
+ return; |
+ |
+ Diags.Report(Context->getFullLoc(Loc), RewriteFailedDiag); |
+ } |
+ |
+ // Expression Rewriting. |
+ Stmt *RewriteFunctionBodyOrGlobalInitializer(Stmt *S); |
+ |
+ bool RewriteDeclStmt(DeclStmt *DS); |
+ }; |
+} |
+ |
+RewriteScopedRefptr::RewriteScopedRefptr(std::string inFile, llvm::raw_ostream* OS, |
+ Diagnostic &D, const LangOptions &LOpts, |
+ bool silenceMacroWarn) |
+ : Diags(D), LangOpts(LOpts), InFileName(inFile), OutFile(OS), |
+ SilenceRewriteMacroWarning(silenceMacroWarn) { |
+ RewriteFailedDiag = Diags.getCustomDiagID(Diagnostic::Warning, |
+ "rewriting sub-expression within a macro (may not be correct)"); |
+} |
+ |
+ASTConsumer *clang::CreateScopedRefptrRewriter( |
+ const std::string& InFile, |
+ llvm::raw_ostream* OS, |
+ Diagnostic &Diags, |
+ const LangOptions &LOpts, |
+ bool SilenceRewriteMacroWarning) { |
+//fprintf(stderr, " OMG OMG OMG \n"); |
+ return new RewriteScopedRefptr(InFile, OS, Diags, LOpts, SilenceRewriteMacroWarning); |
+} |
+ |
+void RewriteScopedRefptr::Initialize(ASTContext &context) { |
+ Context = &context; |
+ SM = &Context->getSourceManager(); |
+ TUDecl = Context->getTranslationUnitDecl(); |
+ DisableReplaceStmt = false; |
+ |
+ // Get the ID and start/end of the main file. |
+ MainFileID = SM->getMainFileID(); |
+ const llvm::MemoryBuffer *MainBuf = SM->getBuffer(MainFileID); |
+ MainFileStart = MainBuf->getBufferStart(); |
+ MainFileEnd = MainBuf->getBufferEnd(); |
+ |
+ Rewrite.setSourceMgr(Context->getSourceManager(), Context->getLangOptions()); |
+} |
+ |
+ |
+//===----------------------------------------------------------------------===// |
+// Top Level Driver Code |
+//===----------------------------------------------------------------------===// |
+ |
+void RewriteScopedRefptr::HandleTopLevelSingleDecl(Decl *D) { |
+ if (Diags.hasErrorOccurred()) |
+ return; |
+ |
+ // Two cases: either the decl could be in the main file, or it could be in a |
+ // #included file. If the former, rewrite it now. If the later, check to see |
+ // if we rewrote the #include/#import. |
+ SourceLocation Loc = D->getLocation(); |
+ Loc = SM->getInstantiationLoc(Loc); |
+ |
+ // If this is for a builtin, ignore it. |
+ if (Loc.isInvalid()) return; |
+ |
+ // Look for built-in declarations that we need to refer during the rewrite. |
+ if (LinkageSpecDecl *LSD = dyn_cast<LinkageSpecDecl>(D)) { |
+ // Recurse into linkage specifications |
+ for (DeclContext::decl_iterator DI = LSD->decls_begin(), |
+ DIEnd = LSD->decls_end(); |
+ DI != DIEnd; ++DI) |
+ HandleTopLevelSingleDecl(*DI); |
+ } |
+ // If we have a decl in the main file, see if we should rewrite it. |
+ if (SM->isFromMainFile(Loc)) |
+ return HandleDeclInMainFile(D); |
+} |
+ |
+//===----------------------------------------------------------------------===// |
+// Function Body / Expression rewriting |
+//===----------------------------------------------------------------------===// |
+ |
+static bool HasTypeScopedRefptr(QualType T) { |
+ Type *TT = T.getTypePtr(); |
+ CXXRecordDecl *decl = TT->getAsCXXRecordDecl(); |
+ if (!decl) return false; |
+ return decl->getDeclName().getAsString() == "scoped_refptr"; |
+} |
+ |
+static Expr* GetRawPtrInit(Expr *Init) { |
+ if (!Init) return 0; |
+ |
+ // (Skip any temporary bindings; they're implicit.) |
+ if (CXXExprWithTemporaries *Binder = dyn_cast<CXXExprWithTemporaries>(Init)) |
+ Init = Binder->getSubExpr(); |
+ |
+ // Copy constructor call... |
+ CXXConstructExpr *Outer = dyn_cast<CXXConstructExpr>(Init); |
+ if (!Outer) return 0; |
+ |
+ if (!Outer->isElidable()) return 0; |
+ if (Outer->getNumArgs() != 1) return 0; |
+ |
+ // ...wrapping scoped_ptr -> const scoped_ptr (for copy constructor call)... |
+ ImplicitCastExpr *OuterICE = dyn_cast<ImplicitCastExpr>(Outer->getArg(0)); |
+ if (!OuterICE) return 0; |
+ |
+//fprintf(stderr, "here 1\n"); |
+ |
+ // ...wrapping ICE that... |
+ ImplicitCastExpr *InnerICE = dyn_cast<ImplicitCastExpr>(OuterICE->getSubExpr()); |
+ if (!InnerICE) return 0; |
+ |
+//fprintf(stderr, "here 2\n"); |
+ |
+ Expr *PotentialConstructor = InnerICE->getSubExpr(); |
+ |
+ // (Skip any temporary bindings; they're implicit.) |
+ if (CXXBindTemporaryExpr *Binder = dyn_cast<CXXBindTemporaryExpr>(PotentialConstructor)) |
+ PotentialConstructor = Binder->getSubExpr(); |
+ |
+//fprintf(stderr, "here 3\n"); |
+ |
+ // ...calls the implicit constructor |
+ CXXConstructExpr *Inner = dyn_cast<CXXConstructExpr>(PotentialConstructor); |
+ if (!Inner) return 0; |
+ |
+//fprintf(stderr, "here 4\n"); |
+ |
+ if (Inner->getNumArgs() != 1) return 0; |
+ return Inner->getArg(0); |
+} |
+ |
+bool RewriteScopedRefptr::RewriteDeclStmt(DeclStmt *DS) { |
+ if (!DS) return false; |
+ bool changed = false; |
+ |
+//DS->dump(); |
+ for (DeclStmt::decl_iterator it = DS->decl_begin(); |
+ it != DS->decl_end(); ++it) { |
+ Decl *Dec = *it; |
+ if (!Dec->isCanonicalDecl()) |
+ continue; |
+ |
+ VarDecl* VD = dyn_cast<VarDecl>(Dec); |
+ if (!VD) |
+ continue; |
+ |
+ if (RewrittenDecls.count(VD) > 0) |
+ continue; |
+ |
+ // If variable has type scoped_refptr... |
+ if (!HasTypeScopedRefptr(VD->getType())) |
+ continue; |
+ |
+ // ..and the initializer is a raw ptr (`scoped_refptr<A> a = rawptr;`)... |
+ if (Expr *Init = GetRawPtrInit(VD->getInit())) { |
+ // ...rewrite this to a constructor call (`scoped_refptr<A> a(rawptr);`). |
+ SourceLocation DeclEnd = VD->getLocation(); |
+ SourceLocation Start = DeclEnd.getFileLocWithOffset( |
+ Lexer::MeasureTokenLength(DeclEnd, *SM, LangOpts)); |
+ SourceLocation End = Init->getLocStart(); |
+ CharSourceRange LeftRange( |
+ // FIXME: inst loc for start too? (required for macros for end) |
+ SourceRange(Start, SM->getInstantiationLoc(End)), |
+ /*istokenrange=*/false); |
+ int LeftLen = Rewrite.getRangeSize(LeftRange); |
+if (LeftLen < 0) { |
+// Could actually happen, if the initialization is a macro (` = NULL;`). |
+// (before I added the getInstantiationLoc call) |
+fprintf(stderr, "raw start:\n"); |
+ Start.dump(*SM); |
+fprintf(stderr, "\nlocation:\n"); |
+ VD->getLocation().dump(*SM); |
+fprintf(stderr, "\ntst loc:\n"); |
+ VD->getTypeSpecStartLoc().dump(*SM); |
+fprintf(stderr, "\ninner loc:\n"); |
+ VD->getInnerLocStart().dump(*SM); |
+fprintf(stderr, "\nraw end:\n"); |
+ End.dump(*SM); |
+fprintf(stderr, "\nspel:\n"); |
+ SM->getSpellingLoc(End).dump(*SM); |
+fprintf(stderr, "\ninst:\n"); |
+ SM->getInstantiationLoc(End).dump(*SM); |
+fprintf(stderr, "\n"); |
+} |
+ assert(LeftLen >= 0); |
+ |
+ // Keep indentation on the new line for code like |
+ // scoped_refptr<A> = new A( |
+ // param); |
+ std::string spacing; |
+ bool Invalid; |
+ const char* Data = SM->getCharacterData(Start, &Invalid); |
+ assert(!Invalid); |
+ for (int i = 0; i < LeftLen; ++i) { |
+ if (Data[i] == '\n') { |
+ spacing = std::string(Data + i, LeftLen - i); |
+ } |
+ } |
+ |
+ bool failed; |
+ failed = Rewrite.ReplaceText(Start, LeftLen, "(" + spacing); |
+ assert(!failed); |
+ |
+ //SourceLocation InitEnd = Init->getLocEnd(); |
+ SourceLocation InitEnd = SM->getInstantiationLoc(Init->getLocEnd()); |
+ InitEnd = InitEnd.getFileLocWithOffset(Lexer::MeasureTokenLength(InitEnd, *SM, LangOpts)); |
+ failed = Rewrite.InsertText(InitEnd, ")"); |
+ assert(!failed); |
+//fprintf(stderr, "inserted )\n"); |
+ RewrittenDecls.insert(VD); |
+ changed = true; |
+ } |
+ } |
+ return changed; |
+} |
+ |
+Stmt *RewriteScopedRefptr::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) { |
+ // Try the special-case rewrite first; hope that the general case doesn't |
+ // occur in an initializer. |
+ if (RewriteDeclStmt(dyn_cast<DeclStmt>(S))) { |
+ //assert(false); |
+ return S; |
+ } |
+ |
+ // Perform a bottom up rewrite of all children. |
+ for (Stmt::child_iterator CI = S->child_begin(), E = S->child_end(); |
+ CI != E; ++CI) |
+ if (*CI) { |
+ Stmt *newStmt; |
+ Stmt *S = (*CI); |
+ newStmt = RewriteFunctionBodyOrGlobalInitializer(S); |
+ if (newStmt) |
+ *CI = newStmt; |
+ } |
+ |
+ // Rewrite implicit calls to the rawptr conversion constructor of |
+ // scoped_refptr to calls to make_scoped_refptr. Implicit calls are |
+ // an ImplicitCastExpr containing a CXXConstructExpr |
+ if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(S)) { |
+ |
+ CXXConstructExpr *C = dyn_cast<CXXConstructExpr>(ICE->getSubExpr()); |
+ if (!C) return S; |
+//C->dump(); |
+ if (!HasTypeScopedRefptr(C->getType())) return S; |
+ if (C->getNumArgs() != 1) return S; |
+ Expr *Inner = C->getArg(0); |
+ if (HasTypeScopedRefptr(Inner->getType())) return S; |
+ assert(Inner->getType().getTypePtr()->isPointerType()); |
+ |
+ if (RewrittenExprs.count(Inner) > 0) |
+ return S; |
+ |
+ // Looks like we want to add make_scoped_refptr calls. |
+ bool failed; |
+ failed = Rewrite.InsertText( |
+ SM->getInstantiationLoc(Inner->getLocStart()), |
+ "make_scoped_refptr("); |
+ assert(!failed); |
+ // Need to convert to inst loc before measuring token length, else this |
+ // produces `make_scoped_refptr()NULL`. |
+ SourceLocation End = SM->getInstantiationLoc(Inner->getLocEnd()); |
+ End = End.getFileLocWithOffset( |
+ Lexer::MeasureTokenLength(End, *SM, LangOpts)); |
+ failed = Rewrite.InsertText( |
+ End, |
+ ")"); |
+ assert(!failed); |
+ |
+ RewrittenExprs.insert(Inner); |
+ } |
+ |
+ // Return this stmt unmodified. |
+ return S; |
+} |
+ |
+/// HandleDeclInMainFile - This is called for each top-level decl defined in the |
+/// main file of the input. |
+void RewriteScopedRefptr::HandleDeclInMainFile(Decl *D) { |
+ // A DeclContext is something that can contain declarations, e.g. a namespace, |
+ // a class, or a function. Recurse into these for their declarations. |
+ if (DeclContext* DC = dyn_cast<DeclContext>(D)) { |
+ for (DeclContext::decl_iterator DI = DC->decls_begin(), |
+ DIEnd = DC->decls_end(); |
+ DI != DIEnd; ++DI) { |
+ HandleDeclInMainFile(*DI); |
+ } |
+ } |
+ |
+ // CXXMethodDecls are derived from FunctionDecl |
+ if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { |
+//fprintf(stderr, "functiondecl: %s\n", FD->getNameInfo().getName().getAsString().c_str()); |
+ if (FD->isOverloadedOperator()) |
+ return; |
+ |
+ // FIXME: If this should support Obj-C++, support CXXTryStmt |
+ if (CompoundStmt *Body = dyn_cast_or_null<CompoundStmt>(FD->getBody())) { |
+ Body = |
+ cast_or_null<CompoundStmt>(RewriteFunctionBodyOrGlobalInitializer(Body)); |
+ FD->setBody(Body); |
+ } |
+ return; |
+ } |
+ // ObjCMethodDecl sadly are _not_ derived from FunctionDecl. |
+ if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { |
+ if (CompoundStmt *Body = MD->getCompoundBody()) { |
+ Body = |
+ cast_or_null<CompoundStmt>(RewriteFunctionBodyOrGlobalInitializer(Body)); |
+ MD->setBody(Body); |
+ } |
+ } |
+} |
+ |
+void RewriteScopedRefptr::HandleTranslationUnit(ASTContext &C) { |
+ if (Diags.hasErrorOccurred()) |
+ return; |
+ // Get the buffer corresponding to MainFileID. If we haven't changed it, then |
+ // we are done. |
+ if (const RewriteBuffer *RewriteBuf = |
+ Rewrite.getRewriteBufferFor(MainFileID)) { |
+ Result = std::string(RewriteBuf->begin(), RewriteBuf->end()); |
+ |
+ // HACK: Ignore out file, overwrite input file |
+ FILE* f = fopen(InFileName.c_str(), "wb"); |
+ assert(f); |
+ fwrite(Result.data(), 1, Result.size(), f); |
+ fclose(f); |
+ } else { |
+ llvm::errs() << "No changes\n"; |
+ } |
+} |