aboutsummaryrefslogtreecommitdiffstats
path: root/lib/Tooling
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2019-08-20 20:50:49 +0000
committerDimitry Andric <dim@FreeBSD.org>2019-08-20 20:50:49 +0000
commit2298981669bf3bd63335a4be179bc0f96823a8f4 (patch)
tree1cbe2eb27f030d2d70b80ee5ca3c86bee7326a9f /lib/Tooling
parent9a83721404652cea39e9f02ae3e3b5c964602a5c (diff)
downloadsrc-2298981669bf3bd63335a4be179bc0f96823a8f4.tar.gz
src-2298981669bf3bd63335a4be179bc0f96823a8f4.zip
Vendor import of stripped clang trunk r366426 (just before thevendor/clang/clang-trunk-r366426
Notes
Notes: svn path=/vendor/clang/dist/; revision=351280 svn path=/vendor/clang/clang-trunk-r366426/; revision=351281; tag=vendor/clang/clang-trunk-r366426
Diffstat (limited to 'lib/Tooling')
-rw-r--r--lib/Tooling/ASTDiff/ASTDiff.cpp11
-rw-r--r--lib/Tooling/AllTUsExecution.cpp28
-rw-r--r--lib/Tooling/ArgumentsAdjusters.cpp35
-rw-r--r--lib/Tooling/CommonOptionsParser.cpp11
-rw-r--r--lib/Tooling/CompilationDatabase.cpp7
-rw-r--r--lib/Tooling/Core/Diagnostic.cpp22
-rw-r--r--lib/Tooling/Core/Lookup.cpp92
-rw-r--r--lib/Tooling/Core/Replacement.cpp18
-rw-r--r--lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp149
-rw-r--r--lib/Tooling/Execution.cpp7
-rw-r--r--lib/Tooling/FileMatchTrie.cpp7
-rw-r--r--lib/Tooling/FixIt.cpp14
-rw-r--r--lib/Tooling/GuessTargetAndModeCompilationDatabase.cpp57
-rw-r--r--lib/Tooling/Inclusions/HeaderIncludes.cpp69
-rw-r--r--lib/Tooling/Inclusions/IncludeStyle.cpp7
-rw-r--r--lib/Tooling/InterpolatingCompilationDatabase.cpp28
-rw-r--r--lib/Tooling/JSONCompilationDatabase.cpp69
-rw-r--r--lib/Tooling/Refactoring.cpp7
-rw-r--r--lib/Tooling/Refactoring/ASTSelection.cpp7
-rw-r--r--lib/Tooling/Refactoring/ASTSelectionRequirements.cpp7
-rw-r--r--lib/Tooling/Refactoring/AtomicChange.cpp7
-rw-r--r--lib/Tooling/Refactoring/Extract/Extract.cpp7
-rw-r--r--lib/Tooling/Refactoring/Extract/SourceExtraction.cpp7
-rw-r--r--lib/Tooling/Refactoring/Extract/SourceExtraction.h7
-rw-r--r--lib/Tooling/Refactoring/RangeSelector.cpp296
-rw-r--r--lib/Tooling/Refactoring/RefactoringActions.cpp7
-rw-r--r--lib/Tooling/Refactoring/Rename/RenamingAction.cpp9
-rw-r--r--lib/Tooling/Refactoring/Rename/SymbolOccurrences.cpp7
-rw-r--r--lib/Tooling/Refactoring/Rename/USRFinder.cpp7
-rw-r--r--lib/Tooling/Refactoring/Rename/USRFindingAction.cpp7
-rw-r--r--lib/Tooling/Refactoring/Rename/USRLocFinder.cpp11
-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.cpp7
-rw-r--r--lib/Tooling/StandaloneExecution.cpp7
-rw-r--r--lib/Tooling/Syntax/BuildTree.cpp273
-rw-r--r--lib/Tooling/Syntax/Nodes.cpp35
-rw-r--r--lib/Tooling/Syntax/Tokens.cpp618
-rw-r--r--lib/Tooling/Syntax/Tree.cpp149
-rw-r--r--lib/Tooling/Tooling.cpp18
41 files changed, 2380 insertions, 220 deletions
diff --git a/lib/Tooling/ASTDiff/ASTDiff.cpp b/lib/Tooling/ASTDiff/ASTDiff.cpp
index 592e8572c770..69eff20bff7a 100644
--- a/lib/Tooling/ASTDiff/ASTDiff.cpp
+++ b/lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -1,9 +1,8 @@
//===- ASTDiff.cpp - AST differencing implementation-----------*- C++ -*- -===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -238,8 +237,8 @@ struct PreorderVisitor : public RecursiveASTVisitor<PreorderVisitor> {
return true;
}
bool TraverseStmt(Stmt *S) {
- if (S)
- S = S->IgnoreImplicit();
+ if (auto *E = dyn_cast_or_null<Expr>(S))
+ S = E->IgnoreImplicit();
if (isNodeExcluded(Tree.AST.getSourceManager(), S))
return true;
auto SavedState = PreTraverse(S);
diff --git a/lib/Tooling/AllTUsExecution.cpp b/lib/Tooling/AllTUsExecution.cpp
index 0f172b782963..ca9db7a561be 100644
--- a/lib/Tooling/AllTUsExecution.cpp
+++ b/lib/Tooling/AllTUsExecution.cpp
@@ -1,15 +1,15 @@
//===- lib/Tooling/AllTUsExecution.cpp - Execute actions on all TUs. ------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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/AllTUsExecution.h"
#include "clang/Tooling/ToolExecutorPluginRegistry.h"
#include "llvm/Support/ThreadPool.h"
+#include "llvm/Support/VirtualFileSystem.h"
namespace clang {
namespace tooling {
@@ -115,25 +115,22 @@ llvm::Error AllTUsToolExecutor::execute(
{
llvm::ThreadPool Pool(ThreadCount == 0 ? llvm::hardware_concurrency()
: ThreadCount);
- llvm::SmallString<128> InitialWorkingDir;
- if (auto EC = llvm::sys::fs::current_path(InitialWorkingDir)) {
- InitialWorkingDir = "";
- llvm::errs() << "Error while getting current working directory: "
- << EC.message() << "\n";
- }
for (std::string File : Files) {
Pool.async(
[&](std::string Path) {
Log("[" + std::to_string(Count()) + "/" + TotalNumStr +
"] Processing file " + Path);
- ClangTool Tool(Compilations, {Path});
+ // Each thread gets an indepent copy of a VFS to allow different
+ // concurrent working directories.
+ IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
+ llvm::vfs::createPhysicalFileSystem().release();
+ ClangTool Tool(Compilations, {Path},
+ std::make_shared<PCHContainerOperations>(), FS);
Tool.appendArgumentsAdjuster(Action.second);
Tool.appendArgumentsAdjuster(getDefaultArgumentsAdjusters());
for (const auto &FileAndContent : OverlayFiles)
Tool.mapVirtualFile(FileAndContent.first(),
FileAndContent.second);
- // Do not restore working dir from multiple threads to avoid races.
- Tool.setRestoreWorkingDir(false);
if (Tool.run(Action.first.get()))
AppendError(llvm::Twine("Failed to run action on ") + Path +
"\n");
@@ -142,11 +139,6 @@ llvm::Error AllTUsToolExecutor::execute(
}
// Make sure all tasks have finished before resetting the working directory.
Pool.wait();
- if (!InitialWorkingDir.empty()) {
- if (auto EC = llvm::sys::fs::set_current_path(InitialWorkingDir))
- llvm::errs() << "Error while restoring working directory: "
- << EC.message() << "\n";
- }
}
if (!ErrorMsg.empty())
diff --git a/lib/Tooling/ArgumentsAdjusters.cpp b/lib/Tooling/ArgumentsAdjusters.cpp
index c8e9c167422e..942b35df453e 100644
--- a/lib/Tooling/ArgumentsAdjusters.cpp
+++ b/lib/Tooling/ArgumentsAdjusters.cpp
@@ -1,9 +1,8 @@
//===- ArgumentsAdjusters.cpp - Command line arguments adjuster -----------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -24,14 +23,18 @@ namespace tooling {
ArgumentsAdjuster getClangSyntaxOnlyAdjuster() {
return [](const CommandLineArguments &Args, StringRef /*unused*/) {
CommandLineArguments AdjustedArgs;
+ bool HasSyntaxOnly = false;
for (size_t i = 0, e = Args.size(); i < e; ++i) {
StringRef Arg = Args[i];
// FIXME: Remove options that generate output.
if (!Arg.startswith("-fcolor-diagnostics") &&
!Arg.startswith("-fdiagnostics-color"))
AdjustedArgs.push_back(Args[i]);
+ if (Arg == "-fsyntax-only")
+ HasSyntaxOnly = true;
}
- AdjustedArgs.push_back("-fsyntax-only");
+ if (!HasSyntaxOnly)
+ AdjustedArgs.push_back("-fsyntax-only");
return AdjustedArgs;
};
}
@@ -108,5 +111,27 @@ ArgumentsAdjuster combineAdjusters(ArgumentsAdjuster First,
};
}
+ArgumentsAdjuster getStripPluginsAdjuster() {
+ return [](const CommandLineArguments &Args, StringRef /*unused*/) {
+ CommandLineArguments AdjustedArgs;
+ for (size_t I = 0, E = Args.size(); I != E; I++) {
+ // According to https://clang.llvm.org/docs/ClangPlugins.html
+ // plugin arguments are in the form:
+ // -Xclang {-load, -plugin, -plugin-arg-<plugin-name>, -add-plugin}
+ // -Xclang <arbitrary-argument>
+ if (I + 4 < E && Args[I] == "-Xclang" &&
+ (Args[I + 1] == "-load" || Args[I + 1] == "-plugin" ||
+ llvm::StringRef(Args[I + 1]).startswith("-plugin-arg-") ||
+ Args[I + 1] == "-add-plugin") &&
+ Args[I + 2] == "-Xclang") {
+ I += 3;
+ continue;
+ }
+ AdjustedArgs.push_back(Args[I]);
+ }
+ return AdjustedArgs;
+ };
+}
+
} // end namespace tooling
} // end namespace clang
diff --git a/lib/Tooling/CommonOptionsParser.cpp b/lib/Tooling/CommonOptionsParser.cpp
index 74ad4e83ee3f..f7956f7998f5 100644
--- a/lib/Tooling/CommonOptionsParser.cpp
+++ b/lib/Tooling/CommonOptionsParser.cpp
@@ -1,9 +1,8 @@
//===--- CommonOptionsParser.cpp - common options for clang tools ---------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -40,7 +39,7 @@ const char *const CommonOptionsParser::HelpMessage =
"\tCMake option to get this output). When no build path is specified,\n"
"\ta search for compile_commands.json will be attempted through all\n"
"\tparent paths of the first input file . See:\n"
- "\thttp://clang.llvm.org/docs/HowToSetupToolingForLLVM.html for an\n"
+ "\thttps://clang.llvm.org/docs/HowToSetupToolingForLLVM.html for an\n"
"\texample of setting up Clang Tooling on a source tree.\n"
"\n"
"<source0> ... specify the paths of source files. These paths are\n"
@@ -84,8 +83,6 @@ std::vector<CompileCommand> ArgumentsAdjustingCompilations::adjustCommands(
llvm::Error CommonOptionsParser::init(
int &argc, const char **argv, cl::OptionCategory &Category,
llvm::cl::NumOccurrencesFlag OccurrencesFlag, const char *Overview) {
- static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden,
- cl::sub(*cl::AllSubCommands));
static cl::opt<std::string> BuildPath("p", cl::desc("Build path"),
cl::Optional, cl::cat(Category),
diff --git a/lib/Tooling/CompilationDatabase.cpp b/lib/Tooling/CompilationDatabase.cpp
index cce8e1f1df24..4c64750bef19 100644
--- a/lib/Tooling/CompilationDatabase.cpp
+++ b/lib/Tooling/CompilationDatabase.cpp
@@ -1,9 +1,8 @@
//===- CompilationDatabase.cpp --------------------------------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
diff --git a/lib/Tooling/Core/Diagnostic.cpp b/lib/Tooling/Core/Diagnostic.cpp
index e3a33d9a3755..235bd7fc1433 100644
--- a/lib/Tooling/Core/Diagnostic.cpp
+++ b/lib/Tooling/Core/Diagnostic.cpp
@@ -1,9 +1,8 @@
//===--- Diagnostic.cpp - Framework for clang diagnostics tools ----------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -13,6 +12,7 @@
#include "clang/Tooling/Core/Diagnostic.h"
#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/STLExtras.h"
namespace clang {
namespace tooling {
@@ -41,11 +41,21 @@ Diagnostic::Diagnostic(llvm::StringRef DiagnosticName,
Diagnostic::Diagnostic(llvm::StringRef DiagnosticName,
const DiagnosticMessage &Message,
- const llvm::StringMap<Replacements> &Fix,
const SmallVector<DiagnosticMessage, 1> &Notes,
Level DiagLevel, llvm::StringRef BuildDirectory)
- : DiagnosticName(DiagnosticName), Message(Message), Fix(Fix), Notes(Notes),
+ : DiagnosticName(DiagnosticName), Message(Message), Notes(Notes),
DiagLevel(DiagLevel), BuildDirectory(BuildDirectory) {}
+const llvm::StringMap<Replacements> *selectFirstFix(const Diagnostic& D) {
+ if (!D.Message.Fix.empty())
+ return &D.Message.Fix;
+ auto Iter = llvm::find_if(D.Notes, [](const tooling::DiagnosticMessage &D) {
+ return !D.Fix.empty();
+ });
+ if (Iter != D.Notes.end())
+ return &Iter->Fix;
+ return nullptr;
+}
+
} // end namespace tooling
} // end namespace clang
diff --git a/lib/Tooling/Core/Lookup.cpp b/lib/Tooling/Core/Lookup.cpp
index cc448d144e2c..735a5df5ed21 100644
--- a/lib/Tooling/Core/Lookup.cpp
+++ b/lib/Tooling/Core/Lookup.cpp
@@ -1,9 +1,8 @@
//===--- Lookup.cpp - Framework for clang refactoring tools ---------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -15,6 +14,8 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclarationName.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/SmallVector.h"
using namespace clang;
using namespace clang::tooling;
@@ -115,38 +116,72 @@ static bool isFullyQualified(const NestedNameSpecifier *NNS) {
return false;
}
-// Returns true if spelling symbol \p QName as \p Spelling in \p UseContext is
-// ambiguous. For example, if QName is "::y::bar" and the spelling is "y::bar"
-// in `UseContext` "a" that contains a nested namespace "a::y", then "y::bar"
-// can be resolved to ::a::y::bar, which can cause compile error.
+// Adds more scope specifier to the spelled name until the spelling is not
+// ambiguous. A spelling is ambiguous if the resolution of the symbol is
+// ambiguous. For example, if QName is "::y::bar", the spelling is "y::bar", and
+// context contains a nested namespace "a::y", then "y::bar" can be resolved to
+// ::a::y::bar in the context, which can cause compile error.
// FIXME: consider using namespaces.
-static bool isAmbiguousNameInScope(StringRef Spelling, StringRef QName,
- const DeclContext &UseContext) {
+static std::string disambiguateSpellingInScope(StringRef Spelling,
+ StringRef QName,
+ const DeclContext &UseContext,
+ SourceLocation UseLoc) {
assert(QName.startswith("::"));
+ assert(QName.endswith(Spelling));
if (Spelling.startswith("::"))
- return false;
+ return Spelling;
- // Lookup the first component of Spelling in all enclosing namespaces and
- // check if there is any existing symbols with the same name but in different
- // scope.
- StringRef Head = Spelling.split("::").first;
+ auto UnspelledSpecifier = QName.drop_back(Spelling.size());
+ llvm::SmallVector<llvm::StringRef, 2> UnspelledScopes;
+ UnspelledSpecifier.split(UnspelledScopes, "::", /*MaxSplit=*/-1,
+ /*KeepEmpty=*/false);
- llvm::SmallVector<const NamespaceDecl *, 4> UseNamespaces =
+ llvm::SmallVector<const NamespaceDecl *, 4> EnclosingNamespaces =
getAllNamedNamespaces(&UseContext);
auto &AST = UseContext.getParentASTContext();
StringRef TrimmedQName = QName.substr(2);
- for (const auto *NS : UseNamespaces) {
- auto LookupRes = NS->lookup(DeclarationName(&AST.Idents.get(Head)));
- if (!LookupRes.empty()) {
- for (const NamedDecl *Res : LookupRes)
- if (!TrimmedQName.startswith(Res->getQualifiedNameAsString()))
- return true;
+ const auto &SM = UseContext.getParentASTContext().getSourceManager();
+ UseLoc = SM.getSpellingLoc(UseLoc);
+
+ auto IsAmbiguousSpelling = [&](const llvm::StringRef CurSpelling) {
+ if (CurSpelling.startswith("::"))
+ return false;
+ // Lookup the first component of Spelling in all enclosing namespaces
+ // and check if there is any existing symbols with the same name but in
+ // different scope.
+ StringRef Head = CurSpelling.split("::").first;
+ for (const auto *NS : EnclosingNamespaces) {
+ auto LookupRes = NS->lookup(DeclarationName(&AST.Idents.get(Head)));
+ if (!LookupRes.empty()) {
+ for (const NamedDecl *Res : LookupRes)
+ // If `Res` is not visible in `UseLoc`, we don't consider it
+ // ambiguous. For example, a reference in a header file should not be
+ // affected by a potentially ambiguous name in some file that includes
+ // the header.
+ if (!TrimmedQName.startswith(Res->getQualifiedNameAsString()) &&
+ SM.isBeforeInTranslationUnit(
+ SM.getSpellingLoc(Res->getLocation()), UseLoc))
+ return true;
+ }
+ }
+ return false;
+ };
+
+ // Add more qualifiers until the spelling is not ambiguous.
+ std::string Disambiguated = Spelling;
+ while (IsAmbiguousSpelling(Disambiguated)) {
+ if (UnspelledScopes.empty()) {
+ Disambiguated = "::" + Disambiguated;
+ } else {
+ Disambiguated = (UnspelledScopes.back() + "::" + Disambiguated).str();
+ UnspelledScopes.pop_back();
}
}
- return false;
+ return Disambiguated;
}
std::string tooling::replaceNestedName(const NestedNameSpecifier *Use,
+ SourceLocation UseLoc,
const DeclContext *UseContext,
const NamedDecl *FromDecl,
StringRef ReplacementString) {
@@ -180,12 +215,7 @@ std::string tooling::replaceNestedName(const NestedNameSpecifier *Use,
// specific).
StringRef Suggested = getBestNamespaceSubstr(UseContext, ReplacementString,
isFullyQualified(Use));
- // Use the fully qualified name if the suggested name is ambiguous.
- // FIXME: consider re-shortening the name until the name is not ambiguous. We
- // are not doing this because ambiguity is pretty bad and we should not try to
- // be clever in handling such cases. Making this noticeable to users seems to
- // be a better option.
- return isAmbiguousNameInScope(Suggested, ReplacementString, *UseContext)
- ? ReplacementString
- : Suggested;
+
+ return disambiguateSpellingInScope(Suggested, ReplacementString, *UseContext,
+ UseLoc);
}
diff --git a/lib/Tooling/Core/Replacement.cpp b/lib/Tooling/Core/Replacement.cpp
index 3b7e39814afa..546158714e3c 100644
--- a/lib/Tooling/Core/Replacement.cpp
+++ b/lib/Tooling/Core/Replacement.cpp
@@ -1,9 +1,8 @@
//===- Replacement.cpp - Framework for clang refactoring tools ------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -520,12 +519,11 @@ calculateRangesAfterReplacements(const Replacements &Replaces,
return MergedRanges;
tooling::Replacements FakeReplaces;
for (const auto &R : MergedRanges) {
- auto Err = FakeReplaces.add(Replacement(Replaces.begin()->getFilePath(),
- R.getOffset(), R.getLength(),
- std::string(R.getLength(), ' ')));
- assert(!Err &&
- "Replacements must not conflict since ranges have been merged.");
- llvm::consumeError(std::move(Err));
+ llvm::cantFail(
+ FakeReplaces.add(Replacement(Replaces.begin()->getFilePath(),
+ R.getOffset(), R.getLength(),
+ std::string(R.getLength(), ' '))),
+ "Replacements must not conflict since ranges have been merged.");
}
return FakeReplaces.merge(Replaces).getAffectedRanges();
}
diff --git a/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
new file mode 100644
index 000000000000..4868f2663796
--- /dev/null
+++ b/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
@@ -0,0 +1,149 @@
+//===- DependencyScanningWorker.cpp - clang-scan-deps worker --------------===//
+//
+// 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/DependencyScanningWorker.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Frontend/Utils.h"
+#include "clang/Tooling/Tooling.h"
+
+using namespace clang;
+using namespace tooling;
+using namespace dependencies;
+
+namespace {
+
+/// Prints out all of the gathered dependencies into a string.
+class DependencyPrinter : public DependencyFileGenerator {
+public:
+ DependencyPrinter(std::unique_ptr<DependencyOutputOptions> Opts,
+ std::string &S)
+ : DependencyFileGenerator(*Opts), Opts(std::move(Opts)), S(S) {}
+
+ void finishedMainFile(DiagnosticsEngine &Diags) override {
+ llvm::raw_string_ostream OS(S);
+ outputDependencyFile(OS);
+ }
+
+private:
+ std::unique_ptr<DependencyOutputOptions> Opts;
+ std::string &S;
+};
+
+/// A proxy file system that doesn't call `chdir` when changing the working
+/// directory of a clang tool.
+class ProxyFileSystemWithoutChdir : public llvm::vfs::ProxyFileSystem {
+public:
+ ProxyFileSystemWithoutChdir(
+ llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
+ : ProxyFileSystem(std::move(FS)) {}
+
+ llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
+ assert(!CWD.empty() && "empty CWD");
+ return CWD;
+ }
+
+ std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
+ CWD = Path.str();
+ return {};
+ }
+
+private:
+ std::string CWD;
+};
+
+/// A clang tool that runs the preprocessor in a mode that's optimized for
+/// dependency scanning for the given compiler invocation.
+class DependencyScanningAction : public tooling::ToolAction {
+public:
+ DependencyScanningAction(StringRef WorkingDirectory,
+ std::string &DependencyFileContents)
+ : WorkingDirectory(WorkingDirectory),
+ DependencyFileContents(DependencyFileContents) {}
+
+ bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
+ FileManager *FileMgr,
+ std::shared_ptr<PCHContainerOperations> PCHContainerOps,
+ DiagnosticConsumer *DiagConsumer) override {
+ // 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;
+ // Create the compiler's actual diagnostics engine.
+ Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
+ if (!Compiler.hasDiagnostics())
+ return false;
+
+ Compiler.createSourceManager(*FileMgr);
+
+ // Create the dependency collector that will collect the produced
+ // dependencies.
+ //
+ // This also moves the existing dependency output options from the
+ // 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>(
+ 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));
+
+ auto Action = llvm::make_unique<PreprocessOnlyAction>();
+ const bool Result = Compiler.ExecuteAction(*Action);
+ FileMgr->clearStatCache();
+ return Result;
+ }
+
+private:
+ StringRef WorkingDirectory;
+ /// The dependency file will be written to this string.
+ std::string &DependencyFileContents;
+};
+
+} // end anonymous namespace
+
+DependencyScanningWorker::DependencyScanningWorker() {
+ 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());
+}
+
+llvm::Expected<std::string>
+DependencyScanningWorker::getDependencyFile(const std::string &Input,
+ StringRef WorkingDirectory,
+ const CompilationDatabase &CDB) {
+ // 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;
+}
diff --git a/lib/Tooling/Execution.cpp b/lib/Tooling/Execution.cpp
index 9ddb18a57b46..c39a4fcdac82 100644
--- a/lib/Tooling/Execution.cpp
+++ b/lib/Tooling/Execution.cpp
@@ -1,9 +1,8 @@
//===- lib/Tooling/Execution.cpp - Implements tool execution framework. ---===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/Tooling/FileMatchTrie.cpp b/lib/Tooling/FileMatchTrie.cpp
index 202b3f00f3fb..7df5a16fd88f 100644
--- a/lib/Tooling/FileMatchTrie.cpp
+++ b/lib/Tooling/FileMatchTrie.cpp
@@ -1,9 +1,8 @@
//===- FileMatchTrie.cpp --------------------------------------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
diff --git a/lib/Tooling/FixIt.cpp b/lib/Tooling/FixIt.cpp
index 70942c5ac845..76c92c543736 100644
--- a/lib/Tooling/FixIt.cpp
+++ b/lib/Tooling/FixIt.cpp
@@ -1,9 +1,8 @@
//===--- FixIt.cpp - FixIt Hint utilities -----------------------*- C++ -*-===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -19,12 +18,11 @@ namespace tooling {
namespace fixit {
namespace internal {
-StringRef getText(SourceRange Range, const ASTContext &Context) {
- return Lexer::getSourceText(CharSourceRange::getTokenRange(Range),
- Context.getSourceManager(),
+StringRef getText(CharSourceRange Range, const ASTContext &Context) {
+ return Lexer::getSourceText(Range, Context.getSourceManager(),
Context.getLangOpts());
}
-} // end namespace internal
+} // namespace internal
} // end namespace fixit
} // end namespace tooling
diff --git a/lib/Tooling/GuessTargetAndModeCompilationDatabase.cpp b/lib/Tooling/GuessTargetAndModeCompilationDatabase.cpp
new file mode 100644
index 000000000000..ac3faf1b01f9
--- /dev/null
+++ b/lib/Tooling/GuessTargetAndModeCompilationDatabase.cpp
@@ -0,0 +1,57 @@
+//===- GuessTargetAndModeCompilationDatabase.cpp --------------------------===//
+//
+// 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/CompilationDatabase.h"
+#include "clang/Tooling/Tooling.h"
+#include <memory>
+
+namespace clang {
+namespace tooling {
+
+namespace {
+class TargetAndModeAdderDatabase : public CompilationDatabase {
+public:
+ TargetAndModeAdderDatabase(std::unique_ptr<CompilationDatabase> Base)
+ : Base(std::move(Base)) {
+ assert(this->Base != nullptr);
+ }
+
+ std::vector<std::string> getAllFiles() const override {
+ return Base->getAllFiles();
+ }
+
+ std::vector<CompileCommand> getAllCompileCommands() const override {
+ return addTargetAndMode(Base->getAllCompileCommands());
+ }
+
+ std::vector<CompileCommand>
+ getCompileCommands(StringRef FilePath) const override {
+ return addTargetAndMode(Base->getCompileCommands(FilePath));
+ }
+
+private:
+ std::vector<CompileCommand>
+ addTargetAndMode(std::vector<CompileCommand> Cmds) const {
+ for (auto &Cmd : Cmds) {
+ if (Cmd.CommandLine.empty())
+ continue;
+ addTargetAndModeForProgramName(Cmd.CommandLine, Cmd.CommandLine.front());
+ }
+ return Cmds;
+ }
+ std::unique_ptr<CompilationDatabase> Base;
+};
+} // namespace
+
+std::unique_ptr<CompilationDatabase>
+inferTargetAndDriverMode(std::unique_ptr<CompilationDatabase> Base) {
+ return llvm::make_unique<TargetAndModeAdderDatabase>(std::move(Base));
+}
+
+} // namespace tooling
+} // namespace clang
diff --git a/lib/Tooling/Inclusions/HeaderIncludes.cpp b/lib/Tooling/Inclusions/HeaderIncludes.cpp
index c74ad0b9cd56..a7f79c33bc55 100644
--- a/lib/Tooling/Inclusions/HeaderIncludes.cpp
+++ b/lib/Tooling/Inclusions/HeaderIncludes.cpp
@@ -1,15 +1,15 @@
//===--- HeaderIncludes.cpp - Insert/Delete #includes --*- C++ -*----------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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/Inclusions/HeaderIncludes.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/Optional.h"
#include "llvm/Support/FormatVariadic.h"
namespace clang {
@@ -51,12 +51,16 @@ unsigned getOffsetAfterTokenSequence(
// Check if a sequence of tokens is like "#<Name> <raw_identifier>". If it is,
// \p Tok will be the token after this directive; otherwise, it can be any token
-// after the given \p Tok (including \p Tok).
-bool checkAndConsumeDirectiveWithName(Lexer &Lex, StringRef Name, Token &Tok) {
+// after the given \p Tok (including \p Tok). If \p RawIDName is provided, the
+// (second) raw_identifier name is checked.
+bool checkAndConsumeDirectiveWithName(
+ Lexer &Lex, StringRef Name, Token &Tok,
+ llvm::Optional<StringRef> RawIDName = llvm::None) {
bool Matched = Tok.is(tok::hash) && !Lex.LexFromRawLexer(Tok) &&
Tok.is(tok::raw_identifier) &&
Tok.getRawIdentifier() == Name && !Lex.LexFromRawLexer(Tok) &&
- Tok.is(tok::raw_identifier);
+ Tok.is(tok::raw_identifier) &&
+ (!RawIDName || Tok.getRawIdentifier() == *RawIDName);
if (Matched)
Lex.LexFromRawLexer(Tok);
return Matched;
@@ -69,24 +73,45 @@ void skipComments(Lexer &Lex, Token &Tok) {
}
// Returns the offset after header guard directives and any comments
-// before/after header guards. If no header guard presents in the code, this
-// will returns the offset after skipping all comments from the start of the
-// code.
+// before/after header guards (e.g. #ifndef/#define pair, #pragma once). If no
+// header guard is present in the code, this will return the offset after
+// skipping all comments from the start of the code.
unsigned getOffsetAfterHeaderGuardsAndComments(StringRef FileName,
StringRef Code,
const IncludeStyle &Style) {
- return getOffsetAfterTokenSequence(
- FileName, Code, Style,
- [](const SourceManager &SM, Lexer &Lex, Token Tok) {
- skipComments(Lex, Tok);
- unsigned InitialOffset = SM.getFileOffset(Tok.getLocation());
- if (checkAndConsumeDirectiveWithName(Lex, "ifndef", Tok)) {
- skipComments(Lex, Tok);
- if (checkAndConsumeDirectiveWithName(Lex, "define", Tok))
- return SM.getFileOffset(Tok.getLocation());
- }
- return InitialOffset;
- });
+ // \p Consume returns location after header guard or 0 if no header guard is
+ // found.
+ auto ConsumeHeaderGuardAndComment =
+ [&](std::function<unsigned(const SourceManager &SM, Lexer &Lex,
+ Token Tok)>
+ Consume) {
+ return getOffsetAfterTokenSequence(
+ FileName, Code, Style,
+ [&Consume](const SourceManager &SM, Lexer &Lex, Token Tok) {
+ skipComments(Lex, Tok);
+ unsigned InitialOffset = SM.getFileOffset(Tok.getLocation());
+ return std::max(InitialOffset, Consume(SM, Lex, Tok));
+ });
+ };
+ return std::max(
+ // #ifndef/#define
+ ConsumeHeaderGuardAndComment(
+ [](const SourceManager &SM, Lexer &Lex, Token Tok) -> unsigned {
+ if (checkAndConsumeDirectiveWithName(Lex, "ifndef", Tok)) {
+ skipComments(Lex, Tok);
+ if (checkAndConsumeDirectiveWithName(Lex, "define", Tok))
+ return SM.getFileOffset(Tok.getLocation());
+ }
+ return 0;
+ }),
+ // #pragma once
+ ConsumeHeaderGuardAndComment(
+ [](const SourceManager &SM, Lexer &Lex, Token Tok) -> unsigned {
+ if (checkAndConsumeDirectiveWithName(Lex, "pragma", Tok,
+ StringRef("once")))
+ return SM.getFileOffset(Tok.getLocation());
+ return 0;
+ }));
}
// Check if a sequence of tokens is like
diff --git a/lib/Tooling/Inclusions/IncludeStyle.cpp b/lib/Tooling/Inclusions/IncludeStyle.cpp
index 3597710f1f6e..c53c82c83630 100644
--- a/lib/Tooling/Inclusions/IncludeStyle.cpp
+++ b/lib/Tooling/Inclusions/IncludeStyle.cpp
@@ -1,9 +1,8 @@
//===--- IncludeStyle.cpp - Style of C++ #include directives -----*- C++-*-===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/Tooling/InterpolatingCompilationDatabase.cpp b/lib/Tooling/InterpolatingCompilationDatabase.cpp
index 4d0d84f660a2..53c8dd448fd9 100644
--- a/lib/Tooling/InterpolatingCompilationDatabase.cpp
+++ b/lib/Tooling/InterpolatingCompilationDatabase.cpp
@@ -1,9 +1,8 @@
//===- InterpolatingCompilationDatabase.cpp ---------------------*- C++ -*-===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -151,7 +150,8 @@ struct TransferableCommand {
// 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();
- Cmd.CommandLine.emplace_back(OldArgs.front());
+ if (!OldArgs.empty())
+ Cmd.CommandLine.emplace_back(OldArgs.front());
for (unsigned Pos = 1; Pos < OldArgs.size();) {
using namespace driver::options;
@@ -206,10 +206,13 @@ struct TransferableCommand {
bool TypeCertain;
auto TargetType = guessType(Filename, &TypeCertain);
// If the filename doesn't determine the language (.h), transfer with -x.
- if (TargetType != types::TY_INVALID && !TypeCertain && Type) {
- TargetType = types::onlyPrecompileType(TargetType) // header?
- ? types::lookupHeaderTypeForSourceType(*Type)
- : *Type;
+ if ((!TargetType || !TypeCertain) && Type) {
+ // Use *Type, or its header variant if the file is a header.
+ // Treat no/invalid extension as header (e.g. C++ standard library).
+ TargetType =
+ (!TargetType || types::onlyPrecompileType(TargetType)) // header?
+ ? types::lookupHeaderTypeForSourceType(*Type)
+ : *Type;
if (ClangCLMode) {
const StringRef Flag = toCLFlag(TargetType);
if (!Flag.empty())
@@ -227,6 +230,7 @@ struct TransferableCommand {
LangStandard::getLangStandardForKind(Std).getName()).str());
}
Result.CommandLine.push_back(Filename);
+ Result.Heuristic = "inferred from " + Cmd.Filename;
return Result;
}
@@ -240,7 +244,8 @@ private:
}
// Otherwise just check the clang executable file name.
- return llvm::sys::path::stem(CmdLine.front()).endswith_lower("cl");
+ return !CmdLine.empty() &&
+ llvm::sys::path::stem(CmdLine.front()).endswith_lower("cl");
}
// Map the language from the --std flag to that of the -x flag.
@@ -473,8 +478,7 @@ private:
ArrayRef<SubstringAndIndex> Idx) const {
assert(!Idx.empty());
// Longest substring match will be adjacent to a direct lookup.
- auto It =
- std::lower_bound(Idx.begin(), Idx.end(), SubstringAndIndex{Key, 0});
+ auto It = llvm::lower_bound(Idx, SubstringAndIndex{Key, 0});
if (It == Idx.begin())
return *It;
if (It == Idx.end())
diff --git a/lib/Tooling/JSONCompilationDatabase.cpp b/lib/Tooling/JSONCompilationDatabase.cpp
index b0feaa229c11..f19a0f7550b9 100644
--- a/lib/Tooling/JSONCompilationDatabase.cpp
+++ b/lib/Tooling/JSONCompilationDatabase.cpp
@@ -1,9 +1,8 @@
//===- JSONCompilationDatabase.cpp ----------------------------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -15,7 +14,9 @@
#include "clang/Basic/LLVM.h"
#include "clang/Tooling/CompilationDatabase.h"
#include "clang/Tooling/CompilationDatabasePluginRegistry.h"
+#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
@@ -166,7 +167,9 @@ class JSONCompilationDatabasePlugin : public CompilationDatabasePlugin {
llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
auto Base = JSONCompilationDatabase::loadFromFile(
JSONDatabasePath, ErrorMessage, JSONCommandLineSyntax::AutoDetect);
- return Base ? inferMissingCompileCommands(std::move(Base)) : nullptr;
+ return Base ? inferTargetAndDriverMode(
+ inferMissingCompileCommands(std::move(Base)))
+ : nullptr;
}
};
@@ -191,8 +194,11 @@ std::unique_ptr<JSONCompilationDatabase>
JSONCompilationDatabase::loadFromFile(StringRef FilePath,
std::string &ErrorMessage,
JSONCommandLineSyntax Syntax) {
+ // Don't mmap: if we're a long-lived process, the build system may overwrite.
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> DatabaseBuffer =
- llvm::MemoryBuffer::getFile(FilePath);
+ llvm::MemoryBuffer::getFile(FilePath, /*FileSize=*/-1,
+ /*RequiresNullTerminator=*/true,
+ /*IsVolatile=*/true);
if (std::error_code Result = DatabaseBuffer.getError()) {
ErrorMessage = "Error while opening JSON database: " + Result.message();
return nullptr;
@@ -250,15 +256,57 @@ JSONCompilationDatabase::getAllCompileCommands() const {
return Commands;
}
+static llvm::StringRef stripExecutableExtension(llvm::StringRef Name) {
+ Name.consume_back(".exe");
+ return Name;
+}
+
+// There are compiler-wrappers (ccache, distcc, gomacc) that take the "real"
+// compiler as an argument, e.g. distcc gcc -O3 foo.c.
+// These end up in compile_commands.json when people set CC="distcc gcc".
+// Clang's driver doesn't understand this, so we need to unwrap.
+static bool unwrapCommand(std::vector<std::string> &Args) {
+ if (Args.size() < 2)
+ return false;
+ StringRef Wrapper =
+ stripExecutableExtension(llvm::sys::path::filename(Args.front()));
+ if (Wrapper == "distcc" || Wrapper == "gomacc" || Wrapper == "ccache") {
+ // Most of these wrappers support being invoked 3 ways:
+ // `distcc g++ file.c` This is the mode we're trying to match.
+ // We need to drop `distcc`.
+ // `distcc file.c` This acts like compiler is cc or similar.
+ // Clang's driver can handle this, no change needed.
+ // `g++ file.c` g++ is a symlink to distcc.
+ // We don't even notice this case, and all is well.
+ //
+ // We need to distinguish between the first and second case.
+ // The wrappers themselves don't take flags, so Args[1] is a compiler flag,
+ // an input file, or a compiler. Inputs have extensions, compilers don't.
+ bool HasCompiler =
+ (Args[1][0] != '-') &&
+ !llvm::sys::path::has_extension(stripExecutableExtension(Args[1]));
+ if (HasCompiler) {
+ Args.erase(Args.begin());
+ return true;
+ }
+ // If !HasCompiler, wrappers act like GCC. Fine: so do we.
+ }
+ return false;
+}
+
static std::vector<std::string>
nodeToCommandLine(JSONCommandLineSyntax Syntax,
const std::vector<llvm::yaml::ScalarNode *> &Nodes) {
SmallString<1024> Storage;
- if (Nodes.size() == 1)
- return unescapeCommandLine(Syntax, Nodes[0]->getValue(Storage));
std::vector<std::string> Arguments;
- for (const auto *Node : Nodes)
- Arguments.push_back(Node->getValue(Storage));
+ if (Nodes.size() == 1)
+ Arguments = unescapeCommandLine(Syntax, Nodes[0]->getValue(Storage));
+ else
+ for (const auto *Node : Nodes)
+ Arguments.push_back(Node->getValue(Storage));
+ // There may be multiple wrappers: using distcc and ccache together is common.
+ while (unwrapCommand(Arguments))
+ ;
return Arguments;
}
@@ -371,6 +419,7 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
SmallString<128> AbsolutePath(
Directory->getValue(DirectoryStorage));
llvm::sys::path::append(AbsolutePath, FileName);
+ llvm::sys::path::remove_dots(AbsolutePath, /*remove_dot_dot=*/ true);
llvm::sys::path::native(AbsolutePath, NativeFilePath);
} else {
llvm::sys::path::native(FileName, NativeFilePath);
diff --git a/lib/Tooling/Refactoring.cpp b/lib/Tooling/Refactoring.cpp
index db34c952d794..f379a9c3bcf6 100644
--- a/lib/Tooling/Refactoring.cpp
+++ b/lib/Tooling/Refactoring.cpp
@@ -1,9 +1,8 @@
//===--- Refactoring.cpp - Framework for clang refactoring tools ----------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
diff --git a/lib/Tooling/Refactoring/ASTSelection.cpp b/lib/Tooling/Refactoring/ASTSelection.cpp
index b8f996d8218c..64e57af59011 100644
--- a/lib/Tooling/Refactoring/ASTSelection.cpp
+++ b/lib/Tooling/Refactoring/ASTSelection.cpp
@@ -1,9 +1,8 @@
//===--- ASTSelection.cpp - Clang refactoring library ---------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/Tooling/Refactoring/ASTSelectionRequirements.cpp b/lib/Tooling/Refactoring/ASTSelectionRequirements.cpp
index c0232c5da442..14fc66a979ae 100644
--- a/lib/Tooling/Refactoring/ASTSelectionRequirements.cpp
+++ b/lib/Tooling/Refactoring/ASTSelectionRequirements.cpp
@@ -1,9 +1,8 @@
//===--- ASTSelectionRequirements.cpp - Clang refactoring library ---------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/Tooling/Refactoring/AtomicChange.cpp b/lib/Tooling/Refactoring/AtomicChange.cpp
index e8b0fdbeb662..4cf63306d262 100644
--- a/lib/Tooling/Refactoring/AtomicChange.cpp
+++ b/lib/Tooling/Refactoring/AtomicChange.cpp
@@ -1,9 +1,8 @@
//===--- AtomicChange.cpp - AtomicChange implementation -----------------*- C++ -*-===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/Tooling/Refactoring/Extract/Extract.cpp b/lib/Tooling/Refactoring/Extract/Extract.cpp
index 7a741bdb2e91..f5b94a462103 100644
--- a/lib/Tooling/Refactoring/Extract/Extract.cpp
+++ b/lib/Tooling/Refactoring/Extract/Extract.cpp
@@ -1,9 +1,8 @@
//===--- Extract.cpp - Clang refactoring library --------------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
///
diff --git a/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp b/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp
index 7fd8cc2d3c7f..533c373e35c4 100644
--- a/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp
+++ b/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp
@@ -1,9 +1,8 @@
//===--- SourceExtraction.cpp - Clang refactoring library -----------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/Tooling/Refactoring/Extract/SourceExtraction.h b/lib/Tooling/Refactoring/Extract/SourceExtraction.h
index 4b4bd8b477ff..545eb6c1a11c 100644
--- a/lib/Tooling/Refactoring/Extract/SourceExtraction.h
+++ b/lib/Tooling/Refactoring/Extract/SourceExtraction.h
@@ -1,9 +1,8 @@
//===--- SourceExtraction.cpp - Clang refactoring library -----------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/Tooling/Refactoring/RangeSelector.cpp b/lib/Tooling/Refactoring/RangeSelector.cpp
new file mode 100644
index 000000000000..768c02e2277b
--- /dev/null
+++ b/lib/Tooling/Refactoring/RangeSelector.cpp
@@ -0,0 +1,296 @@
+//===--- RangeSelector.cpp - RangeSelector implementations ------*- 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/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 "llvm/ADT/StringRef.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include <string>
+#include <utility>
+#include <vector>
+
+using namespace clang;
+using namespace tooling;
+
+using ast_matchers::MatchFinder;
+using ast_type_traits::ASTNodeKind;
+using ast_type_traits::DynTypedNode;
+using llvm::Error;
+using llvm::StringError;
+
+using MatchResult = MatchFinder::MatchResult;
+
+static Error invalidArgumentError(Twine Message) {
+ return llvm::make_error<StringError>(llvm::errc::invalid_argument, Message);
+}
+
+static Error typeError(StringRef ID, const ASTNodeKind &Kind) {
+ return invalidArgumentError("mismatched type (node id=" + ID +
+ " kind=" + Kind.asStringRef() + ")");
+}
+
+static Error typeError(StringRef ID, const ASTNodeKind &Kind,
+ Twine ExpectedType) {
+ return invalidArgumentError("mismatched type: expected one of " +
+ ExpectedType + " (node id=" + ID +
+ " kind=" + Kind.asStringRef() + ")");
+}
+
+static Error missingPropertyError(StringRef ID, Twine Description,
+ StringRef Property) {
+ return invalidArgumentError(Description + " requires property '" + Property +
+ "' (node id=" + ID + ")");
+}
+
+static Expected<DynTypedNode> getNode(const ast_matchers::BoundNodes &Nodes,
+ StringRef ID) {
+ auto &NodesMap = Nodes.getMap();
+ auto It = NodesMap.find(ID);
+ if (It == NodesMap.end())
+ return invalidArgumentError("ID not bound: " + ID);
+ return It->second;
+}
+
+// FIXME: handling of macros should be configurable.
+static SourceLocation findPreviousTokenStart(SourceLocation Start,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ if (Start.isInvalid() || Start.isMacroID())
+ return SourceLocation();
+
+ SourceLocation BeforeStart = Start.getLocWithOffset(-1);
+ if (BeforeStart.isInvalid() || BeforeStart.isMacroID())
+ return SourceLocation();
+
+ return Lexer::GetBeginningOfToken(BeforeStart, SM, LangOpts);
+}
+
+// Finds the start location of the previous token of kind \p TK.
+// FIXME: handling of macros should be configurable.
+static SourceLocation findPreviousTokenKind(SourceLocation Start,
+ const SourceManager &SM,
+ const LangOptions &LangOpts,
+ tok::TokenKind TK) {
+ while (true) {
+ SourceLocation L = findPreviousTokenStart(Start, SM, LangOpts);
+ if (L.isInvalid() || L.isMacroID())
+ return SourceLocation();
+
+ Token T;
+ if (Lexer::getRawToken(L, T, SM, LangOpts, /*IgnoreWhiteSpace=*/true))
+ return SourceLocation();
+
+ if (T.is(TK))
+ return T.getLocation();
+
+ Start = L;
+ }
+}
+
+static SourceLocation findOpenParen(const CallExpr &E, const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ SourceLocation EndLoc =
+ E.getNumArgs() == 0 ? E.getRParenLoc() : E.getArg(0)->getBeginLoc();
+ return findPreviousTokenKind(EndLoc, SM, LangOpts, tok::TokenKind::l_paren);
+}
+
+RangeSelector tooling::before(RangeSelector Selector) {
+ return [Selector](const MatchResult &Result) -> Expected<CharSourceRange> {
+ Expected<CharSourceRange> SelectedRange = Selector(Result);
+ if (!SelectedRange)
+ return SelectedRange.takeError();
+ return CharSourceRange::getCharRange(SelectedRange->getBegin());
+ };
+}
+
+RangeSelector tooling::after(RangeSelector Selector) {
+ return [Selector](const MatchResult &Result) -> Expected<CharSourceRange> {
+ Expected<CharSourceRange> SelectedRange = Selector(Result);
+ if (!SelectedRange)
+ return SelectedRange.takeError();
+ if (SelectedRange->isCharRange())
+ return CharSourceRange::getCharRange(SelectedRange->getEnd());
+ return CharSourceRange::getCharRange(Lexer::getLocForEndOfToken(
+ SelectedRange->getEnd(), 0, Result.Context->getSourceManager(),
+ Result.Context->getLangOpts()));
+ };
+}
+
+RangeSelector tooling::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)
+ : CharSourceRange::getTokenRange(Node->getSourceRange());
+ };
+}
+
+RangeSelector tooling::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);
+ };
+}
+
+RangeSelector tooling::range(RangeSelector Begin, RangeSelector End) {
+ return [Begin, End](const MatchResult &Result) -> Expected<CharSourceRange> {
+ Expected<CharSourceRange> BeginRange = Begin(Result);
+ if (!BeginRange)
+ return BeginRange.takeError();
+ Expected<CharSourceRange> EndRange = End(Result);
+ if (!EndRange)
+ return EndRange.takeError();
+ SourceLocation B = BeginRange->getBegin();
+ SourceLocation E = EndRange->getEnd();
+ // Note: we are precluding the possibility of sub-token ranges in the case
+ // that EndRange is a token range.
+ if (Result.SourceManager->isBeforeInTranslationUnit(E, B)) {
+ return invalidArgumentError("Bad range: out of order");
+ }
+ return CharSourceRange(SourceRange(B, E), EndRange->isTokenRange());
+ };
+}
+
+RangeSelector tooling::range(std::string BeginID, std::string EndID) {
+ return tooling::range(node(std::move(BeginID)), node(std::move(EndID)));
+}
+
+RangeSelector tooling::member(std::string ID) {
+ return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {
+ Expected<DynTypedNode> Node = getNode(Result.Nodes, ID);
+ if (!Node)
+ return Node.takeError();
+ if (auto *M = Node->get<clang::MemberExpr>())
+ return CharSourceRange::getTokenRange(
+ M->getMemberNameInfo().getSourceRange());
+ return typeError(ID, Node->getNodeKind(), "MemberExpr");
+ };
+}
+
+RangeSelector tooling::name(std::string ID) {
+ return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {
+ Expected<DynTypedNode> N = getNode(Result.Nodes, ID);
+ if (!N)
+ return N.takeError();
+ auto &Node = *N;
+ if (const auto *D = Node.get<NamedDecl>()) {
+ if (!D->getDeclName().isIdentifier())
+ return missingPropertyError(ID, "name", "identifier");
+ SourceLocation L = D->getLocation();
+ auto R = CharSourceRange::getTokenRange(L, L);
+ // Verify that the range covers exactly the name.
+ // FIXME: extend this code to support cases like `operator +` or
+ // `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())
+ return CharSourceRange();
+ return R;
+ }
+ if (const auto *E = Node.get<DeclRefExpr>()) {
+ if (!E->getNameInfo().getName().isIdentifier())
+ return missingPropertyError(ID, "name", "identifier");
+ SourceLocation L = E->getLocation();
+ return CharSourceRange::getTokenRange(L, L);
+ }
+ if (const auto *I = Node.get<CXXCtorInitializer>()) {
+ if (!I->isMemberInitializer() && I->isWritten())
+ return missingPropertyError(ID, "name", "explicit member initializer");
+ SourceLocation L = I->getMemberLocation();
+ return CharSourceRange::getTokenRange(L, L);
+ }
+ return typeError(ID, Node.getNodeKind(),
+ "DeclRefExpr, NamedDecl, CXXCtorInitializer");
+ };
+}
+
+namespace {
+// 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.
+template <typename T, CharSourceRange (*Func)(const MatchResult &, const T &)>
+class RelativeSelector {
+ std::string ID;
+
+public:
+ RelativeSelector(std::string ID) : ID(std::move(ID)) {}
+
+ Expected<CharSourceRange> operator()(const MatchResult &Result) {
+ Expected<DynTypedNode> N = getNode(Result.Nodes, ID);
+ if (!N)
+ return N.takeError();
+ if (const auto *Arg = N->get<T>())
+ return Func(Result, *Arg);
+ return typeError(ID, N->getNodeKind());
+ }
+};
+} // namespace
+
+// FIXME: Change the following functions from being in an anonymous namespace
+// to static functions, after the minimum Visual C++ has _MSC_VER >= 1915
+// (equivalent to Visual Studio 2017 v15.8 or higher). Using the anonymous
+// namespace works around a bug in earlier versions.
+namespace {
+// Returns the range of the statements (all source between the braces).
+CharSourceRange getStatementsRange(const MatchResult &,
+ const CompoundStmt &CS) {
+ return CharSourceRange::getCharRange(CS.getLBracLoc().getLocWithOffset(1),
+ CS.getRBracLoc());
+}
+} // namespace
+
+RangeSelector tooling::statements(std::string ID) {
+ return RelativeSelector<CompoundStmt, getStatementsRange>(std::move(ID));
+}
+
+namespace {
+// Returns the range of the source between the call's parentheses.
+CharSourceRange getCallArgumentsRange(const MatchResult &Result,
+ const CallExpr &CE) {
+ return CharSourceRange::getCharRange(
+ findOpenParen(CE, *Result.SourceManager, Result.Context->getLangOpts())
+ .getLocWithOffset(1),
+ CE.getRParenLoc());
+}
+} // namespace
+
+RangeSelector tooling::callArgs(std::string ID) {
+ return RelativeSelector<CallExpr, getCallArgumentsRange>(std::move(ID));
+}
+
+namespace {
+// Returns the range of the elements of the initializer list. Includes all
+// source between the braces.
+CharSourceRange getElementsRange(const MatchResult &,
+ const InitListExpr &E) {
+ return CharSourceRange::getCharRange(E.getLBraceLoc().getLocWithOffset(1),
+ E.getRBraceLoc());
+}
+} // namespace
+
+RangeSelector tooling::initListElements(std::string ID) {
+ return RelativeSelector<InitListExpr, getElementsRange>(std::move(ID));
+}
+
+RangeSelector tooling::expansion(RangeSelector S) {
+ return [S](const MatchResult &Result) -> Expected<CharSourceRange> {
+ Expected<CharSourceRange> SRange = S(Result);
+ if (!SRange)
+ return SRange.takeError();
+ return Result.SourceManager->getExpansionRange(*SRange);
+ };
+}
diff --git a/lib/Tooling/Refactoring/RefactoringActions.cpp b/lib/Tooling/Refactoring/RefactoringActions.cpp
index 37a1639cb446..1a3833243ab4 100644
--- a/lib/Tooling/Refactoring/RefactoringActions.cpp
+++ b/lib/Tooling/Refactoring/RefactoringActions.cpp
@@ -1,9 +1,8 @@
//===--- RefactoringActions.cpp - Constructs refactoring actions ----------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/Tooling/Refactoring/Rename/RenamingAction.cpp b/lib/Tooling/Refactoring/Rename/RenamingAction.cpp
index 44ffae90efa7..1649513a077a 100644
--- a/lib/Tooling/Refactoring/Rename/RenamingAction.cpp
+++ b/lib/Tooling/Refactoring/Rename/RenamingAction.cpp
@@ -1,9 +1,8 @@
//===--- RenamingAction.cpp - Clang refactoring library -------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
///
@@ -75,6 +74,8 @@ RenameOccurrences::initiate(RefactoringRuleContext &Context,
std::move(NewName));
}
+const NamedDecl *RenameOccurrences::getRenameDecl() const { return ND; }
+
Expected<AtomicChanges>
RenameOccurrences::createSourceReplacements(RefactoringRuleContext &Context) {
Expected<SymbolOccurrences> Occurrences = findSymbolOccurrences(ND, Context);
diff --git a/lib/Tooling/Refactoring/Rename/SymbolOccurrences.cpp b/lib/Tooling/Refactoring/Rename/SymbolOccurrences.cpp
index ea64b2c1aa8c..8cc1ffaf4482 100644
--- a/lib/Tooling/Refactoring/Rename/SymbolOccurrences.cpp
+++ b/lib/Tooling/Refactoring/Rename/SymbolOccurrences.cpp
@@ -1,9 +1,8 @@
//===--- SymbolOccurrences.cpp - Clang refactoring library ----------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/Tooling/Refactoring/Rename/USRFinder.cpp b/lib/Tooling/Refactoring/Rename/USRFinder.cpp
index 4ed805fd504c..55111202ac88 100644
--- a/lib/Tooling/Refactoring/Rename/USRFinder.cpp
+++ b/lib/Tooling/Refactoring/Rename/USRFinder.cpp
@@ -1,9 +1,8 @@
//===--- USRFinder.cpp - Clang refactoring library ------------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
///
diff --git a/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp b/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp
index 2e7c9b0cc31b..54c6f3e734b1 100644
--- a/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp
+++ b/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp
@@ -1,9 +1,8 @@
//===--- USRFindingAction.cpp - Clang refactoring library -----------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
///
diff --git a/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp b/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp
index 7f60cf54c8ec..408e184f5bf5 100644
--- a/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp
+++ b/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp
@@ -1,9 +1,8 @@
//===--- USRLocFinder.cpp - Clang refactoring library ---------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
///
@@ -543,8 +542,8 @@ createRenameAtomicChanges(llvm::ArrayRef<std::string> USRs,
if (!llvm::isa<clang::TranslationUnitDecl>(
RenameInfo.Context->getDeclContext())) {
ReplacedName = tooling::replaceNestedName(
- RenameInfo.Specifier, RenameInfo.Context->getDeclContext(),
- RenameInfo.FromDecl,
+ RenameInfo.Specifier, RenameInfo.Begin,
+ RenameInfo.Context->getDeclContext(), RenameInfo.FromDecl,
NewName.startswith("::") ? NewName.str()
: ("::" + NewName).str());
} else {
diff --git a/lib/Tooling/Refactoring/SourceCode.cpp b/lib/Tooling/Refactoring/SourceCode.cpp
new file mode 100644
index 000000000000..3a97e178bbd4
--- /dev/null
+++ b/lib/Tooling/Refactoring/SourceCode.cpp
@@ -0,0 +1,31 @@
+//===--- 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
new file mode 100644
index 000000000000..09eca21c3cef
--- /dev/null
+++ b/lib/Tooling/Refactoring/Stencil.cpp
@@ -0,0 +1,175 @@
+//===--- 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
new file mode 100644
index 000000000000..8e6fe6c7a940
--- /dev/null
+++ b/lib/Tooling/Refactoring/Transformer.cpp
@@ -0,0 +1,263 @@
+//===--- 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 9fd333ca554e..2aa5b5bf9d98 100644
--- a/lib/Tooling/RefactoringCallbacks.cpp
+++ b/lib/Tooling/RefactoringCallbacks.cpp
@@ -1,9 +1,8 @@
//===--- RefactoringCallbacks.cpp - Structural query framework ------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
diff --git a/lib/Tooling/StandaloneExecution.cpp b/lib/Tooling/StandaloneExecution.cpp
index 1daf792fb86f..ad82ee083a40 100644
--- a/lib/Tooling/StandaloneExecution.cpp
+++ b/lib/Tooling/StandaloneExecution.cpp
@@ -1,9 +1,8 @@
//===- lib/Tooling/Execution.cpp - Standalone clang action execution. -----===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
diff --git a/lib/Tooling/Syntax/BuildTree.cpp b/lib/Tooling/Syntax/BuildTree.cpp
new file mode 100644
index 000000000000..03c439c59e39
--- /dev/null
+++ b/lib/Tooling/Syntax/BuildTree.cpp
@@ -0,0 +1,273 @@
+//===- BuildTree.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/Syntax/BuildTree.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TokenKinds.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/Syntax/Nodes.h"
+#include "clang/Tooling/Syntax/Tokens.h"
+#include "clang/Tooling/Syntax/Tree.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/raw_ostream.h"
+#include <map>
+
+using namespace clang;
+
+/// A helper class for constructing the syntax tree while traversing a clang
+/// AST.
+///
+/// At each point of the traversal we maintain a list of pending nodes.
+/// Initially all tokens are added as pending nodes. When processing a clang AST
+/// node, the clients need to:
+/// - create a corresponding syntax node,
+/// - assign roles to all pending child nodes with 'markChild' and
+/// 'markChildToken',
+/// - replace the child nodes with the new syntax node in the pending list
+/// with 'foldNode'.
+///
+/// Note that all children are expected to be processed when building a node.
+///
+/// Call finalize() to finish building the tree and consume the root node.
+class syntax::TreeBuilder {
+public:
+ TreeBuilder(syntax::Arena &Arena) : Arena(Arena), Pending(Arena) {}
+
+ llvm::BumpPtrAllocator &allocator() { return Arena.allocator(); }
+
+ /// Populate children for \p New node, assuming it covers tokens from \p
+ /// Range.
+ void foldNode(llvm::ArrayRef<syntax::Token> Range, syntax::Tree *New);
+
+ /// Set role for a token starting at \p Loc.
+ void markChildToken(SourceLocation Loc, tok::TokenKind Kind, NodeRole R);
+
+ /// Finish building the tree and consume the root node.
+ syntax::TranslationUnit *finalize() && {
+ auto Tokens = Arena.tokenBuffer().expandedTokens();
+ // Build the root of the tree, consuming all the children.
+ Pending.foldChildren(Tokens,
+ new (Arena.allocator()) syntax::TranslationUnit);
+
+ return cast<syntax::TranslationUnit>(std::move(Pending).finalize());
+ }
+
+ /// getRange() finds the syntax tokens corresponding to the passed source
+ /// locations.
+ /// \p First is the start position of the first token and \p Last is the start
+ /// position of the last token.
+ llvm::ArrayRef<syntax::Token> getRange(SourceLocation First,
+ SourceLocation Last) const {
+ assert(First.isValid());
+ assert(Last.isValid());
+ assert(First == Last ||
+ Arena.sourceManager().isBeforeInTranslationUnit(First, Last));
+ return llvm::makeArrayRef(findToken(First), std::next(findToken(Last)));
+ }
+ llvm::ArrayRef<syntax::Token> getRange(const Decl *D) const {
+ return getRange(D->getBeginLoc(), D->getEndLoc());
+ }
+ llvm::ArrayRef<syntax::Token> getRange(const Stmt *S) const {
+ return getRange(S->getBeginLoc(), S->getEndLoc());
+ }
+
+private:
+ /// Finds a token starting at \p L. The token must exist.
+ const syntax::Token *findToken(SourceLocation L) const;
+
+ /// A collection of trees covering the input tokens.
+ /// When created, each tree corresponds to a single token in the file.
+ /// Clients call 'foldChildren' to attach one or more subtrees to a parent
+ /// node and update the list of trees accordingly.
+ ///
+ /// 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.
+
+ // Create all leaf nodes.
+ for (auto &T : A.tokenBuffer().expandedTokens())
+ Trees.insert(Trees.end(),
+ {&T, NodeAndRole{new (A.allocator()) syntax::Leaf(&T)}});
+ }
+
+ void assignRole(llvm::ArrayRef<syntax::Token> Range,
+ syntax::NodeRole Role) {
+ assert(!Range.empty());
+ auto It = Trees.lower_bound(Range.begin());
+ assert(It != Trees.end() && "no node found");
+ assert(It->first == Range.begin() && "no child with the specified range");
+ assert((std::next(It) == Trees.end() ||
+ std::next(It)->first == Range.end()) &&
+ "no child with the specified range");
+ It->second.Role = Role;
+ }
+
+ /// Add \p Node to the forest and fill its children nodes based on the \p
+ /// NodeRange.
+ void foldChildren(llvm::ArrayRef<syntax::Token> NodeTokens,
+ syntax::Tree *Node) {
+ assert(!NodeTokens.empty());
+ assert(Node->firstChild() == nullptr && "node already has children");
+
+ auto *FirstToken = NodeTokens.begin();
+ auto BeginChildren = Trees.lower_bound(FirstToken);
+ assert(BeginChildren != Trees.end() &&
+ BeginChildren->first == FirstToken &&
+ "fold crosses boundaries of existing subtrees");
+ auto EndChildren = Trees.lower_bound(NodeTokens.end());
+ assert((EndChildren == Trees.end() ||
+ EndChildren->first == NodeTokens.end()) &&
+ "fold crosses boundaries of existing subtrees");
+
+ // (!) we need to go in reverse order, because we can only prepend.
+ for (auto It = EndChildren; It != BeginChildren; --It)
+ Node->prependChildLowLevel(std::prev(It)->second.Node,
+ std::prev(It)->second.Role);
+
+ Trees.erase(BeginChildren, EndChildren);
+ Trees.insert({FirstToken, NodeAndRole(Node)});
+ }
+
+ // EXPECTS: all tokens were consumed and are owned by a single root node.
+ syntax::Node *finalize() && {
+ assert(Trees.size() == 1);
+ auto *Root = Trees.begin()->second.Node;
+ Trees = {};
+ return Root;
+ }
+
+ std::string str(const syntax::Arena &A) const {
+ std::string R;
+ for (auto It = Trees.begin(); It != Trees.end(); ++It) {
+ unsigned CoveredTokens =
+ It != Trees.end()
+ ? (std::next(It)->first - It->first)
+ : A.tokenBuffer().expandedTokens().end() - It->first;
+
+ R += llvm::formatv("- '{0}' covers '{1}'+{2} tokens\n",
+ It->second.Node->kind(),
+ It->first->text(A.sourceManager()), CoveredTokens);
+ R += It->second.Node->dump(A);
+ }
+ return R;
+ }
+
+ private:
+ /// A with a role that should be assigned to it when adding to a parent.
+ struct NodeAndRole {
+ explicit NodeAndRole(syntax::Node *Node)
+ : Node(Node), Role(NodeRole::Unknown) {}
+
+ syntax::Node *Node;
+ NodeRole Role;
+ };
+
+ /// Maps from the start token to a subtree starting at that token.
+ /// FIXME: storing the end tokens is redundant.
+ /// FIXME: the key of a map is redundant, it is also stored in NodeForRange.
+ std::map<const syntax::Token *, NodeAndRole> Trees;
+ };
+
+ /// For debugging purposes.
+ std::string str() { return Pending.str(Arena); }
+
+ syntax::Arena &Arena;
+ Forest Pending;
+};
+
+namespace {
+class BuildTreeVisitor : public RecursiveASTVisitor<BuildTreeVisitor> {
+public:
+ explicit BuildTreeVisitor(ASTContext &Ctx, syntax::TreeBuilder &Builder)
+ : Builder(Builder), LangOpts(Ctx.getLangOpts()) {}
+
+ bool shouldTraversePostOrder() const { return true; }
+
+ bool TraverseDecl(Decl *D) {
+ if (!D || isa<TranslationUnitDecl>(D))
+ return RecursiveASTVisitor::TraverseDecl(D);
+ if (!llvm::isa<TranslationUnitDecl>(D->getDeclContext()))
+ return true; // Only build top-level decls for now, do not recurse.
+ return RecursiveASTVisitor::TraverseDecl(D);
+ }
+
+ bool VisitDecl(Decl *D) {
+ assert(llvm::isa<TranslationUnitDecl>(D->getDeclContext()) &&
+ "expected a top-level decl");
+ assert(!D->isImplicit());
+ Builder.foldNode(Builder.getRange(D),
+ new (allocator()) syntax::TopLevelDeclaration());
+ return true;
+ }
+
+ bool WalkUpFromTranslationUnitDecl(TranslationUnitDecl *TU) {
+ // (!) we do not want to call VisitDecl(), the declaration for translation
+ // unit is built by finalize().
+ return true;
+ }
+
+ bool WalkUpFromCompoundStmt(CompoundStmt *S) {
+ using NodeRole = syntax::NodeRole;
+
+ Builder.markChildToken(S->getLBracLoc(), tok::l_brace,
+ NodeRole::CompoundStatement_lbrace);
+ Builder.markChildToken(S->getRBracLoc(), tok::r_brace,
+ NodeRole::CompoundStatement_rbrace);
+
+ Builder.foldNode(Builder.getRange(S),
+ new (allocator()) syntax::CompoundStatement);
+ return true;
+ }
+
+private:
+ /// A small helper to save some typing.
+ llvm::BumpPtrAllocator &allocator() { return Builder.allocator(); }
+
+ syntax::TreeBuilder &Builder;
+ const LangOptions &LangOpts;
+};
+} // namespace
+
+void syntax::TreeBuilder::foldNode(llvm::ArrayRef<syntax::Token> Range,
+ syntax::Tree *New) {
+ Pending.foldChildren(Range, New);
+}
+
+void syntax::TreeBuilder::markChildToken(SourceLocation Loc,
+ tok::TokenKind Kind, NodeRole Role) {
+ if (Loc.isInvalid())
+ return;
+ Pending.assignRole(*findToken(Loc), Role);
+}
+
+const syntax::Token *syntax::TreeBuilder::findToken(SourceLocation L) const {
+ auto Tokens = Arena.tokenBuffer().expandedTokens();
+ auto &SM = Arena.sourceManager();
+ auto It = llvm::partition_point(Tokens, [&](const syntax::Token &T) {
+ return SM.isBeforeInTranslationUnit(T.location(), L);
+ });
+ assert(It != Tokens.end());
+ assert(It->location() == L);
+ return &*It;
+}
+
+syntax::TranslationUnit *
+syntax::buildSyntaxTree(Arena &A, const TranslationUnitDecl &TU) {
+ TreeBuilder Builder(A);
+ BuildTreeVisitor(TU.getASTContext(), Builder).TraverseAST(TU.getASTContext());
+ return std::move(Builder).finalize();
+}
diff --git a/lib/Tooling/Syntax/Nodes.cpp b/lib/Tooling/Syntax/Nodes.cpp
new file mode 100644
index 000000000000..061ed73bbebd
--- /dev/null
+++ b/lib/Tooling/Syntax/Nodes.cpp
@@ -0,0 +1,35 @@
+//===- Nodes.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/Syntax/Nodes.h"
+#include "clang/Basic/TokenKinds.h"
+
+using namespace clang;
+
+llvm::raw_ostream &syntax::operator<<(llvm::raw_ostream &OS, NodeKind K) {
+ switch (K) {
+ case NodeKind::Leaf:
+ return OS << "Leaf";
+ case NodeKind::TranslationUnit:
+ return OS << "TranslationUnit";
+ case NodeKind::TopLevelDeclaration:
+ return OS << "TopLevelDeclaration";
+ case NodeKind::CompoundStatement:
+ return OS << "CompoundStatement";
+ }
+ llvm_unreachable("unknown node kind");
+}
+
+syntax::Leaf *syntax::CompoundStatement::lbrace() {
+ return llvm::cast_or_null<syntax::Leaf>(
+ findChild(NodeRole::CompoundStatement_lbrace));
+}
+
+syntax::Leaf *syntax::CompoundStatement::rbrace() {
+ return llvm::cast_or_null<syntax::Leaf>(
+ findChild(NodeRole::CompoundStatement_rbrace));
+}
diff --git a/lib/Tooling/Syntax/Tokens.cpp b/lib/Tooling/Syntax/Tokens.cpp
new file mode 100644
index 000000000000..d82dc1f35c94
--- /dev/null
+++ b/lib/Tooling/Syntax/Tokens.cpp
@@ -0,0 +1,618 @@
+//===- Tokens.cpp - collect tokens from preprocessing ---------------------===//
+//
+// 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/Syntax/Tokens.h"
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TokenKinds.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Lex/Token.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <string>
+#include <utility>
+#include <vector>
+
+using namespace clang;
+using namespace clang::syntax;
+
+syntax::Token::Token(SourceLocation Location, unsigned Length,
+ tok::TokenKind Kind)
+ : Location(Location), Length(Length), Kind(Kind) {
+ assert(Location.isValid());
+}
+
+syntax::Token::Token(const clang::Token &T)
+ : Token(T.getLocation(), T.getLength(), T.getKind()) {
+ assert(!T.isAnnotation());
+}
+
+llvm::StringRef syntax::Token::text(const SourceManager &SM) const {
+ bool Invalid = false;
+ const char *Start = SM.getCharacterData(location(), &Invalid);
+ assert(!Invalid);
+ return llvm::StringRef(Start, length());
+}
+
+FileRange syntax::Token::range(const SourceManager &SM) const {
+ assert(location().isFileID() && "must be a spelled token");
+ FileID File;
+ unsigned StartOffset;
+ std::tie(File, StartOffset) = SM.getDecomposedLoc(location());
+ return FileRange(File, StartOffset, StartOffset + length());
+}
+
+FileRange syntax::Token::range(const SourceManager &SM,
+ const syntax::Token &First,
+ const syntax::Token &Last) {
+ auto F = First.range(SM);
+ auto L = Last.range(SM);
+ assert(F.file() == L.file() && "tokens from different files");
+ assert(F.endOffset() <= L.beginOffset() && "wrong order of tokens");
+ return FileRange(F.file(), F.beginOffset(), L.endOffset());
+}
+
+llvm::raw_ostream &syntax::operator<<(llvm::raw_ostream &OS, const Token &T) {
+ return OS << T.str();
+}
+
+FileRange::FileRange(FileID File, unsigned BeginOffset, unsigned EndOffset)
+ : File(File), Begin(BeginOffset), End(EndOffset) {
+ assert(File.isValid());
+ assert(BeginOffset <= EndOffset);
+}
+
+FileRange::FileRange(const SourceManager &SM, SourceLocation BeginLoc,
+ unsigned Length) {
+ assert(BeginLoc.isValid());
+ assert(BeginLoc.isFileID());
+
+ std::tie(File, Begin) = SM.getDecomposedLoc(BeginLoc);
+ End = Begin + Length;
+}
+FileRange::FileRange(const SourceManager &SM, SourceLocation BeginLoc,
+ SourceLocation EndLoc) {
+ assert(BeginLoc.isValid());
+ assert(BeginLoc.isFileID());
+ assert(EndLoc.isValid());
+ assert(EndLoc.isFileID());
+ assert(SM.getFileID(BeginLoc) == SM.getFileID(EndLoc));
+ assert(SM.getFileOffset(BeginLoc) <= SM.getFileOffset(EndLoc));
+
+ std::tie(File, Begin) = SM.getDecomposedLoc(BeginLoc);
+ End = SM.getFileOffset(EndLoc);
+}
+
+llvm::raw_ostream &syntax::operator<<(llvm::raw_ostream &OS,
+ const FileRange &R) {
+ return OS << llvm::formatv("FileRange(file = {0}, offsets = {1}-{2})",
+ R.file().getHashValue(), R.beginOffset(),
+ R.endOffset());
+}
+
+llvm::StringRef FileRange::text(const SourceManager &SM) const {
+ bool Invalid = false;
+ StringRef Text = SM.getBufferData(File, &Invalid);
+ if (Invalid)
+ return "";
+ assert(Begin <= Text.size());
+ assert(End <= Text.size());
+ return Text.substr(Begin, length());
+}
+
+std::pair<const syntax::Token *, const TokenBuffer::Mapping *>
+TokenBuffer::spelledForExpandedToken(const syntax::Token *Expanded) const {
+ assert(Expanded);
+ assert(ExpandedTokens.data() <= Expanded &&
+ Expanded < ExpandedTokens.data() + ExpandedTokens.size());
+
+ auto FileIt = Files.find(
+ SourceMgr->getFileID(SourceMgr->getExpansionLoc(Expanded->location())));
+ assert(FileIt != Files.end() && "no file for an expanded token");
+
+ const MarkedFile &File = FileIt->second;
+
+ unsigned ExpandedIndex = Expanded - ExpandedTokens.data();
+ // Find the first mapping that produced tokens after \p Expanded.
+ auto It = llvm::partition_point(File.Mappings, [&](const Mapping &M) {
+ return M.BeginExpanded <= ExpandedIndex;
+ });
+ // Our token could only be produced by the previous mapping.
+ if (It == File.Mappings.begin()) {
+ // No previous mapping, no need to modify offsets.
+ return {&File.SpelledTokens[ExpandedIndex - File.BeginExpanded], nullptr};
+ }
+ --It; // 'It' now points to last mapping that started before our token.
+
+ // Check if the token is part of the mapping.
+ if (ExpandedIndex < It->EndExpanded)
+ return {&File.SpelledTokens[It->BeginSpelled], /*Mapping*/ &*It};
+
+ // Not part of the mapping, use the index from previous mapping to compute the
+ // corresponding spelled token.
+ return {
+ &File.SpelledTokens[It->EndSpelled + (ExpandedIndex - It->EndExpanded)],
+ /*Mapping*/ nullptr};
+}
+
+llvm::ArrayRef<syntax::Token> TokenBuffer::spelledTokens(FileID FID) const {
+ auto It = Files.find(FID);
+ assert(It != Files.end());
+ return It->second.SpelledTokens;
+}
+
+std::string TokenBuffer::Mapping::str() const {
+ return llvm::formatv("spelled tokens: [{0},{1}), expanded tokens: [{2},{3})",
+ BeginSpelled, EndSpelled, BeginExpanded, EndExpanded);
+}
+
+llvm::Optional<llvm::ArrayRef<syntax::Token>>
+TokenBuffer::spelledForExpanded(llvm::ArrayRef<syntax::Token> Expanded) const {
+ // Mapping an empty range is ambiguous in case of empty mappings at either end
+ // of the range, bail out in that case.
+ if (Expanded.empty())
+ return llvm::None;
+
+ // FIXME: also allow changes uniquely mapping to macro arguments.
+
+ const syntax::Token *BeginSpelled;
+ const Mapping *BeginMapping;
+ std::tie(BeginSpelled, BeginMapping) =
+ spelledForExpandedToken(&Expanded.front());
+
+ const syntax::Token *LastSpelled;
+ const Mapping *LastMapping;
+ std::tie(LastSpelled, LastMapping) =
+ spelledForExpandedToken(&Expanded.back());
+
+ FileID FID = SourceMgr->getFileID(BeginSpelled->location());
+ // FIXME: Handle multi-file changes by trying to map onto a common root.
+ if (FID != SourceMgr->getFileID(LastSpelled->location()))
+ return llvm::None;
+
+ const MarkedFile &File = Files.find(FID)->second;
+
+ // Do not allow changes that cross macro expansion boundaries.
+ unsigned BeginExpanded = Expanded.begin() - ExpandedTokens.data();
+ unsigned EndExpanded = Expanded.end() - ExpandedTokens.data();
+ if (BeginMapping && BeginMapping->BeginExpanded < BeginExpanded)
+ return llvm::None;
+ if (LastMapping && EndExpanded < LastMapping->EndExpanded)
+ return llvm::None;
+ // All is good, return the result.
+ return llvm::makeArrayRef(
+ BeginMapping ? File.SpelledTokens.data() + BeginMapping->BeginSpelled
+ : BeginSpelled,
+ LastMapping ? File.SpelledTokens.data() + LastMapping->EndSpelled
+ : LastSpelled + 1);
+}
+
+llvm::Optional<TokenBuffer::Expansion>
+TokenBuffer::expansionStartingAt(const syntax::Token *Spelled) const {
+ assert(Spelled);
+ assert(Spelled->location().isFileID() && "not a spelled token");
+ auto FileIt = Files.find(SourceMgr->getFileID(Spelled->location()));
+ assert(FileIt != Files.end() && "file not tracked by token buffer");
+
+ auto &File = FileIt->second;
+ assert(File.SpelledTokens.data() <= Spelled &&
+ Spelled < (File.SpelledTokens.data() + File.SpelledTokens.size()));
+
+ unsigned SpelledIndex = Spelled - File.SpelledTokens.data();
+ auto M = llvm::partition_point(File.Mappings, [&](const Mapping &M) {
+ return M.BeginSpelled < SpelledIndex;
+ });
+ if (M == File.Mappings.end() || M->BeginSpelled != SpelledIndex)
+ return llvm::None;
+
+ Expansion E;
+ E.Spelled = llvm::makeArrayRef(File.SpelledTokens.data() + M->BeginSpelled,
+ File.SpelledTokens.data() + M->EndSpelled);
+ E.Expanded = llvm::makeArrayRef(ExpandedTokens.data() + M->BeginExpanded,
+ ExpandedTokens.data() + M->EndExpanded);
+ return E;
+}
+
+std::vector<syntax::Token> syntax::tokenize(FileID FID, const SourceManager &SM,
+ const LangOptions &LO) {
+ std::vector<syntax::Token> Tokens;
+ IdentifierTable Identifiers(LO);
+ auto AddToken = [&](clang::Token T) {
+ // Fill the proper token kind for keywords, etc.
+ if (T.getKind() == tok::raw_identifier && !T.needsCleaning() &&
+ !T.hasUCN()) { // FIXME: support needsCleaning and hasUCN cases.
+ clang::IdentifierInfo &II = Identifiers.get(T.getRawIdentifier());
+ T.setIdentifierInfo(&II);
+ T.setKind(II.getTokenID());
+ }
+ Tokens.push_back(syntax::Token(T));
+ };
+
+ Lexer L(FID, SM.getBuffer(FID), SM, LO);
+
+ clang::Token T;
+ while (!L.LexFromRawLexer(T))
+ AddToken(T);
+ // 'eof' is only the last token if the input is null-terminated. Never store
+ // it, for consistency.
+ if (T.getKind() != tok::eof)
+ AddToken(T);
+ return Tokens;
+}
+
+/// Records information reqired to construct mappings for the token buffer that
+/// we are collecting.
+class TokenCollector::CollectPPExpansions : public PPCallbacks {
+public:
+ CollectPPExpansions(TokenCollector &C) : Collector(&C) {}
+
+ /// Disabled instance will stop reporting anything to TokenCollector.
+ /// This ensures that uses of the preprocessor after TokenCollector::consume()
+ /// is called do not access the (possibly invalid) collector instance.
+ void disable() { Collector = nullptr; }
+
+ void MacroExpands(const clang::Token &MacroNameTok, const MacroDefinition &MD,
+ SourceRange Range, const MacroArgs *Args) override {
+ if (!Collector)
+ return;
+ // Only record top-level expansions, not those where:
+ // - the macro use is inside a macro body,
+ // - the macro appears in an argument to another macro.
+ if (!MacroNameTok.getLocation().isFileID() ||
+ (LastExpansionEnd.isValid() &&
+ Collector->PP.getSourceManager().isBeforeInTranslationUnit(
+ Range.getBegin(), LastExpansionEnd)))
+ return;
+ Collector->Expansions[Range.getBegin().getRawEncoding()] = Range.getEnd();
+ LastExpansionEnd = Range.getEnd();
+ }
+ // FIXME: handle directives like #pragma, #include, etc.
+private:
+ TokenCollector *Collector;
+ /// Used to detect recursive macro expansions.
+ SourceLocation LastExpansionEnd;
+};
+
+/// Fills in the TokenBuffer by tracing the run of a preprocessor. The
+/// implementation tracks the tokens, macro expansions and directives coming
+/// from the preprocessor and:
+/// - for each token, figures out if it is a part of an expanded token stream,
+/// spelled token stream or both. Stores the tokens appropriately.
+/// - records mappings from the spelled to expanded token ranges, e.g. for macro
+/// expansions.
+/// FIXME: also properly record:
+/// - #include directives,
+/// - #pragma, #line and other PP directives,
+/// - skipped pp regions,
+/// - ...
+
+TokenCollector::TokenCollector(Preprocessor &PP) : PP(PP) {
+ // Collect the expanded token stream during preprocessing.
+ PP.setTokenWatcher([this](const clang::Token &T) {
+ if (T.isAnnotation())
+ return;
+ DEBUG_WITH_TYPE("collect-tokens", llvm::dbgs()
+ << "Token: "
+ << syntax::Token(T).dumpForTests(
+ this->PP.getSourceManager())
+ << "\n"
+
+ );
+ Expanded.push_back(syntax::Token(T));
+ });
+ // And locations of macro calls, to properly recover boundaries of those in
+ // case of empty expansions.
+ auto CB = llvm::make_unique<CollectPPExpansions>(*this);
+ this->Collector = CB.get();
+ PP.addPPCallbacks(std::move(CB));
+}
+
+/// Builds mappings and spelled tokens in the TokenBuffer based on the expanded
+/// token stream.
+class TokenCollector::Builder {
+public:
+ Builder(std::vector<syntax::Token> Expanded, PPExpansions CollectedExpansions,
+ const SourceManager &SM, const LangOptions &LangOpts)
+ : Result(SM), CollectedExpansions(std::move(CollectedExpansions)), SM(SM),
+ LangOpts(LangOpts) {
+ Result.ExpandedTokens = std::move(Expanded);
+ }
+
+ TokenBuffer build() && {
+ buildSpelledTokens();
+
+ // Walk over expanded tokens and spelled tokens in parallel, building the
+ // mappings between those using source locations.
+ // To correctly recover empty macro expansions, we also take locations
+ // reported to PPCallbacks::MacroExpands into account as we do not have any
+ // expanded tokens with source locations to guide us.
+
+ // The 'eof' token is special, it is not part of spelled token stream. We
+ // handle it separately at the end.
+ assert(!Result.ExpandedTokens.empty());
+ assert(Result.ExpandedTokens.back().kind() == tok::eof);
+ for (unsigned I = 0; I < Result.ExpandedTokens.size() - 1; ++I) {
+ // (!) I might be updated by the following call.
+ processExpandedToken(I);
+ }
+
+ // 'eof' not handled in the loop, do it here.
+ assert(SM.getMainFileID() ==
+ SM.getFileID(Result.ExpandedTokens.back().location()));
+ fillGapUntil(Result.Files[SM.getMainFileID()],
+ Result.ExpandedTokens.back().location(),
+ Result.ExpandedTokens.size() - 1);
+ Result.Files[SM.getMainFileID()].EndExpanded = Result.ExpandedTokens.size();
+
+ // Some files might have unaccounted spelled tokens at the end, add an empty
+ // mapping for those as they did not have expanded counterparts.
+ fillGapsAtEndOfFiles();
+
+ return std::move(Result);
+ }
+
+private:
+ /// Process the next token in an expanded stream and move corresponding
+ /// spelled tokens, record any mapping if needed.
+ /// (!) \p I will be updated if this had to skip tokens, e.g. for macros.
+ void processExpandedToken(unsigned &I) {
+ auto L = Result.ExpandedTokens[I].location();
+ if (L.isMacroID()) {
+ processMacroExpansion(SM.getExpansionRange(L), I);
+ return;
+ }
+ if (L.isFileID()) {
+ auto FID = SM.getFileID(L);
+ TokenBuffer::MarkedFile &File = Result.Files[FID];
+
+ fillGapUntil(File, L, I);
+
+ // Skip the token.
+ assert(File.SpelledTokens[NextSpelled[FID]].location() == L &&
+ "no corresponding token in the spelled stream");
+ ++NextSpelled[FID];
+ return;
+ }
+ }
+
+ /// Skipped expanded and spelled tokens of a macro expansion that covers \p
+ /// SpelledRange. Add a corresponding mapping.
+ /// (!) \p I will be the index of the last token in an expansion after this
+ /// function returns.
+ void processMacroExpansion(CharSourceRange SpelledRange, unsigned &I) {
+ auto FID = SM.getFileID(SpelledRange.getBegin());
+ assert(FID == SM.getFileID(SpelledRange.getEnd()));
+ TokenBuffer::MarkedFile &File = Result.Files[FID];
+
+ fillGapUntil(File, SpelledRange.getBegin(), I);
+
+ // Skip all expanded tokens from the same macro expansion.
+ unsigned BeginExpanded = I;
+ for (; I + 1 < Result.ExpandedTokens.size(); ++I) {
+ auto NextL = Result.ExpandedTokens[I + 1].location();
+ if (!NextL.isMacroID() ||
+ SM.getExpansionLoc(NextL) != SpelledRange.getBegin())
+ break;
+ }
+ unsigned EndExpanded = I + 1;
+ consumeMapping(File, SM.getFileOffset(SpelledRange.getEnd()), BeginExpanded,
+ EndExpanded, NextSpelled[FID]);
+ }
+
+ /// Initializes TokenBuffer::Files and fills spelled tokens and expanded
+ /// ranges for each of the files.
+ void buildSpelledTokens() {
+ for (unsigned I = 0; I < Result.ExpandedTokens.size(); ++I) {
+ auto FID =
+ SM.getFileID(SM.getExpansionLoc(Result.ExpandedTokens[I].location()));
+ auto It = Result.Files.try_emplace(FID);
+ TokenBuffer::MarkedFile &File = It.first->second;
+
+ File.EndExpanded = I + 1;
+ if (!It.second)
+ continue; // we have seen this file before.
+
+ // This is the first time we see this file.
+ File.BeginExpanded = I;
+ File.SpelledTokens = tokenize(FID, SM, LangOpts);
+ }
+ }
+
+ void consumeEmptyMapping(TokenBuffer::MarkedFile &File, unsigned EndOffset,
+ unsigned ExpandedIndex, unsigned &SpelledIndex) {
+ consumeMapping(File, EndOffset, ExpandedIndex, ExpandedIndex, SpelledIndex);
+ }
+
+ /// Consumes spelled tokens that form a macro expansion and adds a entry to
+ /// the resulting token buffer.
+ /// (!) SpelledIndex is updated in-place.
+ void consumeMapping(TokenBuffer::MarkedFile &File, unsigned EndOffset,
+ unsigned BeginExpanded, unsigned EndExpanded,
+ unsigned &SpelledIndex) {
+ // We need to record this mapping before continuing.
+ unsigned MappingBegin = SpelledIndex;
+ ++SpelledIndex;
+
+ bool HitMapping =
+ tryConsumeSpelledUntil(File, EndOffset + 1, SpelledIndex).hasValue();
+ (void)HitMapping;
+ assert(!HitMapping && "recursive macro expansion?");
+
+ TokenBuffer::Mapping M;
+ M.BeginExpanded = BeginExpanded;
+ M.EndExpanded = EndExpanded;
+ M.BeginSpelled = MappingBegin;
+ M.EndSpelled = SpelledIndex;
+
+ File.Mappings.push_back(M);
+ }
+
+ /// Consumes spelled tokens until location \p L is reached and adds a mapping
+ /// covering the consumed tokens. The mapping will point to an empty expanded
+ /// range at position \p ExpandedIndex.
+ void fillGapUntil(TokenBuffer::MarkedFile &File, SourceLocation L,
+ unsigned ExpandedIndex) {
+ assert(L.isFileID());
+ FileID FID;
+ unsigned Offset;
+ std::tie(FID, Offset) = SM.getDecomposedLoc(L);
+
+ unsigned &SpelledIndex = NextSpelled[FID];
+ unsigned MappingBegin = SpelledIndex;
+ while (true) {
+ auto EndLoc = tryConsumeSpelledUntil(File, Offset, SpelledIndex);
+ if (SpelledIndex != MappingBegin) {
+ TokenBuffer::Mapping M;
+ M.BeginSpelled = MappingBegin;
+ M.EndSpelled = SpelledIndex;
+ M.BeginExpanded = M.EndExpanded = ExpandedIndex;
+ File.Mappings.push_back(M);
+ }
+ if (!EndLoc)
+ break;
+ consumeEmptyMapping(File, SM.getFileOffset(*EndLoc), ExpandedIndex,
+ SpelledIndex);
+
+ MappingBegin = SpelledIndex;
+ }
+ };
+
+ /// Consumes spelled tokens until it reaches Offset or a mapping boundary,
+ /// i.e. a name of a macro expansion or the start '#' token of a PP directive.
+ /// (!) NextSpelled is updated in place.
+ ///
+ /// returns None if \p Offset was reached, otherwise returns the end location
+ /// of a mapping that starts at \p NextSpelled.
+ llvm::Optional<SourceLocation>
+ tryConsumeSpelledUntil(TokenBuffer::MarkedFile &File, unsigned Offset,
+ unsigned &NextSpelled) {
+ for (; NextSpelled < File.SpelledTokens.size(); ++NextSpelled) {
+ auto L = File.SpelledTokens[NextSpelled].location();
+ if (Offset <= SM.getFileOffset(L))
+ return llvm::None; // reached the offset we are looking for.
+ auto Mapping = CollectedExpansions.find(L.getRawEncoding());
+ if (Mapping != CollectedExpansions.end())
+ return Mapping->second; // found a mapping before the offset.
+ }
+ return llvm::None; // no more tokens, we "reached" the offset.
+ }
+
+ /// Adds empty mappings for unconsumed spelled tokens at the end of each file.
+ void fillGapsAtEndOfFiles() {
+ for (auto &F : Result.Files) {
+ if (F.second.SpelledTokens.empty())
+ continue;
+ fillGapUntil(F.second, F.second.SpelledTokens.back().endLocation(),
+ F.second.EndExpanded);
+ }
+ }
+
+ TokenBuffer Result;
+ /// For each file, a position of the next spelled token we will consume.
+ llvm::DenseMap<FileID, unsigned> NextSpelled;
+ PPExpansions CollectedExpansions;
+ const SourceManager &SM;
+ const LangOptions &LangOpts;
+};
+
+TokenBuffer TokenCollector::consume() && {
+ PP.setTokenWatcher(nullptr);
+ Collector->disable();
+ return Builder(std::move(Expanded), std::move(Expansions),
+ PP.getSourceManager(), PP.getLangOpts())
+ .build();
+}
+
+std::string syntax::Token::str() const {
+ return llvm::formatv("Token({0}, length = {1})", tok::getTokenName(kind()),
+ length());
+}
+
+std::string syntax::Token::dumpForTests(const SourceManager &SM) const {
+ return llvm::formatv("{0} {1}", tok::getTokenName(kind()), text(SM));
+}
+
+std::string TokenBuffer::dumpForTests() const {
+ auto PrintToken = [this](const syntax::Token &T) -> std::string {
+ if (T.kind() == tok::eof)
+ return "<eof>";
+ return T.text(*SourceMgr);
+ };
+
+ auto DumpTokens = [this, &PrintToken](llvm::raw_ostream &OS,
+ llvm::ArrayRef<syntax::Token> Tokens) {
+ if (Tokens.empty()) {
+ OS << "<empty>";
+ return;
+ }
+ OS << Tokens[0].text(*SourceMgr);
+ for (unsigned I = 1; I < Tokens.size(); ++I) {
+ if (Tokens[I].kind() == tok::eof)
+ continue;
+ OS << " " << PrintToken(Tokens[I]);
+ }
+ };
+
+ std::string Dump;
+ llvm::raw_string_ostream OS(Dump);
+
+ OS << "expanded tokens:\n"
+ << " ";
+ // (!) we do not show '<eof>'.
+ DumpTokens(OS, llvm::makeArrayRef(ExpandedTokens).drop_back());
+ OS << "\n";
+
+ std::vector<FileID> Keys;
+ for (auto F : Files)
+ Keys.push_back(F.first);
+ llvm::sort(Keys);
+
+ for (FileID ID : Keys) {
+ const MarkedFile &File = Files.find(ID)->second;
+ auto *Entry = SourceMgr->getFileEntryForID(ID);
+ if (!Entry)
+ continue; // Skip builtin files.
+ OS << llvm::formatv("file '{0}'\n", Entry->getName())
+ << " spelled tokens:\n"
+ << " ";
+ DumpTokens(OS, File.SpelledTokens);
+ OS << "\n";
+
+ if (File.Mappings.empty()) {
+ OS << " no mappings.\n";
+ continue;
+ }
+ OS << " mappings:\n";
+ for (auto &M : File.Mappings) {
+ OS << llvm::formatv(
+ " ['{0}'_{1}, '{2}'_{3}) => ['{4}'_{5}, '{6}'_{7})\n",
+ PrintToken(File.SpelledTokens[M.BeginSpelled]), M.BeginSpelled,
+ M.EndSpelled == File.SpelledTokens.size()
+ ? "<eof>"
+ : PrintToken(File.SpelledTokens[M.EndSpelled]),
+ M.EndSpelled, PrintToken(ExpandedTokens[M.BeginExpanded]),
+ M.BeginExpanded, PrintToken(ExpandedTokens[M.EndExpanded]),
+ M.EndExpanded);
+ }
+ }
+ return OS.str();
+}
diff --git a/lib/Tooling/Syntax/Tree.cpp b/lib/Tooling/Syntax/Tree.cpp
new file mode 100644
index 000000000000..1549b6724fa4
--- /dev/null
+++ b/lib/Tooling/Syntax/Tree.cpp
@@ -0,0 +1,149 @@
+//===- Tree.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/Syntax/Tree.h"
+#include "clang/Basic/TokenKinds.h"
+#include "clang/Tooling/Syntax/Nodes.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Casting.h"
+
+using namespace clang;
+
+syntax::Arena::Arena(SourceManager &SourceMgr, const LangOptions &LangOpts,
+ TokenBuffer Tokens)
+ : SourceMgr(SourceMgr), LangOpts(LangOpts), Tokens(std::move(Tokens)) {}
+
+const clang::syntax::TokenBuffer &syntax::Arena::tokenBuffer() const {
+ return Tokens;
+}
+
+std::pair<FileID, llvm::ArrayRef<syntax::Token>>
+syntax::Arena::lexBuffer(std::unique_ptr<llvm::MemoryBuffer> Input) {
+ auto FID = SourceMgr.createFileID(std::move(Input));
+ auto It = ExtraTokens.try_emplace(FID, tokenize(FID, SourceMgr, LangOpts));
+ assert(It.second && "duplicate FileID");
+ return {FID, It.first->second};
+}
+
+syntax::Leaf::Leaf(const syntax::Token *Tok) : Node(NodeKind::Leaf), Tok(Tok) {
+ assert(Tok != nullptr);
+}
+
+bool syntax::Leaf::classof(const Node *N) {
+ return N->kind() == NodeKind::Leaf;
+}
+
+syntax::Node::Node(NodeKind Kind)
+ : Parent(nullptr), NextSibling(nullptr), Kind(static_cast<unsigned>(Kind)),
+ Role(static_cast<unsigned>(NodeRole::Detached)) {}
+
+bool syntax::Tree::classof(const Node *N) { return N->kind() > NodeKind::Leaf; }
+
+void syntax::Tree::prependChildLowLevel(Node *Child, NodeRole Role) {
+ assert(Child->Parent == nullptr);
+ assert(Child->NextSibling == nullptr);
+ assert(Child->role() == NodeRole::Detached);
+ assert(Role != NodeRole::Detached);
+
+ Child->Parent = this;
+ Child->NextSibling = this->FirstChild;
+ Child->Role = static_cast<unsigned>(Role);
+ this->FirstChild = Child;
+}
+
+namespace {
+static void traverse(const syntax::Node *N,
+ llvm::function_ref<void(const syntax::Node *)> Visit) {
+ if (auto *T = dyn_cast<syntax::Tree>(N)) {
+ for (auto *C = T->firstChild(); C; C = C->nextSibling())
+ traverse(C, Visit);
+ }
+ Visit(N);
+}
+static void dumpTokens(llvm::raw_ostream &OS, ArrayRef<syntax::Token> Tokens,
+ const SourceManager &SM) {
+ assert(!Tokens.empty());
+ bool First = true;
+ for (const auto &T : Tokens) {
+ if (!First)
+ OS << " ";
+ else
+ First = false;
+ // Handle 'eof' separately, calling text() on it produces an empty string.
+ if (T.kind() == tok::eof) {
+ OS << "<eof>";
+ continue;
+ }
+ OS << T.text(SM);
+ }
+}
+
+static void dumpTree(llvm::raw_ostream &OS, const syntax::Node *N,
+ const syntax::Arena &A, std::vector<bool> IndentMask) {
+ if (N->role() != syntax::NodeRole::Unknown) {
+ // FIXME: print the symbolic name of a role.
+ if (N->role() == syntax::NodeRole::Detached)
+ OS << "*: ";
+ else
+ OS << static_cast<int>(N->role()) << ": ";
+ }
+ if (auto *L = llvm::dyn_cast<syntax::Leaf>(N)) {
+ dumpTokens(OS, *L->token(), A.sourceManager());
+ OS << "\n";
+ return;
+ }
+
+ auto *T = llvm::cast<syntax::Tree>(N);
+ OS << T->kind() << "\n";
+
+ for (auto It = T->firstChild(); It != nullptr; It = It->nextSibling()) {
+ for (bool Filled : IndentMask) {
+ if (Filled)
+ OS << "| ";
+ else
+ OS << " ";
+ }
+ if (!It->nextSibling()) {
+ OS << "`-";
+ IndentMask.push_back(false);
+ } else {
+ OS << "|-";
+ IndentMask.push_back(true);
+ }
+ dumpTree(OS, It, A, IndentMask);
+ IndentMask.pop_back();
+ }
+}
+} // namespace
+
+std::string syntax::Node::dump(const Arena &A) const {
+ std::string Str;
+ llvm::raw_string_ostream OS(Str);
+ dumpTree(OS, this, A, /*IndentMask=*/{});
+ return std::move(OS.str());
+}
+
+std::string syntax::Node::dumpTokens(const Arena &A) const {
+ std::string Storage;
+ llvm::raw_string_ostream OS(Storage);
+ traverse(this, [&](const syntax::Node *N) {
+ auto *L = llvm::dyn_cast<syntax::Leaf>(N);
+ if (!L)
+ return;
+ ::dumpTokens(OS, *L->token(), A.sourceManager());
+ });
+ return OS.str();
+}
+
+syntax::Node *syntax::Tree::findChild(NodeRole R) {
+ for (auto *C = FirstChild; C; C = C->nextSibling()) {
+ if (C->role() == R)
+ return C;
+ }
+ return nullptr;
+}
diff --git a/lib/Tooling/Tooling.cpp b/lib/Tooling/Tooling.cpp
index 63aa64a5330d..291df0ae333d 100644
--- a/lib/Tooling/Tooling.cpp
+++ b/lib/Tooling/Tooling.cpp
@@ -1,9 +1,8 @@
//===- Tooling.cpp - Running clang standalone tools -----------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
//
@@ -302,7 +301,7 @@ bool ToolInvocation::run() {
DiagConsumer ? DiagConsumer : &DiagnosticPrinter, false);
const std::unique_ptr<driver::Driver> Driver(
- newDriver(&Diagnostics, BinaryName, Files->getVirtualFileSystem()));
+ newDriver(&Diagnostics, BinaryName, &Files->getVirtualFileSystem()));
// The "input file not found" diagnostics from the driver are useful.
// The driver is only aware of the VFS working directory, but some clients
// change this at the FileManager level instead.
@@ -482,7 +481,7 @@ int ClangTool::run(ToolAction *Action) {
if (OverlayFileSystem->setCurrentWorkingDirectory(
CompileCommand.Directory))
llvm::report_fatal_error("Cannot chdir into \"" +
- Twine(CompileCommand.Directory) + "\n!");
+ Twine(CompileCommand.Directory) + "\"!");
// Now fill the in-memory VFS with the relative file mappings so it will
// have the correct relative paths. We never remove mappings but that
@@ -518,7 +517,8 @@ int ClangTool::run(ToolAction *Action) {
if (!Invocation.run()) {
// FIXME: Diagnostics should be used instead.
- llvm::errs() << "Error while processing " << File << ".\n";
+ if (PrintErrorMessage)
+ llvm::errs() << "Error while processing " << File << ".\n";
ProcessingFailed = true;
}
}
@@ -570,6 +570,10 @@ void ClangTool::setRestoreWorkingDir(bool RestoreCWD) {
this->RestoreCWD = RestoreCWD;
}
+void ClangTool::setPrintErrorMessage(bool PrintErrorMessage) {
+ this->PrintErrorMessage = PrintErrorMessage;
+}
+
namespace clang {
namespace tooling {