Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(170)

Unified Diff: lib/Rewrite/RewriteScopedRefptr.cpp

Issue 2826041: Rewriter that converts implicit scoped_refptr constructor calls to make_scoped_refptr calls Base URL: https://llvm.org/svn/llvm-project/cfe/trunk/
Patch Set: rebase on clang with correct cxxnewexpr sourceranges Created 13 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/Rewrite/FrontendActions.cpp ('k') | lib/Sema/SemaExpr.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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";
+ }
+}
« no previous file with comments | « lib/Rewrite/FrontendActions.cpp ('k') | lib/Sema/SemaExpr.cpp » ('j') | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b