summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp')
-rw-r--r--clang-tools-extra/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp181
1 files changed, 173 insertions, 8 deletions
diff --git a/clang-tools-extra/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp b/clang-tools-extra/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp
index c714b221c62..15fa0e8da45 100644
--- a/clang-tools-extra/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp
+++ b/clang-tools-extra/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp
@@ -18,6 +18,7 @@
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Version.h"
+#include "clang/Format/Format.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSet.h"
@@ -30,6 +31,8 @@ using namespace clang::replace;
static cl::opt<std::string> Directory(cl::Positional, cl::Required,
cl::desc("<Search Root Directory>"));
+static cl::OptionCategory FormattingCategory("Formatting Options");
+
static cl::opt<bool> RemoveTUReplacementFiles(
"remove-change-desc-files",
cl::desc("Remove the change description files regardless of successful\n"
@@ -39,7 +42,31 @@ static cl::opt<bool> RemoveTUReplacementFiles(
// Update this list of options to show in -help as new options are added.
// Should add even those options marked as 'Hidden'. Any option not listed
// here will get marked 'ReallyHidden' so they don't appear in any -help text.
-const char *OptionsToShow[] = { "help", "version", "remove-change-desc-files" };
+const char *OptionsToShow[] = { "help", "version",
+ "remove-change-desc-files", "format",
+ "style-config", "style" };
+
+static cl::opt<bool> DoFormat(
+ "format",
+ cl::desc("Enable formatting of code changed by applying replacements.\n"
+ "Use -style to choose formatting style.\n"),
+ cl::cat(FormattingCategory));
+
+// FIXME: Consider making the default behaviour for finding a style
+// configuration file to start the search anew for every file being changed to
+// handle situations where the style is different for different parts of a
+// project.
+
+static cl::opt<std::string> FormatStyleConfig(
+ "style-config",
+ cl::desc("Path to a directory containing a .clang-format file\n"
+ "describing a formatting style to use for formatting\n"
+ "code when -style=file.\n"),
+ cl::init(""), cl::cat(FormattingCategory));
+
+static cl::opt<std::string>
+FormatStyleOpt("style", cl::desc(format::StyleOptionHelpDescription),
+ cl::init("LLVM"), cl::cat(FormattingCategory));
// Helper object to remove the TUReplacement files (triggered by
// "remove-change-desc-files" command line option) when exiting current scope.
@@ -62,6 +89,111 @@ void printVersion() {
outs() << "clang-apply-replacements version " CLANG_VERSION_STRING << "\n";
}
+/// \brief Convenience function to get rewritten content for \c Filename from
+/// \c Rewrites.
+///
+/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
+/// \post Replacements.empty() -> Result.empty()
+///
+/// \param[in] Replacements Replacements to apply
+/// \param[in] Rewrites Rewriter to use to apply replacements.
+/// \param[out] Result Contents of the file after applying replacements if
+/// replacements were provided.
+///
+/// \returns \li true if all replacements were applied successfully.
+/// \li false if at least one replacement failed to apply.
+static bool
+getRewrittenData(const std::vector<tooling::Replacement> &Replacements,
+ Rewriter &Rewrites, std::string &Result) {
+ if (Replacements.empty()) return true;
+
+ if (!tooling::applyAllReplacements(Replacements, Rewrites))
+ return false;
+
+ SourceManager &SM = Rewrites.getSourceMgr();
+ FileManager &Files = SM.getFileManager();
+
+ StringRef FileName = Replacements.begin()->getFilePath();
+ const clang::FileEntry *Entry = Files.getFile(FileName);
+ assert(Entry && "Expected an existing file");
+ FileID ID = SM.translateFile(Entry);
+ assert(!ID.isInvalid() && "Expected a valid FileID");
+ const RewriteBuffer *Buffer = Rewrites.getRewriteBufferFor(ID);
+ Result = std::string(Buffer->begin(), Buffer->end());
+
+ return true;
+}
+
+/// \brief Apply \c Replacements and return the new file contents.
+///
+/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
+/// \post Replacements.empty() -> Result.empty()
+///
+/// \param[in] Replacements Replacements to apply.
+/// \param[out] Result Contents of the file after applying replacements if
+/// replacements were provided.
+/// \param[in] Diagnostics For diagnostic output.
+///
+/// \returns \li true if all replacements applied successfully.
+/// \li false if at least one replacement failed to apply.
+bool applyReplacements(const std::vector<tooling::Replacement> &Replacements,
+ std::string &Result,
+ DiagnosticsEngine &Diagnostics) {
+ FileManager Files((FileSystemOptions()));
+ SourceManager SM(Diagnostics, Files);
+ Rewriter Rewrites(SM, LangOptions());
+
+ return getRewrittenData(Replacements, Rewrites, Result);
+}
+
+/// \brief Apply code formatting to all places where replacements were made.
+///
+/// \pre !Replacements.empty().
+/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
+/// \pre Replacements[i].getOffset() <= Replacements[i+1].getOffset().
+///
+/// \param[in] Replacements Replacements that were made to the file. Provided
+/// to indicate where changes were made.
+/// \param[in] FileData The contents of the file \b after \c Replacements have
+/// been applied.
+/// \param[out] FormattedFileData The contents of the file after reformatting.
+/// \param[in] Diagnostics For diagnostic output.
+///
+/// \returns \li true if reformatting replacements were all successfully
+/// applied.
+/// \li false if at least one reformatting replacement failed to apply.
+bool applyFormatting(const std::vector<tooling::Replacement> &Replacements,
+ const StringRef FileData,
+ std::string &FormattedFileData,
+ const format::FormatStyle &FormatStyle,
+ DiagnosticsEngine &Diagnostics) {
+ assert(!Replacements.empty() && "Need at least one replacement");
+
+ RangeVector Ranges = calculateChangedRanges(Replacements);
+
+ StringRef FileName = Replacements.begin()->getFilePath();
+ tooling::Replacements R =
+ format::reformat(FormatStyle, FileData, Ranges, FileName);
+
+ // FIXME: Remove this copy when tooling::Replacements is implemented as a
+ // vector instead of a set.
+ std::vector<tooling::Replacement> FormattingReplacements;
+ std::copy(R.begin(), R.end(), back_inserter(FormattingReplacements));
+
+ if (FormattingReplacements.empty()) {
+ FormattedFileData = FileData;
+ return true;
+ }
+
+ FileManager Files((FileSystemOptions()));
+ SourceManager SM(Diagnostics, Files);
+ SM.overrideFileContents(Files.getFile(FileName),
+ llvm::MemoryBuffer::getMemBufferCopy(FileData));
+ Rewriter Rewrites(SM, LangOptions());
+
+ return getRewrittenData(FormattingReplacements, Rewrites, FormattedFileData);
+}
+
int main(int argc, char **argv) {
// Only include our options in -help output.
StringMap<cl::Option*> OptMap;
@@ -81,6 +213,11 @@ int main(int argc, char **argv) {
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
DiagOpts.getPtr());
+ // Determine a formatting style from options.
+ format::FormatStyle FormatStyle;
+ if (DoFormat)
+ FormatStyle = format::getStyle(FormatStyleOpt, FormatStyleConfig);
+
TUReplacements TUs;
TUReplacementFiles TURFiles;
@@ -106,14 +243,42 @@ int main(int argc, char **argv) {
if (!mergeAndDeduplicate(TUs, GroupedReplacements, SM))
return 1;
- Rewriter DestRewriter(SM, LangOptions());
- if (!applyReplacements(GroupedReplacements, DestRewriter)) {
- errs() << "Failed to apply all replacements. No changes made.\n";
- return 1;
- }
+ Rewriter ReplacementsRewriter(SM, LangOptions());
- if (!writeFiles(DestRewriter))
- return 1;
+ for (FileToReplacementsMap::const_iterator I = GroupedReplacements.begin(),
+ E = GroupedReplacements.end();
+ I != E; ++I) {
+
+ std::string NewFileData;
+
+ // This shouldn't happen but if a file somehow has no replacements skip to
+ // next file.
+ if (I->getValue().empty())
+ continue;
+
+ if (!applyReplacements(I->getValue(), NewFileData, Diagnostics)) {
+ errs() << "Failed to apply replacements to " << I->getKey() << "\n";
+ continue;
+ }
+
+ // Apply formatting if requested.
+ if (DoFormat && !applyFormatting(I->getValue(), NewFileData, NewFileData,
+ FormatStyle, Diagnostics)) {
+ errs() << "Failed to apply reformatting replacements for " << I->getKey()
+ << "\n";
+ continue;
+ }
+
+ // Write new file to disk
+ std::string ErrorInfo;
+ llvm::raw_fd_ostream FileStream(I->getKey().str().c_str(), ErrorInfo);
+ if (!ErrorInfo.empty()) {
+ llvm::errs() << "Could not open " << I->getKey() << " for writing\n";
+ continue;
+ }
+
+ FileStream << NewFileData;
+ }
return 0;
}
OpenPOWER on IntegriCloud