clang-modernize: Apply replacements using clang-apply-replacements

Summary:
The clang-apply-replacements process is now invoked to apply
replacements between applying transforms. This resulted in a massive
simplification of the tool:
- FileOverrides class no longer needed.
- Change tracking and code formatting no longer needed.
- No more dependency on libclangApplyReplacements.
- Final syntax check is easier to do directly now than with a separate
  header/source pair.

Replacement handling stuff abstracted into a new header/source pair to
de-clutter ClangModernize.cpp somewhat.

Tests updated.

Differential Revision: http://llvm-reviews.chandlerc.com/D1836

llvm-svn: 192032
This commit is contained in:
Edwin Vane 2013-10-05 12:15:58 +00:00
parent c83946f7f5
commit c0f00b79f7
38 changed files with 489 additions and 1109 deletions

View File

@ -228,7 +228,7 @@ int main(int argc, char **argv) {
if (ErrorCode) {
errs() << "Trouble iterating over directory '" << Directory
<< "': " << ErrorCode.message() << "\n";
return false;
return 1;
}
// Remove the TUReplacementFiles (triggered by "remove-change-desc-files"

View File

@ -29,8 +29,7 @@ static cl::opt<bool> DetectMacros(
cl::desc("Detect and use macros that expand to the 'override' keyword."),
cl::cat(TransformsOptionsCategory));
int AddOverrideTransform::apply(const FileOverrides &InputStates,
const CompilationDatabase &Database,
int AddOverrideTransform::apply(const CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) {
ClangTool AddOverrideTool(Database, SourcePaths);
unsigned AcceptedChanges = 0;
@ -42,8 +41,6 @@ int AddOverrideTransform::apply(const FileOverrides &InputStates,
// Make Fixer available to handleBeginSource().
this->Fixer = &Fixer;
setOverrides(InputStates);
if (int result = AddOverrideTool.run(createActionFactory(Finder))) {
llvm::errs() << "Error encountered during translation.\n";
return result;

View File

@ -31,8 +31,7 @@ public:
: Transform("AddOverride", Options) {}
/// \see Transform::run().
virtual int apply(const FileOverrides &InputStates,
const clang::tooling::CompilationDatabase &Database,
virtual int apply(const clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) LLVM_OVERRIDE;
virtual bool handleBeginSource(clang::CompilerInstance &CI,

View File

@ -1,6 +1,3 @@
get_filename_component(ClangReplaceLocation
"${CMAKE_CURRENT_SOURCE_DIR}/../clang-apply-replacements/include" ABSOLUTE)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
${ClangReplaceLocation}

View File

@ -1,13 +1,11 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(modernizeCore
FileOverrides.cpp
SyntaxCheck.cpp
ReplacementHandling.cpp
Transforms.cpp
Transform.cpp
IncludeExcludeInfo.cpp
PerfSupport.cpp
Reformatting.cpp
IncludeDirectives.cpp
)
target_link_libraries(modernizeCore

View File

@ -1,198 +0,0 @@
//===-- Core/FileOverrides.cpp --------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file provides types and functionality for dealing with source
/// and header file content overrides.
///
//===----------------------------------------------------------------------===//
#include "Core/FileOverrides.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/Tooling/Tooling.h"
#include "clang/Tooling/ReplacementsYaml.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/system_error.h"
#include <algorithm>
using namespace clang;
using namespace clang::tooling;
bool generateReplacementsFileName(const llvm::StringRef MainSourceFile,
llvm::SmallVectorImpl<char> &Result,
llvm::SmallVectorImpl<char> &Error) {
using namespace llvm::sys;
Error.clear();
if (llvm::error_code EC = fs::createUniqueFile(
MainSourceFile + "_%%_%%_%%_%%_%%_%%.yaml", Result)) {
Error.append(EC.message().begin(), EC.message().end());
return false;
}
return true;
}
namespace {
/// \brief Comparator to be able to order tooling::Range based on their offset.
bool rangeLess(clang::tooling::Range A, clang::tooling::Range B) {
if (A.getOffset() == B.getOffset())
return A.getLength() < B.getLength();
return A.getOffset() < B.getOffset();
}
/// \brief Functor that returns the given range without its overlaps with the
/// replacement given in the constructor.
struct RangeReplacedAdjuster {
RangeReplacedAdjuster(const tooling::Replacement &Replace)
: Replace(Replace.getOffset(), Replace.getLength()),
ReplaceNewSize(Replace.getReplacementText().size()) {}
tooling::Range operator()(clang::tooling::Range Range) const {
if (!Range.overlapsWith(Replace))
return Range;
// range inside replacement -> make the range length null
if (Replace.contains(Range))
return tooling::Range(Range.getOffset(), 0);
// replacement inside range -> resize the range
if (Range.contains(Replace)) {
int Difference = ReplaceNewSize - Replace.getLength();
return tooling::Range(Range.getOffset(), Range.getLength() + Difference);
}
// beginning of the range replaced -> truncate range beginning
if (Range.getOffset() > Replace.getOffset()) {
unsigned ReplaceEnd = Replace.getOffset() + Replace.getLength();
unsigned RangeEnd = Range.getOffset() + Range.getLength();
return tooling::Range(ReplaceEnd, RangeEnd - ReplaceEnd);
}
// end of the range replaced -> truncate range end
if (Range.getOffset() < Replace.getOffset())
return tooling::Range(Range.getOffset(),
Replace.getOffset() - Range.getOffset());
llvm_unreachable("conditions not handled properly");
}
const tooling::Range Replace;
const unsigned ReplaceNewSize;
};
} // end anonymous namespace
void
ChangedRanges::adjustChangedRanges(const tooling::ReplacementsVec &Replaces) {
// first adjust existing ranges in case they overlap with the replacements
for (ReplacementsVec::const_iterator I = Replaces.begin(), E = Replaces.end();
I != E; ++I) {
const tooling::Replacement &Replace = *I;
std::transform(Ranges.begin(), Ranges.end(), Ranges.begin(),
RangeReplacedAdjuster(Replace));
}
// then shift existing ranges to reflect the new positions
for (RangeVec::iterator I = Ranges.begin(), E = Ranges.end(); I != E; ++I) {
unsigned ShiftedOffset =
tooling::shiftedCodePosition(Replaces, I->getOffset());
*I = tooling::Range(ShiftedOffset, I->getLength());
}
// then generate the new ranges from the replacements
for (ReplacementsVec::const_iterator I = Replaces.begin(), E = Replaces.end();
I != E; ++I) {
const tooling::Replacement &R = *I;
unsigned Offset = tooling::shiftedCodePosition(Replaces, R.getOffset());
unsigned Length = R.getReplacementText().size();
Ranges.push_back(tooling::Range(Offset, Length));
}
// cleanups unecessary ranges to finish
coalesceRanges();
}
void ChangedRanges::coalesceRanges() {
// sort the ranges by offset and then for each group of adjacent/overlapping
// ranges the first one in the group is extended to cover the whole group.
std::sort(Ranges.begin(), Ranges.end(), &rangeLess);
RangeVec::iterator FirstInGroup = Ranges.begin();
assert(!Ranges.empty() && "unexpected empty vector");
for (RangeVec::iterator I = Ranges.begin() + 1, E = Ranges.end(); I != E;
++I) {
unsigned GroupEnd = FirstInGroup->getOffset() + FirstInGroup->getLength();
// no contact
if (I->getOffset() > GroupEnd)
FirstInGroup = I;
else {
unsigned GrpBegin = FirstInGroup->getOffset();
unsigned GrpEnd = std::max(GroupEnd, I->getOffset() + I->getLength());
*FirstInGroup = tooling::Range(GrpBegin, GrpEnd - GrpBegin);
}
}
// remove the ranges that are covered by the first member of the group
Ranges.erase(std::unique(Ranges.begin(), Ranges.end(),
std::mem_fun_ref(&Range::contains)),
Ranges.end());
}
void FileOverrides::applyOverrides(clang::SourceManager &SM) const {
FileManager &FM = SM.getFileManager();
for (FileStateMap::const_iterator I = FileStates.begin(),
E = FileStates.end();
I != E; ++I) {
SM.overrideFileContents(FM.getFile(I->getKey()),
llvm::MemoryBuffer::getMemBuffer(I->getValue()));
}
}
void FileOverrides::adjustChangedRanges(
const clang::replace::FileToReplacementsMap &Replaces) {
for (replace::FileToReplacementsMap::const_iterator I = Replaces.begin(),
E = Replaces.end(); I != E; ++I) {
ChangeTracking[I->getKey()].adjustChangedRanges(I->getValue());
}
}
void FileOverrides::updateState(const clang::Rewriter &Rewrites) {
for (Rewriter::const_buffer_iterator BufferI = Rewrites.buffer_begin(),
BufferE = Rewrites.buffer_end();
BufferI != BufferE; ++BufferI) {
const char *FileName =
Rewrites.getSourceMgr().getFileEntryForID(BufferI->first)->getName();
const RewriteBuffer &RewriteBuf = BufferI->second;
FileStates[FileName].assign(RewriteBuf.begin(), RewriteBuf.end());
}
}
bool FileOverrides::writeToDisk(DiagnosticsEngine &Diagnostics) const {
bool Errors = false;
for (FileStateMap::const_iterator I = FileStates.begin(),
E = FileStates.end();
I != E; ++I) {
std::string ErrorInfo;
// The extra transform through std::string is to ensure null-termination
// of the filename stored as the key of the StringMap.
llvm::raw_fd_ostream FileStream(I->getKey().str().c_str(), ErrorInfo);
if (!ErrorInfo.empty()) {
llvm::errs() << "Failed to write new state for " << I->getKey() << ".\n";
Errors = true;
}
FileStream << I->getValue();
}
return !Errors;
}

View File

@ -1,129 +0,0 @@
//===-- Core/FileOverrides.h ------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file provides types and functionality for dealing with source
/// and header file content overrides.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_FILE_OVERRIDES_H
#define CLANG_MODERNIZE_FILE_OVERRIDES_H
#include "Core/Refactoring.h"
#include "clang-apply-replacements/Tooling/ApplyReplacements.h"
#include "clang/Tooling/ReplacementsYaml.h"
#include "llvm/ADT/StringMap.h"
// Forward Declarations
namespace llvm {
template <typename T>
class SmallVectorImpl;
} // namespace llvm
namespace clang {
class SourceManager;
class Rewriter;
} // namespace clang
/// \brief Class encapsulating a list of \c tooling::Range with some
/// convenience methods.
///
/// The ranges stored are used to keep track of the overriden parts of a file.
class ChangedRanges {
typedef std::vector<clang::tooling::Range> RangeVec;
public:
typedef RangeVec::const_iterator const_iterator;
/// \brief Create new ranges from the replacements and adjust existing one
/// to remove replaced parts.
///
/// Note that all replacements should come from the same file.
void adjustChangedRanges(const clang::tooling::ReplacementsVec &Replaces);
/// \brief Iterators.
/// \{
const_iterator begin() const { return Ranges.begin(); }
const_iterator end() const { return Ranges.end(); }
/// \}
private:
void coalesceRanges();
RangeVec Ranges;
};
/// \brief Maintains current state of transformed files and tracks source ranges
/// where changes have been made.
class FileOverrides {
public:
/// \brief Maps file names to file contents.
typedef llvm::StringMap<std::string> FileStateMap;
/// \brief Maps file names to change tracking info for a file.
typedef llvm::StringMap<ChangedRanges> ChangeMap;
/// \brief Override file contents seen by \c SM for all files stored by this
/// object.
void applyOverrides(clang::SourceManager &SM) const;
/// \brief Update change tracking information based on replacements stored in
/// \c Replaces.
void
adjustChangedRanges(const clang::replace::FileToReplacementsMap &Replaces);
/// \brief Accessor for change tracking information.
const ChangeMap &getChangedRanges() const {
return ChangeTracking;
}
/// \brief Coalesce changes stored in \c Rewrites and replace file contents
/// overrides stored in this object.
///
/// \param Rewrites Rewriter containing changes to files.
void updateState(const clang::Rewriter &Rewrites);
/// \brief Accessor for current file state.
const FileStateMap &getState() const { return FileStates; }
/// \brief Write all file contents overrides to disk.
///
/// \param Diagnostics DiagnosticsEngine for error output.
///
/// \returns \li true if all files with overridden file contents were written
/// to disk successfully.
/// \li false if any failure occurred.
bool writeToDisk(clang::DiagnosticsEngine &Diagnostics) const;
private:
FileStateMap FileStates;
ChangeMap ChangeTracking;
};
/// \brief Generate a unique filename to store the replacements.
///
/// Generates a unique filename in the same directory as \c MainSourceFile. The
/// filename is generated following this pattern:
///
/// MainSourceFile_%%_%%_%%_%%_%%_%%.yaml
///
/// where all '%' will be replaced by a randomly chosen hex number.
///
/// \param[in] MainSourceFile Full path to the source file.
/// \param[out] Result The resulting unique filename in the same directory as
/// the \c MainSourceFile.
/// \param[out] Error If an error occurs a description of that error is
/// placed in this string.
/// \returns true on success, false if a unique file name could not be created.
bool generateReplacementsFileName(const llvm::StringRef MainSourceFile,
llvm::SmallVectorImpl<char> &Result,
llvm::SmallVectorImpl<char> &Error);
#endif // CLANG_MODERNIZE_FILE_OVERRIDES_H

View File

@ -1,62 +0,0 @@
//===-- Core/Reformatting.cpp - LibFormat integration ---------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file provides the LibFormat integration used to reformat
/// migrated code.
///
//===----------------------------------------------------------------------===//
#include "Core/Reformatting.h"
#include "Core/FileOverrides.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
using namespace clang;
void Reformatter::reformatChanges(const FileOverrides &FileStates,
clang::SourceManager &SM,
clang::tooling::ReplacementsVec &Replaces) {
FileStates.applyOverrides(SM);
for (FileOverrides::ChangeMap::const_iterator
I = FileStates.getChangedRanges().begin(),
E = FileStates.getChangedRanges().end();
I != E; ++I) {
reformatSingleFile(I->getKey(), I->getValue(), SM, Replaces);
}
}
void Reformatter::reformatSingleFile(
const llvm::StringRef FileName, const ChangedRanges &Changes,
SourceManager &SM, clang::tooling::ReplacementsVec &FormatReplacements) {
const clang::FileEntry *Entry = SM.getFileManager().getFile(FileName);
assert(Entry && "expected an existing file");
FileID ID = SM.translateFile(Entry);
if (ID.isInvalid())
ID = SM.createFileID(Entry, SourceLocation(), clang::SrcMgr::C_User);
std::vector<CharSourceRange> ReformatRanges;
SourceLocation StartOfFile = SM.getLocForStartOfFile(ID);
for (ChangedRanges::const_iterator I = Changes.begin(), E = Changes.end();
I != E; ++I) {
SourceLocation Start = StartOfFile.getLocWithOffset(I->getOffset());
SourceLocation End = Start.getLocWithOffset(I->getLength());
ReformatRanges.push_back(CharSourceRange::getCharRange(Start, End));
}
Lexer Lex(ID, SM.getBuffer(ID), SM, getFormattingLangOpts(Style.Standard));
const tooling::Replacements &R =
format::reformat(Style, Lex, SM, ReformatRanges);
std::copy(R.begin(), R.end(), std::back_inserter(FormatReplacements));
}

View File

@ -1,60 +0,0 @@
//===-- Core/Reformatting.h - LibFormat integration -------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file provides the LibFormat integration used to reformat
/// migrated code.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_REFORMATTING_H
#define CLANG_MODERNIZE_REFORMATTING_H
#include "Core/Refactoring.h"
#include "clang/Format/Format.h"
class FileOverrides;
class ChangedRanges;
class Reformatter {
public:
Reformatter(const clang::format::FormatStyle &Style) : Style(Style) {}
/// \brief Reformat the changes made to the file overrides.
///
/// This function will apply the state of files stored in \c FileState to \c
/// SM.
///
/// \param[in] FileState Files to reformat.
/// \param[in] SM SourceManager for access to source files.
/// \param[out] Replaces Container to store all reformatting replacements.
void reformatChanges(const FileOverrides &FileState, clang::SourceManager &SM,
clang::tooling::ReplacementsVec &Replaces);
/// \brief Produce a list of replacements to apply on \p FileName, only the
/// ranges in \p Changes are replaced.
///
/// Since this routine use \c clang::format::reformat() the rules that
/// function applies to ranges also apply here.
///
/// \param[in] FileName Name of file to reformat.
/// \param[in] Changes Description of where changes were made to the file.
/// \param[in] SM SourceManager required to create replacements.
/// \param[out] FormatReplacements New reformatting replacements are appended
/// to this container.
void reformatSingleFile(const llvm::StringRef FileName,
const ChangedRanges &Changes,
clang::SourceManager &SM,
clang::tooling::ReplacementsVec &FormatReplacements);
private:
clang::format::FormatStyle Style;
};
#endif // CLANG_MODERNIZE_REFORMATTING_H

View File

@ -0,0 +1,155 @@
//===-- Core/ReplacementHandling.cpp --------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file provides implementations for the ReplacementHandling class.
///
//===----------------------------------------------------------------------===//
#include "Core/ReplacementHandling.h"
#include "clang/Tooling/ReplacementsYaml.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/system_error.h"
using namespace llvm;
using namespace llvm::sys;
using namespace clang::tooling;
bool ReplacementHandling::findClangApplyReplacements(const char *Argv0) {
CARPath = FindProgramByName("clang-apply-replacements");
if (!CARPath.empty())
return true;
static int StaticSymbol;
CARPath = fs::getMainExecutable(Argv0, &StaticSymbol);
SmallString<128> TestPath = path::parent_path(CARPath);
path::append(TestPath, "clang-apply-replacements");
if (fs::can_execute(Twine(TestPath)))
CARPath = TestPath.str();
return !CARPath.empty();
}
StringRef ReplacementHandling::useTempDestinationDir() {
DestinationDir = generateTempDir();
return DestinationDir;
}
void ReplacementHandling::enableFormatting(StringRef Style,
StringRef StyleConfigDir) {
DoFormat = true;
FormatStyle = Style;
this->StyleConfigDir = StyleConfigDir;
}
bool ReplacementHandling::serializeReplacements(
const TUReplacementsMap &Replacements) {
assert(!DestinationDir.empty() && "Destination directory not set");
bool Errors = false;
for (TUReplacementsMap::const_iterator I = Replacements.begin(),
E = Replacements.end();
I != E; ++I) {
SmallString<128> ReplacementsFileName;
SmallString<64> Error;
bool Result = generateReplacementsFileName(DestinationDir,
I->getValue().MainSourceFile,
ReplacementsFileName, Error);
if (!Result) {
errs() << "Failed to generate replacements filename:" << Error << "\n";
Errors = true;
continue;
}
std::string ErrorInfo;
raw_fd_ostream ReplacementsFile(ReplacementsFileName.c_str(), ErrorInfo,
fs::F_Binary);
if (!ErrorInfo.empty()) {
errs() << "Error opening file: " << ErrorInfo << "\n";
Errors = true;
continue;
}
yaml::Output YAML(ReplacementsFile);
YAML << const_cast<TranslationUnitReplacements &>(I->getValue());
}
return !Errors;
}
bool ReplacementHandling::applyReplacements() {
SmallVector<const char *, 8> Argv;
Argv.push_back(CARPath.c_str());
std::string Style = "--style=" + FormatStyle;
std::string StyleConfig = "--style-config=" + StyleConfigDir;
if (DoFormat) {
Argv.push_back("--format");
Argv.push_back(Style.c_str());
if (!StyleConfigDir.empty())
Argv.push_back(StyleConfig.c_str());
}
Argv.push_back("--remove-change-desc-files");
Argv.push_back(DestinationDir.c_str());
// Argv array needs to be null terminated.
Argv.push_back(0);
std::string ErrorMsg;
bool ExecutionFailed = false;
int ReturnCode = ExecuteAndWait(CARPath.c_str(), Argv.data(), /* env */ 0,
/* redirects */ 0,
/* secondsToWait */ 0, /* memoryLimit */ 0,
&ErrorMsg, &ExecutionFailed);
if (ExecutionFailed || !ErrorMsg.empty()) {
errs() << "Failed to launch clang-apply-replacements: " << ErrorMsg << "\n";
errs() << "Command Line:\n";
for (const char **I = Argv.begin(), **E = Argv.end(); I != E; ++I) {
if (*I)
errs() << *I << "\n";
}
return false;
}
if (ReturnCode != 0) {
errs() << "clang-apply-replacements failed with return code " << ReturnCode
<< "\n";
return false;
}
return true;
}
std::string ReplacementHandling::generateTempDir() {
SmallString<128> Prefix;
path::system_temp_directory(true, Prefix);
path::append(Prefix, "clang-modernize");
SmallString<128> Result;
fs::createUniqueDirectory(Twine(Prefix), Result);
return Result.str();
}
bool ReplacementHandling::generateReplacementsFileName(
StringRef DestinationDir, StringRef MainSourceFile,
SmallVectorImpl<char> &Result, SmallVectorImpl<char> &Error) {
Error.clear();
SmallString<128> Prefix = DestinationDir;
path::append(Prefix, path::filename(MainSourceFile));
if (error_code EC =
fs::createUniqueFile(Prefix + "_%%_%%_%%_%%_%%_%%.yaml", Result)) {
const std::string &Msg = EC.message();
Error.append(Msg.begin(), Msg.end());
return false;
}
return true;
}

View File

@ -0,0 +1,119 @@
//===-- Core/ReplacementHandling.h ------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file defines the ReplacementHandling class which abstracts
/// serialization and application of serialized replacements.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_REPLACEMENTHANDLING_H
#define CLANG_MODERNIZE_REPLACEMENTHANDLING_H
#include "llvm/ADT/StringRef.h"
#include "Core/Transform.h"
class ReplacementHandling {
public:
/// \brief Finds the path to the executable 'clang-apply-replacements'.
///
/// The executable is searched for on the PATH. If not found, looks in the
/// same directory as the image used to start the current executable.
///
/// \param[in] Argv0 argv[0] as passed to main().
///
/// \returns \li true if clang-apply-replacements was found.
/// \li false otherwise.
bool findClangApplyReplacements(const char *Argv0);
/// \brief Set the name of the directory in which replacements will be
/// serialized.
///
/// \param[in] Dir Destination directory name
void setDestinationDir(llvm::StringRef Dir) { DestinationDir = Dir; }
/// \brief Create a new temporary directory to serialize replacements into.
///
/// \returns The name of the directory createdy.
llvm::StringRef useTempDestinationDir();
/// \brief Enable clang-apply-replacements do code reformatting when applying
/// serialized replacements.
///
/// \param[in] Style Value to pass to clang-apply-replacement's --style
/// option.
/// \param[in] StyleConfigDir If non-empty, value to pass to
/// clang-apply-replacement's --style-config option.
void enableFormatting(llvm::StringRef Style,
llvm::StringRef StyleConfigDir = "");
/// \brief Write all TranslationUnitReplacements stored in \c Replacements
/// to disk.
///
/// \pre Destination directory must have been previously set by calling
/// setDestiantionDir() or useTempDestinationDir().
/// \pre Destination dir must exist.
///
/// \param[in] Replacements Container of replacements to serialize.
///
/// \returns \li true if all replacements were serialized successfully to
/// disk.
/// \li false otherwise.
bool serializeReplacements(const TUReplacementsMap &Replacements);
/// \brief Invoke clang-apply-replacements to apply all serialized
/// replacements stored in the destination directory.
///
/// \pre Destination directory must have been previously set by calling
/// setDestiantionDir() or useTempDestinationDir().
///
/// \returns \li true if clang-apply-replacements was successfully launched
/// and successfully completed.
/// \li false otherwise.
bool applyReplacements();
/// \brief Generate a unique filename to store the replacements.
///
/// Generates a unique filename in \c DestinationDir. The filename is generated
/// following this pattern:
///
/// DestinationDir/Prefix_%%_%%_%%_%%_%%_%%.yaml
///
/// where Prefix := llvm::sys::path::filename(MainSourceFile) and all '%' will
/// be replaced by a randomly chosen hex digit.
///
/// \param[in] DestinationDir Directory the unique file should be placed in.
/// \param[in] MainSourceFile Full path to the source file.
/// \param[out] Result The resulting unique filename.
/// \param[out] Error If an error occurs a description of that error is
/// placed in this string.
///
/// \returns \li true on success
/// \li false if a unique file name could not be created.
static bool generateReplacementsFileName(llvm::StringRef DestinationDir,
llvm::StringRef MainSourceFile,
llvm::SmallVectorImpl<char> &Result,
llvm::SmallVectorImpl<char> &Error);
/// \brief Helper to create a temporary directory name.
///
/// \param[out] Resulting name is placed here.
static std::string generateTempDir();
private:
std::string CARPath;
std::string DestinationDir;
bool DoFormat;
std::string FormatStyle;
std::string StyleConfigDir;
};
#endif // CLANG_MODERNIZE_REPLACEMENTHANDLING_H

View File

@ -1,73 +0,0 @@
//===-- Core/SyntaxCheck.cpp ----------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file exposes functionaliy for doing a syntax-only check on
/// files with overridden contents.
///
//===----------------------------------------------------------------------===//
#include "Core/SyntaxCheck.h"
#include "Core/FileOverrides.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/Tooling.h"
using namespace clang;
using namespace tooling;
class SyntaxCheck : public SyntaxOnlyAction {
public:
SyntaxCheck(const FileOverrides &Overrides) : Overrides(Overrides) {}
virtual bool BeginSourceFileAction(CompilerInstance &CI, StringRef Filename) {
if (!SyntaxOnlyAction::BeginSourceFileAction(CI, Filename))
return false;
Overrides.applyOverrides(CI.getSourceManager());
return true;
}
private:
const FileOverrides &Overrides;
};
class SyntaxCheckFactory : public FrontendActionFactory {
public:
SyntaxCheckFactory(const FileOverrides &Overrides)
: Overrides(Overrides) {}
virtual FrontendAction *create() { return new SyntaxCheck(Overrides); }
private:
const FileOverrides &Overrides;
};
class SyntaxArgumentsAdjuster : public ArgumentsAdjuster {
CommandLineArguments Adjust(const CommandLineArguments &Args) {
CommandLineArguments AdjustedArgs = Args;
AdjustedArgs.push_back("-fsyntax-only");
AdjustedArgs.push_back("-std=c++11");
return AdjustedArgs;
}
};
bool doSyntaxCheck(const CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths,
const FileOverrides &Overrides) {
ClangTool SyntaxTool(Database, SourcePaths);
// Ensure C++11 support is enabled.
// FIXME: This isn't necessary anymore since the Modernizer requires C++11
// to be enabled in the CompilationDatabase. Remove later.
SyntaxTool.setArgumentsAdjuster(new SyntaxArgumentsAdjuster);
return SyntaxTool.run(new SyntaxCheckFactory(Overrides)) == 0;
}

View File

@ -1,38 +0,0 @@
//===-- Core/SyntaxCheck.h --------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file exposes functionaliy for doing a syntax-only check on
/// files with overridden contents.
///
//===----------------------------------------------------------------------===//
#ifndef CLANG_MODERNIZE_SYNTAX_CHECK_H
#define CLANG_MODERNIZE_SYNTAX_CHECK_H
#include <string>
#include <vector>
// Forward Declarations
namespace clang {
namespace tooling {
class CompilationDatabase;
} // namespace tooling
} // namespace clang
class FileOverrides;
/// \brief Perform a syntax-only check over all files in \c SourcePaths using
/// options provided by \c Database using file contents from \c Overrides if
/// available.
extern bool doSyntaxCheck(const clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths,
const FileOverrides &Overrides);
#endif // CLANG_MODERNIZE_SYNTAX_CHECK_H

View File

@ -14,7 +14,6 @@
//===----------------------------------------------------------------------===//
#include "Core/Transform.h"
#include "Core/FileOverrides.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
@ -76,7 +75,7 @@ private:
} // namespace
Transform::Transform(llvm::StringRef Name, const TransformOptions &Options)
: Name(Name), GlobalOptions(Options), Overrides(0) {
: Name(Name), GlobalOptions(Options) {
Reset();
}
@ -95,9 +94,6 @@ bool Transform::isFileModifiable(const SourceManager &SM,
}
bool Transform::handleBeginSource(CompilerInstance &CI, StringRef Filename) {
assert(Overrides != 0 && "Subclass transform didn't provide InputState");
Overrides->applyOverrides(CI.getSourceManager());
CurrentSource = Filename;
if (Options().EnableTiming) {

View File

@ -50,9 +50,6 @@ class MatchFinder;
} // namespace ast_matchers
} // namespace clang
class FileOverrides;
// \brief Maps main source file names to a TranslationUnitReplacements
// structure storing replacements for that translation unit.
typedef llvm::StringMap<clang::tooling::TranslationUnitReplacements>
@ -95,13 +92,13 @@ public:
/// \brief Apply a transform to all files listed in \p SourcePaths.
///
/// \p Database must contain information for how to compile all files in \p
/// SourcePaths. \p InputStates contains the file contents of files in \p
/// 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 FileOverrides &InputStates,
const clang::tooling::CompilationDatabase &Database,
/// \param[in] Database Contains information for how to compile all files in
/// \p SourcePaths.
/// \param[in] SourcePaths list of sources to transform.
///
/// \returns \li 0 if successful
/// \li 1 otherwise
virtual int apply(const clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) = 0;
/// \brief Query if changes were made during the last call to apply().
@ -207,15 +204,6 @@ protected:
/// created with.
const TransformOptions &Options() { return GlobalOptions; }
/// \brief Affords a subclass to provide file contents overrides before
/// applying frontend actions.
///
/// It is an error not to call this function before calling ClangTool::run()
/// with the factory provided by createActionFactory().
void setOverrides(const FileOverrides &Overrides) {
this->Overrides = &Overrides;
}
/// \brief Subclasses must call this function to create a
/// FrontendActionFactory to pass to ClangTool.
///
@ -227,7 +215,6 @@ protected:
private:
const std::string Name;
const TransformOptions &GlobalOptions;
const FileOverrides *Overrides;
TUReplacementsMap Replacements;
std::string CurrentSource;
TimingVec Timings;

View File

@ -24,8 +24,7 @@ using clang::ast_matchers::MatchFinder;
using namespace clang::tooling;
using namespace clang;
int LoopConvertTransform::apply(const FileOverrides &InputStates,
const CompilationDatabase &Database,
int LoopConvertTransform::apply(const CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) {
ClangTool LoopTool(Database, SourcePaths);
@ -49,8 +48,6 @@ int LoopConvertTransform::apply(const FileOverrides &InputStates,
LFK_PseudoArray, /*Owner=*/ *this);
Finder.addMatcher(makePseudoArrayLoopMatcher(), &PseudoarrrayLoopFixer);
setOverrides(InputStates);
if (int result = LoopTool.run(createActionFactory(Finder))) {
llvm::errs() << "Error encountered during translation.\n";
return result;

View File

@ -31,8 +31,7 @@ public:
: Transform("LoopConvert", Options) {}
/// \see Transform::run().
virtual int apply(const FileOverrides &InputStates,
const clang::tooling::CompilationDatabase &Database,
virtual int apply(const clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) LLVM_OVERRIDE;
virtual bool handleBeginSource(clang::CompilerInstance &CI,

View File

@ -21,8 +21,7 @@ using namespace clang;
using namespace clang::tooling;
using namespace clang::ast_matchers;
int PassByValueTransform::apply(const FileOverrides &InputStates,
const tooling::CompilationDatabase &Database,
int PassByValueTransform::apply(const tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) {
ClangTool Tool(Database, SourcePaths);
unsigned AcceptedChanges = 0;
@ -36,8 +35,6 @@ int PassByValueTransform::apply(const FileOverrides &InputStates,
// make the replacer available to handleBeginSource()
this->Replacer = &Replacer;
setOverrides(InputStates);
if (Tool.run(createActionFactory(Finder))) {
llvm::errs() << "Error encountered during translation.\n";
return 1;

View File

@ -58,8 +58,7 @@ public:
: Transform("PassByValue", Options), Replacer(0) {}
/// \see Transform::apply().
virtual int apply(const FileOverrides &InputStates,
const clang::tooling::CompilationDatabase &Database,
virtual int apply(const clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) LLVM_OVERRIDE;
private:

View File

@ -22,8 +22,7 @@ using namespace clang::tooling;
using namespace clang::ast_matchers;
int
ReplaceAutoPtrTransform::apply(const FileOverrides &InputStates,
const CompilationDatabase &Database,
ReplaceAutoPtrTransform::apply(const CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) {
ClangTool Tool(Database, SourcePaths);
unsigned AcceptedChanges = 0;
@ -35,8 +34,6 @@ ReplaceAutoPtrTransform::apply(const FileOverrides &InputStates,
Finder.addMatcher(makeAutoPtrUsingDeclMatcher(), &Replacer);
Finder.addMatcher(makeTransferOwnershipExprMatcher(), &Fixer);
setOverrides(InputStates);
if (Tool.run(createActionFactory(Finder))) {
llvm::errs() << "Error encountered during translation.\n";
return 1;

View File

@ -47,8 +47,7 @@ public:
: Transform("ReplaceAutoPtr", Options) {}
/// \see Transform::run().
virtual int apply(const FileOverrides &InputStates,
const clang::tooling::CompilationDatabase &Database,
virtual int apply(const clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) LLVM_OVERRIDE;
};

View File

@ -20,8 +20,7 @@ using clang::ast_matchers::MatchFinder;
using namespace clang;
using namespace clang::tooling;
int UseAutoTransform::apply(const FileOverrides &InputStates,
const clang::tooling::CompilationDatabase &Database,
int UseAutoTransform::apply(const clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) {
ClangTool UseAutoTool(Database, SourcePaths);
@ -37,8 +36,6 @@ int UseAutoTransform::apply(const FileOverrides &InputStates,
Finder.addMatcher(makeIteratorDeclMatcher(), &ReplaceIterators);
Finder.addMatcher(makeDeclWithNewMatcher(), &ReplaceNew);
setOverrides(InputStates);
if (int Result = UseAutoTool.run(createActionFactory(Finder))) {
llvm::errs() << "Error encountered during translation.\n";
return Result;

View File

@ -34,8 +34,7 @@ public:
: Transform("UseAuto", Options) {}
/// \see Transform::run().
virtual int apply(const FileOverrides &InputStates,
const clang::tooling::CompilationDatabase &Database,
virtual int apply(const clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) LLVM_OVERRIDE;
};

View File

@ -24,8 +24,7 @@ using clang::ast_matchers::MatchFinder;
using namespace clang::tooling;
using namespace clang;
int UseNullptrTransform::apply(const FileOverrides &InputStates,
const CompilationDatabase &Database,
int UseNullptrTransform::apply(const CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) {
ClangTool UseNullptrTool(Database, SourcePaths);
@ -36,8 +35,6 @@ int UseNullptrTransform::apply(const FileOverrides &InputStates,
Finder.addMatcher(makeCastSequenceMatcher(), &Fixer);
setOverrides(InputStates);
if (int result = UseNullptrTool.run(createActionFactory(Finder))) {
llvm::errs() << "Error encountered during translation.\n";
return result;

View File

@ -28,8 +28,7 @@ public:
: Transform("UseNullptr", Options) {}
/// \see Transform::run().
virtual int apply(const FileOverrides &InputStates,
const clang::tooling::CompilationDatabase &Database,
virtual int apply(const clang::tooling::CompilationDatabase &Database,
const std::vector<std::string> &SourcePaths) LLVM_OVERRIDE;
};

View File

@ -34,7 +34,6 @@ add_dependencies(clang-modernize
)
target_link_libraries(clang-modernize
clangApplyReplacements
modernizeCore
)

View File

@ -15,23 +15,20 @@
///
//===----------------------------------------------------------------------===//
#include "Core/FileOverrides.h"
#include "Core/PerfSupport.h"
#include "Core/SyntaxCheck.h"
#include "Core/ReplacementHandling.h"
#include "Core/Transform.h"
#include "Core/Transforms.h"
#include "Core/Reformatting.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Format/Format.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
#include "clang-apply-replacements/Tooling/ApplyReplacements.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Signals.h"
namespace cl = llvm::cl;
@ -81,13 +78,29 @@ static cl::opt<bool> FinalSyntaxCheck(
cl::desc("Check for correct syntax after applying transformations"),
cl::init(false));
static cl::opt<std::string> FormatStyleOpt(
"format-style",
cl::desc("Coding style to use on the replacements, either a builtin style\n"
"or a YAML config file (see: clang-format -dump-config).\n"
"Currently supports 4 builtins style:\n"
" LLVM, Google, Chromium, Mozilla.\n"),
cl::value_desc("string"));
static cl::OptionCategory FormattingCategory("Formatting Options");
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));
static cl::opt<std::string>
FormatStyleOpt("style", cl::desc(format::StyleOptionHelpDescription),
cl::init("LLVM"), 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<bool>
SummaryMode("summary", cl::desc("Print transform summary"),
@ -98,29 +111,46 @@ static cl::opt<std::string> TimingDirectoryName(
"directory. Default: ./migrate_perf"),
cl::ValueOptional, cl::value_desc("directory name"));
static cl::OptionCategory IncludeExcludeCategory("Inclusion/Exclusion Options");
static cl::opt<std::string>
IncludePaths("include",
cl::desc("Comma seperated list of paths to consider to be "
"transformed"));
"transformed"),
cl::cat(IncludeExcludeCategory));
static cl::opt<std::string>
ExcludePaths("exclude",
cl::desc("Comma seperated list of paths that can not "
"be transformed"));
ExcludePaths("exclude", cl::desc("Comma seperated list of paths that can not "
"be transformed"),
cl::cat(IncludeExcludeCategory));
static cl::opt<std::string>
IncludeFromFile("include-from", cl::value_desc("filename"),
cl::desc("File containing a list of paths to consider to "
"be transformed"));
"be transformed"),
cl::cat(IncludeExcludeCategory));
static cl::opt<std::string>
ExcludeFromFile("exclude-from", cl::value_desc("filename"),
cl::desc("File containing a list of paths that can not be "
"transforms"));
"transforms"),
cl::cat(IncludeExcludeCategory));
static cl::OptionCategory SerializeCategory("Serialization Options");
static cl::opt<bool>
SerializeReplacements("serialize-replacements",
cl::Hidden,
cl::desc("Serialize translation unit replacements to "
"disk instead of changing files."),
cl::init(false));
SerializeOnly("serialize-replacements",
cl::desc("Serialize translation unit replacements to "
"disk instead of changing files."),
cl::init(false),
cl::cat(SerializeCategory));
static cl::opt<std::string>
SerializeLocation("serialize-dir",
cl::desc("Path to an existing directory in which to write\n"
"serialized replacements. Default behaviour is to\n"
"write to a temporary directory.\n"),
cl::cat(SerializeCategory));
cl::opt<std::string> SupportedCompilers(
"for-compilers", cl::value_desc("string"),
@ -184,102 +214,6 @@ static CompilerVersions handleSupportedCompilers(const char *ProgName,
return RequiredVersions;
}
/// \brief Creates the Reformatter if the format style option is provided,
/// return a null pointer otherwise.
///
/// \param ProgName The name of the program, \c argv[0], used to print errors.
/// \param Error If the \c -format-style is provided but with wrong parameters
/// this is parameter is set to \c true, left untouched otherwise. An error
/// message is printed with an explanation.
static Reformatter *handleFormatStyle(const char *ProgName, bool &Error) {
if (FormatStyleOpt.getNumOccurrences() > 0) {
format::FormatStyle Style;
if (!format::getPredefinedStyle(FormatStyleOpt, &Style)) {
llvm::StringRef ConfigFilePath = FormatStyleOpt;
llvm::OwningPtr<llvm::MemoryBuffer> Text;
llvm::error_code ec;
ec = llvm::MemoryBuffer::getFile(ConfigFilePath, Text);
if (!ec)
ec = parseConfiguration(Text->getBuffer(), &Style);
if (ec) {
llvm::errs() << ProgName << ": invalid format style " << FormatStyleOpt
<< ": " << ec.message() << "\n";
Error = true;
return 0;
}
}
// force mode to C++11
Style.Standard = clang::format::FormatStyle::LS_Cpp11;
return new Reformatter(Style);
}
return 0;
}
/// \brief Use \c ChangesReformatter to reformat all changed regions of all
/// files stored in \c Overrides and write the result to disk.
///
/// \returns \li true if reformatting replacements were successfully applied
/// without conflicts and all files were successfully written to
/// disk.
/// \li false if reformatting could not be successfully applied or
/// if at least one file failed to write to disk.
void reformat(Reformatter &ChangesReformatter, FileOverrides &Overrides,
DiagnosticsEngine &Diagnostics) {
FileManager Files((FileSystemOptions()));
SourceManager SM(Diagnostics, Files);
replace::TUReplacements AllReplacements(1);
ChangesReformatter.reformatChanges(Overrides, SM,
AllReplacements.front().Replacements);
replace::FileToReplacementsMap GroupedReplacements;
if (!replace::mergeAndDeduplicate(AllReplacements, GroupedReplacements, SM)) {
llvm::errs() << "Warning: Reformatting produced conflicts.\n";
return;
}
Rewriter DestRewriter(SM, LangOptions());
if (!replace::applyReplacements(GroupedReplacements, DestRewriter)) {
llvm::errs() << "Warning: Failed to apply reformatting conflicts!\n";
return;
}
Overrides.updateState(DestRewriter);
}
bool serializeReplacements(const replace::TUReplacements &Replacements) {
bool Errors = false;
for (replace::TUReplacements::const_iterator I = Replacements.begin(),
E = Replacements.end();
I != E; ++I) {
llvm::SmallString<128> ReplacementsFileName;
llvm::SmallString<64> Error;
bool Result = generateReplacementsFileName(I->MainSourceFile,
ReplacementsFileName, Error);
if (!Result) {
llvm::errs() << "Failed to generate replacements filename:" << Error
<< "\n";
Errors = true;
continue;
}
std::string ErrorInfo;
llvm::raw_fd_ostream ReplacementsFile(ReplacementsFileName.c_str(),
ErrorInfo, llvm::sys::fs::F_Binary);
if (!ErrorInfo.empty()) {
llvm::errs() << "Error opening file: " << ErrorInfo << "\n";
Errors = true;
continue;
}
llvm::yaml::Output YAML(ReplacementsFile);
YAML << const_cast<TranslationUnitReplacements &>(*I);
}
return !Errors;
}
CompilationDatabase *autoDetectCompilations(std::string &ErrorMessage) {
// Auto-detect a compilation database from BuildPath.
if (BuildPath.getNumOccurrences() > 0)
@ -332,6 +266,7 @@ static bool isFileExplicitlyExcludedPredicate(llvm::StringRef FilePath) {
int main(int argc, const char **argv) {
llvm::sys::PrintStackTraceOnErrorSignal();
Transforms TransformManager;
ReplacementHandling ReplacementHandler;
TransformManager.registerTransforms();
@ -386,11 +321,7 @@ int main(int argc, const char **argv) {
// Enable timming.
GlobalOptions.EnableTiming = TimingDirectoryName.getNumOccurrences() > 0;
// Check the reformatting style option
bool CmdSwitchError = false;
llvm::OwningPtr<Reformatter> ChangesReformatter(
handleFormatStyle(argv[0], CmdSwitchError));
CompilerVersions RequiredVersions =
handleSupportedCompilers(argv[0], CmdSwitchError);
if (CmdSwitchError)
@ -398,15 +329,6 @@ int main(int argc, const char **argv) {
TransformManager.createSelectedTransforms(GlobalOptions, RequiredVersions);
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts(
new DiagnosticOptions());
DiagnosticsEngine Diagnostics(
llvm::IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
DiagOpts.getPtr());
// FIXME: Make this DiagnosticsEngine available to all Transforms probably via
// GlobalOptions.
if (TransformManager.begin() == TransformManager.end()) {
if (SupportedCompilers.empty())
llvm::errs() << llvm::sys::path::filename(argv[0])
@ -417,28 +339,52 @@ int main(int argc, const char **argv) {
return 1;
}
// If SerializeReplacements is requested, then change reformatting must be
// turned off and only one transform should be requested. Reformatting is
// basically another transform so even if there's only one other transform,
// the reformatting pass would make two.
if (SerializeReplacements &&
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts(
new DiagnosticOptions());
DiagnosticsEngine Diagnostics(
llvm::IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
DiagOpts.getPtr());
// FIXME: Make this DiagnosticsEngine available to all Transforms probably via
// GlobalOptions.
// If SerializeReplacements is requested, then code reformatting must be
// turned off and only one transform should be requested.
if (SerializeOnly &&
(std::distance(TransformManager.begin(), TransformManager.end()) > 1 ||
ChangesReformatter)) {
DoFormat)) {
llvm::errs() << "Serialization of replacements requested for multiple "
"transforms.\nChanges from only one transform can be "
"serialized.\n";
return 1;
}
// If we're asked to apply changes to files on disk, need to locate
// clang-apply-replacements.
if (!SerializeOnly) {
if (!ReplacementHandler.findClangApplyReplacements(argv[0])) {
llvm::errs() << "Could not find clang-apply-replacements\n";
return 1;
}
if (DoFormat)
ReplacementHandler.enableFormatting(FormatStyleOpt, FormatStyleConfig);
}
StringRef TempDestinationDir;
if (SerializeLocation.getNumOccurrences() > 0)
ReplacementHandler.setDestinationDir(SerializeLocation);
else
TempDestinationDir = ReplacementHandler.useTempDestinationDir();
SourcePerfData PerfData;
FileOverrides FileStates;
for (Transforms::const_iterator I = TransformManager.begin(),
E = TransformManager.end();
I != E; ++I) {
Transform *T = *I;
if (T->apply(FileStates, *Compilations, Sources) != 0) {
if (T->apply(*Compilations, Sources) != 0) {
// FIXME: Improve ClangTool to not abort if just one file fails.
return 1;
}
@ -456,65 +402,25 @@ int main(int argc, const char **argv) {
llvm::outs() << "\n";
}
// Collect all TranslationUnitReplacements generated from the translation
// units the transform worked on and store them in AllReplacements.
replace::TUReplacements AllReplacements;
const TUReplacementsMap &ReplacementsMap = T->getAllReplacements();
const TranslationUnitReplacements &(
TUReplacementsMap::value_type::*getValue)() const =
&TUReplacementsMap::value_type::getValue;
std::transform(ReplacementsMap.begin(), ReplacementsMap.end(),
std::back_inserter(AllReplacements),
std::mem_fun_ref(getValue));
if (SerializeReplacements)
serializeReplacements(AllReplacements);
FileManager Files((FileSystemOptions()));
SourceManager SM(Diagnostics, Files);
// Make sure SourceManager is updated to have the same initial state as the
// transforms.
FileStates.applyOverrides(SM);
replace::FileToReplacementsMap GroupedReplacements;
if (!replace::mergeAndDeduplicate(AllReplacements, GroupedReplacements,
SM)) {
llvm::outs() << "Transform " << T->getName()
<< " resulted in conflicts. Discarding all "
<< "replacements.\n";
continue;
}
// Apply replacements and update FileStates with new state.
Rewriter DestRewriter(SM, LangOptions());
if (!replace::applyReplacements(GroupedReplacements, DestRewriter)) {
llvm::outs() << "Some replacements failed to apply. Discarding "
"all replacements.\n";
continue;
}
// Update contents of files in memory to serve as initial state for next
// transform.
FileStates.updateState(DestRewriter);
// Update changed ranges for reformatting
if (ChangesReformatter)
FileStates.adjustChangedRanges(GroupedReplacements);
}
// Skip writing final file states to disk if we were asked to serialize
// replacements. Otherwise reformat changes if reformatting is enabled.
if (!SerializeReplacements) {
if (ChangesReformatter)
reformat(*ChangesReformatter, FileStates, Diagnostics);
FileStates.writeToDisk(Diagnostics);
}
if (FinalSyntaxCheck)
if (!doSyntaxCheck(*Compilations, Sources, FileStates))
if (!ReplacementHandler.serializeReplacements(T->getAllReplacements()))
return 1;
if (!SerializeOnly)
if (!ReplacementHandler.applyReplacements())
return 1;
}
// Let the user know which temporary directory the replacements got written
// to.
if (SerializeOnly && !TempDestinationDir.empty())
llvm::errs() << "Replacements serialized to: " << TempDestinationDir << "\n";
if (FinalSyntaxCheck) {
ClangTool SyntaxTool(*Compilations, SourcePaths);
if (SyntaxTool.run(newFrontendActionFactory<SyntaxOnlyAction>()) != 0)
return 1;
}
// Report execution times.
if (GlobalOptions.EnableTiming && !PerfData.empty()) {
std::string DirectoryName = TimingDirectoryName;

View File

@ -36,14 +36,14 @@ SOURCES += $(addprefix ../ReplaceAutoPtr/,$(notdir $(wildcard $(PROJ_SRC_DIR)/..
BUILT_SOURCES += $(ObjDir)/../ReplaceAutoPtr/.objdir
LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc mcparser option
USEDLIBS = modernizeCore.a clangFormat.a clangApplyReplacements.a clangTooling.a clangFrontend.a \
USEDLIBS = modernizeCore.a clangFormat.a clangTooling.a clangFrontend.a \
clangSerialization.a clangDriver.a clangRewriteFrontend.a \
clangRewriteCore.a clangParse.a clangSema.a clangAnalysis.a \
clangAST.a clangASTMatchers.a clangEdit.a clangLex.a clangBasic.a
include $(CLANG_LEVEL)/Makefile
CPP.Flags += -I$(PROJ_SRC_DIR)/.. -I$(PROJ_SRC_DIR)/../../clang-apply-replacements/include
CPP.Flags += -I$(PROJ_SRC_DIR)/..
# BUILT_SOURCES gets used as a prereq for many top-level targets. However, at
# the point those targets are defined, $(ObjDir) hasn't been defined and so the

View File

@ -0,0 +1,43 @@
---
# BasedOnStyle: Google
AccessModifierOffset: -1
ConstructorInitializerIndentWidth: 4
AlignEscapedNewlinesLeft: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: true
AlwaysBreakTemplateDeclarations: true
AlwaysBreakBeforeMultilineStrings: true
BreakBeforeBinaryOperators: false
BreakConstructorInitializersBeforeComma: false
BinPackParameters: true
ColumnLimit: 80
ConstructorInitializerAllOnOneLineOrOnePerLine: true
DerivePointerBinding: true
ExperimentalAutoDetectBinPacking: false
IndentCaseLabels: true
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCSpaceBeforeProtocolList: false
PenaltyBreakComment: 60
PenaltyBreakString: 1000
PenaltyBreakFirstLessLess: 120
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerBindsToType: true
SpacesBeforeTrailingComments: 2
Cpp11BracedListStyle: true
Standard: Auto
IndentWidth: 2
TabWidth: 8
UseTab: Never
BreakBeforeBraces: Attach
IndentFunctionDeclarationAfterType: true
SpacesInParentheses: false
SpaceInEmptyParentheses: false
SpacesInCStyleCastParentheses: false
SpaceAfterControlStatementKeyword: true
SpaceBeforeAssignmentOperators: true
...

View File

@ -1,10 +0,0 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: clang-modernize -format-style=LLVM -use-auto %t.cpp -- -std=c++11
// RUN: FileCheck --strict-whitespace -input-file=%t.cpp %s
class C {};
void f() { //
C *a = new C();
// CHECK: {{^\ \ auto\ a\ \=\ new\ C\(\);}}
}

View File

@ -1,11 +1,37 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: not clang-modernize -format-style=non_existent_file.yaml -use-auto %t.cpp -- -std=c++11
// RUN: touch %T/non_format_config.yaml
// RUN: not clang-modernize -format-style=%T/non_format_config.yaml -use-auto %t.cpp -- -std=c++11
// RUN: clang-modernize -format-style=LLVM -use-auto %t.cpp -- -std=c++11
// RUN: clang-modernize -format -use-auto %t.cpp
// RUN: FileCheck --strict-whitespace -input-file=%t.cpp %s
class MyType012345678901234567890123456789 {};
// Ensure that -style is forwarded to clang-apply-replacements by using a style
// other than LLVM and ensuring the result is styled as requested.
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: clang-modernize -format -style=Google -use-nullptr %t.cpp
// RUN: FileCheck --check-prefix=Google --strict-whitespace -input-file=%t.cpp %s
// Ensure -style-config is forwarded to clang-apply-replacements. The .clang-format
// in %S/Inputs is a dump of the Google style so the same test can be used.
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: clang-modernize -format -style=file -style-config=%S/Inputs -use-nullptr %t.cpp
// RUN: FileCheck --check-prefix=Google --strict-whitespace -input-file=%t.cpp %s
class MyType012345678901234567890123456789 {
public:
MyType012345678901234567890123456789()
: iiiiiiiiiiii(0), jjjjjjjjjjjj(0), kkkkkkkkkkkk(0), mmmmmmmmmmmm(0),
nnnnnnnnnnnn(0) {}
// Google: iiiiiiiiiiii(nullptr),
// Google-NEXT: jjjjjjjjjjjj(nullptr),
// Google-NEXT: kkkkkkkkkkkk(nullptr),
// Google-NEXT: mmmmmmmmmmmm(nullptr),
// Google-NEXT: nnnnnnnnnnnn(nullptr) {}
private:
int *iiiiiiiiiiii;
int *jjjjjjjjjjjj;
int *kkkkkkkkkkkk;
int *mmmmmmmmmmmm;
int *nnnnnnnnnnnn;
};
int f() {
MyType012345678901234567890123456789 *a =
@ -13,4 +39,6 @@ int f() {
// CHECK: {{^\ \ auto\ a\ \=\ new\ MyType012345678901234567890123456789\(\);}}
delete a;
return 0;
}

View File

@ -7,7 +7,7 @@
// RUN: rm -rf %T/SerializeTest
// RUN: mkdir -p %T/SerializeTest
// RUN: cp %S/main.cpp %S/common.cpp %S/common.h %T/SerializeTest
// RUN: clang-modernize -loop-convert -serialize-replacements -include=%T/SerializeTest %T/SerializeTest/main.cpp %T/SerializeTest/common.cpp --
// RUN: clang-modernize -loop-convert -serialize-replacements -serialize-dir=%T/SerializeTest -include=%T/SerializeTest %T/SerializeTest/main.cpp %T/SerializeTest/common.cpp --
// Check that only 1 file is generated per translation unit
// RUN: ls -1 %T/SerializeTest | FileCheck %s --check-prefix=MAIN_CPP
// RUN: ls -1 %T/SerializeTest | FileCheck %s --check-prefix=COMMON_CPP

View File

@ -15,8 +15,6 @@ include_directories(
)
add_extra_unittest(ClangModernizeTests
FileOverridesTest.cpp
ReformattingTest.cpp
IncludeExcludeTest.cpp
PerfSupportTest.cpp
TransformTest.cpp

View File

@ -1,187 +0,0 @@
//===- clang-modernize/FileOverridesTest.cpp - File overrides unit tests --===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Core/FileOverrides.h"
#include "Core/Refactoring.h"
#include "gtest/gtest.h"
#include "common/VirtualFileHelper.h"
#include "clang/Rewrite/Core/Rewriter.h"
using namespace clang;
using namespace clang::tooling;
static Replacement makeReplacement(unsigned Offset, unsigned Length,
unsigned ReplacementLength,
llvm::StringRef FilePath) {
return Replacement(FilePath, Offset, Length,
std::string(ReplacementLength, '~'));
}
// generate a set of replacements containing one element
static ReplacementsVec makeReplacements(unsigned Offset, unsigned Length,
unsigned ReplacementLength,
llvm::StringRef FilePath = "~") {
ReplacementsVec Replaces;
Replaces.push_back(
makeReplacement(Offset, Length, ReplacementLength, FilePath));
return Replaces;
}
static bool equalRanges(Range A, Range B) {
return A.getOffset() == B.getOffset() && A.getLength() == B.getLength();
}
TEST(ChangedRangesTest, adjustChangedRangesShrink) {
ChangedRanges Changes;
Changes.adjustChangedRanges(makeReplacements(0, 0, 4));
EXPECT_NE(Changes.begin(), Changes.end());
EXPECT_TRUE(equalRanges(Range(0, 4), *Changes.begin()));
// create a replacement that cuts the end of the last insertion
Changes.adjustChangedRanges(makeReplacements(2, 4, 0));
Range ExpectedChanges[] = { Range(0, 2) };
EXPECT_TRUE(
std::equal(Changes.begin(), Changes.end(), ExpectedChanges, equalRanges));
}
TEST(ChangedRangesTest, adjustChangedRangesExtend) {
ChangedRanges Changes;
Changes.adjustChangedRanges(makeReplacements(1, 0, 4));
// cut the old one by a bigger one
Changes.adjustChangedRanges(makeReplacements(3, 4, 6));
Range ExpectedChanges[] = { Range(1, 8) };
EXPECT_TRUE(
std::equal(Changes.begin(), Changes.end(), ExpectedChanges, equalRanges));
}
TEST(ChangedRangesTest, adjustChangedRangesNoOverlap) {
ChangedRanges Changes;
Changes.adjustChangedRanges(makeReplacements(0, 0, 4));
Changes.adjustChangedRanges(makeReplacements(6, 0, 4));
Range ExpectedChanges[] = { Range(0, 4), Range(6, 4) };
EXPECT_TRUE(
std::equal(Changes.begin(), Changes.end(), ExpectedChanges, equalRanges));
}
TEST(ChangedRangesTest, adjustChangedRangesNullRange) {
ChangedRanges Changes;
Changes.adjustChangedRanges(makeReplacements(0, 4, 0));
Range ExpectedChanges[] = { Range(0, 0) };
EXPECT_TRUE(
std::equal(Changes.begin(), Changes.end(), ExpectedChanges, equalRanges));
}
TEST(ChangedRangesTest, adjustChangedRangesExtendExisting) {
ChangedRanges Changes;
Changes.adjustChangedRanges(makeReplacements(0, 0, 3));
Changes.adjustChangedRanges(makeReplacements(2, 5, 8));
Range ExpectedChanges[] = { Range(0, 10) };
EXPECT_TRUE(
std::equal(Changes.begin(), Changes.end(), ExpectedChanges, equalRanges));
}
TEST(ChangedRangesTest, adjustChangedRangesSplit) {
ChangedRanges Changes;
Changes.adjustChangedRanges(makeReplacements(0, 0, 3));
Changes.adjustChangedRanges(makeReplacements(1, 1, 0));
Range ExpectedChanges[] = { Range(0, 2) };
EXPECT_TRUE(
std::equal(Changes.begin(), Changes.end(), ExpectedChanges, equalRanges));
}
TEST(ChangedRangesTest, adjustChangedRangesRangeContained) {
ChangedRanges Changes;
Changes.adjustChangedRanges(makeReplacements(3, 0, 2));
Changes.adjustChangedRanges(makeReplacements(1, 4, 5));
Range ExpectedChanges[] = { Range(1, 5) };
EXPECT_TRUE(
std::equal(Changes.begin(), Changes.end(), ExpectedChanges, equalRanges));
}
TEST(ChangedRangesTest, adjustChangedRangesRangeResized) {
ChangedRanges Changes;
Changes.adjustChangedRanges(makeReplacements(2, 0, 5));
// first make the range bigger
Changes.adjustChangedRanges(makeReplacements(4, 1, 3));
Range ExpectedChanges[] = { Range(2, 7) };
EXPECT_TRUE(
std::equal(Changes.begin(), Changes.end(), ExpectedChanges, equalRanges));
// then smaller
Changes.adjustChangedRanges(makeReplacements(3, 3, 1));
ExpectedChanges[0] = Range(2, 5);
EXPECT_TRUE(
std::equal(Changes.begin(), Changes.end(), ExpectedChanges, equalRanges));
}
TEST(FileOverridesTest, applyOverrides) {
// Set up initial state
VirtualFileHelper VFHelper;
SmallString<128> fileAPath("fileA.cpp");
ASSERT_FALSE(llvm::sys::fs::make_absolute(fileAPath));
SmallString<128> fileBPath("fileB.cpp");
ASSERT_FALSE(llvm::sys::fs::make_absolute(fileBPath));
VFHelper.mapFile(fileAPath, "Content A");
VFHelper.mapFile(fileBPath, "Content B");
SourceManager &SM = VFHelper.getNewSourceManager();
// Fill a Rewriter with changes
Rewriter Rewrites(SM, LangOptions());
ReplacementsVec R(1, Replacement(fileAPath, 0, 7, "Stuff"));
ASSERT_TRUE(applyAllReplacements(R, Rewrites));
FileOverrides Overrides;
Overrides.updateState(Rewrites);
const FileOverrides::FileStateMap &State = Overrides.getState();
// Ensure state updated
ASSERT_TRUE(State.end() == State.find(fileBPath));
ASSERT_TRUE(State.begin() == State.find(fileAPath));
ASSERT_EQ("Stuff A", State.begin()->getValue());
Overrides.applyOverrides(SM);
const FileEntry *EntryA = SM.getFileManager().getFile(fileAPath);
FileID IdA = SM.translateFile(EntryA);
ASSERT_FALSE(IdA.isInvalid());
// Ensure the contents of the buffer matches what we'd expect.
const llvm::MemoryBuffer *BufferA = SM.getBuffer(IdA);
ASSERT_FALSE(0 == BufferA);
ASSERT_EQ("Stuff A", BufferA->getBuffer());
}
TEST(FileOverridesTest, adjustChangedRanges) {
SmallString<128> fileAPath("fileA.cpp");
ASSERT_FALSE(llvm::sys::fs::make_absolute(fileAPath));
SmallString<128> fileBPath("fileB.cpp");
ASSERT_FALSE(llvm::sys::fs::make_absolute(fileBPath));
replace::FileToReplacementsMap GroupedReplacements;
GroupedReplacements[fileAPath] = makeReplacements(0, 5, 4, fileAPath);
GroupedReplacements[fileBPath] = makeReplacements(10, 0, 6, fileBPath);
FileOverrides Overrides;
const FileOverrides::ChangeMap &Map = Overrides.getChangedRanges();
ASSERT_TRUE(Map.empty());
Overrides.adjustChangedRanges(GroupedReplacements);
ASSERT_TRUE(Map.end() != Map.find(fileAPath));
ASSERT_TRUE(Map.end() != Map.find(fileBPath));
const Range &RA = *Map.find(fileAPath)->second.begin();
EXPECT_EQ(0u, RA.getOffset());
EXPECT_EQ(4u, RA.getLength());
const Range &RB = *Map.find(fileBPath)->second.begin();
EXPECT_EQ(10u, RB.getOffset());
EXPECT_EQ(6u, RB.getLength());
}

View File

@ -18,8 +18,7 @@ public:
TransformA(const TransformOptions &Options)
: Transform("TransformA", Options) {}
virtual int apply(const FileOverrides &,
const tooling::CompilationDatabase &,
virtual int apply(const tooling::CompilationDatabase &,
const std::vector<std::string> &) {
return 0;
}
@ -34,8 +33,7 @@ public:
TransformB(const TransformOptions &Options)
: Transform("TransformB", Options) {}
virtual int apply(const FileOverrides &,
const tooling::CompilationDatabase &,
virtual int apply(const tooling::CompilationDatabase &,
const std::vector<std::string> &) {
return 0;
}

View File

@ -1,55 +0,0 @@
//===- clang-modernize/ReformattingTest.cpp - Reformatting unit tests -----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Core/Reformatting.h"
#include "Core/FileOverrides.h"
#include "Core/Refactoring.h"
#include "gtest/gtest.h"
#include "common/VirtualFileHelper.h"
using namespace clang;
using namespace clang::tooling;
namespace {
// convenience function to create a ChangedRanges containing one Range
ChangedRanges makeChangedRanges(unsigned Offset, unsigned Length) {
ChangedRanges Changes;
ReplacementsVec Replaces;
Replaces.push_back(Replacement("", Offset, 0, std::string(Length, '~')));
Changes.adjustChangedRanges(Replaces);
return Changes;
}
} // end anonymous namespace
TEST(Reformatter, SingleReformat) {
VirtualFileHelper VFHelper;
llvm::StringRef FileName = "<test>";
VFHelper.mapFile(FileName, "int a;\n"
"int b;\n");
Reformatter ChangesReformatter(format::getLLVMStyle());
ChangedRanges Changes = makeChangedRanges(0, 6);
tooling::ReplacementsVec Replaces;
ChangesReformatter.reformatSingleFile(
FileName, Changes, VFHelper.getNewSourceManager(), Replaces);
// We expect the code above to reformatted like so:
//
// int a;
// int b;
//
// This test is slightly fragile since there's more than one Replacement that
// results in the above change. However, testing the result of applying the
// replacement is more trouble than it's worth in this context.
ASSERT_EQ(1u, Replaces.size());
EXPECT_EQ(3u, Replaces[0].getOffset());
EXPECT_EQ(2u, Replaces[0].getLength());
EXPECT_EQ(" ", Replaces[0].getReplacementText());
}

View File

@ -8,7 +8,6 @@
//===----------------------------------------------------------------------===//
#include "gtest/gtest.h"
#include "Core/FileOverrides.h"
#include "Core/Transform.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/DeclGroup.h"
@ -26,8 +25,7 @@ public:
DummyTransform(llvm::StringRef Name, const TransformOptions &Options)
: Transform(Name, Options) {}
virtual int apply(const FileOverrides &,
const tooling::CompilationDatabase &,
virtual int apply(const tooling::CompilationDatabase &,
const std::vector<std::string> &) { return 0; }
void setAcceptedChanges(unsigned Changes) {
@ -40,10 +38,6 @@ public:
Transform::setDeferredChanges(Changes);
}
void setOverrides(FileOverrides &Overrides) {
Transform::setOverrides(Overrides);
}
};
TEST(Transform, Interface) {
@ -159,11 +153,6 @@ TEST(Transform, Timings) {
// handleEndSource() calls to it.
CallbackForwarder Callbacks(T);
// Transform's handle* functions require FileOverrides to be set, even if
// there aren't any.
FileOverrides Overrides;
T.setOverrides(Overrides);
Tool.run(clang::tooling::newFrontendActionFactory(&Factory, &Callbacks));
EXPECT_TRUE(Factory.Called);

View File

@ -12,7 +12,7 @@
//===----------------------------------------------------------------------===//
#include "gtest/gtest.h"
#include "Core/FileOverrides.h"
#include "Core/ReplacementHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Regex.h"
@ -28,10 +28,13 @@ TEST(UniqueHeaderName, testUniqueHeaderName) {
append(SourceFile, "project/lib/feature.cpp");
native(SourceFile.str().str(), SourceFile);
llvm::SmallString<128> DestDir(TmpDir);
append(DestDir, "replacements");
llvm::SmallString<128> FullActualPath;
llvm::SmallString<128> Error;
bool Result =
generateReplacementsFileName(SourceFile, FullActualPath, Error);
bool Result = ReplacementHandling::generateReplacementsFileName(
DestDir, SourceFile, FullActualPath, Error);
ASSERT_TRUE(Result);
EXPECT_TRUE(Error.empty());
@ -45,7 +48,7 @@ TEST(UniqueHeaderName, testUniqueHeaderName) {
llvm::SmallString<128> ActualName =
llvm::sys::path::filename(FullActualPath);
EXPECT_STREQ(ExpectedPath.c_str(), ActualPath.c_str());
EXPECT_STREQ(DestDir.c_str(), ActualPath.c_str());
llvm::StringRef ExpectedName =
"^feature.cpp_[0-9a-f]{2}_[0-9a-f]{2}_[0-9a-f]{2}_[0-9a-f]{2}_["