Write transform results to disk only once
Instead of writing the result of each transform to disk for every transform, write the results to buffers in memory and pass those buffers to the next transform as input. Only write the buffers to disk if the final syntax check passes. Reviewers: klimek llvm-svn: 172657
This commit is contained in:
parent
00dfc68c2d
commit
862fec8835
|
@ -5,6 +5,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
set (Cpp11MigrateSources
|
set (Cpp11MigrateSources
|
||||||
Cpp11Migrate.cpp
|
Cpp11Migrate.cpp
|
||||||
Transforms.cpp
|
Transforms.cpp
|
||||||
|
Transform.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# For each transform subdirectory.
|
# For each transform subdirectory.
|
||||||
|
|
|
@ -27,27 +27,26 @@
|
||||||
///
|
///
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "Transforms.h"
|
||||||
|
#include "Transform.h"
|
||||||
#include "clang/Frontend/FrontendActions.h"
|
#include "clang/Frontend/FrontendActions.h"
|
||||||
#include "clang/Tooling/CommonOptionsParser.h"
|
#include "clang/Tooling/CommonOptionsParser.h"
|
||||||
#include "clang/Tooling/Tooling.h"
|
#include "clang/Tooling/Tooling.h"
|
||||||
#include "Transforms.h"
|
|
||||||
#include "Transform.h"
|
|
||||||
|
|
||||||
namespace cl = llvm::cl;
|
namespace cl = llvm::cl;
|
||||||
using namespace clang::tooling;
|
using namespace clang::tooling;
|
||||||
|
|
||||||
static cl::opt<RiskLevel>
|
static cl::opt<RiskLevel> MaxRiskLevel(
|
||||||
MaxRiskLevel("risk", cl::desc("Select a maximum risk level:"),
|
"risk", cl::desc("Select a maximum risk level:"),
|
||||||
cl::values(
|
cl::values(clEnumValN(RL_Safe, "safe", "Only safe transformations"),
|
||||||
clEnumValN(RL_Safe, "safe", "Only safe transformations"),
|
clEnumValN(RL_Reasonable, "reasonable",
|
||||||
clEnumValN(RL_Reasonable, "reasonable",
|
"Enable transformations that might change "
|
||||||
"Enable transformations that might change "
|
"semantics (default)"),
|
||||||
"semantics (default)"),
|
clEnumValN(RL_Risky, "risky",
|
||||||
clEnumValN(RL_Risky, "risky",
|
"Enable transformations that are likely to "
|
||||||
"Enable transformations that are likely to "
|
"change semantics"),
|
||||||
"change semantics"),
|
|
||||||
clEnumValEnd),
|
clEnumValEnd),
|
||||||
cl::init(RL_Reasonable));
|
cl::init(RL_Reasonable));
|
||||||
|
|
||||||
int main(int argc, const char **argv) {
|
int main(int argc, const char **argv) {
|
||||||
Transforms TransformManager;
|
Transforms TransformManager;
|
||||||
|
@ -74,23 +73,53 @@ int main(int argc, const char **argv) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int NumFiles = OptionsParser.getSourcePathList().size();
|
||||||
|
|
||||||
|
FileContentsByPath FileStates1, FileStates2,
|
||||||
|
*InputFileStates = &FileStates1, *OutputFileStates = &FileStates2;
|
||||||
|
FileStates1.reserve(NumFiles);
|
||||||
|
FileStates2.reserve(NumFiles);
|
||||||
|
|
||||||
// Apply transforms.
|
// Apply transforms.
|
||||||
for (Transforms::const_iterator I = TransformManager.begin(),
|
for (Transforms::const_iterator I = TransformManager.begin(),
|
||||||
E = TransformManager.end(); I != E; ++I) {
|
E = TransformManager.end();
|
||||||
if ((*I)->apply(MaxRiskLevel, OptionsParser.getCompilations(),
|
I != E; ++I) {
|
||||||
OptionsParser.getSourcePathList()) != 0) {
|
if ((*I)->apply(*InputFileStates, MaxRiskLevel,
|
||||||
|
OptionsParser.getCompilations(),
|
||||||
|
OptionsParser.getSourcePathList(), *OutputFileStates) !=
|
||||||
|
0) {
|
||||||
|
// FIXME: Improve ClangTool to not abort if just one file fails.
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
std::swap(InputFileStates, OutputFileStates);
|
||||||
|
OutputFileStates->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Final state of files is pointed at by InputFileStates.
|
||||||
|
|
||||||
// Final Syntax check.
|
// Final Syntax check.
|
||||||
ClangTool EndSyntaxTool(OptionsParser.getCompilations(),
|
ClangTool EndSyntaxTool(OptionsParser.getCompilations(),
|
||||||
OptionsParser.getSourcePathList());
|
OptionsParser.getSourcePathList());
|
||||||
if (EndSyntaxTool.run(
|
for (FileContentsByPath::const_iterator I = InputFileStates->begin(),
|
||||||
newFrontendActionFactory<clang::SyntaxOnlyAction>()) != 0) {
|
E = InputFileStates->end();
|
||||||
// FIXME: Revert changes made to files that fail the syntax test.
|
I != E; ++I) {
|
||||||
|
EndSyntaxTool.mapVirtualFile(I->first, I->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EndSyntaxTool.run(newFrontendActionFactory<clang::SyntaxOnlyAction>()) !=
|
||||||
|
0) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Syntax check passed, write results to file.
|
||||||
|
for (FileContentsByPath::const_iterator I = InputFileStates->begin(),
|
||||||
|
E = InputFileStates->end();
|
||||||
|
I != E; ++I) {
|
||||||
|
std::string ErrorInfo;
|
||||||
|
llvm::raw_fd_ostream FileStream(I->first.c_str(), ErrorInfo,
|
||||||
|
llvm::raw_fd_ostream::F_Binary);
|
||||||
|
FileStream << I->second;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,15 +19,25 @@
|
||||||
#include "clang/Frontend/FrontendActions.h"
|
#include "clang/Frontend/FrontendActions.h"
|
||||||
#include "clang/Tooling/Refactoring.h"
|
#include "clang/Tooling/Refactoring.h"
|
||||||
#include "clang/Tooling/Tooling.h"
|
#include "clang/Tooling/Tooling.h"
|
||||||
|
#include "clang/Rewrite/Core/Rewriter.h"
|
||||||
|
|
||||||
using clang::ast_matchers::MatchFinder;
|
using clang::ast_matchers::MatchFinder;
|
||||||
using namespace clang::tooling;
|
using namespace clang::tooling;
|
||||||
using namespace clang;
|
using namespace clang;
|
||||||
|
|
||||||
int LoopConvertTransform::apply(RiskLevel MaxRisk,
|
int LoopConvertTransform::apply(const FileContentsByPath &InputStates,
|
||||||
|
RiskLevel MaxRisk,
|
||||||
const CompilationDatabase &Database,
|
const CompilationDatabase &Database,
|
||||||
const std::vector<std::string> &SourcePaths) {
|
const std::vector<std::string> &SourcePaths,
|
||||||
|
FileContentsByPath &ResultStates) {
|
||||||
RefactoringTool LoopTool(Database, SourcePaths);
|
RefactoringTool LoopTool(Database, SourcePaths);
|
||||||
|
|
||||||
|
for (FileContentsByPath::const_iterator I = InputStates.begin(),
|
||||||
|
E = InputStates.end();
|
||||||
|
I != E; ++I) {
|
||||||
|
LoopTool.mapVirtualFile(I->first, I->second);
|
||||||
|
}
|
||||||
|
|
||||||
StmtAncestorASTVisitor ParentFinder;
|
StmtAncestorASTVisitor ParentFinder;
|
||||||
StmtGeneratedVarNameMap GeneratedDecls;
|
StmtGeneratedVarNameMap GeneratedDecls;
|
||||||
ReplacedVarsMap ReplacedVars;
|
ReplacedVarsMap ReplacedVars;
|
||||||
|
@ -53,11 +63,19 @@ int LoopConvertTransform::apply(RiskLevel MaxRisk,
|
||||||
&RejectedChanges,
|
&RejectedChanges,
|
||||||
MaxRisk, LFK_PseudoArray);
|
MaxRisk, LFK_PseudoArray);
|
||||||
Finder.addMatcher(makePseudoArrayLoopMatcher(), &PseudoarrrayLoopFixer);
|
Finder.addMatcher(makePseudoArrayLoopMatcher(), &PseudoarrrayLoopFixer);
|
||||||
if (int result = LoopTool.runAndSave(newFrontendActionFactory(&Finder))) {
|
|
||||||
|
if (int result = LoopTool.run(newFrontendActionFactory(&Finder))) {
|
||||||
llvm::errs() << "Error encountered during translation.\n";
|
llvm::errs() << "Error encountered during translation.\n";
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RewriterContainer Rewrite(LoopTool.getFiles());
|
||||||
|
|
||||||
|
// FIXME: Do something if some replacements didn't get applied?
|
||||||
|
LoopTool.applyAllReplacements(Rewrite.getRewriter());
|
||||||
|
|
||||||
|
collectResults(Rewrite.getRewriter(), ResultStates);
|
||||||
|
|
||||||
if (AcceptedChanges > 0) {
|
if (AcceptedChanges > 0) {
|
||||||
setChangesMade();
|
setChangesMade();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,19 @@
|
||||||
#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_LOOP_CONVERT_H
|
#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_LOOP_CONVERT_H
|
||||||
|
|
||||||
#include "Transform.h"
|
#include "Transform.h"
|
||||||
|
#include "llvm/Support/Compiler.h" // For LLVM_OVERRIDE
|
||||||
|
|
||||||
|
/// \brief Subclass of Transform that transforms for-loops into range-based
|
||||||
|
/// for-loops where possible.
|
||||||
class LoopConvertTransform : public Transform {
|
class LoopConvertTransform : public Transform {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual int apply(RiskLevel MaxRiskLEvel,
|
/// \brief \see Transform::run().
|
||||||
|
virtual int apply(const FileContentsByPath &InputStates,
|
||||||
|
RiskLevel MaxRiskLevel,
|
||||||
const clang::tooling::CompilationDatabase &Database,
|
const clang::tooling::CompilationDatabase &Database,
|
||||||
const std::vector<std::string> &SourcePaths) LLVM_OVERRIDE;
|
const std::vector<std::string> &SourcePaths,
|
||||||
|
FileContentsByPath &ResultStates) LLVM_OVERRIDE;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_LOOP_CONVERT_H
|
#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_LOOP_CONVERT_H
|
||||||
|
|
|
@ -17,7 +17,7 @@ TOOL_NO_EXPORTS = 1
|
||||||
|
|
||||||
include $(CLANG_LEVEL)/../../Makefile.config
|
include $(CLANG_LEVEL)/../../Makefile.config
|
||||||
|
|
||||||
SOURCES = Cpp11Migrate.cpp Transforms.cpp
|
SOURCES = Cpp11Migrate.cpp Transforms.cpp Transform.cpp
|
||||||
|
|
||||||
# For each Transform subdirectory add to SOURCES and BUILT_SOURCES.
|
# For each Transform subdirectory add to SOURCES and BUILT_SOURCES.
|
||||||
# BUILT_SOURCES ensures a subdirectory is created to house object files from
|
# BUILT_SOURCES ensures a subdirectory is created to house object files from
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
#include "Transform.h"
|
||||||
|
#include "clang/Rewrite/Core/Rewriter.h"
|
||||||
|
#include "clang/Basic/FileManager.h"
|
||||||
|
#include "clang/Basic/SourceManager.h"
|
||||||
|
#include "llvm/Support/raw_ostream.h"
|
||||||
|
|
||||||
|
using namespace clang;
|
||||||
|
|
||||||
|
void collectResults(clang::Rewriter &Rewrite,
|
||||||
|
FileContentsByPath &Results) {
|
||||||
|
for (Rewriter::buffer_iterator I = Rewrite.buffer_begin(),
|
||||||
|
E = Rewrite.buffer_end();
|
||||||
|
I != E; ++I) {
|
||||||
|
const FileEntry *Entry = Rewrite.getSourceMgr().getFileEntryForID(I->first);
|
||||||
|
assert(Entry != 0 && "Expected a FileEntry");
|
||||||
|
assert(Entry->getName() != 0 &&
|
||||||
|
"Unexpected NULL return from FileEntry::getName()");
|
||||||
|
|
||||||
|
FileContentsByPath::value_type ResultEntry;
|
||||||
|
|
||||||
|
ResultEntry.first = Entry->getName();
|
||||||
|
|
||||||
|
// Get a copy of the rewritten buffer from the Rewriter.
|
||||||
|
llvm::raw_string_ostream StringStream(ResultEntry.second);
|
||||||
|
I->second.write(StringStream);
|
||||||
|
|
||||||
|
// Cause results to be written to ResultEntry.second.
|
||||||
|
StringStream.str();
|
||||||
|
|
||||||
|
// FIXME: Use move semantics to avoid copies of the buffer contents if
|
||||||
|
// benchmarking shows the copies are expensive, especially for large source
|
||||||
|
// files.
|
||||||
|
Results.push_back(ResultEntry);
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,8 +15,18 @@
|
||||||
#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_TRANSFORM_H
|
#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_TRANSFORM_H
|
||||||
#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_TRANSFORM_H
|
#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_TRANSFORM_H
|
||||||
|
|
||||||
#include "clang/Tooling/CompilationDatabase.h"
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// For RewriterContainer
|
||||||
|
#include "clang/Rewrite/Core/Rewriter.h"
|
||||||
|
#include "clang/Basic/LangOptions.h"
|
||||||
|
#include "clang/Basic/DiagnosticOptions.h"
|
||||||
|
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
||||||
|
#include "clang/Basic/SourceManager.h"
|
||||||
|
#include "llvm/Support/raw_ostream.h"
|
||||||
|
////
|
||||||
|
|
||||||
|
|
||||||
/// \brief Description of the riskiness of actions that can be taken by
|
/// \brief Description of the riskiness of actions that can be taken by
|
||||||
/// transforms.
|
/// transforms.
|
||||||
|
@ -31,13 +41,56 @@ enum RiskLevel {
|
||||||
RL_Risky
|
RL_Risky
|
||||||
};
|
};
|
||||||
|
|
||||||
// Forward Declarations
|
// Forward declarations
|
||||||
namespace clang {
|
namespace clang {
|
||||||
namespace tooling {
|
namespace tooling {
|
||||||
class CompilationDatabase;
|
class CompilationDatabase;
|
||||||
} // namespace tooling
|
} // namespace tooling
|
||||||
} // namespace clang
|
} // namespace clang
|
||||||
|
|
||||||
|
/// \brief First item in the pair is the path of a file and the second is a
|
||||||
|
/// buffer with the possibly modified contents of that file.
|
||||||
|
typedef std::vector<std::pair<std::string, std::string> > FileContentsByPath;
|
||||||
|
|
||||||
|
/// \brief In \p Results place copies of the buffers resulting from applying
|
||||||
|
/// all rewrites represented by \p Rewrite.
|
||||||
|
///
|
||||||
|
/// \p Results is made up of pairs {filename, buffer contents}. Pairs are
|
||||||
|
/// simply appended to \p Results.
|
||||||
|
void collectResults(clang::Rewriter &Rewrite, FileContentsByPath &Results);
|
||||||
|
|
||||||
|
/// \brief Class for containing a Rewriter instance and all of
|
||||||
|
/// its lifetime dependencies.
|
||||||
|
///
|
||||||
|
/// Subclasses of Transform using RefactoringTools will need to create
|
||||||
|
/// Rewriters in order to apply Replacements and get the resulting buffer.
|
||||||
|
/// Rewriter requires some objects to exist at least as long as it does so this
|
||||||
|
/// class contains instances of those objects.
|
||||||
|
///
|
||||||
|
/// FIXME: These objects should really come from somewhere more global instead
|
||||||
|
/// of being recreated for every Transform subclass, especially diagnostics.
|
||||||
|
class RewriterContainer {
|
||||||
|
public:
|
||||||
|
RewriterContainer(clang::FileManager &Files)
|
||||||
|
: DiagOpts(new clang::DiagnosticOptions()),
|
||||||
|
DiagnosticPrinter(llvm::errs(), DiagOpts.getPtr()),
|
||||||
|
Diagnostics(llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>(
|
||||||
|
new clang::DiagnosticIDs()),
|
||||||
|
DiagOpts.getPtr(), &DiagnosticPrinter, false),
|
||||||
|
Sources(Diagnostics, Files),
|
||||||
|
Rewrite(Sources, DefaultLangOptions) {}
|
||||||
|
|
||||||
|
clang::Rewriter &getRewriter() { return Rewrite; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
clang::LangOptions DefaultLangOptions;
|
||||||
|
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts;
|
||||||
|
clang::TextDiagnosticPrinter DiagnosticPrinter;
|
||||||
|
clang::DiagnosticsEngine Diagnostics;
|
||||||
|
clang::SourceManager Sources;
|
||||||
|
clang::Rewriter Rewrite;
|
||||||
|
};
|
||||||
|
|
||||||
/// \brief Abstract base class for all C++11 migration transforms.
|
/// \brief Abstract base class for all C++11 migration transforms.
|
||||||
class Transform {
|
class Transform {
|
||||||
public:
|
public:
|
||||||
|
@ -49,11 +102,16 @@ public:
|
||||||
|
|
||||||
/// \brief Apply a transform to all files listed in \p SourcePaths.
|
/// \brief Apply a transform to all files listed in \p SourcePaths.
|
||||||
///
|
///
|
||||||
/// \p Database must contain information for how to compile all files in
|
/// \p Database must contain information for how to compile all files in \p
|
||||||
/// \p SourcePaths.
|
/// SourcePaths. \p InputStates contains the file contents of files in \p
|
||||||
virtual int apply(RiskLevel MaxRiskLevel,
|
/// SourcePaths and should take precedence over content of files on disk.
|
||||||
|
/// Upon return, \p ResultStates shall contain the result of performing this
|
||||||
|
/// transform on the files listed in \p SourcePaths.
|
||||||
|
virtual int apply(const FileContentsByPath &InputStates,
|
||||||
|
RiskLevel MaxRiskLevel,
|
||||||
const clang::tooling::CompilationDatabase &Database,
|
const clang::tooling::CompilationDatabase &Database,
|
||||||
const std::vector<std::string> &SourcePaths) = 0;
|
const std::vector<std::string> &SourcePaths,
|
||||||
|
FileContentsByPath &ResultStates) = 0;
|
||||||
|
|
||||||
/// \brief Query if changes were made during the last call to apply().
|
/// \brief Query if changes were made during the last call to apply().
|
||||||
bool getChangesMade() const { return ChangesMade; }
|
bool getChangesMade() const { return ChangesMade; }
|
||||||
|
|
Loading…
Reference in New Issue