зеркало из https://github.com/microsoft/clang.git
This is the next step in building the standalone tools infrastructure:
This patch simplifies writing of standalone Clang tools. As an example, we add clang-check, a tool that runs a syntax only frontend action over a .cc file. When you integrate this into your favorite editor, you get much faster feedback on your compilation errors, thus reducing your feedback cycle especially when writing new code. The tool depends on integration of an outstanding patch to CMake to work which allows you to always have a current compile command database in your cmake output directory when you set CMAKE_EXPORT_COMPILE_COMMANDS. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@130306 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
4d8d803b06
Коммит
9a05fa97df
|
@ -1,3 +1,3 @@
|
|||
add_subdirectory(clang-interpreter)
|
||||
add_subdirectory(PrintFunctionNames)
|
||||
|
||||
add_subdirectory(Tooling)
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
set(LLVM_USED_LIBS clangTooling clangBasic)
|
||||
|
||||
add_clang_executable(clang-check
|
||||
ClangCheck.cpp
|
||||
)
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
//===- examples/Tooling/ClangCheck.cpp - Clang check tool -----------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements a clang-check tool that runs the
|
||||
// clang::SyntaxOnlyAction over a number of translation units.
|
||||
//
|
||||
// Usage:
|
||||
// clang-check <cmake-output-dir> <file1> <file2> ...
|
||||
//
|
||||
// Where <cmake-output-dir> is a CMake build directory in which a file named
|
||||
// compile_commands.json exists (enable -DCMAKE_EXPORT_COMPILE_COMMANDS in
|
||||
// CMake to get this output).
|
||||
//
|
||||
// <file1> ... specify the paths of files in the CMake source tree. This path
|
||||
// is looked up in the compile command database. If the path of a file is
|
||||
// absolute, it needs to point into CMake's source tree. If the path is
|
||||
// relative, the current working directory needs to be in the CMake source
|
||||
// tree and the file must be in a subdirectory of the current working
|
||||
// directory. "./" prefixes in the relative files will be automatically
|
||||
// removed, but the rest of a relative path must be a suffix of a path in
|
||||
// the compile command line database.
|
||||
//
|
||||
// For example, to use clang-check on all files in a subtree of the source
|
||||
// tree, use:
|
||||
// /path/to/cmake/sources $ find . -name '*.cpp' \
|
||||
// |xargs clang-check /path/to/cmake/build
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Frontend/FrontendActions.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
#include "llvm/ADT/OwningPtr.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Support/system_error.h"
|
||||
|
||||
/// \brief Returns the absolute path of 'File', by prepending it with
|
||||
/// 'BaseDirectory' if 'File' is not absolute. Otherwise returns 'File'.
|
||||
/// If 'File' starts with "./", the returned path will not contain the "./".
|
||||
/// Otherwise, the returned path will contain the literal path-concatenation of
|
||||
/// 'BaseDirectory' and 'File'.
|
||||
///
|
||||
/// \param File Either an absolute or relative path.
|
||||
/// \param BaseDirectory An absolute path.
|
||||
///
|
||||
/// FIXME: Put this somewhere where it is more generally available.
|
||||
static std::string GetAbsolutePath(
|
||||
llvm::StringRef File, llvm::StringRef BaseDirectory) {
|
||||
assert(llvm::sys::path::is_absolute(BaseDirectory));
|
||||
if (llvm::sys::path::is_absolute(File)) {
|
||||
return File;
|
||||
}
|
||||
llvm::StringRef RelativePath(File);
|
||||
if (RelativePath.startswith("./")) {
|
||||
RelativePath = RelativePath.substr(strlen("./"));
|
||||
}
|
||||
llvm::SmallString<1024> AbsolutePath(BaseDirectory);
|
||||
llvm::sys::path::append(AbsolutePath, RelativePath);
|
||||
return AbsolutePath.str();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 3) {
|
||||
llvm::outs() << "Usage: " << argv[0] << " <cmake-output-dir> "
|
||||
<< "<file1> <file2> ...\n";
|
||||
return 1;
|
||||
}
|
||||
// FIXME: We should pull how to find the database into the Tooling package.
|
||||
llvm::OwningPtr<llvm::MemoryBuffer> JsonDatabase;
|
||||
llvm::SmallString<1024> JsonDatabasePath(argv[1]);
|
||||
llvm::sys::path::append(JsonDatabasePath, "compile_commands.json");
|
||||
llvm::error_code Result =
|
||||
llvm::MemoryBuffer::getFile(JsonDatabasePath, JsonDatabase);
|
||||
if (Result != 0) {
|
||||
llvm::outs() << "Error while opening JSON database: " << Result.message()
|
||||
<< "\n";
|
||||
return 1;
|
||||
}
|
||||
llvm::StringRef BaseDirectory(::getenv("PWD"));
|
||||
for (int I = 2; I < argc; ++I) {
|
||||
llvm::SmallString<1024> File(GetAbsolutePath(argv[I], BaseDirectory));
|
||||
llvm::outs() << "Processing " << File << ".\n";
|
||||
std::string ErrorMessage;
|
||||
clang::tooling::CompileCommand LookupResult =
|
||||
clang::tooling::FindCompileArgsInJsonDatabase(
|
||||
File.str(), JsonDatabase->getBuffer(), ErrorMessage);
|
||||
if (!LookupResult.CommandLine.empty()) {
|
||||
if (!clang::tooling::RunToolWithFlags(
|
||||
new clang::SyntaxOnlyAction,
|
||||
LookupResult.CommandLine.size(),
|
||||
clang::tooling::CommandLineToArgv(
|
||||
&LookupResult.CommandLine).data())) {
|
||||
llvm::outs() << "Error while processing " << File << ".\n";
|
||||
}
|
||||
} else {
|
||||
llvm::outs() << "Skipping " << File << ". Command line not found.\n";
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -16,6 +16,8 @@
|
|||
#define LLVM_CLANG_TOOLING_TOOLING_H
|
||||
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace clang {
|
||||
|
||||
|
@ -26,12 +28,53 @@ namespace tooling {
|
|||
/// \brief Runs (and deletes) the tool on 'Code' with the -fsynatx-only flag.
|
||||
///
|
||||
/// \param ToolAction The action to run over the code.
|
||||
// \param Code C++ code.
|
||||
/// \param Code C++ code.
|
||||
///
|
||||
/// \return - True if 'ToolAction' was successfully executed.
|
||||
bool RunSyntaxOnlyToolOnCode(
|
||||
clang::FrontendAction *ToolAction, llvm::StringRef Code);
|
||||
|
||||
/// \brief Runs (and deletes) the tool with the given Clang flags.
|
||||
///
|
||||
/// \param ToolAction The action to run over the code.
|
||||
/// \param Argc The number of elements in Argv.
|
||||
/// \param Argv The command line arguments, including the path the binary
|
||||
/// was started with (Argv[0]).
|
||||
bool RunToolWithFlags(
|
||||
clang::FrontendAction* ToolAction, int Argc, char *Argv[]);
|
||||
|
||||
/// \brief Converts a vector<string> into a vector<char*> suitable to pass
|
||||
/// to main-style functions taking (int Argc, char *Argv[]).
|
||||
std::vector<char*> CommandLineToArgv(const std::vector<std::string>* Command);
|
||||
|
||||
/// \brief Specifies the working directory and command of a compilation.
|
||||
struct CompileCommand {
|
||||
/// \brief The working directory the command was executed from.
|
||||
std::string Directory;
|
||||
|
||||
/// \brief The command line that was executed.
|
||||
std::vector<std::string> CommandLine;
|
||||
};
|
||||
|
||||
/// \brief Looks up the compile command for 'FileName' in 'JsonDatabase'.
|
||||
///
|
||||
/// \param FileName The path to an input file for which we want the compile
|
||||
/// command line. If the 'JsonDatabase' was created by CMake, this must be
|
||||
/// an absolute path inside the CMake source directory which does not have
|
||||
/// symlinks resolved.
|
||||
///
|
||||
/// \param JsonDatabase A JSON formatted list of compile commands. This lookup
|
||||
/// command supports only a subset of the JSON standard as written by CMake.
|
||||
///
|
||||
/// \param ErrorMessage If non-empty, an error occurred and 'ErrorMessage' will
|
||||
/// be set to contain the error message. In this case CompileCommand will
|
||||
/// contain an empty directory and command line.
|
||||
///
|
||||
/// \see JsonCompileCommandLineDatabase
|
||||
CompileCommand FindCompileArgsInJsonDatabase(
|
||||
llvm::StringRef FileName, llvm::StringRef JsonDatabase,
|
||||
std::string &ErrorMessage);
|
||||
|
||||
} // end namespace tooling
|
||||
} // end namespace clang
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
SET(LLVM_USED_LIBS clangBasic clangFrontend clangAST)
|
||||
|
||||
add_clang_library(clangTooling
|
||||
JsonCompileCommandLineDatabase.cpp
|
||||
Tooling.cpp
|
||||
)
|
||||
|
|
|
@ -0,0 +1,214 @@
|
|||
//===--- JsonCompileCommandLineDatabase.cpp - Simple JSON database --------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements reading a compile command line database, as written
|
||||
// out for example by CMake.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "JsonCompileCommandLineDatabase.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tooling {
|
||||
|
||||
namespace {
|
||||
|
||||
// A parser for JSON escaped strings of command line arguments with \-escaping
|
||||
// for quoted arguments (see the documentation of UnescapeJsonCommandLine(...)).
|
||||
class CommandLineArgumentParser {
|
||||
public:
|
||||
CommandLineArgumentParser(llvm::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 (!ParseQuotedStringInto(String)) return false;
|
||||
} else {
|
||||
if (!ParseFreeStringInto(String)) return false;
|
||||
}
|
||||
} while (*Position != ' ');
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseQuotedStringInto(std::string &String) {
|
||||
if (!Next()) return false;
|
||||
while (*Position != '"') {
|
||||
if (!SkipEscapeCharacter()) return false;
|
||||
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 != '"');
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkipEscapeCharacter() {
|
||||
if (*Position == '\\') {
|
||||
return Next();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NextNonWhitespace() {
|
||||
do {
|
||||
if (!Next()) return false;
|
||||
} while (*Position == ' ');
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Next() {
|
||||
++Position;
|
||||
if (Position == Input.end()) return false;
|
||||
// Remove the JSON escaping first. This is done unconditionally.
|
||||
if (*Position == '\\') ++Position;
|
||||
return Position != Input.end();
|
||||
}
|
||||
|
||||
const llvm::StringRef Input;
|
||||
llvm::StringRef::iterator Position;
|
||||
std::vector<std::string> CommandLine;
|
||||
};
|
||||
|
||||
} // end namespace
|
||||
|
||||
std::vector<std::string> UnescapeJsonCommandLine(
|
||||
llvm::StringRef JsonEscapedCommandLine) {
|
||||
CommandLineArgumentParser parser(JsonEscapedCommandLine);
|
||||
return parser.Parse();
|
||||
}
|
||||
|
||||
JsonCompileCommandLineParser::JsonCompileCommandLineParser(
|
||||
const llvm::StringRef Input, CompileCommandHandler *CommandHandler)
|
||||
: Input(Input), Position(Input.begin()-1), CommandHandler(CommandHandler) {}
|
||||
|
||||
bool JsonCompileCommandLineParser::Parse() {
|
||||
NextNonWhitespace();
|
||||
return ParseTranslationUnits();
|
||||
}
|
||||
|
||||
std::string JsonCompileCommandLineParser::GetErrorMessage() const {
|
||||
return ErrorMessage;
|
||||
}
|
||||
|
||||
bool JsonCompileCommandLineParser::ParseTranslationUnits() {
|
||||
if (!ConsumeOrError('[', "at start of compile command file")) return false;
|
||||
if (!ParseTranslationUnit(/*First=*/true)) return false;
|
||||
while (Consume(',')) {
|
||||
if (!ParseTranslationUnit(/*First=*/false)) return false;
|
||||
}
|
||||
if (!ConsumeOrError(']', "at end of array")) return false;
|
||||
if (CommandHandler != NULL) {
|
||||
CommandHandler->EndTranslationUnits();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JsonCompileCommandLineParser::ParseTranslationUnit(bool First) {
|
||||
if (First) {
|
||||
if (!Consume('{')) return true;
|
||||
} else {
|
||||
if (!ConsumeOrError('{', "at start of object")) return false;
|
||||
}
|
||||
if (!Consume('}')) {
|
||||
if (!ParseObjectKeyValuePairs()) return false;
|
||||
if (!ConsumeOrError('}', "at end of object")) return false;
|
||||
}
|
||||
if (CommandHandler != NULL) {
|
||||
CommandHandler->EndTranslationUnit();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JsonCompileCommandLineParser::ParseObjectKeyValuePairs() {
|
||||
do {
|
||||
llvm::StringRef Key;
|
||||
if (!ParseString(Key)) return false;
|
||||
if (!ConsumeOrError(':', "between name and value")) return false;
|
||||
llvm::StringRef Value;
|
||||
if (!ParseString(Value)) return false;
|
||||
if (CommandHandler != NULL) {
|
||||
CommandHandler->HandleKeyValue(Key, Value);
|
||||
}
|
||||
} while (Consume(','));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JsonCompileCommandLineParser::ParseString(llvm::StringRef &String) {
|
||||
if (!ConsumeOrError('"', "at start of string")) return false;
|
||||
llvm::StringRef::iterator First = Position;
|
||||
llvm::StringRef::iterator Last = Position;
|
||||
while (!Consume('"')) {
|
||||
Consume('\\');
|
||||
++Position;
|
||||
// We need to store Position, as Consume will change Last before leaving
|
||||
// the loop.
|
||||
Last = Position;
|
||||
}
|
||||
String = llvm::StringRef(First, Last - First);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JsonCompileCommandLineParser::Consume(char C) {
|
||||
if (Position == Input.end()) return false;
|
||||
if (*Position != C) return false;
|
||||
NextNonWhitespace();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JsonCompileCommandLineParser::ConsumeOrError(
|
||||
char C, llvm::StringRef Message) {
|
||||
if (!Consume(C)) {
|
||||
SetExpectError(C, Message);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void JsonCompileCommandLineParser::SetExpectError(
|
||||
char C, llvm::StringRef Message) {
|
||||
ErrorMessage = (llvm::Twine("'") + llvm::StringRef(&C, 1) +
|
||||
"' expected " + Message + ".").str();
|
||||
}
|
||||
|
||||
void JsonCompileCommandLineParser::NextNonWhitespace() {
|
||||
do {
|
||||
++Position;
|
||||
} while (IsWhitespace());
|
||||
}
|
||||
|
||||
bool JsonCompileCommandLineParser::IsWhitespace() {
|
||||
if (Position == Input.end()) return false;
|
||||
return (*Position == ' ' || *Position == '\t' ||
|
||||
*Position == '\n' || *Position == '\r');
|
||||
}
|
||||
|
||||
} // end namespace tooling
|
||||
} // end namespace clang
|
|
@ -0,0 +1,107 @@
|
|||
//===--- JsonCompileCommandLineDatabase - Simple JSON database --*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements reading a compile command line database, as written
|
||||
// out for example by CMake. It only supports the subset of the JSON standard
|
||||
// that is needed to parse the CMake output.
|
||||
// See http://www.json.org/ for the full standard.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLING_JSON_COMPILE_COMMAND_LINE_DATABASE_H
|
||||
#define LLVM_CLANG_TOOLING_JSON_COMPILE_COMMAND_LINE_DATABASE_H
|
||||
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace clang {
|
||||
namespace tooling {
|
||||
|
||||
/// \brief Converts a JSON escaped command line to a vector of arguments.
|
||||
///
|
||||
/// \param JsonEscapedCommandLine The escaped command line as a string. This
|
||||
/// is assumed to be escaped as a JSON string (e.g. " and \ are escaped).
|
||||
/// In addition, any arguments containing spaces are assumed to be \-escaped
|
||||
///
|
||||
/// For example, the input (|| denoting non C-escaped strings):
|
||||
/// |./call a \"b \\\" c \\\\ \" d|
|
||||
/// would yield:
|
||||
/// [ |./call|, |a|, |b " c \ |, |d| ].
|
||||
std::vector<std::string> UnescapeJsonCommandLine(
|
||||
llvm::StringRef JsonEscapedCommandLine);
|
||||
|
||||
/// \brief Interface for users of the JsonCompileCommandLineParser.
|
||||
class CompileCommandHandler {
|
||||
public:
|
||||
virtual ~CompileCommandHandler() {};
|
||||
|
||||
/// \brief Called after all translation units are parsed.
|
||||
virtual void EndTranslationUnits() {}
|
||||
|
||||
/// \brief Called at the end of a single translation unit.
|
||||
virtual void EndTranslationUnit() {}
|
||||
|
||||
/// \brief Called for every (Key, Value) pair in a translation unit
|
||||
/// description.
|
||||
virtual void HandleKeyValue(llvm::StringRef Key, llvm::StringRef Value) {}
|
||||
};
|
||||
|
||||
/// \brief A JSON parser that supports the subset of JSON needed to parse
|
||||
/// JSON compile command line databases as written out by CMake.
|
||||
///
|
||||
/// The supported subset describes a list of compile command lines for
|
||||
/// each processed translation unit. The translation units are stored in a
|
||||
/// JSON array, where each translation unit is described by a JSON object
|
||||
/// containing (Key, Value) pairs for the working directory the compile command
|
||||
/// line was executed from, the main C/C++ input file of the translation unit
|
||||
/// and the actual compile command line, for example:
|
||||
/// [
|
||||
/// {
|
||||
/// "file":"/file.cpp",
|
||||
/// "directory":"/",
|
||||
/// "command":"/cc /file.cpp"
|
||||
/// }
|
||||
/// ]
|
||||
class JsonCompileCommandLineParser {
|
||||
public:
|
||||
/// \brief Create a parser on 'Input', calling 'CommandHandler' to handle the
|
||||
/// parsed constructs. 'CommandHandler' may be NULL in order to just check
|
||||
/// the validity of 'Input'.
|
||||
JsonCompileCommandLineParser(const llvm::StringRef Input,
|
||||
CompileCommandHandler *CommandHandler);
|
||||
|
||||
/// \brief Parses the specified input. Returns true if no parsing errors were
|
||||
/// foudn.
|
||||
bool Parse();
|
||||
|
||||
/// \brief Returns an error message if Parse() returned false previously.
|
||||
std::string GetErrorMessage() const;
|
||||
|
||||
private:
|
||||
bool ParseTranslationUnits();
|
||||
bool ParseTranslationUnit(bool First);
|
||||
bool ParseObjectKeyValuePairs();
|
||||
bool ParseString(llvm::StringRef &String);
|
||||
bool Consume(char C);
|
||||
bool ConsumeOrError(char C, llvm::StringRef Message);
|
||||
void NextNonWhitespace();
|
||||
bool IsWhitespace();
|
||||
void SetExpectError(char C, llvm::StringRef Message);
|
||||
|
||||
const llvm::StringRef Input;
|
||||
llvm::StringRef::iterator Position;
|
||||
std::string ErrorMessage;
|
||||
CompileCommandHandler * const CommandHandler;
|
||||
};
|
||||
|
||||
} // end namespace tooling
|
||||
} // end namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLING_JSON_COMPILE_COMMAND_LINE_DATABASE_H
|
|
@ -29,14 +29,41 @@
|
|||
#include "clang/Frontend/FrontendAction.h"
|
||||
#include "clang/Frontend/FrontendDiagnostic.h"
|
||||
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
||||
|
||||
#include <string>
|
||||
#include "JsonCompileCommandLineDatabase.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <cstdio>
|
||||
|
||||
namespace clang {
|
||||
namespace tooling {
|
||||
|
||||
namespace {
|
||||
|
||||
// Checks that the input conforms to the argv[] convention as in
|
||||
// main(). Namely:
|
||||
// - it must contain at least a program path,
|
||||
// - argv[0], ..., and argv[argc - 1] mustn't be NULL, and
|
||||
// - argv[argc] must be NULL.
|
||||
void ValidateArgv(int argc, char* argv[]) {
|
||||
if (argc < 1) {
|
||||
fprintf(stderr, "ERROR: argc is %d. It must be >= 1.\n", argc);
|
||||
abort();
|
||||
}
|
||||
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
if (argv[i] == NULL) {
|
||||
fprintf(stderr, "ERROR: argv[%d] is NULL.\n", i);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
if (argv[argc] != NULL) {
|
||||
fprintf(stderr, "ERROR: argv[argc] isn't NULL.\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
|
||||
// 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.
|
||||
|
@ -156,6 +183,25 @@ std::vector<char*> CommandLineToArgv(const std::vector<std::string>* Command) {
|
|||
return Result;
|
||||
}
|
||||
|
||||
bool RunToolWithFlags(
|
||||
clang::FrontendAction* ToolAction, int Args, char* Argv[]) {
|
||||
ValidateArgv(Args, Argv);
|
||||
const llvm::OwningPtr<clang::Diagnostic> Diagnostics(NewTextDiagnostics());
|
||||
const llvm::OwningPtr<clang::driver::Driver> Driver(
|
||||
NewDriver(Diagnostics.get(), Argv[0]));
|
||||
const llvm::OwningPtr<clang::driver::Compilation> Compilation(
|
||||
Driver->BuildCompilation(llvm::ArrayRef<const char*>(Argv, Args)));
|
||||
const clang::driver::ArgStringList* const CC1Args = GetCC1Arguments(
|
||||
Diagnostics.get(), Compilation.get());
|
||||
if (CC1Args == NULL) {
|
||||
return false;
|
||||
}
|
||||
llvm::OwningPtr<clang::CompilerInvocation> Invocation(
|
||||
NewInvocation(Diagnostics.get(), *CC1Args));
|
||||
return RunInvocation(Argv[0], Compilation.get(), Invocation.take(),
|
||||
*CC1Args, ToolAction);
|
||||
}
|
||||
|
||||
/// \brief Runs 'ToolAction' on the code specified by 'FileContents'.
|
||||
///
|
||||
/// \param FileContents A mapping from file name to source code. For each
|
||||
|
@ -213,6 +259,64 @@ bool RunSyntaxOnlyToolOnCode(
|
|||
FileContents, ToolAction);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// A CompileCommandHandler implementation that finds compile commands for a
|
||||
// specific input file.
|
||||
//
|
||||
// FIXME: Implement early exit when JsonCompileCommandLineParser supports it.
|
||||
class FindHandler : public clang::tooling::CompileCommandHandler {
|
||||
public:
|
||||
explicit FindHandler(llvm::StringRef File)
|
||||
: FileToMatch(File), FoundMatchingCommand(false) {};
|
||||
|
||||
virtual void EndTranslationUnits() {
|
||||
if (!FoundMatchingCommand && ErrorMessage.empty()) {
|
||||
ErrorMessage = "ERROR: No matching command found.";
|
||||
}
|
||||
}
|
||||
|
||||
virtual void EndTranslationUnit() {
|
||||
if (File == FileToMatch) {
|
||||
FoundMatchingCommand = true;
|
||||
MatchingCommand.Directory = Directory;
|
||||
MatchingCommand.CommandLine = UnescapeJsonCommandLine(Command);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void HandleKeyValue(llvm::StringRef Key, llvm::StringRef Value) {
|
||||
if (Key == "directory") { Directory = Value; }
|
||||
else if (Key == "file") { File = Value; }
|
||||
else if (Key == "command") { Command = Value; }
|
||||
else {
|
||||
ErrorMessage = (llvm::Twine("Unknown key: \"") + Key + "\"").str();
|
||||
}
|
||||
}
|
||||
|
||||
const llvm::StringRef FileToMatch;
|
||||
bool FoundMatchingCommand;
|
||||
CompileCommand MatchingCommand;
|
||||
std::string ErrorMessage;
|
||||
|
||||
llvm::StringRef Directory;
|
||||
llvm::StringRef File;
|
||||
llvm::StringRef Command;
|
||||
};
|
||||
|
||||
} // end namespace
|
||||
|
||||
CompileCommand FindCompileArgsInJsonDatabase(
|
||||
llvm::StringRef FileName, llvm::StringRef JsonDatabase,
|
||||
std::string &ErrorMessage) {
|
||||
FindHandler find_handler(FileName);
|
||||
JsonCompileCommandLineParser parser(JsonDatabase, &find_handler);
|
||||
if (!parser.Parse()) {
|
||||
ErrorMessage = parser.GetErrorMessage();
|
||||
return CompileCommand();
|
||||
}
|
||||
return find_handler.MatchingCommand;
|
||||
}
|
||||
|
||||
} // end namespace tooling
|
||||
} // end namespace clang
|
||||
|
||||
|
|
|
@ -64,3 +64,8 @@ add_clang_unittest(Tooling
|
|||
Tooling/ToolingTest.cpp
|
||||
USED_LIBS gtest gtest_main clangTooling
|
||||
)
|
||||
|
||||
add_clang_unittest(JsonCompileCommandLineDatabase
|
||||
Tooling/JsonCompileCommandLineDatabaseTest.cpp
|
||||
USED_LIBS gtest gtest_main clangTooling
|
||||
)
|
||||
|
|
|
@ -0,0 +1,232 @@
|
|||
//===- unittest/Tooling/JsonCompileCommandLineDatabaseTest ----------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "../../lib/Tooling/JsonCompileCommandLineDatabase.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace clang {
|
||||
namespace tooling {
|
||||
|
||||
TEST(UnescapeJsonCommandLine, ReturnsEmptyArrayOnEmptyString) {
|
||||
std::vector<std::string> Result = UnescapeJsonCommandLine("");
|
||||
EXPECT_TRUE(Result.empty());
|
||||
}
|
||||
|
||||
TEST(UnescapeJsonCommandLine, SplitsOnSpaces) {
|
||||
std::vector<std::string> Result = UnescapeJsonCommandLine("a b c");
|
||||
ASSERT_EQ(3ul, Result.size());
|
||||
EXPECT_EQ("a", Result[0]);
|
||||
EXPECT_EQ("b", Result[1]);
|
||||
EXPECT_EQ("c", Result[2]);
|
||||
}
|
||||
|
||||
TEST(UnescapeJsonCommandLine, MungesMultipleSpaces) {
|
||||
std::vector<std::string> Result = UnescapeJsonCommandLine(" a b ");
|
||||
ASSERT_EQ(2ul, Result.size());
|
||||
EXPECT_EQ("a", Result[0]);
|
||||
EXPECT_EQ("b", Result[1]);
|
||||
}
|
||||
|
||||
TEST(UnescapeJsonCommandLine, UnescapesBackslashCharacters) {
|
||||
std::vector<std::string> Backslash = UnescapeJsonCommandLine("a\\\\\\\\");
|
||||
ASSERT_EQ(1ul, Backslash.size());
|
||||
EXPECT_EQ("a\\", Backslash[0]);
|
||||
std::vector<std::string> Quote = UnescapeJsonCommandLine("a\\\\\\\"");
|
||||
ASSERT_EQ(1ul, Quote.size());
|
||||
EXPECT_EQ("a\"", Quote[0]);
|
||||
}
|
||||
|
||||
TEST(UnescapeJsonCommandLine, DoesNotMungeSpacesBetweenQuotes) {
|
||||
std::vector<std::string> Result = UnescapeJsonCommandLine("\\\" a b \\\"");
|
||||
ASSERT_EQ(1ul, Result.size());
|
||||
EXPECT_EQ(" a b ", Result[0]);
|
||||
}
|
||||
|
||||
TEST(UnescapeJsonCommandLine, AllowsMultipleQuotedArguments) {
|
||||
std::vector<std::string> Result = UnescapeJsonCommandLine(
|
||||
" \\\" a \\\" \\\" b \\\" ");
|
||||
ASSERT_EQ(2ul, Result.size());
|
||||
EXPECT_EQ(" a ", Result[0]);
|
||||
EXPECT_EQ(" b ", Result[1]);
|
||||
}
|
||||
|
||||
TEST(UnescapeJsonCommandLine, AllowsEmptyArgumentsInQuotes) {
|
||||
std::vector<std::string> Result = UnescapeJsonCommandLine(
|
||||
"\\\"\\\"\\\"\\\"");
|
||||
ASSERT_EQ(1ul, Result.size());
|
||||
EXPECT_TRUE(Result[0].empty()) << Result[0];
|
||||
}
|
||||
|
||||
TEST(UnescapeJsonCommandLine, ParsesEscapedQuotesInQuotedStrings) {
|
||||
std::vector<std::string> Result = UnescapeJsonCommandLine(
|
||||
"\\\"\\\\\\\"\\\"");
|
||||
ASSERT_EQ(1ul, Result.size());
|
||||
EXPECT_EQ("\"", Result[0]);
|
||||
}
|
||||
|
||||
TEST(UnescapeJsonCommandLine, ParsesMultipleArgumentsWithEscapedCharacters) {
|
||||
std::vector<std::string> Result = UnescapeJsonCommandLine(
|
||||
" \\\\\\\" \\\"a \\\\\\\" b \\\" \\\"and\\\\\\\\c\\\" \\\\\\\"");
|
||||
ASSERT_EQ(4ul, Result.size());
|
||||
EXPECT_EQ("\"", Result[0]);
|
||||
EXPECT_EQ("a \" b ", Result[1]);
|
||||
EXPECT_EQ("and\\c", Result[2]);
|
||||
EXPECT_EQ("\"", Result[3]);
|
||||
}
|
||||
|
||||
TEST(UnescapeJsonCommandLine, ParsesStringsWithoutSpacesIntoSingleArgument) {
|
||||
std::vector<std::string> QuotedNoSpaces = UnescapeJsonCommandLine(
|
||||
"\\\"a\\\"\\\"b\\\"");
|
||||
ASSERT_EQ(1ul, QuotedNoSpaces.size());
|
||||
EXPECT_EQ("ab", QuotedNoSpaces[0]);
|
||||
|
||||
std::vector<std::string> MixedNoSpaces = UnescapeJsonCommandLine(
|
||||
"\\\"a\\\"bcd\\\"ef\\\"\\\"\\\"\\\"g\\\"");
|
||||
ASSERT_EQ(1ul, MixedNoSpaces.size());
|
||||
EXPECT_EQ("abcdefg", MixedNoSpaces[0]);
|
||||
}
|
||||
|
||||
TEST(JsonCompileCommandLineParser, FailsOnEmptyString) {
|
||||
JsonCompileCommandLineParser Parser("", NULL);
|
||||
EXPECT_FALSE(Parser.Parse()) << Parser.GetErrorMessage();
|
||||
}
|
||||
|
||||
TEST(JsonCompileCommandLineParser, DoesNotReadAfterInput) {
|
||||
JsonCompileCommandLineParser Parser(llvm::StringRef(NULL, 0), NULL);
|
||||
EXPECT_FALSE(Parser.Parse()) << Parser.GetErrorMessage();
|
||||
}
|
||||
|
||||
TEST(JsonCompileCommandLineParser, ParsesEmptyArray) {
|
||||
JsonCompileCommandLineParser Parser("[]", NULL);
|
||||
EXPECT_TRUE(Parser.Parse()) << Parser.GetErrorMessage();
|
||||
}
|
||||
|
||||
TEST(JsonCompileCommandLineParser, FailsIfNotClosingArray) {
|
||||
JsonCompileCommandLineParser JustOpening("[", NULL);
|
||||
EXPECT_FALSE(JustOpening.Parse()) << JustOpening.GetErrorMessage();
|
||||
JsonCompileCommandLineParser WithSpaces(" [ ", NULL);
|
||||
EXPECT_FALSE(WithSpaces.Parse()) << WithSpaces.GetErrorMessage();
|
||||
JsonCompileCommandLineParser WithGarbage(" [x", NULL);
|
||||
EXPECT_FALSE(WithGarbage.Parse()) << WithGarbage.GetErrorMessage();
|
||||
}
|
||||
|
||||
TEST(JsonCompileCommandLineParser, ParsesEmptyArrayWithWhitespace) {
|
||||
JsonCompileCommandLineParser Spaces(" [ ] ", NULL);
|
||||
EXPECT_TRUE(Spaces.Parse()) << Spaces.GetErrorMessage();
|
||||
JsonCompileCommandLineParser AllWhites("\t\r\n[\t\n \t\r ]\t\r \n\n", NULL);
|
||||
EXPECT_TRUE(AllWhites.Parse()) << AllWhites.GetErrorMessage();
|
||||
}
|
||||
|
||||
TEST(JsonCompileCommandLineParser, FailsIfNotStartingArray) {
|
||||
JsonCompileCommandLineParser ObjectStart("{", NULL);
|
||||
EXPECT_FALSE(ObjectStart.Parse()) << ObjectStart.GetErrorMessage();
|
||||
// We don't implement a full JSON parser, and thus parse only a subset
|
||||
// of valid JSON.
|
||||
JsonCompileCommandLineParser Object("{}", NULL);
|
||||
EXPECT_FALSE(Object.Parse()) << Object.GetErrorMessage();
|
||||
JsonCompileCommandLineParser Character("x", NULL);
|
||||
EXPECT_FALSE(Character.Parse()) << Character.GetErrorMessage();
|
||||
}
|
||||
|
||||
TEST(JsonCompileCommandLineParser, ParsesEmptyObject) {
|
||||
JsonCompileCommandLineParser Parser("[{}]", NULL);
|
||||
EXPECT_TRUE(Parser.Parse()) << Parser.GetErrorMessage();
|
||||
}
|
||||
|
||||
TEST(JsonCompileCommandLineParser, ParsesObject) {
|
||||
JsonCompileCommandLineParser Parser("[{\"a\":\"/b\"}]", NULL);
|
||||
EXPECT_TRUE(Parser.Parse()) << Parser.GetErrorMessage();
|
||||
}
|
||||
|
||||
TEST(JsonCompileCommandLineParser, ParsesMultipleKeyValuePairsInObject) {
|
||||
JsonCompileCommandLineParser Parser(
|
||||
"[{\"a\":\"/b\",\"c\":\"d\",\"e\":\"f\"}]", NULL);
|
||||
EXPECT_TRUE(Parser.Parse()) << Parser.GetErrorMessage();
|
||||
}
|
||||
|
||||
TEST(JsonCompileCommandLineParser, FailsIfNotClosingObject) {
|
||||
JsonCompileCommandLineParser MissingCloseOnEmpty("[{]", NULL);
|
||||
EXPECT_FALSE(MissingCloseOnEmpty.Parse())
|
||||
<< MissingCloseOnEmpty.GetErrorMessage();
|
||||
JsonCompileCommandLineParser MissingCloseAfterPair("[{\"a\":\"b\"]", NULL);
|
||||
EXPECT_FALSE(MissingCloseAfterPair.Parse())
|
||||
<< MissingCloseAfterPair.GetErrorMessage();
|
||||
}
|
||||
|
||||
TEST(JsonCompileCommandLineParser, FailsIfMissingColon) {
|
||||
JsonCompileCommandLineParser StringString("[{\"a\"\"/b\"}]", NULL);
|
||||
EXPECT_FALSE(StringString.Parse()) << StringString.GetErrorMessage();
|
||||
JsonCompileCommandLineParser StringSpaceString("[{\"a\" \"b\"}]", NULL);
|
||||
EXPECT_FALSE(StringSpaceString.Parse())
|
||||
<< StringSpaceString.GetErrorMessage();
|
||||
}
|
||||
|
||||
TEST(JsonCompileCommandLineParser, FailsOnMissingQuote) {
|
||||
JsonCompileCommandLineParser OpenQuote("[{a\":\"b\"}]", NULL);
|
||||
EXPECT_FALSE(OpenQuote.Parse()) << OpenQuote.GetErrorMessage();
|
||||
JsonCompileCommandLineParser CloseQuote("[{\"a\":\"b}]", NULL);
|
||||
EXPECT_FALSE(CloseQuote.Parse()) << CloseQuote.GetErrorMessage();
|
||||
}
|
||||
|
||||
TEST(JsonCompileCommandLineParser, ParsesEscapedQuotes) {
|
||||
JsonCompileCommandLineParser Parser(
|
||||
"[{\"a\":\"\\\"b\\\" \\\" \\\"\"}]", NULL);
|
||||
EXPECT_TRUE(Parser.Parse()) << Parser.GetErrorMessage();
|
||||
}
|
||||
|
||||
TEST(JsonCompileCommandLineParser, ParsesEmptyString) {
|
||||
JsonCompileCommandLineParser Parser("[{\"a\":\"\"}]", NULL);
|
||||
EXPECT_TRUE(Parser.Parse()) << Parser.GetErrorMessage();
|
||||
}
|
||||
|
||||
TEST(JsonCompileCommandLineParser, FailsOnMissingString) {
|
||||
JsonCompileCommandLineParser MissingValue("[{\"a\":}]", NULL);
|
||||
EXPECT_FALSE(MissingValue.Parse()) << MissingValue.GetErrorMessage();
|
||||
JsonCompileCommandLineParser MissingKey("[{:\"b\"}]", NULL);
|
||||
EXPECT_FALSE(MissingKey.Parse()) << MissingKey.GetErrorMessage();
|
||||
}
|
||||
|
||||
TEST(JsonCompileCommandLineParser, ParsesMultipleObjects) {
|
||||
JsonCompileCommandLineParser Parser(
|
||||
"["
|
||||
" { \"a\" : \"b\" },"
|
||||
" { \"a\" : \"b\" },"
|
||||
" { \"a\" : \"b\" }"
|
||||
"]", NULL);
|
||||
EXPECT_TRUE(Parser.Parse()) << Parser.GetErrorMessage();
|
||||
}
|
||||
|
||||
TEST(JsonCompileCommandLineParser, FailsOnMissingComma) {
|
||||
JsonCompileCommandLineParser Parser(
|
||||
"["
|
||||
" { \"a\" : \"b\" }"
|
||||
" { \"a\" : \"b\" }"
|
||||
"]", NULL);
|
||||
EXPECT_FALSE(Parser.Parse()) << Parser.GetErrorMessage();
|
||||
}
|
||||
|
||||
TEST(JsonCompileCommandLineParser, FailsOnSuperfluousComma) {
|
||||
JsonCompileCommandLineParser Parser(
|
||||
"[ { \"a\" : \"b\" }, ]", NULL);
|
||||
EXPECT_FALSE(Parser.Parse()) << Parser.GetErrorMessage();
|
||||
}
|
||||
|
||||
TEST(JsonCompileCommandLineParser, ParsesSpacesInBetweenTokens) {
|
||||
JsonCompileCommandLineParser Parser(
|
||||
" \t \n\n \r [ \t \n\n \r"
|
||||
" \t \n\n \r { \t \n\n \r\"a\"\t \n\n \r :"
|
||||
" \t \n\n \r \"b\"\t \n\n \r } \t \n\n \r,\t \n\n \r"
|
||||
" \t \n\n \r { \t \n\n \r\"a\"\t \n\n \r :"
|
||||
" \t \n\n \r \"b\"\t \n\n \r } \t \n\n \r]\t \n\n \r",
|
||||
NULL);
|
||||
EXPECT_TRUE(Parser.Parse()) << Parser.GetErrorMessage();
|
||||
}
|
||||
|
||||
} // end namespace tooling
|
||||
} // end namespace clang
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "clang/AST/ASTConsumer.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "clang/AST/DeclGroup.h"
|
||||
|
@ -86,6 +87,89 @@ TEST(RunSyntaxOnlyToolOnCode, FindsClassDecl) {
|
|||
EXPECT_FALSE(FoundClassDeclX);
|
||||
}
|
||||
|
||||
TEST(FindCompileArgsInJsonDatabase, FindsNothingIfEmpty) {
|
||||
std::string ErrorMessage;
|
||||
CompileCommand NotFound = FindCompileArgsInJsonDatabase(
|
||||
"a-file.cpp", "", ErrorMessage);
|
||||
EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage;
|
||||
EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage;
|
||||
}
|
||||
|
||||
TEST(FindCompileArgsInJsonDatabase, ReadsSingleEntry) {
|
||||
llvm::StringRef Directory("/some/directory");
|
||||
llvm::StringRef FileName("/path/to/a-file.cpp");
|
||||
llvm::StringRef Command("/path/to/compiler and some arguments");
|
||||
std::string ErrorMessage;
|
||||
CompileCommand FoundCommand = FindCompileArgsInJsonDatabase(
|
||||
FileName,
|
||||
(llvm::Twine("[{\"directory\":\"") + Directory + "\"," +
|
||||
"\"command\":\"" + Command + "\","
|
||||
"\"file\":\"" + FileName + "\"}]").str(), ErrorMessage);
|
||||
EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
|
||||
ASSERT_EQ(4u, FoundCommand.CommandLine.size()) << ErrorMessage;
|
||||
EXPECT_EQ("/path/to/compiler", FoundCommand.CommandLine[0]) << ErrorMessage;
|
||||
EXPECT_EQ("and", FoundCommand.CommandLine[1]) << ErrorMessage;
|
||||
EXPECT_EQ("some", FoundCommand.CommandLine[2]) << ErrorMessage;
|
||||
EXPECT_EQ("arguments", FoundCommand.CommandLine[3]) << ErrorMessage;
|
||||
|
||||
CompileCommand NotFound = FindCompileArgsInJsonDatabase(
|
||||
"a-file.cpp",
|
||||
(llvm::Twine("[{\"directory\":\"") + Directory + "\"," +
|
||||
"\"command\":\"" + Command + "\","
|
||||
"\"file\":\"" + FileName + "\"}]").str(), ErrorMessage);
|
||||
EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage;
|
||||
EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage;
|
||||
}
|
||||
|
||||
TEST(FindCompileArgsInJsonDatabase, ReadsCompileCommandLinesWithSpaces) {
|
||||
llvm::StringRef Directory("/some/directory");
|
||||
llvm::StringRef FileName("/path/to/a-file.cpp");
|
||||
llvm::StringRef Command("\\\"/path to compiler\\\" \\\"and an argument\\\"");
|
||||
std::string ErrorMessage;
|
||||
CompileCommand FoundCommand = FindCompileArgsInJsonDatabase(
|
||||
FileName,
|
||||
(llvm::Twine("[{\"directory\":\"") + Directory + "\"," +
|
||||
"\"command\":\"" + Command + "\","
|
||||
"\"file\":\"" + FileName + "\"}]").str(), ErrorMessage);
|
||||
ASSERT_EQ(2u, FoundCommand.CommandLine.size());
|
||||
EXPECT_EQ("/path to compiler", FoundCommand.CommandLine[0]) << ErrorMessage;
|
||||
EXPECT_EQ("and an argument", FoundCommand.CommandLine[1]) << ErrorMessage;
|
||||
}
|
||||
|
||||
TEST(FindCompileArgsInJsonDatabase, ReadsDirectoryWithSpaces) {
|
||||
llvm::StringRef Directory("/some directory / with spaces");
|
||||
llvm::StringRef FileName("/path/to/a-file.cpp");
|
||||
llvm::StringRef Command("a command");
|
||||
std::string ErrorMessage;
|
||||
CompileCommand FoundCommand = FindCompileArgsInJsonDatabase(
|
||||
FileName,
|
||||
(llvm::Twine("[{\"directory\":\"") + Directory + "\"," +
|
||||
"\"command\":\"" + Command + "\","
|
||||
"\"file\":\"" + FileName + "\"}]").str(), ErrorMessage);
|
||||
EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
|
||||
}
|
||||
|
||||
TEST(FindCompileArgsInJsonDatabase, FindsEntry) {
|
||||
llvm::StringRef Directory("directory");
|
||||
llvm::StringRef FileName("file");
|
||||
llvm::StringRef Command("command");
|
||||
std::string JsonDatabase = "[";
|
||||
for (int I = 0; I < 10; ++I) {
|
||||
if (I > 0) JsonDatabase += ",";
|
||||
JsonDatabase += (llvm::Twine(
|
||||
"{\"directory\":\"") + Directory + llvm::Twine(I) + "\"," +
|
||||
"\"command\":\"" + Command + llvm::Twine(I) + "\","
|
||||
"\"file\":\"" + FileName + llvm::Twine(I) + "\"}").str();
|
||||
}
|
||||
JsonDatabase += "]";
|
||||
std::string ErrorMessage;
|
||||
CompileCommand FoundCommand = FindCompileArgsInJsonDatabase(
|
||||
"file4", JsonDatabase, ErrorMessage);
|
||||
EXPECT_EQ("directory4", FoundCommand.Directory) << ErrorMessage;
|
||||
ASSERT_EQ(1u, FoundCommand.CommandLine.size()) << ErrorMessage;
|
||||
EXPECT_EQ("command4", FoundCommand.CommandLine[0]) << ErrorMessage;
|
||||
}
|
||||
|
||||
} // end namespace tooling
|
||||
} // end namespace clang
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче