summaryrefslogtreecommitdiffstats
path: root/contrib/llvm/tools/clang/lib/Tooling
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Tooling')
-rw-r--r--contrib/llvm/tools/clang/lib/Tooling/ArgumentsAdjusters.cpp88
-rw-r--r--contrib/llvm/tools/clang/lib/Tooling/CommonOptionsParser.cpp143
-rw-r--r--contrib/llvm/tools/clang/lib/Tooling/CompilationDatabase.cpp331
-rw-r--r--contrib/llvm/tools/clang/lib/Tooling/Core/Replacement.cpp297
-rw-r--r--contrib/llvm/tools/clang/lib/Tooling/FileMatchTrie.cpp189
-rw-r--r--contrib/llvm/tools/clang/lib/Tooling/JSONCompilationDatabase.cpp322
-rw-r--r--contrib/llvm/tools/clang/lib/Tooling/Refactoring.cpp65
-rw-r--r--contrib/llvm/tools/clang/lib/Tooling/RefactoringCallbacks.cpp81
-rw-r--r--contrib/llvm/tools/clang/lib/Tooling/Tooling.cpp446
9 files changed, 1962 insertions, 0 deletions
diff --git a/contrib/llvm/tools/clang/lib/Tooling/ArgumentsAdjusters.cpp b/contrib/llvm/tools/clang/lib/Tooling/ArgumentsAdjusters.cpp
new file mode 100644
index 0000000..1722ede
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/Tooling/ArgumentsAdjusters.cpp
@@ -0,0 +1,88 @@
+//===--- 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.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains definitions of classes which implement ArgumentsAdjuster
+// interface.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/ArgumentsAdjusters.h"
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+namespace tooling {
+
+/// Add -fsyntax-only option to the commnand line arguments.
+ArgumentsAdjuster getClangSyntaxOnlyAdjuster() {
+ return [](const CommandLineArguments &Args) {
+ CommandLineArguments AdjustedArgs;
+ 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]);
+ }
+ AdjustedArgs.push_back("-fsyntax-only");
+ return AdjustedArgs;
+ };
+}
+
+ArgumentsAdjuster getClangStripOutputAdjuster() {
+ return [](const CommandLineArguments &Args) {
+ CommandLineArguments AdjustedArgs;
+ for (size_t i = 0, e = Args.size(); i < e; ++i) {
+ StringRef Arg = Args[i];
+ if (!Arg.startswith("-o"))
+ AdjustedArgs.push_back(Args[i]);
+
+ if (Arg == "-o") {
+ // Output is specified as -o foo. Skip the next argument also.
+ ++i;
+ }
+ // Else, the output is specified as -ofoo. Just do nothing.
+ }
+ return AdjustedArgs;
+ };
+}
+
+ArgumentsAdjuster getInsertArgumentAdjuster(const CommandLineArguments &Extra,
+ ArgumentInsertPosition Pos) {
+ return [Extra, Pos](const CommandLineArguments &Args) {
+ CommandLineArguments Return(Args);
+
+ CommandLineArguments::iterator I;
+ if (Pos == ArgumentInsertPosition::END) {
+ I = Return.end();
+ } else {
+ I = Return.begin();
+ ++I; // To leave the program name in place
+ }
+
+ Return.insert(I, Extra.begin(), Extra.end());
+ return Return;
+ };
+}
+
+ArgumentsAdjuster getInsertArgumentAdjuster(const char *Extra,
+ ArgumentInsertPosition Pos) {
+ return getInsertArgumentAdjuster(CommandLineArguments(1, Extra), Pos);
+}
+
+ArgumentsAdjuster combineAdjusters(ArgumentsAdjuster First,
+ ArgumentsAdjuster Second) {
+ return [First, Second](const CommandLineArguments &Args) {
+ return Second(First(Args));
+ };
+}
+
+} // end namespace tooling
+} // end namespace clang
+
diff --git a/contrib/llvm/tools/clang/lib/Tooling/CommonOptionsParser.cpp b/contrib/llvm/tools/clang/lib/Tooling/CommonOptionsParser.cpp
new file mode 100644
index 0000000..adae178
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/Tooling/CommonOptionsParser.cpp
@@ -0,0 +1,143 @@
+//===--- 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.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the CommonOptionsParser class used to parse common
+// command-line options for clang tools, so that they can be run as separate
+// command-line applications with a consistent common interface for handling
+// compilation database and input files.
+//
+// It provides a common subset of command-line options, common algorithm
+// for locating a compilation database and source files, and help messages
+// for the basic command-line interface.
+//
+// It creates a CompilationDatabase and reads common command-line options.
+//
+// This class uses the Clang Tooling infrastructure, see
+// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
+// for details on setting it up with LLVM source tree.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/CommandLine.h"
+#include "clang/Tooling/ArgumentsAdjusters.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Tooling.h"
+
+using namespace clang::tooling;
+using namespace llvm;
+
+const char *const CommonOptionsParser::HelpMessage =
+ "\n"
+ "-p <build-path> is used to read a compile command database.\n"
+ "\n"
+ "\tFor example, it can be a CMake build directory in which a file named\n"
+ "\tcompile_commands.json exists (use -DCMAKE_EXPORT_COMPILE_COMMANDS=ON\n"
+ "\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"
+ "\texample of setting up Clang Tooling on a source tree.\n"
+ "\n"
+ "<source0> ... specify the paths of source files. These paths are\n"
+ "\tlooked up in the compile command database. If the path of a file is\n"
+ "\tabsolute, it needs to point into CMake's source tree. If the path is\n"
+ "\trelative, the current working directory needs to be in the CMake\n"
+ "\tsource tree and the file must be in a subdirectory of the current\n"
+ "\tworking directory. \"./\" prefixes in the relative files will be\n"
+ "\tautomatically removed, but the rest of a relative path must be a\n"
+ "\tsuffix of a path in the compile command database.\n"
+ "\n";
+
+namespace {
+class ArgumentsAdjustingCompilations : public CompilationDatabase {
+public:
+ ArgumentsAdjustingCompilations(
+ std::unique_ptr<CompilationDatabase> Compilations)
+ : Compilations(std::move(Compilations)) {}
+
+ void appendArgumentsAdjuster(ArgumentsAdjuster Adjuster) {
+ Adjusters.push_back(Adjuster);
+ }
+
+ std::vector<CompileCommand>
+ getCompileCommands(StringRef FilePath) const override {
+ return adjustCommands(Compilations->getCompileCommands(FilePath));
+ }
+
+ std::vector<std::string> getAllFiles() const override {
+ return Compilations->getAllFiles();
+ }
+
+ std::vector<CompileCommand> getAllCompileCommands() const override {
+ return adjustCommands(Compilations->getAllCompileCommands());
+ }
+
+private:
+ std::unique_ptr<CompilationDatabase> Compilations;
+ std::vector<ArgumentsAdjuster> Adjusters;
+
+ std::vector<CompileCommand>
+ adjustCommands(std::vector<CompileCommand> Commands) const {
+ for (CompileCommand &Command : Commands)
+ for (const auto &Adjuster : Adjusters)
+ Command.CommandLine = Adjuster(Command.CommandLine);
+ return Commands;
+ }
+};
+} // namespace
+
+CommonOptionsParser::CommonOptionsParser(int &argc, const char **argv,
+ cl::OptionCategory &Category,
+ const char *Overview) {
+ static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
+
+ static cl::opt<std::string> BuildPath("p", cl::desc("Build path"),
+ cl::Optional, cl::cat(Category));
+
+ static cl::list<std::string> SourcePaths(
+ cl::Positional, cl::desc("<source0> [... <sourceN>]"), cl::OneOrMore,
+ cl::cat(Category));
+
+ static cl::list<std::string> ArgsAfter(
+ "extra-arg",
+ cl::desc("Additional argument to append to the compiler command line"),
+ cl::cat(Category));
+
+ static cl::list<std::string> ArgsBefore(
+ "extra-arg-before",
+ cl::desc("Additional argument to prepend to the compiler command line"),
+ cl::cat(Category));
+
+ cl::HideUnrelatedOptions(Category);
+
+ Compilations.reset(FixedCompilationDatabase::loadFromCommandLine(argc,
+ argv));
+ cl::ParseCommandLineOptions(argc, argv, Overview);
+ SourcePathList = SourcePaths;
+ if (!Compilations) {
+ std::string ErrorMessage;
+ if (!BuildPath.empty()) {
+ Compilations =
+ CompilationDatabase::autoDetectFromDirectory(BuildPath, ErrorMessage);
+ } else {
+ Compilations = CompilationDatabase::autoDetectFromSource(SourcePaths[0],
+ ErrorMessage);
+ }
+ if (!Compilations)
+ llvm::report_fatal_error(ErrorMessage);
+ }
+ auto AdjustingCompilations =
+ llvm::make_unique<ArgumentsAdjustingCompilations>(
+ std::move(Compilations));
+ AdjustingCompilations->appendArgumentsAdjuster(
+ getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN));
+ AdjustingCompilations->appendArgumentsAdjuster(
+ getInsertArgumentAdjuster(ArgsAfter, ArgumentInsertPosition::END));
+ Compilations = std::move(AdjustingCompilations);
+}
diff --git a/contrib/llvm/tools/clang/lib/Tooling/CompilationDatabase.cpp b/contrib/llvm/tools/clang/lib/Tooling/CompilationDatabase.cpp
new file mode 100644
index 0000000..2272be6
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/Tooling/CompilationDatabase.cpp
@@ -0,0 +1,331 @@
+//===--- CompilationDatabase.cpp - ----------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains implementations of the CompilationDatabase base class
+// and the FixedCompilationDatabase.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Driver/Action.h"
+#include "clang/Driver/Compilation.h"
+#include "clang/Driver/Driver.h"
+#include "clang/Driver/DriverDiagnostic.h"
+#include "clang/Driver/Job.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Tooling/CompilationDatabasePluginRegistry.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/Path.h"
+#include <sstream>
+#include <system_error>
+using namespace clang;
+using namespace tooling;
+
+CompilationDatabase::~CompilationDatabase() {}
+
+std::unique_ptr<CompilationDatabase>
+CompilationDatabase::loadFromDirectory(StringRef BuildDirectory,
+ std::string &ErrorMessage) {
+ std::stringstream ErrorStream;
+ for (CompilationDatabasePluginRegistry::iterator
+ It = CompilationDatabasePluginRegistry::begin(),
+ Ie = CompilationDatabasePluginRegistry::end();
+ It != Ie; ++It) {
+ std::string DatabaseErrorMessage;
+ std::unique_ptr<CompilationDatabasePlugin> Plugin(It->instantiate());
+ if (std::unique_ptr<CompilationDatabase> DB =
+ Plugin->loadFromDirectory(BuildDirectory, DatabaseErrorMessage))
+ return DB;
+ ErrorStream << It->getName() << ": " << DatabaseErrorMessage << "\n";
+ }
+ ErrorMessage = ErrorStream.str();
+ return nullptr;
+}
+
+static std::unique_ptr<CompilationDatabase>
+findCompilationDatabaseFromDirectory(StringRef Directory,
+ std::string &ErrorMessage) {
+ std::stringstream ErrorStream;
+ bool HasErrorMessage = false;
+ while (!Directory.empty()) {
+ std::string LoadErrorMessage;
+
+ if (std::unique_ptr<CompilationDatabase> DB =
+ CompilationDatabase::loadFromDirectory(Directory, LoadErrorMessage))
+ return DB;
+
+ if (!HasErrorMessage) {
+ ErrorStream << "No compilation database found in " << Directory.str()
+ << " or any parent directory\n" << LoadErrorMessage;
+ HasErrorMessage = true;
+ }
+
+ Directory = llvm::sys::path::parent_path(Directory);
+ }
+ ErrorMessage = ErrorStream.str();
+ return nullptr;
+}
+
+std::unique_ptr<CompilationDatabase>
+CompilationDatabase::autoDetectFromSource(StringRef SourceFile,
+ std::string &ErrorMessage) {
+ SmallString<1024> AbsolutePath(getAbsolutePath(SourceFile));
+ StringRef Directory = llvm::sys::path::parent_path(AbsolutePath);
+
+ std::unique_ptr<CompilationDatabase> DB =
+ findCompilationDatabaseFromDirectory(Directory, ErrorMessage);
+
+ if (!DB)
+ ErrorMessage = ("Could not auto-detect compilation database for file \"" +
+ SourceFile + "\"\n" + ErrorMessage).str();
+ return DB;
+}
+
+std::unique_ptr<CompilationDatabase>
+CompilationDatabase::autoDetectFromDirectory(StringRef SourceDir,
+ std::string &ErrorMessage) {
+ SmallString<1024> AbsolutePath(getAbsolutePath(SourceDir));
+
+ std::unique_ptr<CompilationDatabase> DB =
+ findCompilationDatabaseFromDirectory(AbsolutePath, ErrorMessage);
+
+ if (!DB)
+ ErrorMessage = ("Could not auto-detect compilation database from directory \"" +
+ SourceDir + "\"\n" + ErrorMessage).str();
+ return DB;
+}
+
+CompilationDatabasePlugin::~CompilationDatabasePlugin() {}
+
+namespace {
+// Helper for recursively searching through a chain of actions and collecting
+// all inputs, direct and indirect, of compile jobs.
+struct CompileJobAnalyzer {
+ void run(const driver::Action *A) {
+ runImpl(A, false);
+ }
+
+ SmallVector<std::string, 2> Inputs;
+
+private:
+
+ void runImpl(const driver::Action *A, bool Collect) {
+ bool CollectChildren = Collect;
+ switch (A->getKind()) {
+ case driver::Action::CompileJobClass:
+ CollectChildren = true;
+ break;
+
+ case driver::Action::InputClass: {
+ if (Collect) {
+ const driver::InputAction *IA = cast<driver::InputAction>(A);
+ Inputs.push_back(IA->getInputArg().getSpelling());
+ }
+ } break;
+
+ default:
+ // Don't care about others
+ ;
+ }
+
+ for (driver::ActionList::const_iterator I = A->begin(), E = A->end();
+ I != E; ++I)
+ runImpl(*I, CollectChildren);
+ }
+};
+
+// Special DiagnosticConsumer that looks for warn_drv_input_file_unused
+// diagnostics from the driver and collects the option strings for those unused
+// options.
+class UnusedInputDiagConsumer : public DiagnosticConsumer {
+public:
+ UnusedInputDiagConsumer() : Other(nullptr) {}
+
+ // Useful for debugging, chain diagnostics to another consumer after
+ // recording for our own purposes.
+ UnusedInputDiagConsumer(DiagnosticConsumer *Other) : Other(Other) {}
+
+ void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
+ const Diagnostic &Info) override {
+ if (Info.getID() == clang::diag::warn_drv_input_file_unused) {
+ // Arg 1 for this diagnostic is the option that didn't get used.
+ UnusedInputs.push_back(Info.getArgStdStr(0));
+ }
+ if (Other)
+ Other->HandleDiagnostic(DiagLevel, Info);
+ }
+
+ DiagnosticConsumer *Other;
+ SmallVector<std::string, 2> UnusedInputs;
+};
+
+// Unary functor for asking "Given a StringRef S1, does there exist a string
+// S2 in Arr where S1 == S2?"
+struct MatchesAny {
+ MatchesAny(ArrayRef<std::string> Arr) : Arr(Arr) {}
+ bool operator() (StringRef S) {
+ for (const std::string *I = Arr.begin(), *E = Arr.end(); I != E; ++I)
+ if (*I == S)
+ return true;
+ return false;
+ }
+private:
+ ArrayRef<std::string> Arr;
+};
+} // namespace
+
+/// \brief Strips any positional args and possible argv[0] from a command-line
+/// provided by the user to construct a FixedCompilationDatabase.
+///
+/// FixedCompilationDatabase requires a command line to be in this format as it
+/// constructs the command line for each file by appending the name of the file
+/// to be compiled. FixedCompilationDatabase also adds its own argv[0] to the
+/// start of the command line although its value is not important as it's just
+/// ignored by the Driver invoked by the ClangTool using the
+/// FixedCompilationDatabase.
+///
+/// FIXME: This functionality should probably be made available by
+/// clang::driver::Driver although what the interface should look like is not
+/// clear.
+///
+/// \param[in] Args Args as provided by the user.
+/// \return Resulting stripped command line.
+/// \li true if successful.
+/// \li false if \c Args cannot be used for compilation jobs (e.g.
+/// contains an option like -E or -version).
+static bool stripPositionalArgs(std::vector<const char *> Args,
+ std::vector<std::string> &Result) {
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
+ UnusedInputDiagConsumer DiagClient;
+ DiagnosticsEngine Diagnostics(
+ IntrusiveRefCntPtr<clang::DiagnosticIDs>(new DiagnosticIDs()),
+ &*DiagOpts, &DiagClient, false);
+
+ // The clang executable path isn't required since the jobs the driver builds
+ // will not be executed.
+ std::unique_ptr<driver::Driver> NewDriver(new driver::Driver(
+ /* ClangExecutable= */ "", llvm::sys::getDefaultTargetTriple(),
+ Diagnostics));
+ NewDriver->setCheckInputsExist(false);
+
+ // This becomes the new argv[0]. The value is actually not important as it
+ // isn't used for invoking Tools.
+ Args.insert(Args.begin(), "clang-tool");
+
+ // By adding -c, we force the driver to treat compilation as the last phase.
+ // It will then issue warnings via Diagnostics about un-used options that
+ // would have been used for linking. If the user provided a compiler name as
+ // the original argv[0], this will be treated as a linker input thanks to
+ // insertng a new argv[0] above. All un-used options get collected by
+ // UnusedInputdiagConsumer and get stripped out later.
+ Args.push_back("-c");
+
+ // Put a dummy C++ file on to ensure there's at least one compile job for the
+ // driver to construct. If the user specified some other argument that
+ // prevents compilation, e.g. -E or something like -version, we may still end
+ // up with no jobs but then this is the user's fault.
+ Args.push_back("placeholder.cpp");
+
+ // Remove -no-integrated-as; it's not used for syntax checking,
+ // and it confuses targets which don't support this option.
+ Args.erase(std::remove_if(Args.begin(), Args.end(),
+ MatchesAny(std::string("-no-integrated-as"))),
+ Args.end());
+
+ const std::unique_ptr<driver::Compilation> Compilation(
+ NewDriver->BuildCompilation(Args));
+
+ const driver::JobList &Jobs = Compilation->getJobs();
+
+ CompileJobAnalyzer CompileAnalyzer;
+
+ for (const auto &Cmd : Jobs) {
+ // Collect only for Assemble jobs. If we do all jobs we get duplicates
+ // since Link jobs point to Assemble jobs as inputs.
+ if (Cmd.getSource().getKind() == driver::Action::AssembleJobClass)
+ CompileAnalyzer.run(&Cmd.getSource());
+ }
+
+ if (CompileAnalyzer.Inputs.empty()) {
+ // No compile jobs found.
+ // FIXME: Emit a warning of some kind?
+ return false;
+ }
+
+ // Remove all compilation input files from the command line. This is
+ // necessary so that getCompileCommands() can construct a command line for
+ // each file.
+ std::vector<const char *>::iterator End = std::remove_if(
+ Args.begin(), Args.end(), MatchesAny(CompileAnalyzer.Inputs));
+
+ // Remove all inputs deemed unused for compilation.
+ End = std::remove_if(Args.begin(), End, MatchesAny(DiagClient.UnusedInputs));
+
+ // Remove the -c add above as well. It will be at the end right now.
+ assert(strcmp(*(End - 1), "-c") == 0);
+ --End;
+
+ Result = std::vector<std::string>(Args.begin() + 1, End);
+ return true;
+}
+
+FixedCompilationDatabase *FixedCompilationDatabase::loadFromCommandLine(
+ int &Argc, const char *const *Argv, Twine Directory) {
+ const char *const *DoubleDash = std::find(Argv, Argv + Argc, StringRef("--"));
+ if (DoubleDash == Argv + Argc)
+ return nullptr;
+ std::vector<const char *> CommandLine(DoubleDash + 1, Argv + Argc);
+ Argc = DoubleDash - Argv;
+
+ std::vector<std::string> StrippedArgs;
+ if (!stripPositionalArgs(CommandLine, StrippedArgs))
+ return nullptr;
+ return new FixedCompilationDatabase(Directory, StrippedArgs);
+}
+
+FixedCompilationDatabase::
+FixedCompilationDatabase(Twine Directory, ArrayRef<std::string> CommandLine) {
+ std::vector<std::string> ToolCommandLine(1, "clang-tool");
+ ToolCommandLine.insert(ToolCommandLine.end(),
+ CommandLine.begin(), CommandLine.end());
+ CompileCommands.emplace_back(Directory, std::move(ToolCommandLine));
+}
+
+std::vector<CompileCommand>
+FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const {
+ std::vector<CompileCommand> Result(CompileCommands);
+ Result[0].CommandLine.push_back(FilePath);
+ return Result;
+}
+
+std::vector<std::string>
+FixedCompilationDatabase::getAllFiles() const {
+ return std::vector<std::string>();
+}
+
+std::vector<CompileCommand>
+FixedCompilationDatabase::getAllCompileCommands() const {
+ return std::vector<CompileCommand>();
+}
+
+namespace clang {
+namespace tooling {
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the JSONCompilationDatabasePlugin.
+extern volatile int JSONAnchorSource;
+static int JSONAnchorDest = JSONAnchorSource;
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/contrib/llvm/tools/clang/lib/Tooling/Core/Replacement.cpp b/contrib/llvm/tools/clang/lib/Tooling/Core/Replacement.cpp
new file mode 100644
index 0000000..6d37a49
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/Tooling/Core/Replacement.cpp
@@ -0,0 +1,297 @@
+//===--- 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.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements classes to support/store refactorings.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticIDs.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_os_ostream.h"
+
+namespace clang {
+namespace tooling {
+
+static const char * const InvalidLocation = "";
+
+Replacement::Replacement()
+ : FilePath(InvalidLocation) {}
+
+Replacement::Replacement(StringRef FilePath, unsigned Offset, unsigned Length,
+ StringRef ReplacementText)
+ : FilePath(FilePath), ReplacementRange(Offset, Length),
+ ReplacementText(ReplacementText) {}
+
+Replacement::Replacement(const SourceManager &Sources, SourceLocation Start,
+ unsigned Length, StringRef ReplacementText) {
+ setFromSourceLocation(Sources, Start, Length, ReplacementText);
+}
+
+Replacement::Replacement(const SourceManager &Sources,
+ const CharSourceRange &Range,
+ StringRef ReplacementText,
+ const LangOptions &LangOpts) {
+ setFromSourceRange(Sources, Range, ReplacementText, LangOpts);
+}
+
+bool Replacement::isApplicable() const {
+ return FilePath != InvalidLocation;
+}
+
+bool Replacement::apply(Rewriter &Rewrite) const {
+ SourceManager &SM = Rewrite.getSourceMgr();
+ const FileEntry *Entry = SM.getFileManager().getFile(FilePath);
+ if (!Entry)
+ return false;
+ FileID ID;
+ // FIXME: Use SM.translateFile directly.
+ SourceLocation Location = SM.translateFileLineCol(Entry, 1, 1);
+ ID = Location.isValid() ?
+ SM.getFileID(Location) :
+ SM.createFileID(Entry, SourceLocation(), SrcMgr::C_User);
+ // FIXME: We cannot check whether Offset + Length is in the file, as
+ // the remapping API is not public in the RewriteBuffer.
+ const SourceLocation Start =
+ SM.getLocForStartOfFile(ID).
+ getLocWithOffset(ReplacementRange.getOffset());
+ // ReplaceText returns false on success.
+ // ReplaceText only fails if the source location is not a file location, in
+ // which case we already returned false earlier.
+ bool RewriteSucceeded = !Rewrite.ReplaceText(
+ Start, ReplacementRange.getLength(), ReplacementText);
+ assert(RewriteSucceeded);
+ return RewriteSucceeded;
+}
+
+std::string Replacement::toString() const {
+ std::string Result;
+ llvm::raw_string_ostream Stream(Result);
+ Stream << FilePath << ": " << ReplacementRange.getOffset() << ":+"
+ << ReplacementRange.getLength() << ":\"" << ReplacementText << "\"";
+ return Stream.str();
+}
+
+bool operator<(const Replacement &LHS, const Replacement &RHS) {
+ if (LHS.getOffset() != RHS.getOffset())
+ return LHS.getOffset() < RHS.getOffset();
+
+ // Apply longer replacements first, specifically so that deletions are
+ // executed before insertions. It is (hopefully) never the intention to
+ // delete parts of newly inserted code.
+ if (LHS.getLength() != RHS.getLength())
+ return LHS.getLength() > RHS.getLength();
+
+ if (LHS.getFilePath() != RHS.getFilePath())
+ return LHS.getFilePath() < RHS.getFilePath();
+ return LHS.getReplacementText() < RHS.getReplacementText();
+}
+
+bool operator==(const Replacement &LHS, const Replacement &RHS) {
+ return LHS.getOffset() == RHS.getOffset() &&
+ LHS.getLength() == RHS.getLength() &&
+ LHS.getFilePath() == RHS.getFilePath() &&
+ LHS.getReplacementText() == RHS.getReplacementText();
+}
+
+void Replacement::setFromSourceLocation(const SourceManager &Sources,
+ SourceLocation Start, unsigned Length,
+ StringRef ReplacementText) {
+ const std::pair<FileID, unsigned> DecomposedLocation =
+ Sources.getDecomposedLoc(Start);
+ const FileEntry *Entry = Sources.getFileEntryForID(DecomposedLocation.first);
+ if (Entry) {
+ // Make FilePath absolute so replacements can be applied correctly when
+ // relative paths for files are used.
+ llvm::SmallString<256> FilePath(Entry->getName());
+ std::error_code EC = llvm::sys::fs::make_absolute(FilePath);
+ this->FilePath = EC ? FilePath.c_str() : Entry->getName();
+ } else {
+ this->FilePath = InvalidLocation;
+ }
+ this->ReplacementRange = Range(DecomposedLocation.second, Length);
+ this->ReplacementText = ReplacementText;
+}
+
+// FIXME: This should go into the Lexer, but we need to figure out how
+// to handle ranges for refactoring in general first - there is no obvious
+// good way how to integrate this into the Lexer yet.
+static int getRangeSize(const SourceManager &Sources,
+ const CharSourceRange &Range,
+ const LangOptions &LangOpts) {
+ SourceLocation SpellingBegin = Sources.getSpellingLoc(Range.getBegin());
+ SourceLocation SpellingEnd = Sources.getSpellingLoc(Range.getEnd());
+ std::pair<FileID, unsigned> Start = Sources.getDecomposedLoc(SpellingBegin);
+ std::pair<FileID, unsigned> End = Sources.getDecomposedLoc(SpellingEnd);
+ if (Start.first != End.first) return -1;
+ if (Range.isTokenRange())
+ End.second += Lexer::MeasureTokenLength(SpellingEnd, Sources, LangOpts);
+ return End.second - Start.second;
+}
+
+void Replacement::setFromSourceRange(const SourceManager &Sources,
+ const CharSourceRange &Range,
+ StringRef ReplacementText,
+ const LangOptions &LangOpts) {
+ setFromSourceLocation(Sources, Sources.getSpellingLoc(Range.getBegin()),
+ getRangeSize(Sources, Range, LangOpts),
+ ReplacementText);
+}
+
+unsigned shiftedCodePosition(const Replacements &Replaces, unsigned Position) {
+ unsigned NewPosition = Position;
+ for (Replacements::iterator I = Replaces.begin(), E = Replaces.end(); I != E;
+ ++I) {
+ if (I->getOffset() >= Position)
+ break;
+ if (I->getOffset() + I->getLength() > Position)
+ NewPosition += I->getOffset() + I->getLength() - Position;
+ NewPosition += I->getReplacementText().size() - I->getLength();
+ }
+ return NewPosition;
+}
+
+// FIXME: Remove this function when Replacements is implemented as std::vector
+// instead of std::set.
+unsigned shiftedCodePosition(const std::vector<Replacement> &Replaces,
+ unsigned Position) {
+ unsigned NewPosition = Position;
+ for (std::vector<Replacement>::const_iterator I = Replaces.begin(),
+ E = Replaces.end();
+ I != E; ++I) {
+ if (I->getOffset() >= Position)
+ break;
+ if (I->getOffset() + I->getLength() > Position)
+ NewPosition += I->getOffset() + I->getLength() - Position;
+ NewPosition += I->getReplacementText().size() - I->getLength();
+ }
+ return NewPosition;
+}
+
+void deduplicate(std::vector<Replacement> &Replaces,
+ std::vector<Range> &Conflicts) {
+ if (Replaces.empty())
+ return;
+
+ auto LessNoPath = [](const Replacement &LHS, const Replacement &RHS) {
+ if (LHS.getOffset() != RHS.getOffset())
+ return LHS.getOffset() < RHS.getOffset();
+ if (LHS.getLength() != RHS.getLength())
+ return LHS.getLength() < RHS.getLength();
+ return LHS.getReplacementText() < RHS.getReplacementText();
+ };
+
+ auto EqualNoPath = [](const Replacement &LHS, const Replacement &RHS) {
+ return LHS.getOffset() == RHS.getOffset() &&
+ LHS.getLength() == RHS.getLength() &&
+ LHS.getReplacementText() == RHS.getReplacementText();
+ };
+
+ // Deduplicate. We don't want to deduplicate based on the path as we assume
+ // that all replacements refer to the same file (or are symlinks).
+ std::sort(Replaces.begin(), Replaces.end(), LessNoPath);
+ Replaces.erase(std::unique(Replaces.begin(), Replaces.end(), EqualNoPath),
+ Replaces.end());
+
+ // Detect conflicts
+ Range ConflictRange(Replaces.front().getOffset(),
+ Replaces.front().getLength());
+ unsigned ConflictStart = 0;
+ unsigned ConflictLength = 1;
+ for (unsigned i = 1; i < Replaces.size(); ++i) {
+ Range Current(Replaces[i].getOffset(), Replaces[i].getLength());
+ if (ConflictRange.overlapsWith(Current)) {
+ // Extend conflicted range
+ ConflictRange = Range(ConflictRange.getOffset(),
+ std::max(ConflictRange.getLength(),
+ Current.getOffset() + Current.getLength() -
+ ConflictRange.getOffset()));
+ ++ConflictLength;
+ } else {
+ if (ConflictLength > 1)
+ Conflicts.push_back(Range(ConflictStart, ConflictLength));
+ ConflictRange = Current;
+ ConflictStart = i;
+ ConflictLength = 1;
+ }
+ }
+
+ if (ConflictLength > 1)
+ Conflicts.push_back(Range(ConflictStart, ConflictLength));
+}
+
+bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite) {
+ bool Result = true;
+ for (Replacements::const_iterator I = Replaces.begin(),
+ E = Replaces.end();
+ I != E; ++I) {
+ if (I->isApplicable()) {
+ Result = I->apply(Rewrite) && Result;
+ } else {
+ Result = false;
+ }
+ }
+ return Result;
+}
+
+// FIXME: Remove this function when Replacements is implemented as std::vector
+// instead of std::set.
+bool applyAllReplacements(const std::vector<Replacement> &Replaces,
+ Rewriter &Rewrite) {
+ bool Result = true;
+ for (std::vector<Replacement>::const_iterator I = Replaces.begin(),
+ E = Replaces.end();
+ I != E; ++I) {
+ if (I->isApplicable()) {
+ Result = I->apply(Rewrite) && Result;
+ } else {
+ Result = false;
+ }
+ }
+ return Result;
+}
+
+std::string applyAllReplacements(StringRef Code, const Replacements &Replaces) {
+ FileManager Files((FileSystemOptions()));
+ DiagnosticsEngine Diagnostics(
+ IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
+ new DiagnosticOptions);
+ SourceManager SourceMgr(Diagnostics, Files);
+ Rewriter Rewrite(SourceMgr, LangOptions());
+ std::unique_ptr<llvm::MemoryBuffer> Buf =
+ llvm::MemoryBuffer::getMemBuffer(Code, "<stdin>");
+ const clang::FileEntry *Entry =
+ Files.getVirtualFile("<stdin>", Buf->getBufferSize(), 0);
+ SourceMgr.overrideFileContents(Entry, std::move(Buf));
+ FileID ID =
+ SourceMgr.createFileID(Entry, SourceLocation(), clang::SrcMgr::C_User);
+ for (Replacements::const_iterator I = Replaces.begin(), E = Replaces.end();
+ I != E; ++I) {
+ Replacement Replace("<stdin>", I->getOffset(), I->getLength(),
+ I->getReplacementText());
+ if (!Replace.apply(Rewrite))
+ return "";
+ }
+ std::string Result;
+ llvm::raw_string_ostream OS(Result);
+ Rewrite.getEditBuffer(ID).write(OS);
+ OS.flush();
+ return Result;
+}
+
+} // end namespace tooling
+} // end namespace clang
+
diff --git a/contrib/llvm/tools/clang/lib/Tooling/FileMatchTrie.cpp b/contrib/llvm/tools/clang/lib/Tooling/FileMatchTrie.cpp
new file mode 100644
index 0000000..86ed036
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/Tooling/FileMatchTrie.cpp
@@ -0,0 +1,189 @@
+//===--- FileMatchTrie.cpp - ----------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the implementation of a FileMatchTrie.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/FileMatchTrie.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+#include <sstream>
+using namespace clang;
+using namespace tooling;
+
+namespace {
+/// \brief Default \c PathComparator using \c llvm::sys::fs::equivalent().
+struct DefaultPathComparator : public PathComparator {
+ bool equivalent(StringRef FileA, StringRef FileB) const override {
+ return FileA == FileB || llvm::sys::fs::equivalent(FileA, FileB);
+ }
+};
+}
+
+namespace clang {
+namespace tooling {
+/// \brief A node of the \c FileMatchTrie.
+///
+/// Each node has storage for up to one path and a map mapping a path segment to
+/// child nodes. The trie starts with an empty root node.
+class FileMatchTrieNode {
+public:
+ /// \brief Inserts 'NewPath' into this trie. \c ConsumedLength denotes
+ /// the number of \c NewPath's trailing characters already consumed during
+ /// recursion.
+ ///
+ /// An insert of a path
+ /// 'p'starts at the root node and does the following:
+ /// - If the node is empty, insert 'p' into its storage and abort.
+ /// - If the node has a path 'p2' but no children, take the last path segment
+ /// 's' of 'p2', put a new child into the map at 's' an insert the rest of
+ /// 'p2' there.
+ /// - Insert a new child for the last segment of 'p' and insert the rest of
+ /// 'p' there.
+ ///
+ /// An insert operation is linear in the number of a path's segments.
+ void insert(StringRef NewPath, unsigned ConsumedLength = 0) {
+ // We cannot put relative paths into the FileMatchTrie as then a path can be
+ // a postfix of another path, violating a core assumption of the trie.
+ if (llvm::sys::path::is_relative(NewPath))
+ return;
+ if (Path.empty()) {
+ // This is an empty leaf. Store NewPath and return.
+ Path = NewPath;
+ return;
+ }
+ if (Children.empty()) {
+ // This is a leaf, ignore duplicate entry if 'Path' equals 'NewPath'.
+ if (NewPath == Path)
+ return;
+ // Make this a node and create a child-leaf with 'Path'.
+ StringRef Element(llvm::sys::path::filename(
+ StringRef(Path).drop_back(ConsumedLength)));
+ Children[Element].Path = Path;
+ }
+ StringRef Element(llvm::sys::path::filename(
+ StringRef(NewPath).drop_back(ConsumedLength)));
+ Children[Element].insert(NewPath, ConsumedLength + Element.size() + 1);
+ }
+
+ /// \brief Tries to find the node under this \c FileMatchTrieNode that best
+ /// matches 'FileName'.
+ ///
+ /// If multiple paths fit 'FileName' equally well, \c IsAmbiguous is set to
+ /// \c true and an empty string is returned. If no path fits 'FileName', an
+ /// empty string is returned. \c ConsumedLength denotes the number of
+ /// \c Filename's trailing characters already consumed during recursion.
+ ///
+ /// To find the best matching node for a given path 'p', the
+ /// \c findEquivalent() function is called recursively for each path segment
+ /// (back to fron) of 'p' until a node 'n' is reached that does not ..
+ /// - .. have children. In this case it is checked
+ /// whether the stored path is equivalent to 'p'. If yes, the best match is
+ /// found. Otherwise continue with the parent node as if this node did not
+ /// exist.
+ /// - .. a child matching the next path segment. In this case, all children of
+ /// 'n' are an equally good match for 'p'. All children are of 'n' are found
+ /// recursively and their equivalence to 'p' is determined. If none are
+ /// equivalent, continue with the parent node as if 'n' didn't exist. If one
+ /// is equivalent, the best match is found. Otherwise, report and ambigiuity
+ /// error.
+ StringRef findEquivalent(const PathComparator& Comparator,
+ StringRef FileName,
+ bool &IsAmbiguous,
+ unsigned ConsumedLength = 0) const {
+ if (Children.empty()) {
+ if (Comparator.equivalent(StringRef(Path), FileName))
+ return StringRef(Path);
+ return StringRef();
+ }
+ StringRef Element(llvm::sys::path::filename(FileName.drop_back(
+ ConsumedLength)));
+ llvm::StringMap<FileMatchTrieNode>::const_iterator MatchingChild =
+ Children.find(Element);
+ if (MatchingChild != Children.end()) {
+ StringRef Result = MatchingChild->getValue().findEquivalent(
+ Comparator, FileName, IsAmbiguous,
+ ConsumedLength + Element.size() + 1);
+ if (!Result.empty() || IsAmbiguous)
+ return Result;
+ }
+ std::vector<StringRef> AllChildren;
+ getAll(AllChildren, MatchingChild);
+ StringRef Result;
+ for (unsigned i = 0; i < AllChildren.size(); i++) {
+ if (Comparator.equivalent(AllChildren[i], FileName)) {
+ if (Result.empty()) {
+ Result = AllChildren[i];
+ } else {
+ IsAmbiguous = true;
+ return StringRef();
+ }
+ }
+ }
+ return Result;
+ }
+
+private:
+ /// \brief Gets all paths under this FileMatchTrieNode.
+ void getAll(std::vector<StringRef> &Results,
+ llvm::StringMap<FileMatchTrieNode>::const_iterator Except) const {
+ if (Path.empty())
+ return;
+ if (Children.empty()) {
+ Results.push_back(StringRef(Path));
+ return;
+ }
+ for (llvm::StringMap<FileMatchTrieNode>::const_iterator
+ It = Children.begin(), E = Children.end();
+ It != E; ++It) {
+ if (It == Except)
+ continue;
+ It->getValue().getAll(Results, Children.end());
+ }
+ }
+
+ // The stored absolute path in this node. Only valid for leaf nodes, i.e.
+ // nodes where Children.empty().
+ std::string Path;
+
+ // The children of this node stored in a map based on the next path segment.
+ llvm::StringMap<FileMatchTrieNode> Children;
+};
+} // end namespace tooling
+} // end namespace clang
+
+FileMatchTrie::FileMatchTrie()
+ : Root(new FileMatchTrieNode), Comparator(new DefaultPathComparator()) {}
+
+FileMatchTrie::FileMatchTrie(PathComparator *Comparator)
+ : Root(new FileMatchTrieNode), Comparator(Comparator) {}
+
+FileMatchTrie::~FileMatchTrie() {
+ delete Root;
+}
+
+void FileMatchTrie::insert(StringRef NewPath) {
+ Root->insert(NewPath);
+}
+
+StringRef FileMatchTrie::findEquivalent(StringRef FileName,
+ raw_ostream &Error) const {
+ if (llvm::sys::path::is_relative(FileName)) {
+ Error << "Cannot resolve relative paths";
+ return StringRef();
+ }
+ bool IsAmbiguous = false;
+ StringRef Result = Root->findEquivalent(*Comparator, FileName, IsAmbiguous);
+ if (IsAmbiguous)
+ Error << "Path is ambiguous";
+ return Result;
+}
diff --git a/contrib/llvm/tools/clang/lib/Tooling/JSONCompilationDatabase.cpp b/contrib/llvm/tools/clang/lib/Tooling/JSONCompilationDatabase.cpp
new file mode 100644
index 0000000..454a2ff
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/Tooling/JSONCompilationDatabase.cpp
@@ -0,0 +1,322 @@
+//===--- JSONCompilationDatabase.cpp - ------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the implementation of the JSONCompilationDatabase.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/JSONCompilationDatabase.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/CompilationDatabasePluginRegistry.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Path.h"
+#include <system_error>
+
+namespace clang {
+namespace tooling {
+
+namespace {
+
+/// \brief A parser for escaped strings of command line arguments.
+///
+/// Assumes \-escaping for quoted arguments (see the documentation of
+/// unescapeCommandLine(...)).
+class CommandLineArgumentParser {
+ public:
+ CommandLineArgumentParser(StringRef CommandLine)
+ : Input(CommandLine), Position(Input.begin()-1) {}
+
+ std::vector<std::string> parse() {
+ bool HasMoreInput = true;
+ while (HasMoreInput && nextNonWhitespace()) {
+ std::string Argument;
+ HasMoreInput = parseStringInto(Argument);
+ CommandLine.push_back(Argument);
+ }
+ return CommandLine;
+ }
+
+ private:
+ // All private methods return true if there is more input available.
+
+ bool parseStringInto(std::string &String) {
+ do {
+ if (*Position == '"') {
+ if (!parseDoubleQuotedStringInto(String)) return false;
+ } else if (*Position == '\'') {
+ if (!parseSingleQuotedStringInto(String)) return false;
+ } else {
+ if (!parseFreeStringInto(String)) return false;
+ }
+ } while (*Position != ' ');
+ return true;
+ }
+
+ bool parseDoubleQuotedStringInto(std::string &String) {
+ if (!next()) return false;
+ while (*Position != '"') {
+ if (!skipEscapeCharacter()) return false;
+ String.push_back(*Position);
+ if (!next()) return false;
+ }
+ return next();
+ }
+
+ bool parseSingleQuotedStringInto(std::string &String) {
+ if (!next()) return false;
+ while (*Position != '\'') {
+ String.push_back(*Position);
+ if (!next()) return false;
+ }
+ return next();
+ }
+
+ bool parseFreeStringInto(std::string &String) {
+ do {
+ if (!skipEscapeCharacter()) return false;
+ String.push_back(*Position);
+ if (!next()) return false;
+ } while (*Position != ' ' && *Position != '"' && *Position != '\'');
+ return true;
+ }
+
+ bool skipEscapeCharacter() {
+ if (*Position == '\\') {
+ return next();
+ }
+ return true;
+ }
+
+ bool nextNonWhitespace() {
+ do {
+ if (!next()) return false;
+ } while (*Position == ' ');
+ return true;
+ }
+
+ bool next() {
+ ++Position;
+ return Position != Input.end();
+ }
+
+ const StringRef Input;
+ StringRef::iterator Position;
+ std::vector<std::string> CommandLine;
+};
+
+std::vector<std::string> unescapeCommandLine(
+ StringRef EscapedCommandLine) {
+ CommandLineArgumentParser parser(EscapedCommandLine);
+ return parser.parse();
+}
+
+class JSONCompilationDatabasePlugin : public CompilationDatabasePlugin {
+ std::unique_ptr<CompilationDatabase>
+ loadFromDirectory(StringRef Directory, std::string &ErrorMessage) override {
+ SmallString<1024> JSONDatabasePath(Directory);
+ llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
+ std::unique_ptr<CompilationDatabase> Database(
+ JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage));
+ if (!Database)
+ return nullptr;
+ return Database;
+ }
+};
+
+} // end namespace
+
+// Register the JSONCompilationDatabasePlugin with the
+// CompilationDatabasePluginRegistry using this statically initialized variable.
+static CompilationDatabasePluginRegistry::Add<JSONCompilationDatabasePlugin>
+X("json-compilation-database", "Reads JSON formatted compilation databases");
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the JSONCompilationDatabasePlugin.
+volatile int JSONAnchorSource = 0;
+
+std::unique_ptr<JSONCompilationDatabase>
+JSONCompilationDatabase::loadFromFile(StringRef FilePath,
+ std::string &ErrorMessage) {
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> DatabaseBuffer =
+ llvm::MemoryBuffer::getFile(FilePath);
+ if (std::error_code Result = DatabaseBuffer.getError()) {
+ ErrorMessage = "Error while opening JSON database: " + Result.message();
+ return nullptr;
+ }
+ std::unique_ptr<JSONCompilationDatabase> Database(
+ new JSONCompilationDatabase(std::move(*DatabaseBuffer)));
+ if (!Database->parse(ErrorMessage))
+ return nullptr;
+ return Database;
+}
+
+std::unique_ptr<JSONCompilationDatabase>
+JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
+ std::string &ErrorMessage) {
+ std::unique_ptr<llvm::MemoryBuffer> DatabaseBuffer(
+ llvm::MemoryBuffer::getMemBuffer(DatabaseString));
+ std::unique_ptr<JSONCompilationDatabase> Database(
+ new JSONCompilationDatabase(std::move(DatabaseBuffer)));
+ if (!Database->parse(ErrorMessage))
+ return nullptr;
+ return Database;
+}
+
+std::vector<CompileCommand>
+JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
+ SmallString<128> NativeFilePath;
+ llvm::sys::path::native(FilePath, NativeFilePath);
+
+ std::string Error;
+ llvm::raw_string_ostream ES(Error);
+ StringRef Match = MatchTrie.findEquivalent(NativeFilePath, ES);
+ if (Match.empty())
+ return std::vector<CompileCommand>();
+ llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
+ CommandsRefI = IndexByFile.find(Match);
+ if (CommandsRefI == IndexByFile.end())
+ return std::vector<CompileCommand>();
+ std::vector<CompileCommand> Commands;
+ getCommands(CommandsRefI->getValue(), Commands);
+ return Commands;
+}
+
+std::vector<std::string>
+JSONCompilationDatabase::getAllFiles() const {
+ std::vector<std::string> Result;
+
+ llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
+ CommandsRefI = IndexByFile.begin();
+ const llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
+ CommandsRefEnd = IndexByFile.end();
+ for (; CommandsRefI != CommandsRefEnd; ++CommandsRefI) {
+ Result.push_back(CommandsRefI->first().str());
+ }
+
+ return Result;
+}
+
+std::vector<CompileCommand>
+JSONCompilationDatabase::getAllCompileCommands() const {
+ std::vector<CompileCommand> Commands;
+ for (llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
+ CommandsRefI = IndexByFile.begin(), CommandsRefEnd = IndexByFile.end();
+ CommandsRefI != CommandsRefEnd; ++CommandsRefI) {
+ getCommands(CommandsRefI->getValue(), Commands);
+ }
+ return Commands;
+}
+
+void JSONCompilationDatabase::getCommands(
+ ArrayRef<CompileCommandRef> CommandsRef,
+ std::vector<CompileCommand> &Commands) const {
+ for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
+ SmallString<8> DirectoryStorage;
+ SmallString<1024> CommandStorage;
+ Commands.emplace_back(
+ // FIXME: Escape correctly:
+ CommandsRef[I].first->getValue(DirectoryStorage),
+ unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage)));
+ }
+}
+
+bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
+ llvm::yaml::document_iterator I = YAMLStream.begin();
+ if (I == YAMLStream.end()) {
+ ErrorMessage = "Error while parsing YAML.";
+ return false;
+ }
+ llvm::yaml::Node *Root = I->getRoot();
+ if (!Root) {
+ ErrorMessage = "Error while parsing YAML.";
+ return false;
+ }
+ llvm::yaml::SequenceNode *Array = dyn_cast<llvm::yaml::SequenceNode>(Root);
+ if (!Array) {
+ ErrorMessage = "Expected array.";
+ return false;
+ }
+ for (llvm::yaml::SequenceNode::iterator AI = Array->begin(),
+ AE = Array->end();
+ AI != AE; ++AI) {
+ llvm::yaml::MappingNode *Object = dyn_cast<llvm::yaml::MappingNode>(&*AI);
+ if (!Object) {
+ ErrorMessage = "Expected object.";
+ return false;
+ }
+ llvm::yaml::ScalarNode *Directory = nullptr;
+ llvm::yaml::ScalarNode *Command = nullptr;
+ llvm::yaml::ScalarNode *File = nullptr;
+ for (llvm::yaml::MappingNode::iterator KVI = Object->begin(),
+ KVE = Object->end();
+ KVI != KVE; ++KVI) {
+ llvm::yaml::Node *Value = (*KVI).getValue();
+ if (!Value) {
+ ErrorMessage = "Expected value.";
+ return false;
+ }
+ llvm::yaml::ScalarNode *ValueString =
+ dyn_cast<llvm::yaml::ScalarNode>(Value);
+ if (!ValueString) {
+ ErrorMessage = "Expected string as value.";
+ return false;
+ }
+ llvm::yaml::ScalarNode *KeyString =
+ dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
+ if (!KeyString) {
+ ErrorMessage = "Expected strings as key.";
+ return false;
+ }
+ SmallString<8> KeyStorage;
+ if (KeyString->getValue(KeyStorage) == "directory") {
+ Directory = ValueString;
+ } else if (KeyString->getValue(KeyStorage) == "command") {
+ Command = ValueString;
+ } else if (KeyString->getValue(KeyStorage) == "file") {
+ File = ValueString;
+ } else {
+ ErrorMessage = ("Unknown key: \"" +
+ KeyString->getRawValue() + "\"").str();
+ return false;
+ }
+ }
+ if (!File) {
+ ErrorMessage = "Missing key: \"file\".";
+ return false;
+ }
+ if (!Command) {
+ ErrorMessage = "Missing key: \"command\".";
+ return false;
+ }
+ if (!Directory) {
+ ErrorMessage = "Missing key: \"directory\".";
+ return false;
+ }
+ SmallString<8> FileStorage;
+ StringRef FileName = File->getValue(FileStorage);
+ SmallString<128> NativeFilePath;
+ if (llvm::sys::path::is_relative(FileName)) {
+ SmallString<8> DirectoryStorage;
+ SmallString<128> AbsolutePath(
+ Directory->getValue(DirectoryStorage));
+ llvm::sys::path::append(AbsolutePath, FileName);
+ llvm::sys::path::native(AbsolutePath, NativeFilePath);
+ } else {
+ llvm::sys::path::native(FileName, NativeFilePath);
+ }
+ IndexByFile[NativeFilePath].push_back(
+ CompileCommandRef(Directory, Command));
+ MatchTrie.insert(NativeFilePath);
+ }
+ return true;
+}
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/contrib/llvm/tools/clang/lib/Tooling/Refactoring.cpp b/contrib/llvm/tools/clang/lib/Tooling/Refactoring.cpp
new file mode 100644
index 0000000..d32452f
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/Tooling/Refactoring.cpp
@@ -0,0 +1,65 @@
+//===--- 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.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements tools to support refactorings.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/Refactoring.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_os_ostream.h"
+
+namespace clang {
+namespace tooling {
+
+RefactoringTool::RefactoringTool(
+ const CompilationDatabase &Compilations, ArrayRef<std::string> SourcePaths,
+ std::shared_ptr<PCHContainerOperations> PCHContainerOps)
+ : ClangTool(Compilations, SourcePaths, PCHContainerOps) {}
+
+Replacements &RefactoringTool::getReplacements() { return Replace; }
+
+int RefactoringTool::runAndSave(FrontendActionFactory *ActionFactory) {
+ if (int Result = run(ActionFactory)) {
+ return Result;
+ }
+
+ LangOptions DefaultLangOptions;
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
+ TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), &*DiagOpts);
+ DiagnosticsEngine Diagnostics(
+ IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
+ &*DiagOpts, &DiagnosticPrinter, false);
+ SourceManager Sources(Diagnostics, getFiles());
+ Rewriter Rewrite(Sources, DefaultLangOptions);
+
+ if (!applyAllReplacements(Rewrite)) {
+ llvm::errs() << "Skipped some replacements.\n";
+ }
+
+ return saveRewrittenFiles(Rewrite);
+}
+
+bool RefactoringTool::applyAllReplacements(Rewriter &Rewrite) {
+ return tooling::applyAllReplacements(Replace, Rewrite);
+}
+
+int RefactoringTool::saveRewrittenFiles(Rewriter &Rewrite) {
+ return Rewrite.overwriteChangedFiles() ? 1 : 0;
+}
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/contrib/llvm/tools/clang/lib/Tooling/RefactoringCallbacks.cpp b/contrib/llvm/tools/clang/lib/Tooling/RefactoringCallbacks.cpp
new file mode 100644
index 0000000..4de125e
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/Tooling/RefactoringCallbacks.cpp
@@ -0,0 +1,81 @@
+//===--- 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.
+//
+//===----------------------------------------------------------------------===//
+//
+//
+//===----------------------------------------------------------------------===//
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/RefactoringCallbacks.h"
+
+namespace clang {
+namespace tooling {
+
+RefactoringCallback::RefactoringCallback() {}
+tooling::Replacements &RefactoringCallback::getReplacements() {
+ return Replace;
+}
+
+static Replacement replaceStmtWithText(SourceManager &Sources,
+ const Stmt &From,
+ StringRef Text) {
+ return tooling::Replacement(Sources, CharSourceRange::getTokenRange(
+ From.getSourceRange()), Text);
+}
+static Replacement replaceStmtWithStmt(SourceManager &Sources,
+ const Stmt &From,
+ const Stmt &To) {
+ return replaceStmtWithText(Sources, From, Lexer::getSourceText(
+ CharSourceRange::getTokenRange(To.getSourceRange()),
+ Sources, LangOptions()));
+}
+
+ReplaceStmtWithText::ReplaceStmtWithText(StringRef FromId, StringRef ToText)
+ : FromId(FromId), ToText(ToText) {}
+
+void ReplaceStmtWithText::run(
+ const ast_matchers::MatchFinder::MatchResult &Result) {
+ if (const Stmt *FromMatch = Result.Nodes.getStmtAs<Stmt>(FromId)) {
+ Replace.insert(tooling::Replacement(
+ *Result.SourceManager,
+ CharSourceRange::getTokenRange(FromMatch->getSourceRange()),
+ ToText));
+ }
+}
+
+ReplaceStmtWithStmt::ReplaceStmtWithStmt(StringRef FromId, StringRef ToId)
+ : FromId(FromId), ToId(ToId) {}
+
+void ReplaceStmtWithStmt::run(
+ const ast_matchers::MatchFinder::MatchResult &Result) {
+ const Stmt *FromMatch = Result.Nodes.getStmtAs<Stmt>(FromId);
+ const Stmt *ToMatch = Result.Nodes.getStmtAs<Stmt>(ToId);
+ if (FromMatch && ToMatch)
+ Replace.insert(replaceStmtWithStmt(
+ *Result.SourceManager, *FromMatch, *ToMatch));
+}
+
+ReplaceIfStmtWithItsBody::ReplaceIfStmtWithItsBody(StringRef Id,
+ bool PickTrueBranch)
+ : Id(Id), PickTrueBranch(PickTrueBranch) {}
+
+void ReplaceIfStmtWithItsBody::run(
+ const ast_matchers::MatchFinder::MatchResult &Result) {
+ if (const IfStmt *Node = Result.Nodes.getStmtAs<IfStmt>(Id)) {
+ const Stmt *Body = PickTrueBranch ? Node->getThen() : Node->getElse();
+ if (Body) {
+ Replace.insert(replaceStmtWithStmt(*Result.SourceManager, *Node, *Body));
+ } else if (!PickTrueBranch) {
+ // If we want to use the 'else'-branch, but it doesn't exist, delete
+ // the whole 'if'.
+ Replace.insert(replaceStmtWithText(*Result.SourceManager, *Node, ""));
+ }
+ }
+}
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/contrib/llvm/tools/clang/lib/Tooling/Tooling.cpp b/contrib/llvm/tools/clang/lib/Tooling/Tooling.cpp
new file mode 100644
index 0000000..f9cb7c6
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/Tooling/Tooling.cpp
@@ -0,0 +1,446 @@
+//===--- 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.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements functions to run clang tools standalone instead
+// of running them as a plugin.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Tooling.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/Driver/Compilation.h"
+#include "clang/Driver/Driver.h"
+#include "clang/Driver/Tool.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendDiagnostic.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Tooling/ArgumentsAdjusters.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/raw_ostream.h"
+
+// For chdir, see the comment in ClangTool::run for more information.
+#ifdef LLVM_ON_WIN32
+# include <direct.h>
+#else
+# include <unistd.h>
+#endif
+
+#define DEBUG_TYPE "clang-tooling"
+
+namespace clang {
+namespace tooling {
+
+ToolAction::~ToolAction() {}
+
+FrontendActionFactory::~FrontendActionFactory() {}
+
+// FIXME: This file contains structural duplication with other parts of the
+// code that sets up a compiler to run tools on it, and we should refactor
+// it to be based on the same framework.
+
+/// \brief Builds a clang driver initialized for running clang tools.
+static clang::driver::Driver *newDriver(clang::DiagnosticsEngine *Diagnostics,
+ const char *BinaryName) {
+ clang::driver::Driver *CompilerDriver = new clang::driver::Driver(
+ BinaryName, llvm::sys::getDefaultTargetTriple(), *Diagnostics);
+ CompilerDriver->setTitle("clang_based_tool");
+ return CompilerDriver;
+}
+
+/// \brief Retrieves the clang CC1 specific flags out of the compilation's jobs.
+///
+/// Returns NULL on error.
+static const llvm::opt::ArgStringList *getCC1Arguments(
+ clang::DiagnosticsEngine *Diagnostics,
+ clang::driver::Compilation *Compilation) {
+ // We expect to get back exactly one Command job, if we didn't something
+ // failed. Extract that job from the Compilation.
+ const clang::driver::JobList &Jobs = Compilation->getJobs();
+ if (Jobs.size() != 1 || !isa<clang::driver::Command>(*Jobs.begin())) {
+ SmallString<256> error_msg;
+ llvm::raw_svector_ostream error_stream(error_msg);
+ Jobs.Print(error_stream, "; ", true);
+ Diagnostics->Report(clang::diag::err_fe_expected_compiler_job)
+ << error_stream.str();
+ return nullptr;
+ }
+
+ // The one job we find should be to invoke clang again.
+ const clang::driver::Command &Cmd =
+ cast<clang::driver::Command>(*Jobs.begin());
+ if (StringRef(Cmd.getCreator().getName()) != "clang") {
+ Diagnostics->Report(clang::diag::err_fe_expected_clang_command);
+ return nullptr;
+ }
+
+ return &Cmd.getArguments();
+}
+
+/// \brief Returns a clang build invocation initialized from the CC1 flags.
+clang::CompilerInvocation *newInvocation(
+ clang::DiagnosticsEngine *Diagnostics,
+ const llvm::opt::ArgStringList &CC1Args) {
+ assert(!CC1Args.empty() && "Must at least contain the program name!");
+ clang::CompilerInvocation *Invocation = new clang::CompilerInvocation;
+ clang::CompilerInvocation::CreateFromArgs(
+ *Invocation, CC1Args.data() + 1, CC1Args.data() + CC1Args.size(),
+ *Diagnostics);
+ Invocation->getFrontendOpts().DisableFree = false;
+ Invocation->getCodeGenOpts().DisableFree = false;
+ Invocation->getDependencyOutputOpts() = DependencyOutputOptions();
+ return Invocation;
+}
+
+bool runToolOnCode(clang::FrontendAction *ToolAction, const Twine &Code,
+ const Twine &FileName,
+ std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
+ return runToolOnCodeWithArgs(ToolAction, Code, std::vector<std::string>(),
+ FileName, PCHContainerOps);
+}
+
+static std::vector<std::string>
+getSyntaxOnlyToolArgs(const std::vector<std::string> &ExtraArgs,
+ StringRef FileName) {
+ std::vector<std::string> Args;
+ Args.push_back("clang-tool");
+ Args.push_back("-fsyntax-only");
+ Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
+ Args.push_back(FileName.str());
+ return Args;
+}
+
+bool runToolOnCodeWithArgs(
+ clang::FrontendAction *ToolAction, const Twine &Code,
+ const std::vector<std::string> &Args, const Twine &FileName,
+ std::shared_ptr<PCHContainerOperations> PCHContainerOps,
+ const FileContentMappings &VirtualMappedFiles) {
+
+ SmallString<16> FileNameStorage;
+ StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
+ llvm::IntrusiveRefCntPtr<FileManager> Files(
+ new FileManager(FileSystemOptions()));
+ ToolInvocation Invocation(getSyntaxOnlyToolArgs(Args, FileNameRef),
+ ToolAction, Files.get(), PCHContainerOps);
+
+ SmallString<1024> CodeStorage;
+ Invocation.mapVirtualFile(FileNameRef,
+ Code.toNullTerminatedStringRef(CodeStorage));
+
+ for (auto &FilenameWithContent : VirtualMappedFiles) {
+ Invocation.mapVirtualFile(FilenameWithContent.first,
+ FilenameWithContent.second);
+ }
+
+ return Invocation.run();
+}
+
+std::string getAbsolutePath(StringRef File) {
+ StringRef RelativePath(File);
+ // FIXME: Should '.\\' be accepted on Win32?
+ if (RelativePath.startswith("./")) {
+ RelativePath = RelativePath.substr(strlen("./"));
+ }
+
+ SmallString<1024> AbsolutePath = RelativePath;
+ std::error_code EC = llvm::sys::fs::make_absolute(AbsolutePath);
+ assert(!EC);
+ (void)EC;
+ llvm::sys::path::native(AbsolutePath);
+ return AbsolutePath.str();
+}
+
+namespace {
+
+class SingleFrontendActionFactory : public FrontendActionFactory {
+ FrontendAction *Action;
+
+public:
+ SingleFrontendActionFactory(FrontendAction *Action) : Action(Action) {}
+
+ FrontendAction *create() override { return Action; }
+};
+
+}
+
+ToolInvocation::ToolInvocation(
+ std::vector<std::string> CommandLine, ToolAction *Action,
+ FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
+ : CommandLine(std::move(CommandLine)), Action(Action), OwnsAction(false),
+ Files(Files), PCHContainerOps(PCHContainerOps), DiagConsumer(nullptr) {}
+
+ToolInvocation::ToolInvocation(
+ std::vector<std::string> CommandLine, FrontendAction *FAction,
+ FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
+ : CommandLine(std::move(CommandLine)),
+ Action(new SingleFrontendActionFactory(FAction)), OwnsAction(true),
+ Files(Files), PCHContainerOps(PCHContainerOps), DiagConsumer(nullptr) {}
+
+ToolInvocation::~ToolInvocation() {
+ if (OwnsAction)
+ delete Action;
+}
+
+void ToolInvocation::mapVirtualFile(StringRef FilePath, StringRef Content) {
+ SmallString<1024> PathStorage;
+ llvm::sys::path::native(FilePath, PathStorage);
+ MappedFileContents[PathStorage] = Content;
+}
+
+bool ToolInvocation::run() {
+ std::vector<const char*> Argv;
+ for (const std::string &Str : CommandLine)
+ Argv.push_back(Str.c_str());
+ const char *const BinaryName = Argv[0];
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
+ TextDiagnosticPrinter DiagnosticPrinter(
+ llvm::errs(), &*DiagOpts);
+ DiagnosticsEngine Diagnostics(
+ IntrusiveRefCntPtr<clang::DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
+ DiagConsumer ? DiagConsumer : &DiagnosticPrinter, false);
+
+ const std::unique_ptr<clang::driver::Driver> Driver(
+ newDriver(&Diagnostics, BinaryName));
+ // Since the input might only be virtual, don't check whether it exists.
+ Driver->setCheckInputsExist(false);
+ const std::unique_ptr<clang::driver::Compilation> Compilation(
+ Driver->BuildCompilation(llvm::makeArrayRef(Argv)));
+ const llvm::opt::ArgStringList *const CC1Args = getCC1Arguments(
+ &Diagnostics, Compilation.get());
+ if (!CC1Args) {
+ return false;
+ }
+ std::unique_ptr<clang::CompilerInvocation> Invocation(
+ newInvocation(&Diagnostics, *CC1Args));
+ for (const auto &It : MappedFileContents) {
+ // Inject the code as the given file name into the preprocessor options.
+ std::unique_ptr<llvm::MemoryBuffer> Input =
+ llvm::MemoryBuffer::getMemBuffer(It.getValue());
+ Invocation->getPreprocessorOpts().addRemappedFile(It.getKey(),
+ Input.release());
+ }
+ return runInvocation(BinaryName, Compilation.get(), Invocation.release(),
+ PCHContainerOps);
+}
+
+bool ToolInvocation::runInvocation(
+ const char *BinaryName, clang::driver::Compilation *Compilation,
+ clang::CompilerInvocation *Invocation,
+ std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
+ // Show the invocation, with -v.
+ if (Invocation->getHeaderSearchOpts().Verbose) {
+ llvm::errs() << "clang Invocation:\n";
+ Compilation->getJobs().Print(llvm::errs(), "\n", true);
+ llvm::errs() << "\n";
+ }
+
+ return Action->runInvocation(Invocation, Files, PCHContainerOps,
+ DiagConsumer);
+}
+
+bool FrontendActionFactory::runInvocation(
+ CompilerInvocation *Invocation, FileManager *Files,
+ std::shared_ptr<PCHContainerOperations> PCHContainerOps,
+ DiagnosticConsumer *DiagConsumer) {
+ // Create a compiler instance to handle the actual work.
+ clang::CompilerInstance Compiler(PCHContainerOps);
+ Compiler.setInvocation(Invocation);
+ Compiler.setFileManager(Files);
+
+ // The FrontendAction can have lifetime requirements for Compiler or its
+ // members, and we need to ensure it's deleted earlier than Compiler. So we
+ // pass it to an std::unique_ptr declared after the Compiler variable.
+ std::unique_ptr<FrontendAction> ScopedToolAction(create());
+
+ // Create the compiler's actual diagnostics engine.
+ Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
+ if (!Compiler.hasDiagnostics())
+ return false;
+
+ Compiler.createSourceManager(*Files);
+
+ const bool Success = Compiler.ExecuteAction(*ScopedToolAction);
+
+ Files->clearStatCaches();
+ return Success;
+}
+
+ClangTool::ClangTool(const CompilationDatabase &Compilations,
+ ArrayRef<std::string> SourcePaths,
+ std::shared_ptr<PCHContainerOperations> PCHContainerOps)
+ : Compilations(Compilations), SourcePaths(SourcePaths),
+ PCHContainerOps(PCHContainerOps),
+ Files(new FileManager(FileSystemOptions())), DiagConsumer(nullptr) {
+ appendArgumentsAdjuster(getClangStripOutputAdjuster());
+ appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
+}
+
+ClangTool::~ClangTool() {}
+
+void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) {
+ MappedFileContents.push_back(std::make_pair(FilePath, Content));
+}
+
+void ClangTool::appendArgumentsAdjuster(ArgumentsAdjuster Adjuster) {
+ if (ArgsAdjuster)
+ ArgsAdjuster = combineAdjusters(ArgsAdjuster, Adjuster);
+ else
+ ArgsAdjuster = Adjuster;
+}
+
+void ClangTool::clearArgumentsAdjusters() {
+ ArgsAdjuster = nullptr;
+}
+
+int ClangTool::run(ToolAction *Action) {
+ // Exists solely for the purpose of lookup of the resource path.
+ // This just needs to be some symbol in the binary.
+ static int StaticSymbol;
+ // The driver detects the builtin header path based on the path of the
+ // executable.
+ // FIXME: On linux, GetMainExecutable is independent of the value of the
+ // first argument, thus allowing ClangTool and runToolOnCode to just
+ // pass in made-up names here. Make sure this works on other platforms.
+ std::string MainExecutable =
+ llvm::sys::fs::getMainExecutable("clang_tool", &StaticSymbol);
+
+ llvm::SmallString<128> InitialDirectory;
+ if (std::error_code EC = llvm::sys::fs::current_path(InitialDirectory))
+ llvm::report_fatal_error("Cannot detect current path: " +
+ Twine(EC.message()));
+ bool ProcessingFailed = false;
+ for (const auto &SourcePath : SourcePaths) {
+ std::string File(getAbsolutePath(SourcePath));
+
+ // Currently implementations of CompilationDatabase::getCompileCommands can
+ // change the state of the file system (e.g. prepare generated headers), so
+ // this method needs to run right before we invoke the tool, as the next
+ // file may require a different (incompatible) state of the file system.
+ //
+ // FIXME: Make the compilation database interface more explicit about the
+ // requirements to the order of invocation of its members.
+ std::vector<CompileCommand> CompileCommandsForFile =
+ Compilations.getCompileCommands(File);
+ if (CompileCommandsForFile.empty()) {
+ // FIXME: There are two use cases here: doing a fuzzy
+ // "find . -name '*.cc' |xargs tool" match, where as a user I don't care
+ // about the .cc files that were not found, and the use case where I
+ // specify all files I want to run over explicitly, where this should
+ // be an error. We'll want to add an option for this.
+ llvm::errs() << "Skipping " << File << ". Compile command not found.\n";
+ continue;
+ }
+ for (CompileCommand &CompileCommand : CompileCommandsForFile) {
+ // FIXME: chdir is thread hostile; on the other hand, creating the same
+ // behavior as chdir is complex: chdir resolves the path once, thus
+ // guaranteeing that all subsequent relative path operations work
+ // on the same path the original chdir resulted in. This makes a
+ // difference for example on network filesystems, where symlinks might be
+ // switched during runtime of the tool. Fixing this depends on having a
+ // file system abstraction that allows openat() style interactions.
+ if (chdir(CompileCommand.Directory.c_str()))
+ llvm::report_fatal_error("Cannot chdir into \"" +
+ Twine(CompileCommand.Directory) + "\n!");
+ std::vector<std::string> CommandLine = CompileCommand.CommandLine;
+ if (ArgsAdjuster)
+ CommandLine = ArgsAdjuster(CommandLine);
+ assert(!CommandLine.empty());
+ CommandLine[0] = MainExecutable;
+ // FIXME: We need a callback mechanism for the tool writer to output a
+ // customized message for each file.
+ DEBUG({ llvm::dbgs() << "Processing: " << File << ".\n"; });
+ ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
+ PCHContainerOps);
+ Invocation.setDiagnosticConsumer(DiagConsumer);
+ for (const auto &MappedFile : MappedFileContents)
+ Invocation.mapVirtualFile(MappedFile.first, MappedFile.second);
+ if (!Invocation.run()) {
+ // FIXME: Diagnostics should be used instead.
+ llvm::errs() << "Error while processing " << File << ".\n";
+ ProcessingFailed = true;
+ }
+ // Return to the initial directory to correctly resolve next file by
+ // relative path.
+ if (chdir(InitialDirectory.c_str()))
+ llvm::report_fatal_error("Cannot chdir into \"" +
+ Twine(InitialDirectory) + "\n!");
+ }
+ }
+ return ProcessingFailed ? 1 : 0;
+}
+
+namespace {
+
+class ASTBuilderAction : public ToolAction {
+ std::vector<std::unique_ptr<ASTUnit>> &ASTs;
+
+public:
+ ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &ASTs) : ASTs(ASTs) {}
+
+ bool runInvocation(CompilerInvocation *Invocation, FileManager *Files,
+ std::shared_ptr<PCHContainerOperations> PCHContainerOps,
+ DiagnosticConsumer *DiagConsumer) override {
+ // FIXME: This should use the provided FileManager.
+ std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
+ Invocation, PCHContainerOps,
+ CompilerInstance::createDiagnostics(&Invocation->getDiagnosticOpts(),
+ DiagConsumer,
+ /*ShouldOwnClient=*/false));
+ if (!AST)
+ return false;
+
+ ASTs.push_back(std::move(AST));
+ return true;
+ }
+};
+
+}
+
+int ClangTool::buildASTs(std::vector<std::unique_ptr<ASTUnit>> &ASTs) {
+ ASTBuilderAction Action(ASTs);
+ return run(&Action);
+}
+
+std::unique_ptr<ASTUnit>
+buildASTFromCode(const Twine &Code, const Twine &FileName,
+ std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
+ return buildASTFromCodeWithArgs(Code, std::vector<std::string>(), FileName,
+ PCHContainerOps);
+}
+
+std::unique_ptr<ASTUnit> buildASTFromCodeWithArgs(
+ const Twine &Code, const std::vector<std::string> &Args,
+ const Twine &FileName,
+ std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
+ SmallString<16> FileNameStorage;
+ StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
+
+ std::vector<std::unique_ptr<ASTUnit>> ASTs;
+ ASTBuilderAction Action(ASTs);
+ ToolInvocation Invocation(getSyntaxOnlyToolArgs(Args, FileNameRef), &Action,
+ nullptr, PCHContainerOps);
+
+ SmallString<1024> CodeStorage;
+ Invocation.mapVirtualFile(FileNameRef,
+ Code.toNullTerminatedStringRef(CodeStorage));
+ if (!Invocation.run())
+ return nullptr;
+
+ assert(ASTs.size() == 1);
+ return std::move(ASTs[0]);
+}
+
+} // end namespace tooling
+} // end namespace clang
OpenPOWER on IntegriCloud