summaryrefslogtreecommitdiff
path: root/lib/Tooling
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Tooling')
-rw-r--r--lib/Tooling/ArgumentsAdjusters.cpp2
-rw-r--r--lib/Tooling/CMakeLists.txt8
-rw-r--r--lib/Tooling/CompilationDatabase.cpp8
-rw-r--r--lib/Tooling/Core/Lookup.cpp94
-rw-r--r--lib/Tooling/Core/QualTypeNames.cpp2
-rw-r--r--lib/Tooling/Core/Replacement.cpp501
-rw-r--r--lib/Tooling/JSONCompilationDatabase.cpp60
-rw-r--r--lib/Tooling/Refactoring.cpp23
-rw-r--r--lib/Tooling/RefactoringCallbacks.cpp50
-rw-r--r--lib/Tooling/Tooling.cpp10
10 files changed, 499 insertions, 259 deletions
diff --git a/lib/Tooling/ArgumentsAdjusters.cpp b/lib/Tooling/ArgumentsAdjusters.cpp
index 2f3d829d7d197..48b925c698a7c 100644
--- a/lib/Tooling/ArgumentsAdjusters.cpp
+++ b/lib/Tooling/ArgumentsAdjusters.cpp
@@ -17,7 +17,7 @@
namespace clang {
namespace tooling {
-/// Add -fsyntax-only option to the commnand line arguments.
+/// Add -fsyntax-only option to the command line arguments.
ArgumentsAdjuster getClangSyntaxOnlyAdjuster() {
return [](const CommandLineArguments &Args, StringRef /*unused*/) {
CommandLineArguments AdjustedArgs;
diff --git a/lib/Tooling/CMakeLists.txt b/lib/Tooling/CMakeLists.txt
index 56134c1164d4e..2eec1dba2f363 100644
--- a/lib/Tooling/CMakeLists.txt
+++ b/lib/Tooling/CMakeLists.txt
@@ -1,4 +1,7 @@
-set(LLVM_LINK_COMPONENTS support)
+set(LLVM_LINK_COMPONENTS
+ Option
+ Support
+ )
add_subdirectory(Core)
@@ -13,6 +16,9 @@ add_clang_library(clangTooling
RefactoringCallbacks.cpp
Tooling.cpp
+ DEPENDS
+ ClangDriverOptions
+
LINK_LIBS
clangAST
clangASTMatchers
diff --git a/lib/Tooling/CompilationDatabase.cpp b/lib/Tooling/CompilationDatabase.cpp
index 8fc4a1fe5beb0..8ca0b2df70130 100644
--- a/lib/Tooling/CompilationDatabase.cpp
+++ b/lib/Tooling/CompilationDatabase.cpp
@@ -32,12 +32,14 @@
using namespace clang;
using namespace tooling;
+LLVM_INSTANTIATE_REGISTRY(CompilationDatabasePluginRegistry)
+
CompilationDatabase::~CompilationDatabase() {}
std::unique_ptr<CompilationDatabase>
CompilationDatabase::loadFromDirectory(StringRef BuildDirectory,
std::string &ErrorMessage) {
- std::stringstream ErrorStream;
+ llvm::raw_string_ostream ErrorStream(ErrorMessage);
for (CompilationDatabasePluginRegistry::iterator
It = CompilationDatabasePluginRegistry::begin(),
Ie = CompilationDatabasePluginRegistry::end();
@@ -49,7 +51,6 @@ CompilationDatabase::loadFromDirectory(StringRef BuildDirectory,
return DB;
ErrorStream << It->getName() << ": " << DatabaseErrorMessage << "\n";
}
- ErrorMessage = ErrorStream.str();
return nullptr;
}
@@ -299,7 +300,8 @@ FixedCompilationDatabase(Twine Directory, ArrayRef<std::string> CommandLine) {
ToolCommandLine.insert(ToolCommandLine.end(),
CommandLine.begin(), CommandLine.end());
CompileCommands.emplace_back(Directory, StringRef(),
- std::move(ToolCommandLine));
+ std::move(ToolCommandLine),
+ StringRef());
}
std::vector<CompileCommand>
diff --git a/lib/Tooling/Core/Lookup.cpp b/lib/Tooling/Core/Lookup.cpp
index 697eeb46ce41e..6edf61b8050db 100644
--- a/lib/Tooling/Core/Lookup.cpp
+++ b/lib/Tooling/Core/Lookup.cpp
@@ -13,37 +13,69 @@
#include "clang/Tooling/Core/Lookup.h"
#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
using namespace clang;
using namespace clang::tooling;
-static bool isInsideDifferentNamespaceWithSameName(const DeclContext *DeclA,
- const DeclContext *DeclB) {
- while (true) {
- // Look past non-namespaces on DeclA.
- while (DeclA && !isa<NamespaceDecl>(DeclA))
- DeclA = DeclA->getParent();
-
- // Look past non-namespaces on DeclB.
- while (DeclB && !isa<NamespaceDecl>(DeclB))
- DeclB = DeclB->getParent();
-
- // We hit the root, no namespace collision.
- if (!DeclA || !DeclB)
- return false;
+// Gets all namespaces that \p Context is in as a vector (ignoring anonymous
+// namespaces). The inner namespaces come before outer namespaces in the vector.
+// For example, if the context is in the following namespace:
+// `namespace a { namespace b { namespace c ( ... ) } }`,
+// the vector will be `{c, b, a}`.
+static llvm::SmallVector<const NamespaceDecl *, 4>
+getAllNamedNamespaces(const DeclContext *Context) {
+ llvm::SmallVector<const NamespaceDecl *, 4> Namespaces;
+ auto GetNextNamedNamespace = [](const DeclContext *Context) {
+ // Look past non-namespaces and anonymous namespaces on FromContext.
+ while (Context && (!isa<NamespaceDecl>(Context) ||
+ cast<NamespaceDecl>(Context)->isAnonymousNamespace()))
+ Context = Context->getParent();
+ return Context;
+ };
+ for (Context = GetNextNamedNamespace(Context); Context != nullptr;
+ Context = GetNextNamedNamespace(Context->getParent()))
+ Namespaces.push_back(cast<NamespaceDecl>(Context));
+ return Namespaces;
+}
+// Returns true if the context in which the type is used and the context in
+// which the type is declared are the same semantical namespace but different
+// lexical namespaces.
+static bool
+usingFromDifferentCanonicalNamespace(const DeclContext *FromContext,
+ const DeclContext *UseContext) {
+ // We can skip anonymous namespace because:
+ // 1. `FromContext` and `UseContext` must be in the same anonymous namespaces
+ // since referencing across anonymous namespaces is not possible.
+ // 2. If `FromContext` and `UseContext` are in the same anonymous namespace,
+ // the function will still return `false` as expected.
+ llvm::SmallVector<const NamespaceDecl *, 4> FromNamespaces =
+ getAllNamedNamespaces(FromContext);
+ llvm::SmallVector<const NamespaceDecl *, 4> UseNamespaces =
+ getAllNamedNamespaces(UseContext);
+ // If `UseContext` has fewer level of nested namespaces, it cannot be in the
+ // same canonical namespace as the `FromContext`.
+ if (UseNamespaces.size() < FromNamespaces.size())
+ return false;
+ unsigned Diff = UseNamespaces.size() - FromNamespaces.size();
+ auto FromIter = FromNamespaces.begin();
+ // Only compare `FromNamespaces` with namespaces in `UseNamespaces` that can
+ // collide, i.e. the top N namespaces where N is the number of namespaces in
+ // `FromNamespaces`.
+ auto UseIter = UseNamespaces.begin() + Diff;
+ for (; FromIter != FromNamespaces.end() && UseIter != UseNamespaces.end();
+ ++FromIter, ++UseIter) {
// Literally the same namespace, not a collision.
- if (DeclA == DeclB)
+ if (*FromIter == *UseIter)
return false;
-
- // Now check the names. If they match we have a different namespace with the
- // same name.
- if (cast<NamespaceDecl>(DeclA)->getDeclName() ==
- cast<NamespaceDecl>(DeclB)->getDeclName())
+ // Now check the names. If they match we have a different canonical
+ // namespace with the same name.
+ if (cast<NamespaceDecl>(*FromIter)->getDeclName() ==
+ cast<NamespaceDecl>(*UseIter)->getDeclName())
return true;
-
- DeclA = DeclA->getParent();
- DeclB = DeclB->getParent();
}
+ assert(FromIter == FromNamespaces.end() && UseIter == UseNamespaces.end());
+ return false;
}
static StringRef getBestNamespaceSubstr(const DeclContext *DeclA,
@@ -90,16 +122,22 @@ std::string tooling::replaceNestedName(const NestedNameSpecifier *Use,
"Expected fully-qualified name!");
// We can do a raw name replacement when we are not inside the namespace for
- // the original function and it is not in the global namespace. The
+ // the original class/function and it is not in the global namespace. The
// assumption is that outside the original namespace we must have a using
// statement that makes this work out and that other parts of this refactor
- // will automatically fix using statements to point to the new function
+ // will automatically fix using statements to point to the new class/function.
+ // However, if the `FromDecl` is a class forward declaration, the reference is
+ // still considered as referring to the original definition, so we can't do a
+ // raw name replacement in this case.
const bool class_name_only = !Use;
const bool in_global_namespace =
isa<TranslationUnitDecl>(FromDecl->getDeclContext());
- if (class_name_only && !in_global_namespace &&
- !isInsideDifferentNamespaceWithSameName(FromDecl->getDeclContext(),
- UseContext)) {
+ const bool is_class_forward_decl =
+ isa<CXXRecordDecl>(FromDecl) &&
+ !cast<CXXRecordDecl>(FromDecl)->isCompleteDefinition();
+ if (class_name_only && !in_global_namespace && !is_class_forward_decl &&
+ !usingFromDifferentCanonicalNamespace(FromDecl->getDeclContext(),
+ UseContext)) {
auto Pos = ReplacementString.rfind("::");
return Pos != StringRef::npos ? ReplacementString.substr(Pos + 2)
: ReplacementString;
diff --git a/lib/Tooling/Core/QualTypeNames.cpp b/lib/Tooling/Core/QualTypeNames.cpp
index 619dae1ee106f..721c2c92fc274 100644
--- a/lib/Tooling/Core/QualTypeNames.cpp
+++ b/lib/Tooling/Core/QualTypeNames.cpp
@@ -14,8 +14,6 @@
#include "clang/AST/DeclarationName.h"
#include "clang/AST/GlobalDecl.h"
#include "clang/AST/Mangle.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/StringRef.h"
#include <stdio.h>
#include <memory>
diff --git a/lib/Tooling/Core/Replacement.cpp b/lib/Tooling/Core/Replacement.cpp
index 4f130709ac16d..e194b59a6e2b1 100644
--- a/lib/Tooling/Core/Replacement.cpp
+++ b/lib/Tooling/Core/Replacement.cpp
@@ -20,6 +20,7 @@
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
#include "clang/Rewrite/Core/Rewriter.h"
+#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_os_ostream.h"
@@ -29,8 +30,7 @@ namespace tooling {
static const char * const InvalidLocation = "";
-Replacement::Replacement()
- : FilePath(InvalidLocation) {}
+Replacement::Replacement() : FilePath(InvalidLocation) {}
Replacement::Replacement(StringRef FilePath, unsigned Offset, unsigned Length,
StringRef ReplacementText)
@@ -84,11 +84,8 @@ bool operator<(const Replacement &LHS, const Replacement &RHS) {
if (LHS.getOffset() != RHS.getOffset())
return LHS.getOffset() < RHS.getOffset();
- // Apply longer replacements first, specifically so that deletions are
- // executed before insertions. It is (hopefully) never the intention to
- // delete parts of newly inserted code.
if (LHS.getLength() != RHS.getLength())
- return LHS.getLength() > RHS.getLength();
+ return LHS.getLength() < RHS.getLength();
if (LHS.getFilePath() != RHS.getFilePath())
return LHS.getFilePath() < RHS.getFilePath();
@@ -138,200 +135,196 @@ void Replacement::setFromSourceRange(const SourceManager &Sources,
ReplacementText);
}
-template <typename T>
-unsigned shiftedCodePositionInternal(const T &Replaces, unsigned Position) {
- unsigned Offset = 0;
- for (const auto& R : Replaces) {
- if (R.getOffset() + R.getLength() <= Position) {
- Offset += R.getReplacementText().size() - R.getLength();
- continue;
- }
- if (R.getOffset() < Position &&
- R.getOffset() + R.getReplacementText().size() <= Position) {
- Position = R.getOffset() + R.getReplacementText().size() - 1;
- }
- break;
- }
- return Position + Offset;
+Replacement
+Replacements::getReplacementInChangedCode(const Replacement &R) const {
+ unsigned NewStart = getShiftedCodePosition(R.getOffset());
+ unsigned NewEnd = getShiftedCodePosition(R.getOffset() + R.getLength());
+ return Replacement(R.getFilePath(), NewStart, NewEnd - NewStart,
+ R.getReplacementText());
}
-unsigned shiftedCodePosition(const Replacements &Replaces, unsigned Position) {
- return shiftedCodePositionInternal(Replaces, Position);
+static std::string getReplacementErrString(replacement_error Err) {
+ switch (Err) {
+ case replacement_error::fail_to_apply:
+ return "Failed to apply a replacement.";
+ case replacement_error::wrong_file_path:
+ return "The new replacement's file path is different from the file path of "
+ "existing replacements";
+ case replacement_error::overlap_conflict:
+ return "The new replacement overlaps with an existing replacement.";
+ case replacement_error::insert_conflict:
+ return "The new insertion has the same insert location as an existing "
+ "replacement.";
+ }
+ llvm_unreachable("A value of replacement_error has no message.");
}
-// FIXME: Remove this function when Replacements is implemented as std::vector
-// instead of std::set.
-unsigned shiftedCodePosition(const std::vector<Replacement> &Replaces,
- unsigned Position) {
- return shiftedCodePositionInternal(Replaces, Position);
+std::string ReplacementError::message() const {
+ std::string Message = getReplacementErrString(Err);
+ if (NewReplacement.hasValue())
+ Message += "\nNew replacement: " + NewReplacement->toString();
+ if (ExistingReplacement.hasValue())
+ Message += "\nExisting replacement: " + ExistingReplacement->toString();
+ return Message;
}
-void deduplicate(std::vector<Replacement> &Replaces,
- std::vector<Range> &Conflicts) {
- if (Replaces.empty())
- return;
-
- auto LessNoPath = [](const Replacement &LHS, const Replacement &RHS) {
- if (LHS.getOffset() != RHS.getOffset())
- return LHS.getOffset() < RHS.getOffset();
- if (LHS.getLength() != RHS.getLength())
- return LHS.getLength() < RHS.getLength();
- return LHS.getReplacementText() < RHS.getReplacementText();
- };
-
- auto EqualNoPath = [](const Replacement &LHS, const Replacement &RHS) {
- return LHS.getOffset() == RHS.getOffset() &&
- LHS.getLength() == RHS.getLength() &&
- LHS.getReplacementText() == RHS.getReplacementText();
- };
+char ReplacementError::ID = 0;
- // Deduplicate. We don't want to deduplicate based on the path as we assume
- // that all replacements refer to the same file (or are symlinks).
- std::sort(Replaces.begin(), Replaces.end(), LessNoPath);
- Replaces.erase(std::unique(Replaces.begin(), Replaces.end(), EqualNoPath),
- Replaces.end());
-
- // Detect conflicts
- Range ConflictRange(Replaces.front().getOffset(),
- Replaces.front().getLength());
- unsigned ConflictStart = 0;
- unsigned ConflictLength = 1;
- for (unsigned i = 1; i < Replaces.size(); ++i) {
- Range Current(Replaces[i].getOffset(), Replaces[i].getLength());
- if (ConflictRange.overlapsWith(Current)) {
- // Extend conflicted range
- ConflictRange = Range(ConflictRange.getOffset(),
- std::max(ConflictRange.getLength(),
- Current.getOffset() + Current.getLength() -
- ConflictRange.getOffset()));
- ++ConflictLength;
- } else {
- if (ConflictLength > 1)
- Conflicts.push_back(Range(ConflictStart, ConflictLength));
- ConflictRange = Current;
- ConflictStart = i;
- ConflictLength = 1;
+Replacements Replacements::getCanonicalReplacements() const {
+ std::vector<Replacement> NewReplaces;
+ // Merge adjacent replacements.
+ for (const auto &R : Replaces) {
+ if (NewReplaces.empty()) {
+ NewReplaces.push_back(R);
+ continue;
}
- }
-
- if (ConflictLength > 1)
- Conflicts.push_back(Range(ConflictStart, ConflictLength));
-}
-
-bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite) {
- bool Result = true;
- for (Replacements::const_iterator I = Replaces.begin(),
- E = Replaces.end();
- I != E; ++I) {
- if (I->isApplicable()) {
- Result = I->apply(Rewrite) && Result;
+ auto &Prev = NewReplaces.back();
+ unsigned PrevEnd = Prev.getOffset() + Prev.getLength();
+ if (PrevEnd < R.getOffset()) {
+ NewReplaces.push_back(R);
} else {
- Result = false;
+ assert(PrevEnd == R.getOffset() &&
+ "Existing replacements must not overlap.");
+ Replacement NewR(
+ R.getFilePath(), Prev.getOffset(), Prev.getLength() + R.getLength(),
+ (Prev.getReplacementText() + R.getReplacementText()).str());
+ Prev = NewR;
}
}
- return Result;
+ ReplacementsImpl NewReplacesImpl(NewReplaces.begin(), NewReplaces.end());
+ return Replacements(NewReplacesImpl.begin(), NewReplacesImpl.end());
}
-// FIXME: Remove this function when Replacements is implemented as std::vector
-// instead of std::set.
-bool applyAllReplacements(const std::vector<Replacement> &Replaces,
- Rewriter &Rewrite) {
- bool Result = true;
- for (std::vector<Replacement>::const_iterator I = Replaces.begin(),
- E = Replaces.end();
- I != E; ++I) {
- if (I->isApplicable()) {
- Result = I->apply(Rewrite) && Result;
- } else {
- Result = false;
- }
- }
- return Result;
+// `R` and `Replaces` are order-independent if applying them in either order
+// has the same effect, so we need to compare replacements associated to
+// applying them in either order.
+llvm::Expected<Replacements>
+Replacements::mergeIfOrderIndependent(const Replacement &R) const {
+ Replacements Rs(R);
+ // A Replacements set containg a single replacement that is `R` referring to
+ // the code after the existing replacements `Replaces` are applied.
+ Replacements RsShiftedByReplaces(getReplacementInChangedCode(R));
+ // A Replacements set that is `Replaces` referring to the code after `R` is
+ // applied.
+ Replacements ReplacesShiftedByRs;
+ for (const auto &Replace : Replaces)
+ ReplacesShiftedByRs.Replaces.insert(
+ Rs.getReplacementInChangedCode(Replace));
+ // This is equivalent to applying `Replaces` first and then `R`.
+ auto MergeShiftedRs = merge(RsShiftedByReplaces);
+ // This is equivalent to applying `R` first and then `Replaces`.
+ auto MergeShiftedReplaces = Rs.merge(ReplacesShiftedByRs);
+
+ // Since empty or segmented replacements around existing replacements might be
+ // produced above, we need to compare replacements in canonical forms.
+ if (MergeShiftedRs.getCanonicalReplacements() ==
+ MergeShiftedReplaces.getCanonicalReplacements())
+ return MergeShiftedRs;
+ return llvm::make_error<ReplacementError>(replacement_error::overlap_conflict,
+ R, *Replaces.begin());
}
-llvm::Expected<std::string> applyAllReplacements(StringRef Code,
- const Replacements &Replaces) {
- if (Replaces.empty())
- return Code.str();
+llvm::Error Replacements::add(const Replacement &R) {
+ // Check the file path.
+ if (!Replaces.empty() && R.getFilePath() != Replaces.begin()->getFilePath())
+ return llvm::make_error<ReplacementError>(
+ replacement_error::wrong_file_path, R, *Replaces.begin());
- IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
- new vfs::InMemoryFileSystem);
- FileManager Files(FileSystemOptions(), InMemoryFileSystem);
- DiagnosticsEngine Diagnostics(
- IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
- new DiagnosticOptions);
- SourceManager SourceMgr(Diagnostics, Files);
- Rewriter Rewrite(SourceMgr, LangOptions());
- InMemoryFileSystem->addFile(
- "<stdin>", 0, llvm::MemoryBuffer::getMemBuffer(Code, "<stdin>"));
- FileID ID = SourceMgr.createFileID(Files.getFile("<stdin>"), SourceLocation(),
- clang::SrcMgr::C_User);
- for (Replacements::const_iterator I = Replaces.begin(), E = Replaces.end();
- I != E; ++I) {
- Replacement Replace("<stdin>", I->getOffset(), I->getLength(),
- I->getReplacementText());
- if (!Replace.apply(Rewrite))
- return llvm::make_error<llvm::StringError>(
- "Failed to apply replacement: " + Replace.toString(),
- llvm::inconvertibleErrorCode());
+ // Special-case header insertions.
+ if (R.getOffset() == UINT_MAX) {
+ Replaces.insert(R);
+ return llvm::Error::success();
}
- std::string Result;
- llvm::raw_string_ostream OS(Result);
- Rewrite.getEditBuffer(ID).write(OS);
- OS.flush();
- return Result;
-}
-// Merge and sort overlapping ranges in \p Ranges.
-static std::vector<Range> mergeAndSortRanges(std::vector<Range> Ranges) {
- std::sort(Ranges.begin(), Ranges.end(),
- [](const Range &LHS, const Range &RHS) {
- if (LHS.getOffset() != RHS.getOffset())
- return LHS.getOffset() < RHS.getOffset();
- return LHS.getLength() < RHS.getLength();
- });
- std::vector<Range> Result;
- for (const auto &R : Ranges) {
- if (Result.empty() ||
- Result.back().getOffset() + Result.back().getLength() < R.getOffset()) {
- Result.push_back(R);
- } else {
- unsigned NewEnd =
- std::max(Result.back().getOffset() + Result.back().getLength(),
- R.getOffset() + R.getLength());
- Result[Result.size() - 1] =
- Range(Result.back().getOffset(), NewEnd - Result.back().getOffset());
+ // This replacement cannot conflict with replacements that end before
+ // this replacement starts or start after this replacement ends.
+ // We also know that there currently are no overlapping replacements.
+ // Thus, we know that all replacements that start after the end of the current
+ // replacement cannot overlap.
+ Replacement AtEnd(R.getFilePath(), R.getOffset() + R.getLength(), 0, "");
+
+ // Find the first entry that starts after or at the end of R. Note that
+ // entries that start at the end can still be conflicting if R is an
+ // insertion.
+ auto I = Replaces.lower_bound(AtEnd);
+ // If `I` starts at the same offset as `R`, `R` must be an insertion.
+ if (I != Replaces.end() && R.getOffset() == I->getOffset()) {
+ assert(R.getLength() == 0);
+ // `I` is also an insertion, `R` and `I` conflict.
+ if (I->getLength() == 0) {
+ // Check if two insertions are order-indepedent: if inserting them in
+ // either order produces the same text, they are order-independent.
+ if ((R.getReplacementText() + I->getReplacementText()).str() !=
+ (I->getReplacementText() + R.getReplacementText()).str())
+ return llvm::make_error<ReplacementError>(
+ replacement_error::insert_conflict, R, *I);
+ // If insertions are order-independent, we can merge them.
+ Replacement NewR(
+ R.getFilePath(), R.getOffset(), 0,
+ (R.getReplacementText() + I->getReplacementText()).str());
+ Replaces.erase(I);
+ Replaces.insert(std::move(NewR));
+ return llvm::Error::success();
}
+ // Insertion `R` is adjacent to a non-insertion replacement `I`, so they
+ // are order-independent. It is safe to assume that `R` will not conflict
+ // with any replacement before `I` since all replacements before `I` must
+ // either end before `R` or end at `R` but has length > 0 (if the
+ // replacement before `I` is an insertion at `R`, it would have been `I`
+ // since it is a lower bound of `AtEnd` and ordered before the current `I`
+ // in the set).
+ Replaces.insert(R);
+ return llvm::Error::success();
}
- return Result;
-}
-std::vector<Range> calculateChangedRanges(const Replacements &Replaces) {
- std::vector<Range> ChangedRanges;
- int Shift = 0;
- for (const Replacement &R : Replaces) {
- unsigned Offset = R.getOffset() + Shift;
- unsigned Length = R.getReplacementText().size();
- Shift += Length - R.getLength();
- ChangedRanges.push_back(Range(Offset, Length));
+ // `I` is the smallest iterator (after `R`) whose entry cannot overlap.
+ // If that is begin(), there are no overlaps.
+ if (I == Replaces.begin()) {
+ Replaces.insert(R);
+ return llvm::Error::success();
}
- return mergeAndSortRanges(ChangedRanges);
-}
-
-std::vector<Range>
-calculateRangesAfterReplacements(const Replacements &Replaces,
- const std::vector<Range> &Ranges) {
- auto MergedRanges = mergeAndSortRanges(Ranges);
- tooling::Replacements FakeReplaces;
- for (const auto &R : MergedRanges)
- FakeReplaces.insert(Replacement(Replaces.begin()->getFilePath(),
- R.getOffset(), R.getLength(),
- std::string(R.getLength(), ' ')));
- tooling::Replacements NewReplaces = mergeReplacements(FakeReplaces, Replaces);
- return calculateChangedRanges(NewReplaces);
+ --I;
+ auto Overlap = [](const Replacement &R1, const Replacement &R2) -> bool {
+ return Range(R1.getOffset(), R1.getLength())
+ .overlapsWith(Range(R2.getOffset(), R2.getLength()));
+ };
+ // If the previous entry does not overlap, we know that entries before it
+ // can also not overlap.
+ if (!Overlap(R, *I)) {
+ // If `R` and `I` do not have the same offset, it is safe to add `R` since
+ // it must come after `I`. Otherwise:
+ // - If `R` is an insertion, `I` must not be an insertion since it would
+ // have come after `AtEnd`.
+ // - If `R` is not an insertion, `I` must be an insertion; otherwise, `R`
+ // and `I` would have overlapped.
+ // In either case, we can safely insert `R`.
+ Replaces.insert(R);
+ } else {
+ // `I` overlaps with `R`. We need to check `R` against all overlapping
+ // replacements to see if they are order-indepedent. If they are, merge `R`
+ // with them and replace them with the merged replacements.
+ auto MergeBegin = I;
+ auto MergeEnd = std::next(I);
+ while (I != Replaces.begin()) {
+ --I;
+ // If `I` doesn't overlap with `R`, don't merge it.
+ if (!Overlap(R, *I))
+ break;
+ MergeBegin = I;
+ }
+ Replacements OverlapReplaces(MergeBegin, MergeEnd);
+ llvm::Expected<Replacements> Merged =
+ OverlapReplaces.mergeIfOrderIndependent(R);
+ if (!Merged)
+ return Merged.takeError();
+ Replaces.erase(MergeBegin, MergeEnd);
+ Replaces.insert(Merged->begin(), Merged->end());
+ }
+ return llvm::Error::success();
}
namespace {
+
// Represents a merged replacement, i.e. a replacement consisting of multiple
// overlapping replacements from 'First' and 'Second' in mergeReplacements.
//
@@ -425,26 +418,19 @@ private:
unsigned Length;
std::string Text;
};
-} // namespace
-std::map<std::string, Replacements>
-groupReplacementsByFile(const Replacements &Replaces) {
- std::map<std::string, Replacements> FileToReplaces;
- for (const auto &Replace : Replaces) {
- FileToReplaces[Replace.getFilePath()].insert(Replace);
- }
- return FileToReplaces;
-}
+} // namespace
-Replacements mergeReplacements(const Replacements &First,
- const Replacements &Second) {
- if (First.empty() || Second.empty())
- return First.empty() ? Second : First;
+Replacements Replacements::merge(const Replacements &ReplacesToMerge) const {
+ if (empty() || ReplacesToMerge.empty())
+ return empty() ? ReplacesToMerge : *this;
+ auto &First = Replaces;
+ auto &Second = ReplacesToMerge.Replaces;
// Delta is the amount of characters that replacements from 'Second' need to
// be shifted so that their offsets refer to the original text.
int Delta = 0;
- Replacements Result;
+ ReplacementsImpl Result;
// Iterate over both sets and always add the next element (smallest total
// Offset) from either 'First' or 'Second'. Merge that element with
@@ -470,6 +456,143 @@ Replacements mergeReplacements(const Replacements &First,
Delta -= Merged.deltaFirst();
Result.insert(Merged.asReplacement());
}
+ return Replacements(Result.begin(), Result.end());
+}
+
+// Combines overlapping ranges in \p Ranges and sorts the combined ranges.
+// Returns a set of non-overlapping and sorted ranges that is equivalent to
+// \p Ranges.
+static std::vector<Range> combineAndSortRanges(std::vector<Range> Ranges) {
+ std::sort(Ranges.begin(), Ranges.end(),
+ [](const Range &LHS, const Range &RHS) {
+ if (LHS.getOffset() != RHS.getOffset())
+ return LHS.getOffset() < RHS.getOffset();
+ return LHS.getLength() < RHS.getLength();
+ });
+ std::vector<Range> Result;
+ for (const auto &R : Ranges) {
+ if (Result.empty() ||
+ Result.back().getOffset() + Result.back().getLength() < R.getOffset()) {
+ Result.push_back(R);
+ } else {
+ unsigned NewEnd =
+ std::max(Result.back().getOffset() + Result.back().getLength(),
+ R.getOffset() + R.getLength());
+ Result[Result.size() - 1] =
+ Range(Result.back().getOffset(), NewEnd - Result.back().getOffset());
+ }
+ }
+ return Result;
+}
+
+std::vector<Range>
+calculateRangesAfterReplacements(const Replacements &Replaces,
+ const std::vector<Range> &Ranges) {
+ // To calculate the new ranges,
+ // - Turn \p Ranges into Replacements at (offset, length) with an empty
+ // (unimportant) replacement text of length "length".
+ // - Merge with \p Replaces.
+ // - The new ranges will be the affected ranges of the merged replacements.
+ auto MergedRanges = combineAndSortRanges(Ranges);
+ if (Replaces.empty())
+ return MergedRanges;
+ tooling::Replacements FakeReplaces;
+ for (const auto &R : MergedRanges) {
+ auto Err = FakeReplaces.add(Replacement(Replaces.begin()->getFilePath(),
+ R.getOffset(), R.getLength(),
+ std::string(R.getLength(), ' ')));
+ assert(!Err &&
+ "Replacements must not conflict since ranges have been merged.");
+ (void)Err;
+ }
+ return FakeReplaces.merge(Replaces).getAffectedRanges();
+}
+
+std::vector<Range> Replacements::getAffectedRanges() const {
+ std::vector<Range> ChangedRanges;
+ int Shift = 0;
+ for (const Replacement &R : Replaces) {
+ unsigned Offset = R.getOffset() + Shift;
+ unsigned Length = R.getReplacementText().size();
+ Shift += Length - R.getLength();
+ ChangedRanges.push_back(Range(Offset, Length));
+ }
+ return combineAndSortRanges(ChangedRanges);
+}
+
+unsigned Replacements::getShiftedCodePosition(unsigned Position) const {
+ unsigned Offset = 0;
+ for (const auto& R : Replaces) {
+ if (R.getOffset() + R.getLength() <= Position) {
+ Offset += R.getReplacementText().size() - R.getLength();
+ continue;
+ }
+ if (R.getOffset() < Position &&
+ R.getOffset() + R.getReplacementText().size() <= Position) {
+ Position = R.getOffset() + R.getReplacementText().size();
+ if (R.getReplacementText().size() > 0)
+ Position--;
+ }
+ break;
+ }
+ return Position + Offset;
+}
+
+bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite) {
+ bool Result = true;
+ for (auto I = Replaces.rbegin(), E = Replaces.rend(); I != E; ++I) {
+ if (I->isApplicable()) {
+ Result = I->apply(Rewrite) && Result;
+ } else {
+ Result = false;
+ }
+ }
+ return Result;
+}
+
+llvm::Expected<std::string> applyAllReplacements(StringRef Code,
+ const Replacements &Replaces) {
+ if (Replaces.empty())
+ return Code.str();
+
+ IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
+ new vfs::InMemoryFileSystem);
+ FileManager Files(FileSystemOptions(), InMemoryFileSystem);
+ DiagnosticsEngine Diagnostics(
+ IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
+ new DiagnosticOptions);
+ SourceManager SourceMgr(Diagnostics, Files);
+ Rewriter Rewrite(SourceMgr, LangOptions());
+ InMemoryFileSystem->addFile(
+ "<stdin>", 0, llvm::MemoryBuffer::getMemBuffer(Code, "<stdin>"));
+ FileID ID = SourceMgr.createFileID(Files.getFile("<stdin>"), SourceLocation(),
+ clang::SrcMgr::C_User);
+ for (auto I = Replaces.rbegin(), E = Replaces.rend(); I != E; ++I) {
+ Replacement Replace("<stdin>", I->getOffset(), I->getLength(),
+ I->getReplacementText());
+ if (!Replace.apply(Rewrite))
+ return llvm::make_error<ReplacementError>(
+ replacement_error::fail_to_apply, Replace);
+ }
+ std::string Result;
+ llvm::raw_string_ostream OS(Result);
+ Rewrite.getEditBuffer(ID).write(OS);
+ OS.flush();
+ return Result;
+}
+
+std::map<std::string, Replacements> groupReplacementsByFile(
+ FileManager &FileMgr,
+ const std::map<std::string, Replacements> &FileToReplaces) {
+ std::map<std::string, Replacements> Result;
+ llvm::SmallPtrSet<const FileEntry *, 16> ProcessedFileEntries;
+ for (const auto &Entry : FileToReplaces) {
+ const FileEntry *FE = FileMgr.getFile(Entry.first);
+ if (!FE)
+ llvm::errs() << "File path " << Entry.first << " is invalid.\n";
+ else if (ProcessedFileEntries.insert(FE).second)
+ Result[Entry.first] = std::move(Entry.second);
+ }
return Result;
}
diff --git a/lib/Tooling/JSONCompilationDatabase.cpp b/lib/Tooling/JSONCompilationDatabase.cpp
index 299fbdc149bfb..738e610ed9469 100644
--- a/lib/Tooling/JSONCompilationDatabase.cpp
+++ b/lib/Tooling/JSONCompilationDatabase.cpp
@@ -16,7 +16,10 @@
#include "clang/Tooling/CompilationDatabasePluginRegistry.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Path.h"
+#include "llvm/Support/StringSaver.h"
#include <system_error>
namespace clang {
@@ -111,8 +114,29 @@ class CommandLineArgumentParser {
std::vector<std::string> CommandLine;
};
-std::vector<std::string> unescapeCommandLine(
- StringRef EscapedCommandLine) {
+std::vector<std::string> unescapeCommandLine(JSONCommandLineSyntax Syntax,
+ StringRef EscapedCommandLine) {
+ if (Syntax == JSONCommandLineSyntax::AutoDetect) {
+ Syntax = JSONCommandLineSyntax::Gnu;
+ llvm::Triple Triple(llvm::sys::getProcessTriple());
+ if (Triple.getOS() == llvm::Triple::OSType::Win32) {
+ // Assume Windows command line parsing on Win32 unless the triple
+ // explicitly tells us otherwise.
+ if (!Triple.hasEnvironment() ||
+ Triple.getEnvironment() == llvm::Triple::EnvironmentType::MSVC)
+ Syntax = JSONCommandLineSyntax::Windows;
+ }
+ }
+
+ if (Syntax == JSONCommandLineSyntax::Windows) {
+ llvm::BumpPtrAllocator Alloc;
+ llvm::StringSaver Saver(Alloc);
+ llvm::SmallVector<const char *, 64> T;
+ llvm::cl::TokenizeWindowsCommandLine(EscapedCommandLine, Saver, T);
+ std::vector<std::string> Result(T.begin(), T.end());
+ return Result;
+ }
+ assert(Syntax == JSONCommandLineSyntax::Gnu);
CommandLineArgumentParser parser(EscapedCommandLine);
return parser.parse();
}
@@ -123,7 +147,8 @@ class JSONCompilationDatabasePlugin : public CompilationDatabasePlugin {
SmallString<1024> JSONDatabasePath(Directory);
llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
std::unique_ptr<CompilationDatabase> Database(
- JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage));
+ JSONCompilationDatabase::loadFromFile(
+ JSONDatabasePath, ErrorMessage, JSONCommandLineSyntax::AutoDetect));
if (!Database)
return nullptr;
return Database;
@@ -143,7 +168,8 @@ volatile int JSONAnchorSource = 0;
std::unique_ptr<JSONCompilationDatabase>
JSONCompilationDatabase::loadFromFile(StringRef FilePath,
- std::string &ErrorMessage) {
+ std::string &ErrorMessage,
+ JSONCommandLineSyntax Syntax) {
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> DatabaseBuffer =
llvm::MemoryBuffer::getFile(FilePath);
if (std::error_code Result = DatabaseBuffer.getError()) {
@@ -151,7 +177,7 @@ JSONCompilationDatabase::loadFromFile(StringRef FilePath,
return nullptr;
}
std::unique_ptr<JSONCompilationDatabase> Database(
- new JSONCompilationDatabase(std::move(*DatabaseBuffer)));
+ new JSONCompilationDatabase(std::move(*DatabaseBuffer), Syntax));
if (!Database->parse(ErrorMessage))
return nullptr;
return Database;
@@ -159,11 +185,12 @@ JSONCompilationDatabase::loadFromFile(StringRef FilePath,
std::unique_ptr<JSONCompilationDatabase>
JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
- std::string &ErrorMessage) {
+ std::string &ErrorMessage,
+ JSONCommandLineSyntax Syntax) {
std::unique_ptr<llvm::MemoryBuffer> DatabaseBuffer(
llvm::MemoryBuffer::getMemBuffer(DatabaseString));
std::unique_ptr<JSONCompilationDatabase> Database(
- new JSONCompilationDatabase(std::move(DatabaseBuffer)));
+ new JSONCompilationDatabase(std::move(DatabaseBuffer), Syntax));
if (!Database->parse(ErrorMessage))
return nullptr;
return Database;
@@ -211,10 +238,11 @@ JSONCompilationDatabase::getAllCompileCommands() const {
}
static std::vector<std::string>
-nodeToCommandLine(const std::vector<llvm::yaml::ScalarNode *> &Nodes) {
+nodeToCommandLine(JSONCommandLineSyntax Syntax,
+ const std::vector<llvm::yaml::ScalarNode *> &Nodes) {
SmallString<1024> Storage;
if (Nodes.size() == 1) {
- return unescapeCommandLine(Nodes[0]->getValue(Storage));
+ return unescapeCommandLine(Syntax, Nodes[0]->getValue(Storage));
}
std::vector<std::string> Arguments;
for (auto *Node : Nodes) {
@@ -229,10 +257,13 @@ void JSONCompilationDatabase::getCommands(
for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
SmallString<8> DirectoryStorage;
SmallString<32> FilenameStorage;
+ SmallString<32> OutputStorage;
+ auto Output = std::get<3>(CommandsRef[I]);
Commands.emplace_back(
- std::get<0>(CommandsRef[I])->getValue(DirectoryStorage),
- std::get<1>(CommandsRef[I])->getValue(FilenameStorage),
- nodeToCommandLine(std::get<2>(CommandsRef[I])));
+ std::get<0>(CommandsRef[I])->getValue(DirectoryStorage),
+ std::get<1>(CommandsRef[I])->getValue(FilenameStorage),
+ nodeToCommandLine(Syntax, std::get<2>(CommandsRef[I])),
+ Output ? Output->getValue(OutputStorage) : "");
}
}
@@ -261,6 +292,7 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
llvm::yaml::ScalarNode *Directory = nullptr;
llvm::Optional<std::vector<llvm::yaml::ScalarNode *>> Command;
llvm::yaml::ScalarNode *File = nullptr;
+ llvm::yaml::ScalarNode *Output = nullptr;
for (auto& NextKeyValue : *Object) {
llvm::yaml::ScalarNode *KeyString =
dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
@@ -303,6 +335,8 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
Command = std::vector<llvm::yaml::ScalarNode *>(1, ValueString);
} else if (KeyValue == "file") {
File = ValueString;
+ } else if (KeyValue == "output") {
+ Output = ValueString;
} else {
ErrorMessage = ("Unknown key: \"" +
KeyString->getRawValue() + "\"").str();
@@ -333,7 +367,7 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
} else {
llvm::sys::path::native(FileName, NativeFilePath);
}
- auto Cmd = CompileCommandRef(Directory, File, *Command);
+ auto Cmd = CompileCommandRef(Directory, File, *Command, Output);
IndexByFile[NativeFilePath].push_back(Cmd);
AllCommands.push_back(Cmd);
MatchTrie.insert(NativeFilePath);
diff --git a/lib/Tooling/Refactoring.cpp b/lib/Tooling/Refactoring.cpp
index 28d535aeb45ff..308c1ac48b281 100644
--- a/lib/Tooling/Refactoring.cpp
+++ b/lib/Tooling/Refactoring.cpp
@@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/Tooling/Refactoring.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
@@ -18,8 +19,6 @@
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Lex/Lexer.h"
#include "clang/Rewrite/Core/Rewriter.h"
-#include "clang/Tooling/Refactoring.h"
-#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_os_ostream.h"
@@ -31,7 +30,9 @@ RefactoringTool::RefactoringTool(
std::shared_ptr<PCHContainerOperations> PCHContainerOps)
: ClangTool(Compilations, SourcePaths, PCHContainerOps) {}
-Replacements &RefactoringTool::getReplacements() { return Replace; }
+std::map<std::string, Replacements> &RefactoringTool::getReplacements() {
+ return FileToReplaces;
+}
int RefactoringTool::runAndSave(FrontendActionFactory *ActionFactory) {
if (int Result = run(ActionFactory)) {
@@ -55,22 +56,26 @@ int RefactoringTool::runAndSave(FrontendActionFactory *ActionFactory) {
}
bool RefactoringTool::applyAllReplacements(Rewriter &Rewrite) {
- return tooling::applyAllReplacements(Replace, Rewrite);
+ bool Result = true;
+ for (const auto &Entry : groupReplacementsByFile(
+ Rewrite.getSourceMgr().getFileManager(), FileToReplaces))
+ Result = tooling::applyAllReplacements(Entry.second, Rewrite) && Result;
+ return Result;
}
int RefactoringTool::saveRewrittenFiles(Rewriter &Rewrite) {
return Rewrite.overwriteChangedFiles() ? 1 : 0;
}
-bool formatAndApplyAllReplacements(const Replacements &Replaces,
- Rewriter &Rewrite, StringRef Style) {
+bool formatAndApplyAllReplacements(
+ const std::map<std::string, Replacements> &FileToReplaces, Rewriter &Rewrite,
+ StringRef Style) {
SourceManager &SM = Rewrite.getSourceMgr();
FileManager &Files = SM.getFileManager();
- auto FileToReplaces = groupReplacementsByFile(Replaces);
-
bool Result = true;
- for (const auto &FileAndReplaces : FileToReplaces) {
+ for (const auto &FileAndReplaces : groupReplacementsByFile(
+ Rewrite.getSourceMgr().getFileManager(), FileToReplaces)) {
const std::string &FilePath = FileAndReplaces.first;
auto &CurReplaces = FileAndReplaces.second;
diff --git a/lib/Tooling/RefactoringCallbacks.cpp b/lib/Tooling/RefactoringCallbacks.cpp
index 4de125ec02aa1..e900c23e4f64e 100644
--- a/lib/Tooling/RefactoringCallbacks.cpp
+++ b/lib/Tooling/RefactoringCallbacks.cpp
@@ -39,11 +39,16 @@ ReplaceStmtWithText::ReplaceStmtWithText(StringRef FromId, StringRef ToText)
void ReplaceStmtWithText::run(
const ast_matchers::MatchFinder::MatchResult &Result) {
- if (const Stmt *FromMatch = Result.Nodes.getStmtAs<Stmt>(FromId)) {
- Replace.insert(tooling::Replacement(
+ if (const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId)) {
+ auto Err = Replace.add(tooling::Replacement(
*Result.SourceManager,
- CharSourceRange::getTokenRange(FromMatch->getSourceRange()),
- ToText));
+ CharSourceRange::getTokenRange(FromMatch->getSourceRange()), ToText));
+ // FIXME: better error handling. For now, just print error message in the
+ // release version.
+ if (Err) {
+ llvm::errs() << llvm::toString(std::move(Err)) << "\n";
+ assert(false);
+ }
}
}
@@ -52,11 +57,18 @@ ReplaceStmtWithStmt::ReplaceStmtWithStmt(StringRef FromId, StringRef ToId)
void ReplaceStmtWithStmt::run(
const ast_matchers::MatchFinder::MatchResult &Result) {
- const Stmt *FromMatch = Result.Nodes.getStmtAs<Stmt>(FromId);
- const Stmt *ToMatch = Result.Nodes.getStmtAs<Stmt>(ToId);
- if (FromMatch && ToMatch)
- Replace.insert(replaceStmtWithStmt(
- *Result.SourceManager, *FromMatch, *ToMatch));
+ const Stmt *FromMatch = Result.Nodes.getNodeAs<Stmt>(FromId);
+ const Stmt *ToMatch = Result.Nodes.getNodeAs<Stmt>(ToId);
+ if (FromMatch && ToMatch) {
+ auto Err = Replace.add(
+ replaceStmtWithStmt(*Result.SourceManager, *FromMatch, *ToMatch));
+ // FIXME: better error handling. For now, just print error message in the
+ // release version.
+ if (Err) {
+ llvm::errs() << llvm::toString(std::move(Err)) << "\n";
+ assert(false);
+ }
+ }
}
ReplaceIfStmtWithItsBody::ReplaceIfStmtWithItsBody(StringRef Id,
@@ -65,14 +77,28 @@ ReplaceIfStmtWithItsBody::ReplaceIfStmtWithItsBody(StringRef Id,
void ReplaceIfStmtWithItsBody::run(
const ast_matchers::MatchFinder::MatchResult &Result) {
- if (const IfStmt *Node = Result.Nodes.getStmtAs<IfStmt>(Id)) {
+ if (const IfStmt *Node = Result.Nodes.getNodeAs<IfStmt>(Id)) {
const Stmt *Body = PickTrueBranch ? Node->getThen() : Node->getElse();
if (Body) {
- Replace.insert(replaceStmtWithStmt(*Result.SourceManager, *Node, *Body));
+ auto Err =
+ Replace.add(replaceStmtWithStmt(*Result.SourceManager, *Node, *Body));
+ // FIXME: better error handling. For now, just print error message in the
+ // release version.
+ if (Err) {
+ llvm::errs() << llvm::toString(std::move(Err)) << "\n";
+ assert(false);
+ }
} else if (!PickTrueBranch) {
// If we want to use the 'else'-branch, but it doesn't exist, delete
// the whole 'if'.
- Replace.insert(replaceStmtWithText(*Result.SourceManager, *Node, ""));
+ auto Err =
+ Replace.add(replaceStmtWithText(*Result.SourceManager, *Node, ""));
+ // FIXME: better error handling. For now, just print error message in the
+ // release version.
+ if (Err) {
+ llvm::errs() << llvm::toString(std::move(Err)) << "\n";
+ assert(false);
+ }
}
}
}
diff --git a/lib/Tooling/Tooling.cpp b/lib/Tooling/Tooling.cpp
index 4c7fed1e617c0..529c47ef1e7a8 100644
--- a/lib/Tooling/Tooling.cpp
+++ b/lib/Tooling/Tooling.cpp
@@ -13,23 +13,26 @@
//===----------------------------------------------------------------------===//
#include "clang/Tooling/Tooling.h"
-#include "clang/AST/ASTConsumer.h"
#include "clang/Driver/Compilation.h"
#include "clang/Driver/Driver.h"
+#include "clang/Driver/Options.h"
#include "clang/Driver/Tool.h"
#include "clang/Driver/ToolChain.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Tooling/ArgumentsAdjusters.h"
#include "clang/Tooling/CompilationDatabase.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Config/llvm-config.h"
+#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
+#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include <utility>
@@ -240,6 +243,11 @@ bool ToolInvocation::run() {
Argv.push_back(Str.c_str());
const char *const BinaryName = Argv[0];
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
+ unsigned MissingArgIndex, MissingArgCount;
+ std::unique_ptr<llvm::opt::OptTable> Opts(driver::createDriverOptTable());
+ llvm::opt::InputArgList ParsedArgs = Opts->ParseArgs(
+ ArrayRef<const char *>(Argv).slice(1), MissingArgIndex, MissingArgCount);
+ ParseDiagnosticArgs(*DiagOpts, ParsedArgs);
TextDiagnosticPrinter DiagnosticPrinter(
llvm::errs(), &*DiagOpts);
DiagnosticsEngine Diagnostics(