aboutsummaryrefslogtreecommitdiffstats
path: root/lib/Tooling
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2019-10-23 17:52:09 +0000
committerDimitry Andric <dim@FreeBSD.org>2019-10-23 17:52:09 +0000
commit519fc96c475680de2cc49e7811dbbfadb912cbcc (patch)
tree310ca684459b7e9ae13c9a3b9abf308b3a634afe /lib/Tooling
parent2298981669bf3bd63335a4be179bc0f96823a8f4 (diff)
downloadsrc-vendor/clang.tar.gz
src-vendor/clang.zip
Vendor import of stripped clang trunk r375505, the last commit beforevendor/clang/clang-trunk-r375505vendor/clang
the upstream Subversion repository was made read-only, and the LLVM project migrated to GitHub: https://llvm.org/svn/llvm-project/cfe/trunk@375505
Notes
Notes: svn path=/vendor/clang/dist/; revision=353942 svn path=/vendor/clang/clang-r375505/; revision=353943; tag=vendor/clang/clang-trunk-r375505
Diffstat (limited to 'lib/Tooling')
-rw-r--r--lib/Tooling/ASTDiff/ASTDiff.cpp16
-rw-r--r--lib/Tooling/AllTUsExecution.cpp5
-rw-r--r--lib/Tooling/ArgumentsAdjusters.cpp16
-rw-r--r--lib/Tooling/CommonOptionsParser.cpp2
-rw-r--r--lib/Tooling/CompilationDatabase.cpp4
-rw-r--r--lib/Tooling/Core/Replacement.cpp11
-rw-r--r--lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp234
-rw-r--r--lib/Tooling/DependencyScanning/DependencyScanningService.cpp19
-rw-r--r--lib/Tooling/DependencyScanning/DependencyScanningTool.cpp71
-rw-r--r--lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp140
-rw-r--r--lib/Tooling/GuessTargetAndModeCompilationDatabase.cpp2
-rw-r--r--lib/Tooling/Inclusions/HeaderIncludes.cpp14
-rw-r--r--lib/Tooling/Inclusions/IncludeStyle.cpp1
-rw-r--r--lib/Tooling/InterpolatingCompilationDatabase.cpp33
-rw-r--r--lib/Tooling/Refactoring.cpp5
-rw-r--r--lib/Tooling/Refactoring/ASTSelectionRequirements.cpp2
-rw-r--r--lib/Tooling/Refactoring/Extract/Extract.cpp2
-rw-r--r--lib/Tooling/Refactoring/Extract/SourceExtraction.cpp8
-rw-r--r--lib/Tooling/Refactoring/Extract/SourceExtraction.h51
-rw-r--r--lib/Tooling/Refactoring/RefactoringActions.cpp4
-rw-r--r--lib/Tooling/Refactoring/Rename/RenamingAction.cpp4
-rw-r--r--lib/Tooling/Refactoring/Rename/SymbolOccurrences.cpp2
-rw-r--r--lib/Tooling/Refactoring/Rename/USRFindingAction.cpp6
-rw-r--r--lib/Tooling/Refactoring/SourceCode.cpp31
-rw-r--r--lib/Tooling/Refactoring/Stencil.cpp175
-rw-r--r--lib/Tooling/Refactoring/Transformer.cpp263
-rw-r--r--lib/Tooling/RefactoringCallbacks.cpp2
-rw-r--r--lib/Tooling/StandaloneExecution.cpp2
-rw-r--r--lib/Tooling/Syntax/BuildTree.cpp12
-rw-r--r--lib/Tooling/Syntax/Tokens.cpp17
-rw-r--r--lib/Tooling/Tooling.cpp84
-rw-r--r--lib/Tooling/Transformer/CMakeLists.txt18
-rw-r--r--lib/Tooling/Transformer/RangeSelector.cpp (renamed from lib/Tooling/Refactoring/RangeSelector.cpp)56
-rw-r--r--lib/Tooling/Transformer/RewriteRule.cpp178
-rw-r--r--lib/Tooling/Transformer/SourceCode.cpp65
-rw-r--r--lib/Tooling/Transformer/SourceCodeBuilders.cpp160
-rw-r--r--lib/Tooling/Transformer/Stencil.cpp318
-rw-r--r--lib/Tooling/Transformer/Transformer.cpp72
38 files changed, 1439 insertions, 666 deletions
diff --git a/lib/Tooling/ASTDiff/ASTDiff.cpp b/lib/Tooling/ASTDiff/ASTDiff.cpp
index 69eff20bff7a..00db7702cd8b 100644
--- a/lib/Tooling/ASTDiff/ASTDiff.cpp
+++ b/lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -35,8 +35,8 @@ public:
Mapping &operator=(Mapping &&Other) = default;
Mapping(size_t Size) {
- SrcToDst = llvm::make_unique<NodeId[]>(Size);
- DstToSrc = llvm::make_unique<NodeId[]>(Size);
+ SrcToDst = std::make_unique<NodeId[]>(Size);
+ DstToSrc = std::make_unique<NodeId[]>(Size);
}
void link(NodeId Src, NodeId Dst) {
@@ -565,13 +565,13 @@ public:
ZhangShashaMatcher(const ASTDiff::Impl &DiffImpl, const SyntaxTree::Impl &T1,
const SyntaxTree::Impl &T2, NodeId Id1, NodeId Id2)
: DiffImpl(DiffImpl), S1(T1, Id1), S2(T2, Id2) {
- TreeDist = llvm::make_unique<std::unique_ptr<double[]>[]>(
+ TreeDist = std::make_unique<std::unique_ptr<double[]>[]>(
size_t(S1.getSize()) + 1);
- ForestDist = llvm::make_unique<std::unique_ptr<double[]>[]>(
+ ForestDist = std::make_unique<std::unique_ptr<double[]>[]>(
size_t(S1.getSize()) + 1);
for (int I = 0, E = S1.getSize() + 1; I < E; ++I) {
- TreeDist[I] = llvm::make_unique<double[]>(size_t(S2.getSize()) + 1);
- ForestDist[I] = llvm::make_unique<double[]>(size_t(S2.getSize()) + 1);
+ TreeDist[I] = std::make_unique<double[]>(size_t(S2.getSize()) + 1);
+ ForestDist[I] = std::make_unique<double[]>(size_t(S2.getSize()) + 1);
}
}
@@ -960,7 +960,7 @@ void ASTDiff::Impl::computeChangeKinds(Mapping &M) {
ASTDiff::ASTDiff(SyntaxTree &T1, SyntaxTree &T2,
const ComparisonOptions &Options)
- : DiffImpl(llvm::make_unique<Impl>(*T1.TreeImpl, *T2.TreeImpl, Options)) {}
+ : DiffImpl(std::make_unique<Impl>(*T1.TreeImpl, *T2.TreeImpl, Options)) {}
ASTDiff::~ASTDiff() = default;
@@ -969,7 +969,7 @@ NodeId ASTDiff::getMapped(const SyntaxTree &SourceTree, NodeId Id) const {
}
SyntaxTree::SyntaxTree(ASTContext &AST)
- : TreeImpl(llvm::make_unique<SyntaxTree::Impl>(
+ : TreeImpl(std::make_unique<SyntaxTree::Impl>(
this, AST.getTranslationUnitDecl(), AST)) {}
SyntaxTree::~SyntaxTree() = default;
diff --git a/lib/Tooling/AllTUsExecution.cpp b/lib/Tooling/AllTUsExecution.cpp
index ca9db7a561be..d85075f59607 100644
--- a/lib/Tooling/AllTUsExecution.cpp
+++ b/lib/Tooling/AllTUsExecution.cpp
@@ -8,6 +8,7 @@
#include "clang/Tooling/AllTUsExecution.h"
#include "clang/Tooling/ToolExecutorPluginRegistry.h"
+#include "llvm/Support/Threading.h"
#include "llvm/Support/ThreadPool.h"
#include "llvm/Support/VirtualFileSystem.h"
@@ -147,7 +148,7 @@ llvm::Error AllTUsToolExecutor::execute(
return llvm::Error::success();
}
-static llvm::cl::opt<unsigned> ExecutorConcurrency(
+llvm::cl::opt<unsigned> ExecutorConcurrency(
"execute-concurrency",
llvm::cl::desc("The number of threads used to process all files in "
"parallel. Set to 0 for hardware concurrency. "
@@ -162,7 +163,7 @@ public:
return make_string_error(
"[AllTUsToolExecutorPlugin] Please provide a directory/file path in "
"the compilation database.");
- return llvm::make_unique<AllTUsToolExecutor>(std::move(OptionsParser),
+ return std::make_unique<AllTUsToolExecutor>(std::move(OptionsParser),
ExecutorConcurrency);
}
};
diff --git a/lib/Tooling/ArgumentsAdjusters.cpp b/lib/Tooling/ArgumentsAdjusters.cpp
index 942b35df453e..f56d08c47b9a 100644
--- a/lib/Tooling/ArgumentsAdjusters.cpp
+++ b/lib/Tooling/ArgumentsAdjusters.cpp
@@ -57,6 +57,22 @@ ArgumentsAdjuster getClangStripOutputAdjuster() {
};
}
+ArgumentsAdjuster getClangStripSerializeDiagnosticAdjuster() {
+ return [](const CommandLineArguments &Args, StringRef /*unused*/) {
+ CommandLineArguments AdjustedArgs;
+ for (size_t i = 0, e = Args.size(); i < e; ++i) {
+ StringRef Arg = Args[i];
+ if (Arg == "--serialize-diagnostics") {
+ // Skip the diagnostic output argument.
+ ++i;
+ continue;
+ }
+ AdjustedArgs.push_back(Args[i]);
+ }
+ return AdjustedArgs;
+ };
+}
+
ArgumentsAdjuster getClangStripDependencyFileAdjuster() {
return [](const CommandLineArguments &Args, StringRef /*unused*/) {
CommandLineArguments AdjustedArgs;
diff --git a/lib/Tooling/CommonOptionsParser.cpp b/lib/Tooling/CommonOptionsParser.cpp
index f7956f7998f5..5d881aab1e0d 100644
--- a/lib/Tooling/CommonOptionsParser.cpp
+++ b/lib/Tooling/CommonOptionsParser.cpp
@@ -142,7 +142,7 @@ llvm::Error CommonOptionsParser::init(
}
}
auto AdjustingCompilations =
- llvm::make_unique<ArgumentsAdjustingCompilations>(
+ std::make_unique<ArgumentsAdjustingCompilations>(
std::move(Compilations));
Adjuster =
getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN);
diff --git a/lib/Tooling/CompilationDatabase.cpp b/lib/Tooling/CompilationDatabase.cpp
index 4c64750bef19..c453e8d7df19 100644
--- a/lib/Tooling/CompilationDatabase.cpp
+++ b/lib/Tooling/CompilationDatabase.cpp
@@ -356,7 +356,7 @@ FixedCompilationDatabase::loadFromCommandLine(int &Argc,
std::vector<std::string> StrippedArgs;
if (!stripPositionalArgs(CommandLine, StrippedArgs, ErrorMsg))
return nullptr;
- return llvm::make_unique<FixedCompilationDatabase>(Directory, StrippedArgs);
+ return std::make_unique<FixedCompilationDatabase>(Directory, StrippedArgs);
}
std::unique_ptr<FixedCompilationDatabase>
@@ -370,7 +370,7 @@ FixedCompilationDatabase::loadFromFile(StringRef Path, std::string &ErrorMsg) {
}
std::vector<std::string> Args{llvm::line_iterator(**File),
llvm::line_iterator()};
- return llvm::make_unique<FixedCompilationDatabase>(
+ return std::make_unique<FixedCompilationDatabase>(
llvm::sys::path::parent_path(Path), std::move(Args));
}
diff --git a/lib/Tooling/Core/Replacement.cpp b/lib/Tooling/Core/Replacement.cpp
index 546158714e3c..9ed03655bf2c 100644
--- a/lib/Tooling/Core/Replacement.cpp
+++ b/lib/Tooling/Core/Replacement.cpp
@@ -67,11 +67,11 @@ bool Replacement::isApplicable() const {
bool Replacement::apply(Rewriter &Rewrite) const {
SourceManager &SM = Rewrite.getSourceMgr();
- const FileEntry *Entry = SM.getFileManager().getFile(FilePath);
+ auto Entry = SM.getFileManager().getFile(FilePath);
if (!Entry)
return false;
- FileID ID = SM.getOrCreateFileID(Entry, SrcMgr::C_User);
+ FileID ID = SM.getOrCreateFileID(*Entry, SrcMgr::C_User);
const SourceLocation Start =
SM.getLocForStartOfFile(ID).
getLocWithOffset(ReplacementRange.getOffset());
@@ -591,7 +591,8 @@ llvm::Expected<std::string> applyAllReplacements(StringRef Code,
Rewriter Rewrite(SourceMgr, LangOptions());
InMemoryFileSystem->addFile(
"<stdin>", 0, llvm::MemoryBuffer::getMemBuffer(Code, "<stdin>"));
- FileID ID = SourceMgr.createFileID(Files.getFile("<stdin>"), SourceLocation(),
+ 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(),
@@ -613,10 +614,10 @@ std::map<std::string, Replacements> groupReplacementsByFile(
std::map<std::string, Replacements> Result;
llvm::SmallPtrSet<const FileEntry *, 16> ProcessedFileEntries;
for (const auto &Entry : FileToReplaces) {
- const FileEntry *FE = FileMgr.getFile(Entry.first);
+ auto FE = FileMgr.getFile(Entry.first);
if (!FE)
llvm::errs() << "File path " << Entry.first << " is invalid.\n";
- else if (ProcessedFileEntries.insert(FE).second)
+ else if (ProcessedFileEntries.insert(*FE).second)
Result[Entry.first] = std::move(Entry.second);
}
return Result;
diff --git a/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp b/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp
new file mode 100644
index 000000000000..7436c7256327
--- /dev/null
+++ b/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp
@@ -0,0 +1,234 @@
+//===- DependencyScanningFilesystem.cpp - clang-scan-deps fs --------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h"
+#include "clang/Lex/DependencyDirectivesSourceMinimizer.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Threading.h"
+
+using namespace clang;
+using namespace tooling;
+using namespace dependencies;
+
+CachedFileSystemEntry CachedFileSystemEntry::createFileEntry(
+ StringRef Filename, llvm::vfs::FileSystem &FS, bool Minimize) {
+ // Load the file and its content from the file system.
+ llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> MaybeFile =
+ FS.openFileForRead(Filename);
+ if (!MaybeFile)
+ return MaybeFile.getError();
+ llvm::ErrorOr<llvm::vfs::Status> Stat = (*MaybeFile)->status();
+ if (!Stat)
+ return Stat.getError();
+
+ llvm::vfs::File &F = **MaybeFile;
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MaybeBuffer =
+ F.getBuffer(Stat->getName());
+ if (!MaybeBuffer)
+ return MaybeBuffer.getError();
+
+ llvm::SmallString<1024> MinimizedFileContents;
+ // Minimize the file down to directives that might affect the dependencies.
+ const auto &Buffer = *MaybeBuffer;
+ SmallVector<minimize_source_to_dependency_directives::Token, 64> Tokens;
+ if (!Minimize || minimizeSourceToDependencyDirectives(
+ Buffer->getBuffer(), MinimizedFileContents, Tokens)) {
+ // Use the original file unless requested otherwise, or
+ // if the minimization failed.
+ // FIXME: Propage the diagnostic if desired by the client.
+ CachedFileSystemEntry Result;
+ Result.MaybeStat = std::move(*Stat);
+ Result.Contents.reserve(Buffer->getBufferSize() + 1);
+ Result.Contents.append(Buffer->getBufferStart(), Buffer->getBufferEnd());
+ // Implicitly null terminate the contents for Clang's lexer.
+ Result.Contents.push_back('\0');
+ Result.Contents.pop_back();
+ return Result;
+ }
+
+ CachedFileSystemEntry Result;
+ size_t Size = MinimizedFileContents.size();
+ Result.MaybeStat = llvm::vfs::Status(Stat->getName(), Stat->getUniqueID(),
+ Stat->getLastModificationTime(),
+ Stat->getUser(), Stat->getGroup(), Size,
+ Stat->getType(), Stat->getPermissions());
+ // The contents produced by the minimizer must be null terminated.
+ assert(MinimizedFileContents.data()[MinimizedFileContents.size()] == '\0' &&
+ "not null terminated contents");
+ // Even though there's an implicit null terminator in the minimized contents,
+ // we want to temporarily make it explicit. This will ensure that the
+ // std::move will preserve it even if it needs to do a copy if the
+ // SmallString still has the small capacity.
+ MinimizedFileContents.push_back('\0');
+ Result.Contents = std::move(MinimizedFileContents);
+ // Now make the null terminator implicit again, so that Clang's lexer can find
+ // it right where the buffer ends.
+ Result.Contents.pop_back();
+
+ // Compute the skipped PP ranges that speedup skipping over inactive
+ // preprocessor blocks.
+ llvm::SmallVector<minimize_source_to_dependency_directives::SkippedRange, 32>
+ SkippedRanges;
+ minimize_source_to_dependency_directives::computeSkippedRanges(Tokens,
+ SkippedRanges);
+ PreprocessorSkippedRangeMapping Mapping;
+ for (const auto &Range : SkippedRanges) {
+ if (Range.Length < 16) {
+ // Ignore small ranges as non-profitable.
+ // FIXME: This is a heuristic, its worth investigating the tradeoffs
+ // when it should be applied.
+ continue;
+ }
+ Mapping[Range.Offset] = Range.Length;
+ }
+ Result.PPSkippedRangeMapping = std::move(Mapping);
+
+ return Result;
+}
+
+CachedFileSystemEntry
+CachedFileSystemEntry::createDirectoryEntry(llvm::vfs::Status &&Stat) {
+ assert(Stat.isDirectory() && "not a directory!");
+ auto Result = CachedFileSystemEntry();
+ Result.MaybeStat = std::move(Stat);
+ return Result;
+}
+
+DependencyScanningFilesystemSharedCache::
+ DependencyScanningFilesystemSharedCache() {
+ // This heuristic was chosen using a empirical testing on a
+ // reasonably high core machine (iMacPro 18 cores / 36 threads). The cache
+ // sharding gives a performance edge by reducing the lock contention.
+ // FIXME: A better heuristic might also consider the OS to account for
+ // the different cost of lock contention on different OSes.
+ NumShards = std::max(2u, llvm::hardware_concurrency() / 4);
+ CacheShards = std::make_unique<CacheShard[]>(NumShards);
+}
+
+/// Returns a cache entry for the corresponding key.
+///
+/// A new cache entry is created if the key is not in the cache. This is a
+/// thread safe call.
+DependencyScanningFilesystemSharedCache::SharedFileSystemEntry &
+DependencyScanningFilesystemSharedCache::get(StringRef Key) {
+ CacheShard &Shard = CacheShards[llvm::hash_value(Key) % NumShards];
+ std::unique_lock<std::mutex> LockGuard(Shard.CacheLock);
+ auto It = Shard.Cache.try_emplace(Key);
+ return It.first->getValue();
+}
+
+llvm::ErrorOr<const CachedFileSystemEntry *>
+DependencyScanningWorkerFilesystem::getOrCreateFileSystemEntry(
+ const StringRef Filename) {
+ if (const CachedFileSystemEntry *Entry = getCachedEntry(Filename)) {
+ return Entry;
+ }
+
+ // FIXME: Handle PCM/PCH files.
+ // FIXME: Handle module map files.
+
+ bool KeepOriginalSource = IgnoredFiles.count(Filename);
+ DependencyScanningFilesystemSharedCache::SharedFileSystemEntry
+ &SharedCacheEntry = SharedCache.get(Filename);
+ const CachedFileSystemEntry *Result;
+ {
+ std::unique_lock<std::mutex> LockGuard(SharedCacheEntry.ValueLock);
+ CachedFileSystemEntry &CacheEntry = SharedCacheEntry.Value;
+
+ if (!CacheEntry.isValid()) {
+ llvm::vfs::FileSystem &FS = getUnderlyingFS();
+ auto MaybeStatus = FS.status(Filename);
+ if (!MaybeStatus)
+ CacheEntry = CachedFileSystemEntry(MaybeStatus.getError());
+ else if (MaybeStatus->isDirectory())
+ CacheEntry = CachedFileSystemEntry::createDirectoryEntry(
+ std::move(*MaybeStatus));
+ else
+ CacheEntry = CachedFileSystemEntry::createFileEntry(
+ Filename, FS, !KeepOriginalSource);
+ }
+
+ Result = &CacheEntry;
+ }
+
+ // Store the result in the local cache.
+ setCachedEntry(Filename, Result);
+ return Result;
+}
+
+llvm::ErrorOr<llvm::vfs::Status>
+DependencyScanningWorkerFilesystem::status(const Twine &Path) {
+ SmallString<256> OwnedFilename;
+ StringRef Filename = Path.toStringRef(OwnedFilename);
+ const llvm::ErrorOr<const CachedFileSystemEntry *> Result =
+ getOrCreateFileSystemEntry(Filename);
+ if (!Result)
+ return Result.getError();
+ return (*Result)->getStatus();
+}
+
+namespace {
+
+/// The VFS that is used by clang consumes the \c CachedFileSystemEntry using
+/// this subclass.
+class MinimizedVFSFile final : public llvm::vfs::File {
+public:
+ MinimizedVFSFile(std::unique_ptr<llvm::MemoryBuffer> Buffer,
+ llvm::vfs::Status Stat)
+ : Buffer(std::move(Buffer)), Stat(std::move(Stat)) {}
+
+ llvm::ErrorOr<llvm::vfs::Status> status() override { return Stat; }
+
+ const llvm::MemoryBuffer *getBufferPtr() const { return Buffer.get(); }
+
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+ getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
+ bool IsVolatile) override {
+ return std::move(Buffer);
+ }
+
+ std::error_code close() override { return {}; }
+
+private:
+ std::unique_ptr<llvm::MemoryBuffer> Buffer;
+ llvm::vfs::Status Stat;
+};
+
+llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
+createFile(const CachedFileSystemEntry *Entry,
+ ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings) {
+ if (Entry->isDirectory())
+ return llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>(
+ std::make_error_code(std::errc::is_a_directory));
+ llvm::ErrorOr<StringRef> Contents = Entry->getContents();
+ if (!Contents)
+ return Contents.getError();
+ auto Result = std::make_unique<MinimizedVFSFile>(
+ llvm::MemoryBuffer::getMemBuffer(*Contents, Entry->getName(),
+ /*RequiresNullTerminator=*/false),
+ *Entry->getStatus());
+ if (!Entry->getPPSkippedRangeMapping().empty() && PPSkipMappings)
+ (*PPSkipMappings)[Result->getBufferPtr()] =
+ &Entry->getPPSkippedRangeMapping();
+ return llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>(
+ std::unique_ptr<llvm::vfs::File>(std::move(Result)));
+}
+
+} // end anonymous namespace
+
+llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
+DependencyScanningWorkerFilesystem::openFileForRead(const Twine &Path) {
+ SmallString<256> OwnedFilename;
+ StringRef Filename = Path.toStringRef(OwnedFilename);
+
+ const llvm::ErrorOr<const CachedFileSystemEntry *> Result =
+ getOrCreateFileSystemEntry(Filename);
+ if (!Result)
+ return Result.getError();
+ return createFile(Result.get(), PPSkipMappings);
+}
diff --git a/lib/Tooling/DependencyScanning/DependencyScanningService.cpp b/lib/Tooling/DependencyScanning/DependencyScanningService.cpp
new file mode 100644
index 000000000000..e5cebe381000
--- /dev/null
+++ b/lib/Tooling/DependencyScanning/DependencyScanningService.cpp
@@ -0,0 +1,19 @@
+//===- DependencyScanningService.cpp - clang-scan-deps service ------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
+
+using namespace clang;
+using namespace tooling;
+using namespace dependencies;
+
+DependencyScanningService::DependencyScanningService(ScanningMode Mode,
+ bool ReuseFileManager,
+ bool SkipExcludedPPRanges)
+ : Mode(Mode), ReuseFileManager(ReuseFileManager),
+ SkipExcludedPPRanges(SkipExcludedPPRanges) {}
diff --git a/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp b/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
new file mode 100644
index 000000000000..82b3ae806c65
--- /dev/null
+++ b/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
@@ -0,0 +1,71 @@
+//===- DependencyScanningTool.cpp - clang-scan-deps service ------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/DependencyScanning/DependencyScanningTool.h"
+#include "clang/Frontend/Utils.h"
+
+namespace clang{
+namespace tooling{
+namespace dependencies{
+
+DependencyScanningTool::DependencyScanningTool(DependencyScanningService &Service,
+const tooling::CompilationDatabase &Compilations) : Worker(Service), Compilations(Compilations) {}
+
+llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(const std::string &Input,
+ StringRef CWD) {
+ /// Prints out all of the gathered dependencies into a string.
+ class DependencyPrinterConsumer : public DependencyConsumer {
+ public:
+ void handleFileDependency(const DependencyOutputOptions &Opts,
+ StringRef File) override {
+ if (!this->Opts)
+ this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
+ Dependencies.push_back(File);
+ }
+
+ void printDependencies(std::string &S) {
+ if (!Opts)
+ return;
+
+ class DependencyPrinter : public DependencyFileGenerator {
+ public:
+ DependencyPrinter(DependencyOutputOptions &Opts,
+ ArrayRef<std::string> Dependencies)
+ : DependencyFileGenerator(Opts) {
+ for (const auto &Dep : Dependencies)
+ addDependency(Dep);
+ }
+
+ void printDependencies(std::string &S) {
+ llvm::raw_string_ostream OS(S);
+ outputDependencyFile(OS);
+ }
+ };
+
+ DependencyPrinter Generator(*Opts, Dependencies);
+ Generator.printDependencies(S);
+ }
+
+ private:
+ std::unique_ptr<DependencyOutputOptions> Opts;
+ std::vector<std::string> Dependencies;
+ };
+
+ DependencyPrinterConsumer Consumer;
+ auto Result =
+ Worker.computeDependencies(Input, CWD, Compilations, Consumer);
+ if (Result)
+ return std::move(Result);
+ std::string Output;
+ Consumer.printDependencies(Output);
+ return Output;
+}
+
+} // end namespace dependencies
+} // end namespace tooling
+} // end namespace clang
diff --git a/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
index 4868f2663796..f382c202f8c2 100644
--- a/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
+++ b/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
@@ -8,9 +8,12 @@
#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Frontend/Utils.h"
+#include "clang/Lex/PreprocessorOptions.h"
+#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
#include "clang/Tooling/Tooling.h"
using namespace clang;
@@ -19,21 +22,25 @@ using namespace dependencies;
namespace {
-/// Prints out all of the gathered dependencies into a string.
-class DependencyPrinter : public DependencyFileGenerator {
+/// Forwards the gatherered dependencies to the consumer.
+class DependencyConsumerForwarder : public DependencyFileGenerator {
public:
- DependencyPrinter(std::unique_ptr<DependencyOutputOptions> Opts,
- std::string &S)
- : DependencyFileGenerator(*Opts), Opts(std::move(Opts)), S(S) {}
+ DependencyConsumerForwarder(std::unique_ptr<DependencyOutputOptions> Opts,
+ DependencyConsumer &C)
+ : DependencyFileGenerator(*Opts), Opts(std::move(Opts)), C(C) {}
void finishedMainFile(DiagnosticsEngine &Diags) override {
- llvm::raw_string_ostream OS(S);
- outputDependencyFile(OS);
+ llvm::SmallString<256> CanonPath;
+ for (const auto &File : getDependencies()) {
+ CanonPath = File;
+ llvm::sys::path::remove_dots(CanonPath, /*remove_dot_dot=*/true);
+ C.handleFileDependency(*Opts, CanonPath);
+ }
}
private:
std::unique_ptr<DependencyOutputOptions> Opts;
- std::string &S;
+ DependencyConsumer &C;
};
/// A proxy file system that doesn't call `chdir` when changing the working
@@ -62,10 +69,12 @@ private:
/// dependency scanning for the given compiler invocation.
class DependencyScanningAction : public tooling::ToolAction {
public:
- DependencyScanningAction(StringRef WorkingDirectory,
- std::string &DependencyFileContents)
- : WorkingDirectory(WorkingDirectory),
- DependencyFileContents(DependencyFileContents) {}
+ DependencyScanningAction(
+ StringRef WorkingDirectory, DependencyConsumer &Consumer,
+ llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS,
+ ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings)
+ : WorkingDirectory(WorkingDirectory), Consumer(Consumer),
+ DepFS(std::move(DepFS)), PPSkipMappings(PPSkipMappings) {}
bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
FileManager *FileMgr,
@@ -74,8 +83,6 @@ public:
// Create a compiler instance to handle the actual work.
CompilerInstance Compiler(std::move(PCHContainerOps));
Compiler.setInvocation(std::move(Invocation));
- FileMgr->getFileSystemOpts().WorkingDir = WorkingDirectory;
- Compiler.setFileManager(FileMgr);
// Don't print 'X warnings and Y errors generated'.
Compiler.getDiagnosticOpts().ShowCarets = false;
@@ -84,6 +91,32 @@ public:
if (!Compiler.hasDiagnostics())
return false;
+ // Use the dependency scanning optimized file system if we can.
+ if (DepFS) {
+ const CompilerInvocation &CI = Compiler.getInvocation();
+ // Add any filenames that were explicity passed in the build settings and
+ // that might be opened, as we want to ensure we don't run source
+ // minimization on them.
+ DepFS->IgnoredFiles.clear();
+ for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries)
+ DepFS->IgnoredFiles.insert(Entry.Path);
+ for (const auto &Entry : CI.getHeaderSearchOpts().VFSOverlayFiles)
+ DepFS->IgnoredFiles.insert(Entry);
+
+ // Support for virtual file system overlays on top of the caching
+ // filesystem.
+ FileMgr->setVirtualFileSystem(createVFSFromCompilerInvocation(
+ CI, Compiler.getDiagnostics(), DepFS));
+
+ // Pass the skip mappings which should speed up excluded conditional block
+ // skipping in the preprocessor.
+ if (PPSkipMappings)
+ Compiler.getPreprocessorOpts()
+ .ExcludedConditionalDirectiveSkipMappings = PPSkipMappings;
+ }
+
+ FileMgr->getFileSystemOpts().WorkingDir = WorkingDirectory;
+ Compiler.setFileManager(FileMgr);
Compiler.createSourceManager(*FileMgr);
// Create the dependency collector that will collect the produced
@@ -93,57 +126,76 @@ public:
// invocation to the collector. The options in the invocation are reset,
// which ensures that the compiler won't create new dependency collectors,
// and thus won't write out the extra '.d' files to disk.
- auto Opts = llvm::make_unique<DependencyOutputOptions>(
+ auto Opts = std::make_unique<DependencyOutputOptions>(
std::move(Compiler.getInvocation().getDependencyOutputOpts()));
// We need at least one -MT equivalent for the generator to work.
if (Opts->Targets.empty())
Opts->Targets = {"clang-scan-deps dependency"};
- Compiler.addDependencyCollector(std::make_shared<DependencyPrinter>(
- std::move(Opts), DependencyFileContents));
+ Compiler.addDependencyCollector(
+ std::make_shared<DependencyConsumerForwarder>(std::move(Opts),
+ Consumer));
- auto Action = llvm::make_unique<PreprocessOnlyAction>();
+ auto Action = std::make_unique<PreprocessOnlyAction>();
const bool Result = Compiler.ExecuteAction(*Action);
- FileMgr->clearStatCache();
+ if (!DepFS)
+ FileMgr->clearStatCache();
return Result;
}
private:
StringRef WorkingDirectory;
- /// The dependency file will be written to this string.
- std::string &DependencyFileContents;
+ DependencyConsumer &Consumer;
+ llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS;
+ ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings;
};
} // end anonymous namespace
-DependencyScanningWorker::DependencyScanningWorker() {
+DependencyScanningWorker::DependencyScanningWorker(
+ DependencyScanningService &Service) {
DiagOpts = new DiagnosticOptions();
PCHContainerOps = std::make_shared<PCHContainerOperations>();
- /// FIXME: Use the shared file system from the service for fast scanning
- /// mode.
- WorkerFS = new ProxyFileSystemWithoutChdir(llvm::vfs::getRealFileSystem());
+ RealFS = new ProxyFileSystemWithoutChdir(llvm::vfs::getRealFileSystem());
+ if (Service.canSkipExcludedPPRanges())
+ PPSkipMappings =
+ std::make_unique<ExcludedPreprocessorDirectiveSkipMapping>();
+ if (Service.getMode() == ScanningMode::MinimizedSourcePreprocessing)
+ DepFS = new DependencyScanningWorkerFilesystem(
+ Service.getSharedCache(), RealFS, PPSkipMappings.get());
+ if (Service.canReuseFileManager())
+ Files = new FileManager(FileSystemOptions(), RealFS);
}
-llvm::Expected<std::string>
-DependencyScanningWorker::getDependencyFile(const std::string &Input,
- StringRef WorkingDirectory,
- const CompilationDatabase &CDB) {
+static llvm::Error runWithDiags(
+ DiagnosticOptions *DiagOpts,
+ llvm::function_ref<bool(DiagnosticConsumer &DC)> BodyShouldSucceed) {
// Capture the emitted diagnostics and report them to the client
// in the case of a failure.
std::string DiagnosticOutput;
llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
- TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts.get());
-
- WorkerFS->setCurrentWorkingDirectory(WorkingDirectory);
- tooling::ClangTool Tool(CDB, Input, PCHContainerOps, WorkerFS);
- Tool.clearArgumentsAdjusters();
- Tool.setRestoreWorkingDir(false);
- Tool.setPrintErrorMessage(false);
- Tool.setDiagnosticConsumer(&DiagPrinter);
- std::string Output;
- DependencyScanningAction Action(WorkingDirectory, Output);
- if (Tool.run(&Action)) {
- return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
- llvm::inconvertibleErrorCode());
- }
- return Output;
+ TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts);
+
+ if (BodyShouldSucceed(DiagPrinter))
+ return llvm::Error::success();
+ return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
+ llvm::inconvertibleErrorCode());
+}
+
+llvm::Error DependencyScanningWorker::computeDependencies(
+ const std::string &Input, StringRef WorkingDirectory,
+ const CompilationDatabase &CDB, DependencyConsumer &Consumer) {
+ RealFS->setCurrentWorkingDirectory(WorkingDirectory);
+ return runWithDiags(DiagOpts.get(), [&](DiagnosticConsumer &DC) {
+ /// Create the tool that uses the underlying file system to ensure that any
+ /// file system requests that are made by the driver do not go through the
+ /// dependency scanning filesystem.
+ tooling::ClangTool Tool(CDB, Input, PCHContainerOps, RealFS, Files);
+ Tool.clearArgumentsAdjusters();
+ Tool.setRestoreWorkingDir(false);
+ Tool.setPrintErrorMessage(false);
+ Tool.setDiagnosticConsumer(&DC);
+ DependencyScanningAction Action(WorkingDirectory, Consumer, DepFS,
+ PPSkipMappings.get());
+ return !Tool.run(&Action);
+ });
}
diff --git a/lib/Tooling/GuessTargetAndModeCompilationDatabase.cpp b/lib/Tooling/GuessTargetAndModeCompilationDatabase.cpp
index ac3faf1b01f9..b6c1c0952aca 100644
--- a/lib/Tooling/GuessTargetAndModeCompilationDatabase.cpp
+++ b/lib/Tooling/GuessTargetAndModeCompilationDatabase.cpp
@@ -50,7 +50,7 @@ private:
std::unique_ptr<CompilationDatabase>
inferTargetAndDriverMode(std::unique_ptr<CompilationDatabase> Base) {
- return llvm::make_unique<TargetAndModeAdderDatabase>(std::move(Base));
+ return std::make_unique<TargetAndModeAdderDatabase>(std::move(Base));
}
} // namespace tooling
diff --git a/lib/Tooling/Inclusions/HeaderIncludes.cpp b/lib/Tooling/Inclusions/HeaderIncludes.cpp
index a7f79c33bc55..e746bbb7f87b 100644
--- a/lib/Tooling/Inclusions/HeaderIncludes.cpp
+++ b/lib/Tooling/Inclusions/HeaderIncludes.cpp
@@ -199,6 +199,20 @@ int IncludeCategoryManager::getIncludePriority(StringRef IncludeName,
return Ret;
}
+int IncludeCategoryManager::getSortIncludePriority(StringRef IncludeName,
+ bool CheckMainHeader) const {
+ int Ret = INT_MAX;
+ for (unsigned i = 0, e = CategoryRegexs.size(); i != e; ++i)
+ if (CategoryRegexs[i].match(IncludeName)) {
+ Ret = Style.IncludeCategories[i].SortPriority;
+ if (Ret == 0)
+ Ret = Style.IncludeCategories[i].Priority;
+ break;
+ }
+ if (CheckMainHeader && IsMainFile && Ret > 0 && isMainHeader(IncludeName))
+ Ret = 0;
+ return Ret;
+}
bool IncludeCategoryManager::isMainHeader(StringRef IncludeName) const {
if (!IncludeName.startswith("\""))
return false;
diff --git a/lib/Tooling/Inclusions/IncludeStyle.cpp b/lib/Tooling/Inclusions/IncludeStyle.cpp
index c53c82c83630..26dc0b87cf9d 100644
--- a/lib/Tooling/Inclusions/IncludeStyle.cpp
+++ b/lib/Tooling/Inclusions/IncludeStyle.cpp
@@ -17,6 +17,7 @@ void MappingTraits<IncludeStyle::IncludeCategory>::mapping(
IO &IO, IncludeStyle::IncludeCategory &Category) {
IO.mapOptional("Regex", Category.Regex);
IO.mapOptional("Priority", Category.Priority);
+ IO.mapOptional("SortPriority", Category.SortPriority);
}
void ScalarEnumerationTraits<IncludeStyle::IncludeBlocksStyle>::enumeration(
diff --git a/lib/Tooling/InterpolatingCompilationDatabase.cpp b/lib/Tooling/InterpolatingCompilationDatabase.cpp
index 53c8dd448fd9..59b66abe65e9 100644
--- a/lib/Tooling/InterpolatingCompilationDatabase.cpp
+++ b/lib/Tooling/InterpolatingCompilationDatabase.cpp
@@ -42,9 +42,9 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/Basic/LangStandard.h"
#include "clang/Driver/Options.h"
#include "clang/Driver/Types.h"
-#include "clang/Frontend/LangStandard.h"
#include "clang/Tooling/CompilationDatabase.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Optional.h"
@@ -149,17 +149,17 @@ struct TransferableCommand {
// We parse each argument individually so that we can retain the exact
// spelling of each argument; re-rendering is lossy for aliased flags.
// E.g. in CL mode, /W4 maps to -Wall.
- auto OptTable = clang::driver::createDriverOptTable();
+ auto &OptTable = clang::driver::getDriverOptTable();
if (!OldArgs.empty())
Cmd.CommandLine.emplace_back(OldArgs.front());
for (unsigned Pos = 1; Pos < OldArgs.size();) {
using namespace driver::options;
const unsigned OldPos = Pos;
- std::unique_ptr<llvm::opt::Arg> Arg(OptTable->ParseOneArg(
+ std::unique_ptr<llvm::opt::Arg> Arg(OptTable.ParseOneArg(
ArgList, Pos,
- /* Include */ClangCLMode ? CoreOption | CLOption : 0,
- /* Exclude */ClangCLMode ? 0 : CLOption));
+ /* Include */ ClangCLMode ? CoreOption | CLOption : 0,
+ /* Exclude */ ClangCLMode ? 0 : CLOption));
if (!Arg)
continue;
@@ -249,15 +249,15 @@ private:
}
// Map the language from the --std flag to that of the -x flag.
- static types::ID toType(InputKind::Language Lang) {
+ static types::ID toType(Language Lang) {
switch (Lang) {
- case InputKind::C:
+ case Language::C:
return types::TY_C;
- case InputKind::CXX:
+ case Language::CXX:
return types::TY_CXX;
- case InputKind::ObjC:
+ case Language::ObjC:
return types::TY_ObjC;
- case InputKind::ObjCXX:
+ case Language::ObjCXX:
return types::TY_ObjCXX;
default:
return types::TY_INVALID;
@@ -297,15 +297,8 @@ private:
// Try to interpret the argument as '-std='.
Optional<LangStandard::Kind> tryParseStdArg(const llvm::opt::Arg &Arg) {
using namespace driver::options;
- if (Arg.getOption().matches(ClangCLMode ? OPT__SLASH_std : OPT_std_EQ)) {
- return llvm::StringSwitch<LangStandard::Kind>(Arg.getValue())
-#define LANGSTANDARD(id, name, lang, ...) .Case(name, LangStandard::lang_##id)
-#define LANGSTANDARD_ALIAS(id, alias) .Case(alias, LangStandard::lang_##id)
-#include "clang/Frontend/LangStandards.def"
-#undef LANGSTANDARD_ALIAS
-#undef LANGSTANDARD
- .Default(LangStandard::lang_unspecified);
- }
+ if (Arg.getOption().matches(ClangCLMode ? OPT__SLASH_std : OPT_std_EQ))
+ return LangStandard::getLangKind(Arg.getValue());
return None;
}
};
@@ -544,7 +537,7 @@ private:
std::unique_ptr<CompilationDatabase>
inferMissingCompileCommands(std::unique_ptr<CompilationDatabase> Inner) {
- return llvm::make_unique<InterpolatingCompilationDatabase>(std::move(Inner));
+ return std::make_unique<InterpolatingCompilationDatabase>(std::move(Inner));
}
} // namespace tooling
diff --git a/lib/Tooling/Refactoring.cpp b/lib/Tooling/Refactoring.cpp
index f379a9c3bcf6..d45cd8c57f10 100644
--- a/lib/Tooling/Refactoring.cpp
+++ b/lib/Tooling/Refactoring.cpp
@@ -78,7 +78,10 @@ bool formatAndApplyAllReplacements(
const std::string &FilePath = FileAndReplaces.first;
auto &CurReplaces = FileAndReplaces.second;
- const FileEntry *Entry = Files.getFile(FilePath);
+ const FileEntry *Entry = nullptr;
+ if (auto File = Files.getFile(FilePath))
+ Entry = *File;
+
FileID ID = SM.getOrCreateFileID(Entry, SrcMgr::C_User);
StringRef Code = SM.getBufferData(ID);
diff --git a/lib/Tooling/Refactoring/ASTSelectionRequirements.cpp b/lib/Tooling/Refactoring/ASTSelectionRequirements.cpp
index 14fc66a979ae..7dfd3988d904 100644
--- a/lib/Tooling/Refactoring/ASTSelectionRequirements.cpp
+++ b/lib/Tooling/Refactoring/ASTSelectionRequirements.cpp
@@ -35,7 +35,7 @@ Expected<CodeRangeASTSelection> CodeRangeASTSelectionRequirement::evaluate(
if (!ASTSelection)
return ASTSelection.takeError();
std::unique_ptr<SelectedASTNode> StoredSelection =
- llvm::make_unique<SelectedASTNode>(std::move(*ASTSelection));
+ std::make_unique<SelectedASTNode>(std::move(*ASTSelection));
Optional<CodeRangeASTSelection> CodeRange = CodeRangeASTSelection::create(
Context.getSelectionRange(), *StoredSelection);
if (!CodeRange)
diff --git a/lib/Tooling/Refactoring/Extract/Extract.cpp b/lib/Tooling/Refactoring/Extract/Extract.cpp
index f5b94a462103..402b56109052 100644
--- a/lib/Tooling/Refactoring/Extract/Extract.cpp
+++ b/lib/Tooling/Refactoring/Extract/Extract.cpp
@@ -13,12 +13,12 @@
//===----------------------------------------------------------------------===//
#include "clang/Tooling/Refactoring/Extract/Extract.h"
-#include "SourceExtraction.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprObjC.h"
#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/Refactoring/Extract/SourceExtraction.h"
namespace clang {
namespace tooling {
diff --git a/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp b/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp
index 533c373e35c4..5d57ecf90a96 100644
--- a/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp
+++ b/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-#include "SourceExtraction.h"
+#include "clang/Tooling/Refactoring/Extract/SourceExtraction.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/StmtCXX.h"
#include "clang/AST/StmtObjC.h"
@@ -40,8 +40,12 @@ bool isSemicolonRequiredAfter(const Stmt *S) {
return isSemicolonRequiredAfter(CXXFor->getBody());
if (const auto *ObjCFor = dyn_cast<ObjCForCollectionStmt>(S))
return isSemicolonRequiredAfter(ObjCFor->getBody());
+ if(const auto *Switch = dyn_cast<SwitchStmt>(S))
+ return isSemicolonRequiredAfter(Switch->getBody());
+ if(const auto *Case = dyn_cast<SwitchCase>(S))
+ return isSemicolonRequiredAfter(Case->getSubStmt());
switch (S->getStmtClass()) {
- case Stmt::SwitchStmtClass:
+ case Stmt::DeclStmtClass:
case Stmt::CXXTryStmtClass:
case Stmt::ObjCAtSynchronizedStmtClass:
case Stmt::ObjCAutoreleasePoolStmtClass:
diff --git a/lib/Tooling/Refactoring/Extract/SourceExtraction.h b/lib/Tooling/Refactoring/Extract/SourceExtraction.h
deleted file mode 100644
index 545eb6c1a11c..000000000000
--- a/lib/Tooling/Refactoring/Extract/SourceExtraction.h
+++ /dev/null
@@ -1,51 +0,0 @@
-//===--- SourceExtraction.cpp - Clang refactoring library -----------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_LIB_TOOLING_REFACTORING_EXTRACT_SOURCE_EXTRACTION_H
-#define LLVM_CLANG_LIB_TOOLING_REFACTORING_EXTRACT_SOURCE_EXTRACTION_H
-
-#include "clang/Basic/LLVM.h"
-
-namespace clang {
-
-class LangOptions;
-class SourceManager;
-class SourceRange;
-class Stmt;
-
-namespace tooling {
-
-/// Determines which semicolons should be inserted during extraction.
-class ExtractionSemicolonPolicy {
-public:
- bool isNeededInExtractedFunction() const {
- return IsNeededInExtractedFunction;
- }
-
- bool isNeededInOriginalFunction() const { return IsNeededInOriginalFunction; }
-
- /// Returns the semicolon insertion policy that is needed for extraction of
- /// the given statement from the given source range.
- static ExtractionSemicolonPolicy compute(const Stmt *S,
- SourceRange &ExtractedRange,
- const SourceManager &SM,
- const LangOptions &LangOpts);
-
-private:
- ExtractionSemicolonPolicy(bool IsNeededInExtractedFunction,
- bool IsNeededInOriginalFunction)
- : IsNeededInExtractedFunction(IsNeededInExtractedFunction),
- IsNeededInOriginalFunction(IsNeededInOriginalFunction) {}
- bool IsNeededInExtractedFunction;
- bool IsNeededInOriginalFunction;
-};
-
-} // end namespace tooling
-} // end namespace clang
-
-#endif // LLVM_CLANG_LIB_TOOLING_REFACTORING_EXTRACT_SOURCE_EXTRACTION_H
diff --git a/lib/Tooling/Refactoring/RefactoringActions.cpp b/lib/Tooling/Refactoring/RefactoringActions.cpp
index 1a3833243ab4..7ac723f67c04 100644
--- a/lib/Tooling/Refactoring/RefactoringActions.cpp
+++ b/lib/Tooling/Refactoring/RefactoringActions.cpp
@@ -98,8 +98,8 @@ public:
std::vector<std::unique_ptr<RefactoringAction>> createRefactoringActions() {
std::vector<std::unique_ptr<RefactoringAction>> Actions;
- Actions.push_back(llvm::make_unique<LocalRename>());
- Actions.push_back(llvm::make_unique<ExtractRefactoring>());
+ Actions.push_back(std::make_unique<LocalRename>());
+ Actions.push_back(std::make_unique<ExtractRefactoring>());
return Actions;
}
diff --git a/lib/Tooling/Refactoring/Rename/RenamingAction.cpp b/lib/Tooling/Refactoring/Rename/RenamingAction.cpp
index 1649513a077a..b0634912e3fc 100644
--- a/lib/Tooling/Refactoring/Rename/RenamingAction.cpp
+++ b/lib/Tooling/Refactoring/Rename/RenamingAction.cpp
@@ -266,12 +266,12 @@ private:
};
std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {
- return llvm::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList,
+ return std::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList,
FileToReplaces, PrintLocations);
}
std::unique_ptr<ASTConsumer> QualifiedRenamingAction::newASTConsumer() {
- return llvm::make_unique<USRSymbolRenamer>(NewNames, USRList, FileToReplaces);
+ return std::make_unique<USRSymbolRenamer>(NewNames, USRList, FileToReplaces);
}
} // end namespace tooling
diff --git a/lib/Tooling/Refactoring/Rename/SymbolOccurrences.cpp b/lib/Tooling/Refactoring/Rename/SymbolOccurrences.cpp
index 8cc1ffaf4482..9e69d37e81ad 100644
--- a/lib/Tooling/Refactoring/Rename/SymbolOccurrences.cpp
+++ b/lib/Tooling/Refactoring/Rename/SymbolOccurrences.cpp
@@ -25,7 +25,7 @@ SymbolOccurrence::SymbolOccurrence(const SymbolName &Name, OccurrenceKind Kind,
Locations[0], Locations[0].getLocWithOffset(NamePieces[0].size()));
return;
}
- MultipleRanges = llvm::make_unique<SourceRange[]>(Locations.size());
+ MultipleRanges = std::make_unique<SourceRange[]>(Locations.size());
RangeOrNumRanges.setBegin(
SourceLocation::getFromRawEncoding(Locations.size()));
for (const auto &Loc : llvm::enumerate(Locations)) {
diff --git a/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp b/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp
index 54c6f3e734b1..e26248f50c29 100644
--- a/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp
+++ b/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp
@@ -102,6 +102,10 @@ public:
private:
void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl) {
+ if (!RecordDecl->getDefinition()) {
+ USRSet.insert(getUSRForDecl(RecordDecl));
+ return;
+ }
RecordDecl = RecordDecl->getDefinition();
if (const auto *ClassTemplateSpecDecl =
dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl))
@@ -264,7 +268,7 @@ private:
};
std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {
- return llvm::make_unique<NamedDeclFindingConsumer>(
+ return std::make_unique<NamedDeclFindingConsumer>(
SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force,
ErrorOccurred);
}
diff --git a/lib/Tooling/Refactoring/SourceCode.cpp b/lib/Tooling/Refactoring/SourceCode.cpp
deleted file mode 100644
index 3a97e178bbd4..000000000000
--- a/lib/Tooling/Refactoring/SourceCode.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-//===--- SourceCode.cpp - Source code manipulation routines -----*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// This file provides functions that simplify extraction of source code.
-//
-//===----------------------------------------------------------------------===//
-#include "clang/Tooling/Refactoring/SourceCode.h"
-#include "clang/Lex/Lexer.h"
-
-using namespace clang;
-
-StringRef clang::tooling::getText(CharSourceRange Range,
- const ASTContext &Context) {
- return Lexer::getSourceText(Range, Context.getSourceManager(),
- Context.getLangOpts());
-}
-
-CharSourceRange clang::tooling::maybeExtendRange(CharSourceRange Range,
- tok::TokenKind Next,
- ASTContext &Context) {
- Optional<Token> Tok = Lexer::findNextToken(
- Range.getEnd(), Context.getSourceManager(), Context.getLangOpts());
- if (!Tok || !Tok->is(Next))
- return Range;
- return CharSourceRange::getTokenRange(Range.getBegin(), Tok->getLocation());
-}
diff --git a/lib/Tooling/Refactoring/Stencil.cpp b/lib/Tooling/Refactoring/Stencil.cpp
deleted file mode 100644
index 09eca21c3cef..000000000000
--- a/lib/Tooling/Refactoring/Stencil.cpp
+++ /dev/null
@@ -1,175 +0,0 @@
-//===--- Stencil.cpp - Stencil implementation -------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/Tooling/Refactoring/Stencil.h"
-#include "clang/AST/ASTContext.h"
-#include "clang/AST/ASTTypeTraits.h"
-#include "clang/AST/Expr.h"
-#include "clang/ASTMatchers/ASTMatchFinder.h"
-#include "clang/ASTMatchers/ASTMatchers.h"
-#include "clang/Lex/Lexer.h"
-#include "clang/Tooling/Refactoring/SourceCode.h"
-#include "llvm/Support/Errc.h"
-#include <atomic>
-#include <memory>
-#include <string>
-
-using namespace clang;
-using namespace tooling;
-
-using ast_matchers::MatchFinder;
-using llvm::Error;
-
-// A down_cast function to safely down cast a StencilPartInterface to a subclass
-// D. Returns nullptr if P is not an instance of D.
-template <typename D> const D *down_cast(const StencilPartInterface *P) {
- if (P == nullptr || D::typeId() != P->typeId())
- return nullptr;
- return static_cast<const D *>(P);
-}
-
-static llvm::Expected<ast_type_traits::DynTypedNode>
-getNode(const ast_matchers::BoundNodes &Nodes, StringRef Id) {
- auto &NodesMap = Nodes.getMap();
- auto It = NodesMap.find(Id);
- if (It == NodesMap.end())
- return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument,
- "Id not bound: " + Id);
- return It->second;
-}
-
-namespace {
-// An arbitrary fragment of code within a stencil.
-struct RawTextData {
- explicit RawTextData(std::string T) : Text(std::move(T)) {}
- std::string Text;
-};
-
-// A debugging operation to dump the AST for a particular (bound) AST node.
-struct DebugPrintNodeOpData {
- explicit DebugPrintNodeOpData(std::string S) : Id(std::move(S)) {}
- std::string Id;
-};
-
-// The fragment of code corresponding to the selected range.
-struct SelectorOpData {
- explicit SelectorOpData(RangeSelector S) : Selector(std::move(S)) {}
- RangeSelector Selector;
-};
-} // namespace
-
-bool isEqualData(const RawTextData &A, const RawTextData &B) {
- return A.Text == B.Text;
-}
-
-bool isEqualData(const DebugPrintNodeOpData &A, const DebugPrintNodeOpData &B) {
- return A.Id == B.Id;
-}
-
-// Equality is not (yet) defined for \c RangeSelector.
-bool isEqualData(const SelectorOpData &, const SelectorOpData &) { return false; }
-
-// The `evalData()` overloads evaluate the given stencil data to a string, given
-// the match result, and append it to `Result`. We define an overload for each
-// type of stencil data.
-
-Error evalData(const RawTextData &Data, const MatchFinder::MatchResult &,
- std::string *Result) {
- Result->append(Data.Text);
- return Error::success();
-}
-
-Error evalData(const DebugPrintNodeOpData &Data,
- const MatchFinder::MatchResult &Match, std::string *Result) {
- std::string Output;
- llvm::raw_string_ostream Os(Output);
- auto NodeOrErr = getNode(Match.Nodes, Data.Id);
- if (auto Err = NodeOrErr.takeError())
- return Err;
- NodeOrErr->print(Os, PrintingPolicy(Match.Context->getLangOpts()));
- *Result += Os.str();
- return Error::success();
-}
-
-Error evalData(const SelectorOpData &Data, const MatchFinder::MatchResult &Match,
- std::string *Result) {
- auto Range = Data.Selector(Match);
- if (!Range)
- return Range.takeError();
- *Result += getText(*Range, *Match.Context);
- return Error::success();
-}
-
-template <typename T>
-class StencilPartImpl : public StencilPartInterface {
- T Data;
-
-public:
- template <typename... Ps>
- explicit StencilPartImpl(Ps &&... Args)
- : StencilPartInterface(StencilPartImpl::typeId()),
- Data(std::forward<Ps>(Args)...) {}
-
- // Generates a unique identifier for this class (specifically, one per
- // instantiation of the template).
- static const void* typeId() {
- static bool b;
- return &b;
- }
-
- Error eval(const MatchFinder::MatchResult &Match,
- std::string *Result) const override {
- return evalData(Data, Match, Result);
- }
-
- bool isEqual(const StencilPartInterface &Other) const override {
- if (const auto *OtherPtr = down_cast<StencilPartImpl>(&Other))
- return isEqualData(Data, OtherPtr->Data);
- return false;
- }
-};
-
-namespace {
-using RawText = StencilPartImpl<RawTextData>;
-using DebugPrintNodeOp = StencilPartImpl<DebugPrintNodeOpData>;
-using SelectorOp = StencilPartImpl<SelectorOpData>;
-} // namespace
-
-StencilPart Stencil::wrap(StringRef Text) {
- return stencil::text(Text);
-}
-
-StencilPart Stencil::wrap(RangeSelector Selector) {
- return stencil::selection(std::move(Selector));
-}
-
-void Stencil::append(Stencil OtherStencil) {
- for (auto &Part : OtherStencil.Parts)
- Parts.push_back(std::move(Part));
-}
-
-llvm::Expected<std::string>
-Stencil::eval(const MatchFinder::MatchResult &Match) const {
- std::string Result;
- for (const auto &Part : Parts)
- if (auto Err = Part.eval(Match, &Result))
- return std::move(Err);
- return Result;
-}
-
-StencilPart stencil::text(StringRef Text) {
- return StencilPart(std::make_shared<RawText>(Text));
-}
-
-StencilPart stencil::selection(RangeSelector Selector) {
- return StencilPart(std::make_shared<SelectorOp>(std::move(Selector)));
-}
-
-StencilPart stencil::dPrint(StringRef Id) {
- return StencilPart(std::make_shared<DebugPrintNodeOp>(Id));
-}
diff --git a/lib/Tooling/Refactoring/Transformer.cpp b/lib/Tooling/Refactoring/Transformer.cpp
deleted file mode 100644
index 8e6fe6c7a940..000000000000
--- a/lib/Tooling/Refactoring/Transformer.cpp
+++ /dev/null
@@ -1,263 +0,0 @@
-//===--- Transformer.cpp - Transformer library implementation ---*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/Tooling/Refactoring/Transformer.h"
-#include "clang/AST/Expr.h"
-#include "clang/ASTMatchers/ASTMatchFinder.h"
-#include "clang/ASTMatchers/ASTMatchers.h"
-#include "clang/Basic/Diagnostic.h"
-#include "clang/Basic/SourceLocation.h"
-#include "clang/Rewrite/Core/Rewriter.h"
-#include "clang/Tooling/Refactoring/AtomicChange.h"
-#include "clang/Tooling/Refactoring/SourceCode.h"
-#include "llvm/ADT/Optional.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/Errc.h"
-#include "llvm/Support/Error.h"
-#include <deque>
-#include <string>
-#include <utility>
-#include <vector>
-
-using namespace clang;
-using namespace tooling;
-
-using ast_matchers::MatchFinder;
-using ast_matchers::internal::DynTypedMatcher;
-using ast_type_traits::ASTNodeKind;
-using ast_type_traits::DynTypedNode;
-using llvm::Error;
-using llvm::StringError;
-
-using MatchResult = MatchFinder::MatchResult;
-
-// Did the text at this location originate in a macro definition (aka. body)?
-// For example,
-//
-// #define NESTED(x) x
-// #define MACRO(y) { int y = NESTED(3); }
-// if (true) MACRO(foo)
-//
-// The if statement expands to
-//
-// if (true) { int foo = 3; }
-// ^ ^
-// Loc1 Loc2
-//
-// For SourceManager SM, SM.isMacroArgExpansion(Loc1) and
-// SM.isMacroArgExpansion(Loc2) are both true, but isOriginMacroBody(sm, Loc1)
-// is false, because "foo" originated in the source file (as an argument to a
-// macro), whereas isOriginMacroBody(SM, Loc2) is true, because "3" originated
-// in the definition of MACRO.
-static bool isOriginMacroBody(const clang::SourceManager &SM,
- clang::SourceLocation Loc) {
- while (Loc.isMacroID()) {
- if (SM.isMacroBodyExpansion(Loc))
- return true;
- // Otherwise, it must be in an argument, so we continue searching up the
- // invocation stack. getImmediateMacroCallerLoc() gives the location of the
- // argument text, inside the call text.
- Loc = SM.getImmediateMacroCallerLoc(Loc);
- }
- return false;
-}
-
-Expected<SmallVector<tooling::detail::Transformation, 1>>
-tooling::detail::translateEdits(const MatchResult &Result,
- llvm::ArrayRef<ASTEdit> Edits) {
- SmallVector<tooling::detail::Transformation, 1> Transformations;
- for (const auto &Edit : Edits) {
- Expected<CharSourceRange> Range = Edit.TargetRange(Result);
- if (!Range)
- return Range.takeError();
- if (Range->isInvalid() ||
- isOriginMacroBody(*Result.SourceManager, Range->getBegin()))
- return SmallVector<Transformation, 0>();
- auto Replacement = Edit.Replacement(Result);
- if (!Replacement)
- return Replacement.takeError();
- tooling::detail::Transformation T;
- T.Range = *Range;
- T.Replacement = std::move(*Replacement);
- Transformations.push_back(std::move(T));
- }
- return Transformations;
-}
-
-ASTEdit tooling::change(RangeSelector S, TextGenerator Replacement) {
- ASTEdit E;
- E.TargetRange = std::move(S);
- E.Replacement = std::move(Replacement);
- return E;
-}
-
-RewriteRule tooling::makeRule(DynTypedMatcher M, SmallVector<ASTEdit, 1> Edits,
- TextGenerator Explanation) {
- return RewriteRule{{RewriteRule::Case{
- std::move(M), std::move(Edits), std::move(Explanation), {}}}};
-}
-
-void tooling::addInclude(RewriteRule &Rule, StringRef Header,
- IncludeFormat Format) {
- for (auto &Case : Rule.Cases)
- Case.AddedIncludes.emplace_back(Header.str(), Format);
-}
-
-// Determines whether A is a base type of B in the class hierarchy, including
-// the implicit relationship of Type and QualType.
-static bool isBaseOf(ASTNodeKind A, ASTNodeKind B) {
- static auto TypeKind = ASTNodeKind::getFromNodeKind<Type>();
- static auto QualKind = ASTNodeKind::getFromNodeKind<QualType>();
- /// Mimic the implicit conversions of Matcher<>.
- /// - From Matcher<Type> to Matcher<QualType>
- /// - From Matcher<Base> to Matcher<Derived>
- return (A.isSame(TypeKind) && B.isSame(QualKind)) || A.isBaseOf(B);
-}
-
-// Try to find a common kind to which all of the rule's matchers can be
-// converted.
-static ASTNodeKind
-findCommonKind(const SmallVectorImpl<RewriteRule::Case> &Cases) {
- assert(!Cases.empty() && "Rule must have at least one case.");
- ASTNodeKind JoinKind = Cases[0].Matcher.getSupportedKind();
- // Find a (least) Kind K, for which M.canConvertTo(K) holds, for all matchers
- // M in Rules.
- for (const auto &Case : Cases) {
- auto K = Case.Matcher.getSupportedKind();
- if (isBaseOf(JoinKind, K)) {
- JoinKind = K;
- continue;
- }
- if (K.isSame(JoinKind) || isBaseOf(K, JoinKind))
- // JoinKind is already the lowest.
- continue;
- // K and JoinKind are unrelated -- there is no least common kind.
- return ASTNodeKind();
- }
- return JoinKind;
-}
-
-// Binds each rule's matcher to a unique (and deterministic) tag based on
-// `TagBase`.
-static std::vector<DynTypedMatcher>
-taggedMatchers(StringRef TagBase,
- const SmallVectorImpl<RewriteRule::Case> &Cases) {
- std::vector<DynTypedMatcher> Matchers;
- Matchers.reserve(Cases.size());
- size_t count = 0;
- for (const auto &Case : Cases) {
- std::string Tag = (TagBase + Twine(count)).str();
- ++count;
- auto M = Case.Matcher.tryBind(Tag);
- assert(M && "RewriteRule matchers should be bindable.");
- Matchers.push_back(*std::move(M));
- }
- return Matchers;
-}
-
-// Simply gathers the contents of the various rules into a single rule. The
-// actual work to combine these into an ordered choice is deferred to matcher
-// registration.
-RewriteRule tooling::applyFirst(ArrayRef<RewriteRule> Rules) {
- RewriteRule R;
- for (auto &Rule : Rules)
- R.Cases.append(Rule.Cases.begin(), Rule.Cases.end());
- return R;
-}
-
-static DynTypedMatcher joinCaseMatchers(const RewriteRule &Rule) {
- assert(!Rule.Cases.empty() && "Rule must have at least one case.");
- if (Rule.Cases.size() == 1)
- return Rule.Cases[0].Matcher;
-
- auto CommonKind = findCommonKind(Rule.Cases);
- assert(!CommonKind.isNone() && "Cases must have compatible matchers.");
- return DynTypedMatcher::constructVariadic(
- DynTypedMatcher::VO_AnyOf, CommonKind, taggedMatchers("Tag", Rule.Cases));
-}
-
-DynTypedMatcher tooling::detail::buildMatcher(const RewriteRule &Rule) {
- DynTypedMatcher M = joinCaseMatchers(Rule);
- M.setAllowBind(true);
- // `tryBind` is guaranteed to succeed, because `AllowBind` was set to true.
- return *M.tryBind(RewriteRule::RootID);
-}
-
-// Finds the case that was "selected" -- that is, whose matcher triggered the
-// `MatchResult`.
-const RewriteRule::Case &
-tooling::detail::findSelectedCase(const MatchResult &Result,
- const RewriteRule &Rule) {
- if (Rule.Cases.size() == 1)
- return Rule.Cases[0];
-
- auto &NodesMap = Result.Nodes.getMap();
- for (size_t i = 0, N = Rule.Cases.size(); i < N; ++i) {
- std::string Tag = ("Tag" + Twine(i)).str();
- if (NodesMap.find(Tag) != NodesMap.end())
- return Rule.Cases[i];
- }
- llvm_unreachable("No tag found for this rule.");
-}
-
-constexpr llvm::StringLiteral RewriteRule::RootID;
-
-void Transformer::registerMatchers(MatchFinder *MatchFinder) {
- MatchFinder->addDynamicMatcher(tooling::detail::buildMatcher(Rule), this);
-}
-
-void Transformer::run(const MatchResult &Result) {
- if (Result.Context->getDiagnostics().hasErrorOccurred())
- return;
-
- // Verify the existence and validity of the AST node that roots this rule.
- auto &NodesMap = Result.Nodes.getMap();
- auto Root = NodesMap.find(RewriteRule::RootID);
- assert(Root != NodesMap.end() && "Transformation failed: missing root node.");
- SourceLocation RootLoc = Result.SourceManager->getExpansionLoc(
- Root->second.getSourceRange().getBegin());
- assert(RootLoc.isValid() && "Invalid location for Root node of match.");
-
- RewriteRule::Case Case = tooling::detail::findSelectedCase(Result, Rule);
- auto Transformations = tooling::detail::translateEdits(Result, Case.Edits);
- if (!Transformations) {
- Consumer(Transformations.takeError());
- return;
- }
-
- if (Transformations->empty()) {
- // No rewrite applied (but no error encountered either).
- RootLoc.print(llvm::errs() << "note: skipping match at loc ",
- *Result.SourceManager);
- llvm::errs() << "\n";
- return;
- }
-
- // Record the results in the AtomicChange.
- AtomicChange AC(*Result.SourceManager, RootLoc);
- for (const auto &T : *Transformations) {
- if (auto Err = AC.replace(*Result.SourceManager, T.Range, T.Replacement)) {
- Consumer(std::move(Err));
- return;
- }
- }
-
- for (const auto &I : Case.AddedIncludes) {
- auto &Header = I.first;
- switch (I.second) {
- case IncludeFormat::Quoted:
- AC.addHeader(Header);
- break;
- case IncludeFormat::Angled:
- AC.addHeader((llvm::Twine("<") + Header + ">").str());
- break;
- }
- }
-
- Consumer(std::move(AC));
-}
diff --git a/lib/Tooling/RefactoringCallbacks.cpp b/lib/Tooling/RefactoringCallbacks.cpp
index 2aa5b5bf9d98..919b83beb357 100644
--- a/lib/Tooling/RefactoringCallbacks.cpp
+++ b/lib/Tooling/RefactoringCallbacks.cpp
@@ -66,7 +66,7 @@ private:
};
std::unique_ptr<ASTConsumer> ASTMatchRefactorer::newASTConsumer() {
- return llvm::make_unique<RefactoringASTConsumer>(*this);
+ return std::make_unique<RefactoringASTConsumer>(*this);
}
static Replacement replaceStmtWithText(SourceManager &Sources, const Stmt &From,
diff --git a/lib/Tooling/StandaloneExecution.cpp b/lib/Tooling/StandaloneExecution.cpp
index ad82ee083a40..09094c3c23f3 100644
--- a/lib/Tooling/StandaloneExecution.cpp
+++ b/lib/Tooling/StandaloneExecution.cpp
@@ -76,7 +76,7 @@ public:
if (OptionsParser.getSourcePathList().empty())
return make_string_error(
"[StandaloneToolExecutorPlugin] No positional argument found.");
- return llvm::make_unique<StandaloneToolExecutor>(std::move(OptionsParser));
+ return std::make_unique<StandaloneToolExecutor>(std::move(OptionsParser));
}
};
diff --git a/lib/Tooling/Syntax/BuildTree.cpp b/lib/Tooling/Syntax/BuildTree.cpp
index 03c439c59e39..a0b653df133d 100644
--- a/lib/Tooling/Syntax/BuildTree.cpp
+++ b/lib/Tooling/Syntax/BuildTree.cpp
@@ -58,8 +58,11 @@ public:
/// Finish building the tree and consume the root node.
syntax::TranslationUnit *finalize() && {
auto Tokens = Arena.tokenBuffer().expandedTokens();
+ assert(!Tokens.empty());
+ assert(Tokens.back().kind() == tok::eof);
+
// Build the root of the tree, consuming all the children.
- Pending.foldChildren(Tokens,
+ Pending.foldChildren(Tokens.drop_back(),
new (Arena.allocator()) syntax::TranslationUnit);
return cast<syntax::TranslationUnit>(std::move(Pending).finalize());
@@ -96,10 +99,11 @@ private:
/// Ensures that added nodes properly nest and cover the whole token stream.
struct Forest {
Forest(syntax::Arena &A) {
- // FIXME: do not add 'eof' to the tree.
-
+ assert(!A.tokenBuffer().expandedTokens().empty());
+ assert(A.tokenBuffer().expandedTokens().back().kind() == tok::eof);
// Create all leaf nodes.
- for (auto &T : A.tokenBuffer().expandedTokens())
+ // Note that we do not have 'eof' in the tree.
+ for (auto &T : A.tokenBuffer().expandedTokens().drop_back())
Trees.insert(Trees.end(),
{&T, NodeAndRole{new (A.allocator()) syntax::Leaf(&T)}});
}
diff --git a/lib/Tooling/Syntax/Tokens.cpp b/lib/Tooling/Syntax/Tokens.cpp
index d82dc1f35c94..a2c3bc137d6b 100644
--- a/lib/Tooling/Syntax/Tokens.cpp
+++ b/lib/Tooling/Syntax/Tokens.cpp
@@ -232,6 +232,21 @@ TokenBuffer::expansionStartingAt(const syntax::Token *Spelled) const {
return E;
}
+std::vector<const syntax::Token *>
+TokenBuffer::macroExpansions(FileID FID) const {
+ auto FileIt = Files.find(FID);
+ assert(FileIt != Files.end() && "file not tracked by token buffer");
+ auto &File = FileIt->second;
+ std::vector<const syntax::Token *> Expansions;
+ auto &Spelled = File.SpelledTokens;
+ for (auto Mapping : File.Mappings) {
+ const syntax::Token *Token = &Spelled[Mapping.BeginSpelled];
+ if (Token->kind() == tok::TokenKind::identifier)
+ Expansions.push_back(Token);
+ }
+ return Expansions;
+}
+
std::vector<syntax::Token> syntax::tokenize(FileID FID, const SourceManager &SM,
const LangOptions &LO) {
std::vector<syntax::Token> Tokens;
@@ -321,7 +336,7 @@ TokenCollector::TokenCollector(Preprocessor &PP) : PP(PP) {
});
// And locations of macro calls, to properly recover boundaries of those in
// case of empty expansions.
- auto CB = llvm::make_unique<CollectPPExpansions>(*this);
+ auto CB = std::make_unique<CollectPPExpansions>(*this);
this->Collector = CB.get();
PP.addPPCallbacks(std::move(CB));
}
diff --git a/lib/Tooling/Tooling.cpp b/lib/Tooling/Tooling.cpp
index 291df0ae333d..1d6a968331b5 100644
--- a/lib/Tooling/Tooling.cpp
+++ b/lib/Tooling/Tooling.cpp
@@ -91,7 +91,34 @@ static const llvm::opt::ArgStringList *getCC1Arguments(
// We expect to get back exactly one Command job, if we didn't something
// failed. Extract that job from the Compilation.
const driver::JobList &Jobs = Compilation->getJobs();
- if (Jobs.size() != 1 || !isa<driver::Command>(*Jobs.begin())) {
+ const driver::ActionList &Actions = Compilation->getActions();
+ bool OffloadCompilation = false;
+ if (Jobs.size() > 1) {
+ for (auto A : Actions){
+ // On MacOSX real actions may end up being wrapped in BindArchAction
+ if (isa<driver::BindArchAction>(A))
+ A = *A->input_begin();
+ if (isa<driver::OffloadAction>(A)) {
+ // Offload compilation has 2 top-level actions, one (at the front) is
+ // the original host compilation and the other is offload action
+ // composed of at least one device compilation. For such case, general
+ // tooling will consider host-compilation only. For tooling on device
+ // compilation, device compilation only option, such as
+ // `--cuda-device-only`, needs specifying.
+ assert(Actions.size() > 1);
+ assert(
+ isa<driver::CompileJobAction>(Actions.front()) ||
+ // On MacOSX real actions may end up being wrapped in
+ // BindArchAction.
+ (isa<driver::BindArchAction>(Actions.front()) &&
+ isa<driver::CompileJobAction>(*Actions.front()->input_begin())));
+ OffloadCompilation = true;
+ break;
+ }
+ }
+ }
+ if (Jobs.size() == 0 || !isa<driver::Command>(*Jobs.begin()) ||
+ (Jobs.size() > 1 && !OffloadCompilation)) {
SmallString<256> error_msg;
llvm::raw_svector_ostream error_stream(error_msg);
Jobs.Print(error_stream, "; ", true);
@@ -118,20 +145,18 @@ CompilerInvocation *newInvocation(
DiagnosticsEngine *Diagnostics, const llvm::opt::ArgStringList &CC1Args) {
assert(!CC1Args.empty() && "Must at least contain the program name!");
CompilerInvocation *Invocation = new CompilerInvocation;
- CompilerInvocation::CreateFromArgs(
- *Invocation, CC1Args.data() + 1, CC1Args.data() + CC1Args.size(),
- *Diagnostics);
+ CompilerInvocation::CreateFromArgs(*Invocation, CC1Args, *Diagnostics);
Invocation->getFrontendOpts().DisableFree = false;
Invocation->getCodeGenOpts().DisableFree = false;
return Invocation;
}
-bool runToolOnCode(FrontendAction *ToolAction, const Twine &Code,
- const Twine &FileName,
+bool runToolOnCode(std::unique_ptr<FrontendAction> ToolAction,
+ const Twine &Code, const Twine &FileName,
std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
- return runToolOnCodeWithArgs(ToolAction, Code, std::vector<std::string>(),
- FileName, "clang-tool",
- std::move(PCHContainerOps));
+ return runToolOnCodeWithArgs(std::move(ToolAction), Code,
+ std::vector<std::string>(), FileName,
+ "clang-tool", std::move(PCHContainerOps));
}
} // namespace tooling
@@ -153,7 +178,7 @@ namespace clang {
namespace tooling {
bool runToolOnCodeWithArgs(
- FrontendAction *ToolAction, const Twine &Code,
+ std::unique_ptr<FrontendAction> ToolAction, const Twine &Code,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
const std::vector<std::string> &Args, const Twine &FileName,
const Twine &ToolName,
@@ -166,13 +191,12 @@ bool runToolOnCodeWithArgs(
ArgumentsAdjuster Adjuster = getClangStripDependencyFileAdjuster();
ToolInvocation Invocation(
getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileNameRef), FileNameRef),
- ToolAction, Files.get(),
- std::move(PCHContainerOps));
+ std::move(ToolAction), Files.get(), std::move(PCHContainerOps));
return Invocation.run();
}
bool runToolOnCodeWithArgs(
- FrontendAction *ToolAction, const Twine &Code,
+ std::unique_ptr<FrontendAction> ToolAction, const Twine &Code,
const std::vector<std::string> &Args, const Twine &FileName,
const Twine &ToolName,
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
@@ -194,8 +218,8 @@ bool runToolOnCodeWithArgs(
llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
}
- return runToolOnCodeWithArgs(ToolAction, Code, OverlayFileSystem, Args,
- FileName, ToolName);
+ return runToolOnCodeWithArgs(std::move(ToolAction), Code, OverlayFileSystem,
+ Args, FileName, ToolName);
}
llvm::Expected<std::string> getAbsolutePath(llvm::vfs::FileSystem &FS,
@@ -249,12 +273,15 @@ void addTargetAndModeForProgramName(std::vector<std::string> &CommandLine,
namespace {
class SingleFrontendActionFactory : public FrontendActionFactory {
- FrontendAction *Action;
+ std::unique_ptr<FrontendAction> Action;
public:
- SingleFrontendActionFactory(FrontendAction *Action) : Action(Action) {}
+ SingleFrontendActionFactory(std::unique_ptr<FrontendAction> Action)
+ : Action(std::move(Action)) {}
- FrontendAction *create() override { return Action; }
+ std::unique_ptr<FrontendAction> create() override {
+ return std::move(Action);
+ }
};
} // namespace
@@ -266,11 +293,13 @@ ToolInvocation::ToolInvocation(
Files(Files), PCHContainerOps(std::move(PCHContainerOps)) {}
ToolInvocation::ToolInvocation(
- std::vector<std::string> CommandLine, FrontendAction *FAction,
- FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
+ std::vector<std::string> CommandLine,
+ std::unique_ptr<FrontendAction> FAction, FileManager *Files,
+ std::shared_ptr<PCHContainerOperations> PCHContainerOps)
: CommandLine(std::move(CommandLine)),
- Action(new SingleFrontendActionFactory(FAction)), OwnsAction(true),
- Files(Files), PCHContainerOps(std::move(PCHContainerOps)) {}
+ Action(new SingleFrontendActionFactory(std::move(FAction))),
+ OwnsAction(true), Files(Files),
+ PCHContainerOps(std::move(PCHContainerOps)) {}
ToolInvocation::~ToolInvocation() {
if (OwnsAction)
@@ -290,8 +319,7 @@ bool ToolInvocation::run() {
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(
+ llvm::opt::InputArgList ParsedArgs = driver::getDriverOptTable().ParseArgs(
ArrayRef<const char *>(Argv).slice(1), MissingArgIndex, MissingArgCount);
ParseDiagnosticArgs(*DiagOpts, ParsedArgs);
TextDiagnosticPrinter DiagnosticPrinter(
@@ -375,16 +403,20 @@ bool FrontendActionFactory::runInvocation(
ClangTool::ClangTool(const CompilationDatabase &Compilations,
ArrayRef<std::string> SourcePaths,
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
- IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS)
+ IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
+ IntrusiveRefCntPtr<FileManager> Files)
: Compilations(Compilations), SourcePaths(SourcePaths),
PCHContainerOps(std::move(PCHContainerOps)),
OverlayFileSystem(new llvm::vfs::OverlayFileSystem(std::move(BaseFS))),
InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
- Files(new FileManager(FileSystemOptions(), OverlayFileSystem)) {
+ Files(Files ? Files
+ : new FileManager(FileSystemOptions(), OverlayFileSystem)) {
OverlayFileSystem->pushOverlay(InMemoryFileSystem);
appendArgumentsAdjuster(getClangStripOutputAdjuster());
appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
+ if (Files)
+ Files->setVirtualFileSystem(OverlayFileSystem);
}
ClangTool::~ClangTool() = default;
diff --git a/lib/Tooling/Transformer/CMakeLists.txt b/lib/Tooling/Transformer/CMakeLists.txt
new file mode 100644
index 000000000000..68f0cfeee8f6
--- /dev/null
+++ b/lib/Tooling/Transformer/CMakeLists.txt
@@ -0,0 +1,18 @@
+set(LLVM_LINK_COMPONENTS Support)
+
+add_clang_library(clangTransformer
+ RangeSelector.cpp
+ RewriteRule.cpp
+ SourceCode.cpp
+ SourceCodeBuilders.cpp
+ Stencil.cpp
+ Transformer.cpp
+
+ LINK_LIBS
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangLex
+ clangToolingCore
+ clangToolingRefactoring
+ )
diff --git a/lib/Tooling/Refactoring/RangeSelector.cpp b/lib/Tooling/Transformer/RangeSelector.cpp
index 768c02e2277b..9f81423c9022 100644
--- a/lib/Tooling/Refactoring/RangeSelector.cpp
+++ b/lib/Tooling/Transformer/RangeSelector.cpp
@@ -6,12 +6,12 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/Tooling/Refactoring/RangeSelector.h"
+#include "clang/Tooling/Transformer/RangeSelector.h"
#include "clang/AST/Expr.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/Lexer.h"
-#include "clang/Tooling/Refactoring/SourceCode.h"
+#include "clang/Tooling/Transformer/SourceCode.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
@@ -20,7 +20,7 @@
#include <vector>
using namespace clang;
-using namespace tooling;
+using namespace transformer;
using ast_matchers::MatchFinder;
using ast_type_traits::ASTNodeKind;
@@ -104,7 +104,7 @@ static SourceLocation findOpenParen(const CallExpr &E, const SourceManager &SM,
return findPreviousTokenKind(EndLoc, SM, LangOpts, tok::TokenKind::l_paren);
}
-RangeSelector tooling::before(RangeSelector Selector) {
+RangeSelector transformer::before(RangeSelector Selector) {
return [Selector](const MatchResult &Result) -> Expected<CharSourceRange> {
Expected<CharSourceRange> SelectedRange = Selector(Result);
if (!SelectedRange)
@@ -113,7 +113,7 @@ RangeSelector tooling::before(RangeSelector Selector) {
};
}
-RangeSelector tooling::after(RangeSelector Selector) {
+RangeSelector transformer::after(RangeSelector Selector) {
return [Selector](const MatchResult &Result) -> Expected<CharSourceRange> {
Expected<CharSourceRange> SelectedRange = Selector(Result);
if (!SelectedRange)
@@ -126,27 +126,29 @@ RangeSelector tooling::after(RangeSelector Selector) {
};
}
-RangeSelector tooling::node(std::string ID) {
+RangeSelector transformer::node(std::string ID) {
return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {
Expected<DynTypedNode> Node = getNode(Result.Nodes, ID);
if (!Node)
return Node.takeError();
return Node->get<Stmt>() != nullptr && Node->get<Expr>() == nullptr
- ? getExtendedRange(*Node, tok::TokenKind::semi, *Result.Context)
+ ? tooling::getExtendedRange(*Node, tok::TokenKind::semi,
+ *Result.Context)
: CharSourceRange::getTokenRange(Node->getSourceRange());
};
}
-RangeSelector tooling::statement(std::string ID) {
+RangeSelector transformer::statement(std::string ID) {
return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {
Expected<DynTypedNode> Node = getNode(Result.Nodes, ID);
if (!Node)
return Node.takeError();
- return getExtendedRange(*Node, tok::TokenKind::semi, *Result.Context);
+ return tooling::getExtendedRange(*Node, tok::TokenKind::semi,
+ *Result.Context);
};
}
-RangeSelector tooling::range(RangeSelector Begin, RangeSelector End) {
+RangeSelector transformer::range(RangeSelector Begin, RangeSelector End) {
return [Begin, End](const MatchResult &Result) -> Expected<CharSourceRange> {
Expected<CharSourceRange> BeginRange = Begin(Result);
if (!BeginRange)
@@ -165,11 +167,11 @@ RangeSelector tooling::range(RangeSelector Begin, RangeSelector End) {
};
}
-RangeSelector tooling::range(std::string BeginID, std::string EndID) {
- return tooling::range(node(std::move(BeginID)), node(std::move(EndID)));
+RangeSelector transformer::range(std::string BeginID, std::string EndID) {
+ return transformer::range(node(std::move(BeginID)), node(std::move(EndID)));
}
-RangeSelector tooling::member(std::string ID) {
+RangeSelector transformer::member(std::string ID) {
return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {
Expected<DynTypedNode> Node = getNode(Result.Nodes, ID);
if (!Node)
@@ -181,7 +183,7 @@ RangeSelector tooling::member(std::string ID) {
};
}
-RangeSelector tooling::name(std::string ID) {
+RangeSelector transformer::name(std::string ID) {
return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {
Expected<DynTypedNode> N = getNode(Result.Nodes, ID);
if (!N)
@@ -197,7 +199,7 @@ RangeSelector tooling::name(std::string ID) {
// `foo<int>` for which this range will be too short. Doing so will
// require subcasing `NamedDecl`, because it doesn't provide virtual
// access to the \c DeclarationNameInfo.
- if (getText(R, *Result.Context) != D->getName())
+ if (tooling::getText(R, *Result.Context) != D->getName())
return CharSourceRange();
return R;
}
@@ -219,6 +221,9 @@ RangeSelector tooling::name(std::string ID) {
}
namespace {
+// FIXME: make this available in the public API for users to easily create their
+// own selectors.
+
// Creates a selector from a range-selection function \p Func, which selects a
// range that is relative to a bound node id. \c T is the node type expected by
// \p Func.
@@ -253,7 +258,7 @@ CharSourceRange getStatementsRange(const MatchResult &,
}
} // namespace
-RangeSelector tooling::statements(std::string ID) {
+RangeSelector transformer::statements(std::string ID) {
return RelativeSelector<CompoundStmt, getStatementsRange>(std::move(ID));
}
@@ -268,7 +273,7 @@ CharSourceRange getCallArgumentsRange(const MatchResult &Result,
}
} // namespace
-RangeSelector tooling::callArgs(std::string ID) {
+RangeSelector transformer::callArgs(std::string ID) {
return RelativeSelector<CallExpr, getCallArgumentsRange>(std::move(ID));
}
@@ -282,11 +287,24 @@ CharSourceRange getElementsRange(const MatchResult &,
}
} // namespace
-RangeSelector tooling::initListElements(std::string ID) {
+RangeSelector transformer::initListElements(std::string ID) {
return RelativeSelector<InitListExpr, getElementsRange>(std::move(ID));
}
-RangeSelector tooling::expansion(RangeSelector S) {
+namespace {
+// Returns the range of the else branch, including the `else` keyword.
+CharSourceRange getElseRange(const MatchResult &Result, const IfStmt &S) {
+ return tooling::maybeExtendRange(
+ CharSourceRange::getTokenRange(S.getElseLoc(), S.getEndLoc()),
+ tok::TokenKind::semi, *Result.Context);
+}
+} // namespace
+
+RangeSelector transformer::elseBranch(std::string ID) {
+ return RelativeSelector<IfStmt, getElseRange>(std::move(ID));
+}
+
+RangeSelector transformer::expansion(RangeSelector S) {
return [S](const MatchResult &Result) -> Expected<CharSourceRange> {
Expected<CharSourceRange> SRange = S(Result);
if (!SRange)
diff --git a/lib/Tooling/Transformer/RewriteRule.cpp b/lib/Tooling/Transformer/RewriteRule.cpp
new file mode 100644
index 000000000000..6fa558f7b2ee
--- /dev/null
+++ b/lib/Tooling/Transformer/RewriteRule.cpp
@@ -0,0 +1,178 @@
+//===--- Transformer.cpp - Transformer library implementation ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Transformer/RewriteRule.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Tooling/Transformer/SourceCode.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+using namespace clang;
+using namespace transformer;
+
+using ast_matchers::MatchFinder;
+using ast_matchers::internal::DynTypedMatcher;
+using ast_type_traits::ASTNodeKind;
+
+using MatchResult = MatchFinder::MatchResult;
+
+Expected<SmallVector<transformer::detail::Transformation, 1>>
+transformer::detail::translateEdits(const MatchResult &Result,
+ llvm::ArrayRef<ASTEdit> Edits) {
+ SmallVector<transformer::detail::Transformation, 1> Transformations;
+ for (const auto &Edit : Edits) {
+ Expected<CharSourceRange> Range = Edit.TargetRange(Result);
+ if (!Range)
+ return Range.takeError();
+ llvm::Optional<CharSourceRange> EditRange =
+ tooling::getRangeForEdit(*Range, *Result.Context);
+ // FIXME: let user specify whether to treat this case as an error or ignore
+ // it as is currently done.
+ if (!EditRange)
+ return SmallVector<Transformation, 0>();
+ auto Replacement = Edit.Replacement(Result);
+ if (!Replacement)
+ return Replacement.takeError();
+ transformer::detail::Transformation T;
+ T.Range = *EditRange;
+ T.Replacement = std::move(*Replacement);
+ Transformations.push_back(std::move(T));
+ }
+ return Transformations;
+}
+
+ASTEdit transformer::change(RangeSelector S, TextGenerator Replacement) {
+ ASTEdit E;
+ E.TargetRange = std::move(S);
+ E.Replacement = std::move(Replacement);
+ return E;
+}
+
+RewriteRule transformer::makeRule(DynTypedMatcher M, SmallVector<ASTEdit, 1> Edits,
+ TextGenerator Explanation) {
+ return RewriteRule{{RewriteRule::Case{
+ std::move(M), std::move(Edits), std::move(Explanation), {}}}};
+}
+
+void transformer::addInclude(RewriteRule &Rule, StringRef Header,
+ IncludeFormat Format) {
+ for (auto &Case : Rule.Cases)
+ Case.AddedIncludes.emplace_back(Header.str(), Format);
+}
+
+#ifndef NDEBUG
+// Filters for supported matcher kinds. FIXME: Explicitly list the allowed kinds
+// (all node matcher types except for `QualType` and `Type`), rather than just
+// banning `QualType` and `Type`.
+static bool hasValidKind(const DynTypedMatcher &M) {
+ return !M.canConvertTo<QualType>();
+}
+#endif
+
+// Binds each rule's matcher to a unique (and deterministic) tag based on
+// `TagBase` and the id paired with the case.
+static std::vector<DynTypedMatcher> taggedMatchers(
+ StringRef TagBase,
+ const SmallVectorImpl<std::pair<size_t, RewriteRule::Case>> &Cases) {
+ std::vector<DynTypedMatcher> Matchers;
+ Matchers.reserve(Cases.size());
+ for (const auto &Case : Cases) {
+ std::string Tag = (TagBase + Twine(Case.first)).str();
+ // HACK: Many matchers are not bindable, so ensure that tryBind will work.
+ DynTypedMatcher BoundMatcher(Case.second.Matcher);
+ BoundMatcher.setAllowBind(true);
+ auto M = BoundMatcher.tryBind(Tag);
+ Matchers.push_back(*std::move(M));
+ }
+ return Matchers;
+}
+
+// Simply gathers the contents of the various rules into a single rule. The
+// actual work to combine these into an ordered choice is deferred to matcher
+// registration.
+RewriteRule transformer::applyFirst(ArrayRef<RewriteRule> Rules) {
+ RewriteRule R;
+ for (auto &Rule : Rules)
+ R.Cases.append(Rule.Cases.begin(), Rule.Cases.end());
+ return R;
+}
+
+std::vector<DynTypedMatcher>
+transformer::detail::buildMatchers(const RewriteRule &Rule) {
+ // Map the cases into buckets of matchers -- one for each "root" AST kind,
+ // which guarantees that they can be combined in a single anyOf matcher. Each
+ // case is paired with an identifying number that is converted to a string id
+ // in `taggedMatchers`.
+ std::map<ASTNodeKind, SmallVector<std::pair<size_t, RewriteRule::Case>, 1>>
+ Buckets;
+ const SmallVectorImpl<RewriteRule::Case> &Cases = Rule.Cases;
+ for (int I = 0, N = Cases.size(); I < N; ++I) {
+ assert(hasValidKind(Cases[I].Matcher) &&
+ "Matcher must be non-(Qual)Type node matcher");
+ Buckets[Cases[I].Matcher.getSupportedKind()].emplace_back(I, Cases[I]);
+ }
+
+ std::vector<DynTypedMatcher> Matchers;
+ for (const auto &Bucket : Buckets) {
+ DynTypedMatcher M = DynTypedMatcher::constructVariadic(
+ DynTypedMatcher::VO_AnyOf, Bucket.first,
+ taggedMatchers("Tag", Bucket.second));
+ M.setAllowBind(true);
+ // `tryBind` is guaranteed to succeed, because `AllowBind` was set to true.
+ Matchers.push_back(*M.tryBind(RewriteRule::RootID));
+ }
+ return Matchers;
+}
+
+DynTypedMatcher transformer::detail::buildMatcher(const RewriteRule &Rule) {
+ std::vector<DynTypedMatcher> Ms = buildMatchers(Rule);
+ assert(Ms.size() == 1 && "Cases must have compatible matchers.");
+ return Ms[0];
+}
+
+SourceLocation transformer::detail::getRuleMatchLoc(const MatchResult &Result) {
+ auto &NodesMap = Result.Nodes.getMap();
+ auto Root = NodesMap.find(RewriteRule::RootID);
+ assert(Root != NodesMap.end() && "Transformation failed: missing root node.");
+ llvm::Optional<CharSourceRange> RootRange = tooling::getRangeForEdit(
+ CharSourceRange::getTokenRange(Root->second.getSourceRange()),
+ *Result.Context);
+ if (RootRange)
+ return RootRange->getBegin();
+ // The match doesn't have a coherent range, so fall back to the expansion
+ // location as the "beginning" of the match.
+ return Result.SourceManager->getExpansionLoc(
+ Root->second.getSourceRange().getBegin());
+}
+
+// Finds the case that was "selected" -- that is, whose matcher triggered the
+// `MatchResult`.
+const RewriteRule::Case &
+transformer::detail::findSelectedCase(const MatchResult &Result,
+ const RewriteRule &Rule) {
+ if (Rule.Cases.size() == 1)
+ return Rule.Cases[0];
+
+ auto &NodesMap = Result.Nodes.getMap();
+ for (size_t i = 0, N = Rule.Cases.size(); i < N; ++i) {
+ std::string Tag = ("Tag" + Twine(i)).str();
+ if (NodesMap.find(Tag) != NodesMap.end())
+ return Rule.Cases[i];
+ }
+ llvm_unreachable("No tag found for this rule.");
+}
+
+constexpr llvm::StringLiteral RewriteRule::RootID;
diff --git a/lib/Tooling/Transformer/SourceCode.cpp b/lib/Tooling/Transformer/SourceCode.cpp
new file mode 100644
index 000000000000..836401d1e605
--- /dev/null
+++ b/lib/Tooling/Transformer/SourceCode.cpp
@@ -0,0 +1,65 @@
+//===--- SourceCode.cpp - Source code manipulation routines -----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file provides functions that simplify extraction of source code.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/Tooling/Transformer/SourceCode.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang;
+
+StringRef clang::tooling::getText(CharSourceRange Range,
+ const ASTContext &Context) {
+ return Lexer::getSourceText(Range, Context.getSourceManager(),
+ Context.getLangOpts());
+}
+
+CharSourceRange clang::tooling::maybeExtendRange(CharSourceRange Range,
+ tok::TokenKind Next,
+ ASTContext &Context) {
+ Optional<Token> Tok = Lexer::findNextToken(
+ Range.getEnd(), Context.getSourceManager(), Context.getLangOpts());
+ if (!Tok || !Tok->is(Next))
+ return Range;
+ return CharSourceRange::getTokenRange(Range.getBegin(), Tok->getLocation());
+}
+
+llvm::Optional<CharSourceRange>
+clang::tooling::getRangeForEdit(const CharSourceRange &EditRange,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ // FIXME: makeFileCharRange() has the disadvantage of stripping off "identity"
+ // macros. For example, if we're looking to rewrite the int literal 3 to 6,
+ // and we have the following definition:
+ // #define DO_NOTHING(x) x
+ // then
+ // foo(DO_NOTHING(3))
+ // will be rewritten to
+ // foo(6)
+ // rather than the arguably better
+ // foo(DO_NOTHING(6))
+ // Decide whether the current behavior is desirable and modify if not.
+ CharSourceRange Range = Lexer::makeFileCharRange(EditRange, SM, LangOpts);
+ if (Range.isInvalid())
+ return None;
+
+ if (Range.getBegin().isMacroID() || Range.getEnd().isMacroID())
+ return None;
+ if (SM.isInSystemHeader(Range.getBegin()) ||
+ SM.isInSystemHeader(Range.getEnd()))
+ return None;
+
+ std::pair<FileID, unsigned> BeginInfo = SM.getDecomposedLoc(Range.getBegin());
+ std::pair<FileID, unsigned> EndInfo = SM.getDecomposedLoc(Range.getEnd());
+ if (BeginInfo.first != EndInfo.first ||
+ BeginInfo.second > EndInfo.second)
+ return None;
+
+ return Range;
+}
diff --git a/lib/Tooling/Transformer/SourceCodeBuilders.cpp b/lib/Tooling/Transformer/SourceCodeBuilders.cpp
new file mode 100644
index 000000000000..56ec45e8fd1d
--- /dev/null
+++ b/lib/Tooling/Transformer/SourceCodeBuilders.cpp
@@ -0,0 +1,160 @@
+//===--- SourceCodeBuilder.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Transformer/SourceCodeBuilders.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/Tooling/Transformer/SourceCode.h"
+#include "llvm/ADT/Twine.h"
+#include <string>
+
+using namespace clang;
+using namespace tooling;
+
+const Expr *tooling::reallyIgnoreImplicit(const Expr &E) {
+ const Expr *Expr = E.IgnoreImplicit();
+ if (const auto *CE = dyn_cast<CXXConstructExpr>(Expr)) {
+ if (CE->getNumArgs() > 0 &&
+ CE->getArg(0)->getSourceRange() == Expr->getSourceRange())
+ return CE->getArg(0)->IgnoreImplicit();
+ }
+ return Expr;
+}
+
+bool tooling::mayEverNeedParens(const Expr &E) {
+ const Expr *Expr = reallyIgnoreImplicit(E);
+ // We always want parens around unary, binary, and ternary operators, because
+ // they are lower precedence.
+ if (isa<UnaryOperator>(Expr) || isa<BinaryOperator>(Expr) ||
+ isa<AbstractConditionalOperator>(Expr))
+ return true;
+
+ // We need parens around calls to all overloaded operators except: function
+ // calls, subscripts, and expressions that are already part of an (implicit)
+ // call to operator->. These latter are all in the same precedence level as
+ // dot/arrow and that level is left associative, so they don't need parens
+ // when appearing on the left.
+ if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr))
+ return Op->getOperator() != OO_Call && Op->getOperator() != OO_Subscript &&
+ Op->getOperator() != OO_Arrow;
+
+ return false;
+}
+
+bool tooling::needParensAfterUnaryOperator(const Expr &E) {
+ const Expr *Expr = reallyIgnoreImplicit(E);
+ if (isa<BinaryOperator>(Expr) || isa<AbstractConditionalOperator>(Expr))
+ return true;
+
+ if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr))
+ return Op->getNumArgs() == 2 && Op->getOperator() != OO_PlusPlus &&
+ Op->getOperator() != OO_MinusMinus && Op->getOperator() != OO_Call &&
+ Op->getOperator() != OO_Subscript;
+
+ return false;
+}
+
+llvm::Optional<std::string> tooling::buildParens(const Expr &E,
+ const ASTContext &Context) {
+ StringRef Text = getText(E, Context);
+ if (Text.empty())
+ return llvm::None;
+ if (mayEverNeedParens(E))
+ return ("(" + Text + ")").str();
+ return Text.str();
+}
+
+llvm::Optional<std::string>
+tooling::buildDereference(const Expr &E, const ASTContext &Context) {
+ if (const auto *Op = dyn_cast<UnaryOperator>(&E))
+ if (Op->getOpcode() == UO_AddrOf) {
+ // Strip leading '&'.
+ StringRef Text =
+ getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);
+ if (Text.empty())
+ return llvm::None;
+ return Text.str();
+ }
+
+ StringRef Text = getText(E, Context);
+ if (Text.empty())
+ return llvm::None;
+ // Add leading '*'.
+ if (needParensAfterUnaryOperator(E))
+ return ("*(" + Text + ")").str();
+ return ("*" + Text).str();
+}
+
+llvm::Optional<std::string> tooling::buildAddressOf(const Expr &E,
+ const ASTContext &Context) {
+ if (const auto *Op = dyn_cast<UnaryOperator>(&E))
+ if (Op->getOpcode() == UO_Deref) {
+ // Strip leading '*'.
+ StringRef Text =
+ getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);
+ if (Text.empty())
+ return llvm::None;
+ return Text.str();
+ }
+ // Add leading '&'.
+ StringRef Text = getText(E, Context);
+ if (Text.empty())
+ return llvm::None;
+ if (needParensAfterUnaryOperator(E)) {
+ return ("&(" + Text + ")").str();
+ }
+ return ("&" + Text).str();
+}
+
+llvm::Optional<std::string> tooling::buildDot(const Expr &E,
+ const ASTContext &Context) {
+ if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E))
+ if (Op->getOpcode() == UO_Deref) {
+ // Strip leading '*', add following '->'.
+ const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();
+ StringRef DerefText = getText(*SubExpr, Context);
+ if (DerefText.empty())
+ return llvm::None;
+ if (needParensBeforeDotOrArrow(*SubExpr))
+ return ("(" + DerefText + ")->").str();
+ return (DerefText + "->").str();
+ }
+
+ // Add following '.'.
+ StringRef Text = getText(E, Context);
+ if (Text.empty())
+ return llvm::None;
+ if (needParensBeforeDotOrArrow(E)) {
+ return ("(" + Text + ").").str();
+ }
+ return (Text + ".").str();
+}
+
+llvm::Optional<std::string> tooling::buildArrow(const Expr &E,
+ const ASTContext &Context) {
+ if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E))
+ if (Op->getOpcode() == UO_AddrOf) {
+ // Strip leading '&', add following '.'.
+ const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();
+ StringRef DerefText = getText(*SubExpr, Context);
+ if (DerefText.empty())
+ return llvm::None;
+ if (needParensBeforeDotOrArrow(*SubExpr))
+ return ("(" + DerefText + ").").str();
+ return (DerefText + ".").str();
+ }
+
+ // Add following '->'.
+ StringRef Text = getText(E, Context);
+ if (Text.empty())
+ return llvm::None;
+ if (needParensBeforeDotOrArrow(E))
+ return ("(" + Text + ")->").str();
+ return (Text + "->").str();
+}
diff --git a/lib/Tooling/Transformer/Stencil.cpp b/lib/Tooling/Transformer/Stencil.cpp
new file mode 100644
index 000000000000..984950a54e96
--- /dev/null
+++ b/lib/Tooling/Transformer/Stencil.cpp
@@ -0,0 +1,318 @@
+//===--- Stencil.cpp - Stencil implementation -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Transformer/Stencil.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTTypeTraits.h"
+#include "clang/AST/Expr.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/Transformer/SourceCode.h"
+#include "clang/Tooling/Transformer/SourceCodeBuilders.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/Errc.h"
+#include <atomic>
+#include <memory>
+#include <string>
+
+using namespace clang;
+using namespace transformer;
+
+using ast_matchers::MatchFinder;
+using ast_type_traits::DynTypedNode;
+using llvm::errc;
+using llvm::Error;
+using llvm::Expected;
+using llvm::StringError;
+
+static llvm::Expected<DynTypedNode>
+getNode(const ast_matchers::BoundNodes &Nodes, StringRef Id) {
+ auto &NodesMap = Nodes.getMap();
+ auto It = NodesMap.find(Id);
+ if (It == NodesMap.end())
+ return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument,
+ "Id not bound: " + Id);
+ return It->second;
+}
+
+namespace {
+// An arbitrary fragment of code within a stencil.
+struct RawTextData {
+ explicit RawTextData(std::string T) : Text(std::move(T)) {}
+ std::string Text;
+};
+
+// A debugging operation to dump the AST for a particular (bound) AST node.
+struct DebugPrintNodeData {
+ explicit DebugPrintNodeData(std::string S) : Id(std::move(S)) {}
+ std::string Id;
+};
+
+// Operators that take a single node Id as an argument.
+enum class UnaryNodeOperator {
+ Parens,
+ Deref,
+ Address,
+};
+
+// Generic container for stencil operations with a (single) node-id argument.
+struct UnaryOperationData {
+ UnaryOperationData(UnaryNodeOperator Op, std::string Id)
+ : Op(Op), Id(std::move(Id)) {}
+ UnaryNodeOperator Op;
+ std::string Id;
+};
+
+// The fragment of code corresponding to the selected range.
+struct SelectorData {
+ explicit SelectorData(RangeSelector S) : Selector(std::move(S)) {}
+ RangeSelector Selector;
+};
+
+// A stencil operation to build a member access `e.m` or `e->m`, as appropriate.
+struct AccessData {
+ AccessData(StringRef BaseId, StencilPart Member)
+ : BaseId(BaseId), Member(std::move(Member)) {}
+ std::string BaseId;
+ StencilPart Member;
+};
+
+struct IfBoundData {
+ IfBoundData(StringRef Id, StencilPart TruePart, StencilPart FalsePart)
+ : Id(Id), TruePart(std::move(TruePart)), FalsePart(std::move(FalsePart)) {
+ }
+ std::string Id;
+ StencilPart TruePart;
+ StencilPart FalsePart;
+};
+
+std::string toStringData(const RawTextData &Data) {
+ std::string Result;
+ llvm::raw_string_ostream OS(Result);
+ OS << "\"";
+ OS.write_escaped(Data.Text);
+ OS << "\"";
+ OS.flush();
+ return Result;
+}
+
+std::string toStringData(const DebugPrintNodeData &Data) {
+ return (llvm::Twine("dPrint(\"") + Data.Id + "\")").str();
+}
+
+std::string toStringData(const UnaryOperationData &Data) {
+ StringRef OpName;
+ switch (Data.Op) {
+ case UnaryNodeOperator::Parens:
+ OpName = "expression";
+ break;
+ case UnaryNodeOperator::Deref:
+ OpName = "deref";
+ break;
+ case UnaryNodeOperator::Address:
+ OpName = "addressOf";
+ break;
+ }
+ return (OpName + "(\"" + Data.Id + "\")").str();
+}
+
+std::string toStringData(const SelectorData &) { return "selection(...)"; }
+
+std::string toStringData(const AccessData &Data) {
+ return (llvm::Twine("access(\"") + Data.BaseId + "\", " +
+ Data.Member.toString() + ")")
+ .str();
+}
+
+std::string toStringData(const IfBoundData &Data) {
+ return (llvm::Twine("ifBound(\"") + Data.Id + "\", " +
+ Data.TruePart.toString() + ", " + Data.FalsePart.toString() + ")")
+ .str();
+}
+
+std::string toStringData(const MatchConsumer<std::string> &) {
+ return "run(...)";
+}
+
+// The `evalData()` overloads evaluate the given stencil data to a string, given
+// the match result, and append it to `Result`. We define an overload for each
+// type of stencil data.
+
+Error evalData(const RawTextData &Data, const MatchFinder::MatchResult &,
+ std::string *Result) {
+ Result->append(Data.Text);
+ return Error::success();
+}
+
+Error evalData(const DebugPrintNodeData &Data,
+ const MatchFinder::MatchResult &Match, std::string *Result) {
+ std::string Output;
+ llvm::raw_string_ostream Os(Output);
+ auto NodeOrErr = getNode(Match.Nodes, Data.Id);
+ if (auto Err = NodeOrErr.takeError())
+ return Err;
+ NodeOrErr->print(Os, PrintingPolicy(Match.Context->getLangOpts()));
+ *Result += Os.str();
+ return Error::success();
+}
+
+Error evalData(const UnaryOperationData &Data,
+ const MatchFinder::MatchResult &Match, std::string *Result) {
+ const auto *E = Match.Nodes.getNodeAs<Expr>(Data.Id);
+ if (E == nullptr)
+ return llvm::make_error<StringError>(
+ errc::invalid_argument, "Id not bound or not Expr: " + Data.Id);
+ llvm::Optional<std::string> Source;
+ switch (Data.Op) {
+ case UnaryNodeOperator::Parens:
+ Source = tooling::buildParens(*E, *Match.Context);
+ break;
+ case UnaryNodeOperator::Deref:
+ Source = tooling::buildDereference(*E, *Match.Context);
+ break;
+ case UnaryNodeOperator::Address:
+ Source = tooling::buildAddressOf(*E, *Match.Context);
+ break;
+ }
+ if (!Source)
+ return llvm::make_error<StringError>(
+ errc::invalid_argument,
+ "Could not construct expression source from ID: " + Data.Id);
+ *Result += *Source;
+ return Error::success();
+}
+
+Error evalData(const SelectorData &Data, const MatchFinder::MatchResult &Match,
+ std::string *Result) {
+ auto Range = Data.Selector(Match);
+ if (!Range)
+ return Range.takeError();
+ *Result += tooling::getText(*Range, *Match.Context);
+ return Error::success();
+}
+
+Error evalData(const AccessData &Data, const MatchFinder::MatchResult &Match,
+ std::string *Result) {
+ const auto *E = Match.Nodes.getNodeAs<Expr>(Data.BaseId);
+ if (E == nullptr)
+ return llvm::make_error<StringError>(errc::invalid_argument,
+ "Id not bound: " + Data.BaseId);
+ if (!E->isImplicitCXXThis()) {
+ if (llvm::Optional<std::string> S =
+ E->getType()->isAnyPointerType()
+ ? tooling::buildArrow(*E, *Match.Context)
+ : tooling::buildDot(*E, *Match.Context))
+ *Result += *S;
+ else
+ return llvm::make_error<StringError>(
+ errc::invalid_argument,
+ "Could not construct object text from ID: " + Data.BaseId);
+ }
+ return Data.Member.eval(Match, Result);
+}
+
+Error evalData(const IfBoundData &Data, const MatchFinder::MatchResult &Match,
+ std::string *Result) {
+ auto &M = Match.Nodes.getMap();
+ return (M.find(Data.Id) != M.end() ? Data.TruePart : Data.FalsePart)
+ .eval(Match, Result);
+}
+
+Error evalData(const MatchConsumer<std::string> &Fn,
+ const MatchFinder::MatchResult &Match, std::string *Result) {
+ Expected<std::string> Value = Fn(Match);
+ if (!Value)
+ return Value.takeError();
+ *Result += *Value;
+ return Error::success();
+}
+
+template <typename T>
+class StencilPartImpl : public StencilPartInterface {
+ T Data;
+
+public:
+ template <typename... Ps>
+ explicit StencilPartImpl(Ps &&... Args) : Data(std::forward<Ps>(Args)...) {}
+
+ Error eval(const MatchFinder::MatchResult &Match,
+ std::string *Result) const override {
+ return evalData(Data, Match, Result);
+ }
+
+ std::string toString() const override { return toStringData(Data); }
+};
+} // namespace
+
+StencilPart Stencil::wrap(StringRef Text) {
+ return transformer::text(Text);
+}
+
+StencilPart Stencil::wrap(RangeSelector Selector) {
+ return transformer::selection(std::move(Selector));
+}
+
+void Stencil::append(Stencil OtherStencil) {
+ for (auto &Part : OtherStencil.Parts)
+ Parts.push_back(std::move(Part));
+}
+
+llvm::Expected<std::string>
+Stencil::eval(const MatchFinder::MatchResult &Match) const {
+ std::string Result;
+ for (const auto &Part : Parts)
+ if (auto Err = Part.eval(Match, &Result))
+ return std::move(Err);
+ return Result;
+}
+
+StencilPart transformer::text(StringRef Text) {
+ return StencilPart(std::make_shared<StencilPartImpl<RawTextData>>(Text));
+}
+
+StencilPart transformer::selection(RangeSelector Selector) {
+ return StencilPart(
+ std::make_shared<StencilPartImpl<SelectorData>>(std::move(Selector)));
+}
+
+StencilPart transformer::dPrint(StringRef Id) {
+ return StencilPart(std::make_shared<StencilPartImpl<DebugPrintNodeData>>(Id));
+}
+
+StencilPart transformer::expression(llvm::StringRef Id) {
+ return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>(
+ UnaryNodeOperator::Parens, Id));
+}
+
+StencilPart transformer::deref(llvm::StringRef ExprId) {
+ return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>(
+ UnaryNodeOperator::Deref, ExprId));
+}
+
+StencilPart transformer::addressOf(llvm::StringRef ExprId) {
+ return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>(
+ UnaryNodeOperator::Address, ExprId));
+}
+
+StencilPart transformer::access(StringRef BaseId, StencilPart Member) {
+ return StencilPart(
+ std::make_shared<StencilPartImpl<AccessData>>(BaseId, std::move(Member)));
+}
+
+StencilPart transformer::ifBound(StringRef Id, StencilPart TruePart,
+ StencilPart FalsePart) {
+ return StencilPart(std::make_shared<StencilPartImpl<IfBoundData>>(
+ Id, std::move(TruePart), std::move(FalsePart)));
+}
+
+StencilPart transformer::run(MatchConsumer<std::string> Fn) {
+ return StencilPart(
+ std::make_shared<StencilPartImpl<MatchConsumer<std::string>>>(
+ std::move(Fn)));
+}
diff --git a/lib/Tooling/Transformer/Transformer.cpp b/lib/Tooling/Transformer/Transformer.cpp
new file mode 100644
index 000000000000..71f0646f4c0e
--- /dev/null
+++ b/lib/Tooling/Transformer/Transformer.cpp
@@ -0,0 +1,72 @@
+//===--- Transformer.cpp - Transformer library implementation ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Transformer/Transformer.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchersInternal.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Tooling/Refactoring/AtomicChange.h"
+#include "llvm/Support/Error.h"
+#include <utility>
+#include <vector>
+
+using namespace clang;
+using namespace tooling;
+
+using ast_matchers::MatchFinder;
+
+void Transformer::registerMatchers(MatchFinder *MatchFinder) {
+ for (auto &Matcher : transformer::detail::buildMatchers(Rule))
+ MatchFinder->addDynamicMatcher(Matcher, this);
+}
+
+void Transformer::run(const MatchFinder::MatchResult &Result) {
+ if (Result.Context->getDiagnostics().hasErrorOccurred())
+ return;
+
+ transformer::RewriteRule::Case Case =
+ transformer::detail::findSelectedCase(Result, Rule);
+ auto Transformations = transformer::detail::translateEdits(Result, Case.Edits);
+ if (!Transformations) {
+ Consumer(Transformations.takeError());
+ return;
+ }
+
+ if (Transformations->empty()) {
+ // No rewrite applied (but no error encountered either).
+ transformer::detail::getRuleMatchLoc(Result).print(
+ llvm::errs() << "note: skipping match at loc ", *Result.SourceManager);
+ llvm::errs() << "\n";
+ return;
+ }
+
+ // Record the results in the AtomicChange, anchored at the location of the
+ // first change.
+ AtomicChange AC(*Result.SourceManager,
+ (*Transformations)[0].Range.getBegin());
+ for (const auto &T : *Transformations) {
+ if (auto Err = AC.replace(*Result.SourceManager, T.Range, T.Replacement)) {
+ Consumer(std::move(Err));
+ return;
+ }
+ }
+
+ for (const auto &I : Case.AddedIncludes) {
+ auto &Header = I.first;
+ switch (I.second) {
+ case transformer::IncludeFormat::Quoted:
+ AC.addHeader(Header);
+ break;
+ case transformer::IncludeFormat::Angled:
+ AC.addHeader((llvm::Twine("<") + Header + ">").str());
+ break;
+ }
+ }
+
+ Consumer(std::move(AC));
+}