This commit is contained in:
Jacob Dufault 2017-02-22 00:52:00 -08:00
parent 358b4434c2
commit ce4c2232d7
20 changed files with 619 additions and 1873 deletions

View File

@ -0,0 +1,41 @@
#include "compilation_database_loader.h"
#include <iostream>
#include <clang-c/CXCompilationDatabase.h>
#include "libclangmm/Utility.h"
std::vector<CompilationEntry> LoadCompilationEntriesFromDirectory(const std::string& project_directory) {
CXCompilationDatabase_Error cx_db_load_error;
CXCompilationDatabase cx_db = clang_CompilationDatabase_fromDirectory(project_directory.c_str(), &cx_db_load_error);
if (cx_db_load_error == CXCompilationDatabase_CanNotLoadDatabase) {
std::cerr << "[FATAL]: Unable to load compile_commands.json located at \"" << project_directory << "\"";
exit(1);
}
CXCompileCommands cx_commands = clang_CompilationDatabase_getAllCompileCommands(cx_db);
unsigned int num_commands = clang_CompileCommands_getSize(cx_commands);
std::vector<CompilationEntry> result;
for (unsigned int i = 0; i < num_commands; i++) {
CXCompileCommand cx_command = clang_CompileCommands_getCommand(cx_commands, i);
CompilationEntry entry;
entry.directory = clang::ToString(clang_CompileCommand_getDirectory(cx_command));
entry.filename = clang::ToString(clang_CompileCommand_getFilename(cx_command));
unsigned int num_args = clang_CompileCommand_getNumArgs(cx_command);
entry.args.reserve(num_args);
for (unsigned int j = 0; j < num_args; ++j) {
entry.args.push_back(clang::ToString(clang_CompileCommand_getArg(cx_command, j)));
}
result.push_back(entry);
}
clang_CompileCommands_dispose(cx_commands);
clang_CompilationDatabase_dispose(cx_db);
return result;
}

View File

@ -0,0 +1,12 @@
#pragma once
#include <string>
#include <vector>
struct CompilationEntry {
std::string directory;
std::string filename;
std::vector<std::string> args;
};
std::vector<CompilationEntry> LoadCompilationEntriesFromDirectory(const std::string& project_directory);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,9 @@
#include "a.h"
namespace {
void LocalA() {
Common();
}
} // namespace
void Common() {}

View File

@ -0,0 +1,6 @@
#ifndef A_H_
#define A_H_
void Common();
#endif // A_H_

View File

@ -0,0 +1,7 @@
#include "a.h"
#if RANDOM_DEFINE
static void LocaB() {
Common();
}
#endif // RANDOM_DEFINE

View File

@ -0,0 +1,12 @@
[
{
"directory" : "full_tests/simple_cross_reference",
"command" : "clang -c -o a.o a.cc",
"file" : "full_tests/simple_cross_reference/a.cc"
},
{
"directory" : "full_tests/simple_cross_reference",
"command" : "clang -DRANDOM_DEFINE -c -o b.o b.cc",
"file" : "full_tests/simple_cross_reference/b.cc"
}
]

View File

@ -1,324 +1,4 @@
#include <algorithm>
#include <iostream>
#include <cstdint>
#include <cassert>
#include <fstream>
#include <unordered_map>
#include "libclangmm/clangmm.h"
#include "libclangmm/Utility.h"
#include "bitfield.h"
#include "utils.h"
#include "optional.h"
#include <rapidjson/writer.h>
#include <rapidjson/prettywriter.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/document.h>
struct TypeDef;
struct FuncDef;
struct VarDef;
using FileId = int64_t;
using namespace std::experimental;
// TODO: Move off of this weird wrapper, use struct with custom wrappers
// directly.
BEGIN_BITFIELD_TYPE(Location, uint64_t)
ADD_BITFIELD_MEMBER(interesting, /*start:*/ 0, /*len:*/ 1); // 2 values
ADD_BITFIELD_MEMBER(file_id, /*start:*/ 1, /*len:*/ 29); // 536,870,912 values
ADD_BITFIELD_MEMBER(line, /*start:*/ 30, /*len:*/ 20); // 1,048,576 values
ADD_BITFIELD_MEMBER(column, /*start:*/ 50, /*len:*/ 14); // 16,384 values
Location(bool interesting, FileId file_id, uint32_t line, uint32_t column) {
this->interesting = interesting;
this->file_id = file_id;
this->line = line;
this->column = column;
}
std::string ToString() {
// Output looks like this:
//
// *1:2:3
//
// * => interesting
// 1 => file id
// 2 => line
// 3 => column
std::string result;
if (interesting)
result += '*';
result += std::to_string(file_id);
result += ':';
result += std::to_string(line);
result += ':';
result += std::to_string(column);
return result;
}
// Compare two Locations and check if they are equal. Ignores the value of
// |interesting|.
// operator== doesn't seem to work properly...
bool IsEqualTo(const Location& o) {
// When comparing, ignore the value of |interesting|.
return (wrapper.value >> 1) == (o.wrapper.value >> 1);
}
Location WithInteresting(bool interesting) {
Location result = *this;
result.interesting = interesting;
return result;
}
END_BITFIELD_TYPE()
struct FileDb {
std::unordered_map<std::string, FileId> file_path_to_file_id;
std::unordered_map<FileId, std::string> file_id_to_file_path;
FileDb() {
// Reserve id 0 for unfound.
file_path_to_file_id[""] = 0;
file_id_to_file_path[0] = "";
}
Location Resolve(const CXSourceLocation& cx_loc, bool interesting) {
CXFile file;
unsigned int line, column, offset;
clang_getSpellingLocation(cx_loc, &file, &line, &column, &offset);
FileId file_id;
if (file != nullptr) {
std::string path = clang::ToString(clang_getFileName(file));
auto it = file_path_to_file_id.find(path);
if (it != file_path_to_file_id.end()) {
file_id = it->second;
}
else {
file_id = file_path_to_file_id.size();
file_path_to_file_id[path] = file_id;
file_id_to_file_path[file_id] = path;
}
}
return Location(interesting, file_id, line, column);
}
Location Resolve(const CXIdxLoc& cx_idx_loc, bool interesting) {
CXSourceLocation cx_loc = clang_indexLoc_getCXSourceLocation(cx_idx_loc);
return Resolve(cx_loc, interesting);
}
Location Resolve(const CXCursor& cx_cursor, bool interesting) {
return Resolve(clang_getCursorLocation(cx_cursor), interesting);
}
Location Resolve(const clang::Cursor& cursor, bool interesting) {
return Resolve(cursor.cx_cursor, interesting);
}
};
template<typename T>
struct LocalId {
uint64_t local_id;
LocalId() : local_id(0) {} // Needed for containers. Do not use directly.
explicit LocalId(uint64_t local_id) : local_id(local_id) {}
bool operator==(const LocalId<T>& other) {
return local_id == other.local_id;
}
};
template<typename T>
bool operator==(const LocalId<T>& a, const LocalId<T>& b) {
return a.local_id == b.local_id;
}
using TypeId = LocalId<TypeDef>;
using FuncId = LocalId<FuncDef>;
using VarId = LocalId<VarDef>;
template<typename T>
struct Ref {
LocalId<T> id;
Location loc;
Ref(LocalId<T> id, Location loc) : id(id), loc(loc) {}
};
using TypeRef = Ref<TypeDef>;
using FuncRef = Ref<FuncDef>;
using VarRef = Ref<VarDef>;
// TODO: skip as much forward-processing as possible when |is_system_def| is
// set to false.
// TODO: Either eliminate the defs created as a by-product of cross-referencing,
// or do not emit things we don't have definitions for.
struct TypeDef {
bool is_system_def = false;
// General metadata.
TypeId id;
std::string usr;
std::string short_name;
std::string qualified_name;
// While a class/type can technically have a separate declaration/definition,
// it doesn't really happen in practice. The declaration never contains
// comments or insightful information. The user always wants to jump from
// the declaration to the definition - never the other way around like in
// functions and (less often) variables.
//
// It's also difficult to identify a `class Foo;` statement with the clang
// indexer API (it's doable using cursor AST traversal), so we don't bother
// supporting the feature.
optional<Location> definition;
// If set, then this is the same underlying type as the given value (ie, this
// type comes from a using or typedef statement).
optional<TypeId> alias_of;
// Immediate parent and immediate derived types.
std::vector<TypeId> parents;
std::vector<TypeId> derived;
// Types, functions, and variables defined in this type.
std::vector<TypeId> types;
std::vector<FuncId> funcs;
std::vector<VarId> vars;
// Every usage, useful for things like renames.
// NOTE: Do not insert directly! Use AddUsage instead.
std::vector<Location> uses;
TypeDef(TypeId id, const std::string& usr) : id(id), usr(usr) {
assert(usr.size() > 0);
//std::cout << "Creating type with usr " << usr << std::endl;
}
void AddUsage(Location loc, bool insert_if_not_present = true) {
if (is_system_def)
return;
for (int i = uses.size() - 1; i >= 0; --i) {
if (uses[i].IsEqualTo(loc)) {
if (loc.interesting)
uses[i].interesting = true;
return;
}
}
if (insert_if_not_present)
uses.push_back(loc);
}
};
struct FuncDef {
bool is_system_def = false;
// General metadata.
FuncId id;
std::string usr;
std::string short_name;
std::string qualified_name;
optional<Location> declaration;
optional<Location> definition;
// Type which declares this one (ie, it is a method)
optional<TypeId> declaring_type;
// Method this method overrides.
optional<FuncId> base;
// Methods which directly override this one.
std::vector<FuncId> derived;
// Local variables defined in this function.
std::vector<VarId> locals;
// Functions which call this one.
// TODO: Functions can get called outside of just functions - for example,
// they can get called in static context (maybe redirect to main?)
// or in class initializer list (redirect to class ctor?)
// - Right now those usages will not get listed here (but they should be
// inside of all_uses).
std::vector<FuncRef> callers;
// Functions that this function calls.
std::vector<FuncRef> callees;
// All usages. For interesting usages, see callees.
std::vector<Location> uses;
FuncDef(FuncId id, const std::string& usr) : id(id), usr(usr) {
assert(usr.size() > 0);
}
};
struct VarDef {
bool is_system_def = false;
// General metadata.
VarId id;
std::string usr;
std::string short_name;
std::string qualified_name;
optional<Location> declaration;
// TODO: definitions should be a list of locations, since there can be more
// than one.
optional<Location> definition;
// Type of the variable.
optional<TypeId> variable_type;
// Type which declares this one (ie, it is a method)
optional<TypeId> declaring_type;
// Usages.
std::vector<Location> uses;
VarDef(VarId id, const std::string& usr) : id(id), usr(usr) {
assert(usr.size() > 0);
}
};
struct ParsingDatabase {
// NOTE: Every Id is resolved to a file_id of 0. The correct file_id needs
// to get fixed up when inserting into the real db.
std::unordered_map<std::string, TypeId> usr_to_type_id;
std::unordered_map<std::string, FuncId> usr_to_func_id;
std::unordered_map<std::string, VarId> usr_to_var_id;
std::vector<TypeDef> types;
std::vector<FuncDef> funcs;
std::vector<VarDef> vars;
FileDb file_db;
ParsingDatabase();
TypeId ToTypeId(const std::string& usr);
FuncId ToFuncId(const std::string& usr);
VarId ToVarId(const std::string& usr);
TypeId ToTypeId(const CXCursor& usr);
FuncId ToFuncId(const CXCursor& usr);
VarId ToVarId(const CXCursor& usr);
TypeDef* Resolve(TypeId id);
FuncDef* Resolve(FuncId id);
VarDef* Resolve(VarId id);
std::string ToString();
};
#include "indexer.h"
ParsingDatabase::ParsingDatabase() {}
@ -1284,9 +964,7 @@ void indexEntityReference(CXClientData client_data, const CXIdxEntityRefInfo* re
static bool DUMP_AST = true;
ParsingDatabase Parse(std::string filename) {
std::vector<std::string> args;
ParsingDatabase Parse(std::string filename, std::vector<std::string> args) {
clang::Index index(0 /*excludeDeclarationsFromPCH*/, 0 /*displayDiagnostics*/);
clang::TranslationUnit tu(index, filename, args);
@ -1412,6 +1090,8 @@ void WriteToFile(const std::string& filename, const std::string& content) {
}
int mai2n(int argc, char** argv) {
// TODO: Assert that we need to be on clang >= 3.9.1
/*
ParsingDatabase db = Parse("tests/vars/function_local.cc");
std::cout << std::endl << "== Database ==" << std::endl;
@ -1448,7 +1128,7 @@ int mai2n(int argc, char** argv) {
// Run test.
std::cout << "[START] " << path << std::endl;
ParsingDatabase db = Parse(path);
ParsingDatabase db = Parse(path, {});
std::string actual_output = db.ToString();
//WriteToFile("output.json", actual_output);

325
indexer.h Normal file
View File

@ -0,0 +1,325 @@
#pragma once
#include <algorithm>
#include <iostream>
#include <cstdint>
#include <cassert>
#include <fstream>
#include <unordered_map>
#include "libclangmm/clangmm.h"
#include "libclangmm/Utility.h"
#include "bitfield.h"
#include "utils.h"
#include "optional.h"
#include <rapidjson/writer.h>
#include <rapidjson/prettywriter.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/document.h>
struct TypeDef;
struct FuncDef;
struct VarDef;
using FileId = int64_t;
using namespace std::experimental;
// TODO: Move off of this weird wrapper, use struct with custom wrappers
// directly.
BEGIN_BITFIELD_TYPE(Location, uint64_t)
ADD_BITFIELD_MEMBER(interesting, /*start:*/ 0, /*len:*/ 1); // 2 values
ADD_BITFIELD_MEMBER(file_id, /*start:*/ 1, /*len:*/ 29); // 536,870,912 values
ADD_BITFIELD_MEMBER(line, /*start:*/ 30, /*len:*/ 20); // 1,048,576 values
ADD_BITFIELD_MEMBER(column, /*start:*/ 50, /*len:*/ 14); // 16,384 values
Location(bool interesting, FileId file_id, uint32_t line, uint32_t column) {
this->interesting = interesting;
this->file_id = file_id;
this->line = line;
this->column = column;
}
std::string ToString() {
// Output looks like this:
//
// *1:2:3
//
// * => interesting
// 1 => file id
// 2 => line
// 3 => column
std::string result;
if (interesting)
result += '*';
result += std::to_string(file_id);
result += ':';
result += std::to_string(line);
result += ':';
result += std::to_string(column);
return result;
}
// Compare two Locations and check if they are equal. Ignores the value of
// |interesting|.
// operator== doesn't seem to work properly...
bool IsEqualTo(const Location& o) {
// When comparing, ignore the value of |interesting|.
return (wrapper.value >> 1) == (o.wrapper.value >> 1);
}
Location WithInteresting(bool interesting) {
Location result = *this;
result.interesting = interesting;
return result;
}
END_BITFIELD_TYPE()
struct FileDb {
std::unordered_map<std::string, FileId> file_path_to_file_id;
std::unordered_map<FileId, std::string> file_id_to_file_path;
FileDb() {
// Reserve id 0 for unfound.
file_path_to_file_id[""] = 0;
file_id_to_file_path[0] = "";
}
Location Resolve(const CXSourceLocation& cx_loc, bool interesting) {
CXFile file;
unsigned int line, column, offset;
clang_getSpellingLocation(cx_loc, &file, &line, &column, &offset);
FileId file_id;
if (file != nullptr) {
std::string path = clang::ToString(clang_getFileName(file));
auto it = file_path_to_file_id.find(path);
if (it != file_path_to_file_id.end()) {
file_id = it->second;
}
else {
file_id = file_path_to_file_id.size();
file_path_to_file_id[path] = file_id;
file_id_to_file_path[file_id] = path;
}
}
return Location(interesting, file_id, line, column);
}
Location Resolve(const CXIdxLoc& cx_idx_loc, bool interesting) {
CXSourceLocation cx_loc = clang_indexLoc_getCXSourceLocation(cx_idx_loc);
return Resolve(cx_loc, interesting);
}
Location Resolve(const CXCursor& cx_cursor, bool interesting) {
return Resolve(clang_getCursorLocation(cx_cursor), interesting);
}
Location Resolve(const clang::Cursor& cursor, bool interesting) {
return Resolve(cursor.cx_cursor, interesting);
}
};
template<typename T>
struct LocalId {
uint64_t local_id;
LocalId() : local_id(0) {} // Needed for containers. Do not use directly.
explicit LocalId(uint64_t local_id) : local_id(local_id) {}
bool operator==(const LocalId<T>& other) {
return local_id == other.local_id;
}
};
template<typename T>
bool operator==(const LocalId<T>& a, const LocalId<T>& b) {
return a.local_id == b.local_id;
}
using TypeId = LocalId<TypeDef>;
using FuncId = LocalId<FuncDef>;
using VarId = LocalId<VarDef>;
template<typename T>
struct Ref {
LocalId<T> id;
Location loc;
Ref(LocalId<T> id, Location loc) : id(id), loc(loc) {}
};
using TypeRef = Ref<TypeDef>;
using FuncRef = Ref<FuncDef>;
using VarRef = Ref<VarDef>;
// TODO: skip as much forward-processing as possible when |is_system_def| is
// set to false.
// TODO: Either eliminate the defs created as a by-product of cross-referencing,
// or do not emit things we don't have definitions for.
struct TypeDef {
bool is_system_def = false;
// General metadata.
TypeId id;
std::string usr;
std::string short_name;
std::string qualified_name;
// While a class/type can technically have a separate declaration/definition,
// it doesn't really happen in practice. The declaration never contains
// comments or insightful information. The user always wants to jump from
// the declaration to the definition - never the other way around like in
// functions and (less often) variables.
//
// It's also difficult to identify a `class Foo;` statement with the clang
// indexer API (it's doable using cursor AST traversal), so we don't bother
// supporting the feature.
optional<Location> definition;
// If set, then this is the same underlying type as the given value (ie, this
// type comes from a using or typedef statement).
optional<TypeId> alias_of;
// Immediate parent and immediate derived types.
std::vector<TypeId> parents;
std::vector<TypeId> derived;
// Types, functions, and variables defined in this type.
std::vector<TypeId> types;
std::vector<FuncId> funcs;
std::vector<VarId> vars;
// Every usage, useful for things like renames.
// NOTE: Do not insert directly! Use AddUsage instead.
std::vector<Location> uses;
TypeDef(TypeId id, const std::string& usr) : id(id), usr(usr) {
assert(usr.size() > 0);
//std::cout << "Creating type with usr " << usr << std::endl;
}
void AddUsage(Location loc, bool insert_if_not_present = true) {
if (is_system_def)
return;
for (int i = uses.size() - 1; i >= 0; --i) {
if (uses[i].IsEqualTo(loc)) {
if (loc.interesting)
uses[i].interesting = true;
return;
}
}
if (insert_if_not_present)
uses.push_back(loc);
}
};
struct FuncDef {
bool is_system_def = false;
// General metadata.
FuncId id;
std::string usr;
std::string short_name;
std::string qualified_name;
optional<Location> declaration;
optional<Location> definition;
// Type which declares this one (ie, it is a method)
optional<TypeId> declaring_type;
// Method this method overrides.
optional<FuncId> base;
// Methods which directly override this one.
std::vector<FuncId> derived;
// Local variables defined in this function.
std::vector<VarId> locals;
// Functions which call this one.
// TODO: Functions can get called outside of just functions - for example,
// they can get called in static context (maybe redirect to main?)
// or in class initializer list (redirect to class ctor?)
// - Right now those usages will not get listed here (but they should be
// inside of all_uses).
std::vector<FuncRef> callers;
// Functions that this function calls.
std::vector<FuncRef> callees;
// All usages. For interesting usages, see callees.
std::vector<Location> uses;
FuncDef(FuncId id, const std::string& usr) : id(id), usr(usr) {
assert(usr.size() > 0);
}
};
struct VarDef {
bool is_system_def = false;
// General metadata.
VarId id;
std::string usr;
std::string short_name;
std::string qualified_name;
optional<Location> declaration;
// TODO: definitions should be a list of locations, since there can be more
// than one.
optional<Location> definition;
// Type of the variable.
optional<TypeId> variable_type;
// Type which declares this one (ie, it is a method)
optional<TypeId> declaring_type;
// Usages.
std::vector<Location> uses;
VarDef(VarId id, const std::string& usr) : id(id), usr(usr) {
assert(usr.size() > 0);
}
};
struct ParsingDatabase {
// NOTE: Every Id is resolved to a file_id of 0. The correct file_id needs
// to get fixed up when inserting into the real db.
std::unordered_map<std::string, TypeId> usr_to_type_id;
std::unordered_map<std::string, FuncId> usr_to_func_id;
std::unordered_map<std::string, VarId> usr_to_var_id;
std::vector<TypeDef> types;
std::vector<FuncDef> funcs;
std::vector<VarDef> vars;
FileDb file_db;
ParsingDatabase();
TypeId ToTypeId(const std::string& usr);
FuncId ToFuncId(const std::string& usr);
VarId ToVarId(const std::string& usr);
TypeId ToTypeId(const CXCursor& usr);
FuncId ToFuncId(const CXCursor& usr);
VarId ToVarId(const CXCursor& usr);
TypeDef* Resolve(TypeId id);
FuncDef* Resolve(FuncId id);
VarDef* Resolve(VarId id);
std::string ToString();
};
ParsingDatabase Parse(std::string filename, std::vector<std::string> args);

19
indexer.vcxproj.user Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LocalDebuggerCommandArguments>--project C:\Users\jacob\Desktop\superindex\indexer\full_tests\simple_cross_reference</LocalDebuggerCommandArguments>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LocalDebuggerCommandArguments>--project C:\Users\jacob\Desktop\superindex\indexer\full_tests\simple_cross_reference</LocalDebuggerCommandArguments>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LocalDebuggerCommandArguments>--project C:\Users\jacob\Desktop\superindex\indexer\full_tests\simple_cross_reference</LocalDebuggerCommandArguments>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LocalDebuggerCommandArguments>--project C:\Users\jacob\Desktop\superindex\indexer\full_tests\simple_cross_reference</LocalDebuggerCommandArguments>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
</Project>

View File

@ -1,11 +1,14 @@
#include "CompilationDatabase.h"
#include <exception>
clang::CompilationDatabase::CompilationDatabase(const std::string &project_path) {
#include <iostream>
clang::CompilationDatabase::CompilationDatabase(const std::string& project_path) {
CXCompilationDatabase_Error error;
cx_db = clang_CompilationDatabase_fromDirectory(project_path.c_str(), &error);
if(error) {
//TODO: compile_commands.json is missing, create it?
if (error == CXCompilationDatabase_CanNotLoadDatabase) {
std::cerr << "[FATAL]: Unable to load compile_commands.json located at \""
<< project_path << "\"";
exit(1);
}
}

View File

@ -1,18 +1,20 @@
#ifndef COMPILATIONDATABASE_H_
#define COMPILATIONDATABASE_H_
#pragma once
#include <clang-c/CXCompilationDatabase.h>
#include <clang-c/Index.h>
#include <string>
#include <clang-c/CXCompilationDatabase.h>
namespace clang {
class CompilationDatabase {
public:
explicit CompilationDatabase(const std::string &project_path);
~CompilationDatabase();
CXCompilationDatabase cx_db;
};
struct CompilationCommand {
std::string path;
std::string args;
};
class CompilationDatabase {
public:
explicit CompilationDatabase(const std::string &project_path);
~CompilationDatabase();
CXCompilationDatabase cx_db;
};
}
#endif // COMPILATIONDATABASE_H_

View File

@ -2,20 +2,27 @@
#include "CompileCommands.h"
#include "Utility.h"
std::string clang::CompileCommand::get_command() {
std::string res;
unsigned N = clang_CompileCommand_getNumArgs(cx_command);
for (unsigned i = 0; i < N; i++) {
res += ToString(clang_CompileCommand_getArg(cx_command, i));
}
return res;
namespace clang {
CompileCommand::CompileCommand(const CXCompileCommand& command)
: cx_command(command) {};
std::string CompileCommand::get_command() const {
std::string result;
unsigned int num_args = clang_CompileCommand_getNumArgs(cx_command);
for (unsigned int i = 0; i < num_args; i++)
result += ToString(clang_CompileCommand_getArg(cx_command, i));
return result;
}
std::vector<std::string> clang::CompileCommand::get_command_as_args() {
unsigned N = clang_CompileCommand_getNumArgs(cx_command);
std::vector<std::string> res(N);
for (unsigned i = 0; i < N; i++) {
res[i] = ToString(clang_CompileCommand_getArg(cx_command, i));
}
return res;
std::vector<std::string> CompileCommand::get_command_as_args() const {
unsigned num_args = clang_CompileCommand_getNumArgs(cx_command);
std::vector<std::string> result(num_args);
for (unsigned i = 0; i < num_args; i++)
result[i] = ToString(clang_CompileCommand_getArg(cx_command, i));
return result;
}
} // namespace clang

View File

@ -1,17 +1,16 @@
#ifndef COMPILECOMMAND_H_
#define COMPILECOMMAND_H_
#pragma once
#include <clang-c/CXCompilationDatabase.h>
#include <vector>
#include <string>
namespace clang {
class CompileCommand {
public:
CompileCommand(const CXCompileCommand& cx_command) : cx_command(cx_command) {};
std::string get_command();
std::vector<std::string> get_command_as_args();
class CompileCommand {
public:
CompileCommand(const CXCompileCommand& cx_command);
std::string get_command() const;
std::vector<std::string> get_command_as_args() const;
CXCompileCommand cx_command;
};
CXCompileCommand cx_command;
};
}
#endif // COMPILECOMMAND_H_

View File

@ -1,10 +1,10 @@
#include "CompileCommands.h"
clang::CompileCommands::CompileCommands(const std::string &filename, CompilationDatabase &db) {
clang::CompileCommands::CompileCommands(const CompilationDatabase& db) {
cx_commands =
clang_CompilationDatabase_getCompileCommands(db.cx_db, filename.c_str());
if(clang_CompileCommands_getSize(cx_commands)==0)
cx_commands = clang_CompilationDatabase_getAllCompileCommands(db.cx_db);
clang_CompilationDatabase_getAllCompileCommands(db.cx_db);
//if (clang_CompileCommands_getSize(cx_commands) == 0)
// cx_commands = clang_CompilationDatabase_getAllCompileCommands(db.cx_db);
}
clang::CompileCommands::~CompileCommands() {
@ -15,7 +15,8 @@ std::vector<clang::CompileCommand> clang::CompileCommands::get_commands() {
unsigned N = clang_CompileCommands_getSize(cx_commands);
std::vector<CompileCommand> res;
for (unsigned i = 0; i < N; i++) {
res.emplace_back(clang_CompileCommands_getCommand(cx_commands, i));
CXCompileCommand command = clang_CompileCommands_getCommand(cx_commands, i);
res.push_back(command);
}
return res;
}

View File

@ -1,19 +1,19 @@
#ifndef COMPILECOMMANDS_H_
#define COMPILECOMMANDS_H_
#include "CompilationDatabase.h"
#include "CompileCommand.h"
#include <clang-c/CXCompilationDatabase.h>
#pragma once
#include <string>
#include <vector>
#include <clang-c/CXCompilationDatabase.h>
#include "CompilationDatabase.h"
#include "CompileCommand.h"
namespace clang {
class CompileCommands {
public:
CompileCommands(const std::string &filename, CompilationDatabase &db);
std::vector<CompileCommand> get_commands();
~CompileCommands();
class CompileCommands {
public:
CompileCommands(const CompilationDatabase& db);
std::vector<CompileCommand> get_commands();
~CompileCommands();
CXCompileCommands cx_commands;
};
CXCompileCommands cx_commands;
};
}
#endif // COMPILECOMMANDS_H_

View File

@ -211,6 +211,7 @@ std::string Cursor::get_type_description() const {
return spelling;
}
#if false
std::string Cursor::evaluate() const {
CXEvalResult eval = clang_Cursor_Evaluate(cx_cursor);
@ -293,6 +294,7 @@ std::string Cursor::evaluate() const {
}
#endif
std::string Cursor::get_comments() const {
Cursor referenced = get_referenced();

View File

@ -67,7 +67,7 @@ public:
std::vector<Cursor> get_arguments() const;
bool is_valid_kind() const;
std::string evaluate() const;
//std::string evaluate() const;
std::string get_type_description() const;
std::string get_comments() const;

View File

@ -4,6 +4,7 @@
#include "Utility.h"
#include <fstream>
#include <sstream>
#include <cassert>
namespace clang {
@ -20,20 +21,24 @@ TranslationUnit::TranslationUnit(Index &index, const std::string &file_path,
files[0].Contents = buffer.c_str();
files[0].Length = buffer.size();
cx_tu = clang_parseTranslationUnit(index.cx_index, file_path.c_str(), args.data(),
args.size(), files, 1, flags);
CXErrorCode error_code = clang_parseTranslationUnit2(
index.cx_index, file_path.c_str(), args.data(), args.size(), files, 1, flags, &cx_tu);
assert(!error_code);
}
TranslationUnit::TranslationUnit(Index &index, const std::string &file_path,
const std::vector<std::string> &command_line_args,
unsigned flags) {
const std::vector<std::string> &command_line_args, unsigned flags) {
// TODO: only push defines for the time being. Might need to pass more flags.
std::vector<const char*> args;
for (auto &a : command_line_args) {
args.push_back(a.c_str());
for (const std::string& a : command_line_args) {
if (a.size() >= 2 && a[0] == '-' && a[1] == 'D')
args.push_back(a.c_str());
}
cx_tu = clang_parseTranslationUnit(index.cx_index, file_path.c_str(), args.data(),
args.size(), nullptr, 0, flags);
CXErrorCode error_code = clang_parseTranslationUnit2(
index.cx_index, file_path.c_str(), args.data(), args.size(), nullptr, 0, flags, &cx_tu);
assert(!error_code);
}
TranslationUnit::~TranslationUnit() {

View File

@ -3,10 +3,11 @@
#include <string>
#include <iostream>
#include "cxxopts.hpp"
#include "optional.h"
#include "compilation_database_loader.h"
using FileId = uint64_t;
//#include "cxxopts.hpp"
#include "optional.h"
#include "indexer.h"
struct FileDatabase {
std::unordered_map<std::string, FileId> filename_to_file_id;
@ -30,10 +31,6 @@ struct File {
std::vector<SymbolIdx> defined_symbols;
};
struct TypeDef {};
struct FuncDef {};
struct VarDef {};
struct QueryableEntry {
const char* const str;
};
@ -169,36 +166,87 @@ std::istream& operator >> (std::istream& is, Command& obj) {
// TODO: allow user to decide some indexer choices, ie, do we define
// TODO: may want to run indexer in separate process to avoid indexer/compiler crashes?
std::unordered_map<std::string, std::string> ParseOptions(int argc, char** argv) {
std::unordered_map<std::string, std::string> output;
std::string previous_arg;
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i];
if (arg[0] != '-') {
if (previous_arg.size() == 0) {
std::cerr << "Invalid arguments; switches must start with -" << std::endl;
exit(1);
}
output[previous_arg] = arg;
previous_arg = "";
}
else {
output[arg] = "";
previous_arg = arg;
}
}
return output;
}
bool HasOption(const std::unordered_map<std::string, std::string>& options, const std::string& option) {
return options.find(option) != options.end();
}
int main(int argc, char** argv) {
// cxxopts throws exceptions... replace/rewrite lib?
try {
cxxopts::Options options("indexer", "C++ indexer powered by libclang");
std::unordered_map<std::string, std::string> options = ParseOptions(argc, argv);
options.add_options()
("h,help", "Print help (this output)")
("list-commands", "Print information about the query commands")
("p,project", "Path to compile_commands.json. Needed for the server, and optionally by clients if there are multiple servers running.")
;
if (argc == 1 || options.find("--help") != options.end()) {
std::cout << R"help(clang-indexer help:
// Server options.
options.add_options("server")
("server", "Flag indicating that the indexer should create an index that can be queried against using separate invocations of this binary (using the --command flag).", cxxopts::value<bool>())
("cache-dir", "Path to cache index. Database cache will be restored if present. If not present, the index will be in-memory only.")
("threads", "Number of threads to use for indexing and querying tasks.")
;
General:
--help Print this help information.
--help-commands
Print all available query commands.
--project Path to compile_commands.json. Needed for the server, and
optionally by clients if there are multiple servers running.
--print-config
Emit all configuration data this executable is using.
// Client options.
options.add_options("client")
("c,command", "Execute a query command against the index. See --command-help. Presence of this flag indicates that the indexer is in client mode.", cxxopts::value<Command>())
("f,file", "File name to run the index query on", cxxopts::value<std::string>())
("l,location", "A symbol location in the active file the query will operate on. Format is line:column, ie, 10:5, for line 10, column 5.", cxxopts::value<std::string>())
("preferred-symbol-location", "When looking up symbols, try to return the either the 'declaration' or the 'definition'. Defaults to 'definition'.", cxxopts::value<PreferredSymbolLocation>())
;
Server:
--server If present, this binary will run in server mode. The binary
will not return until killed or an exit is requested. The
server computes and caches an index of the entire program
which is then queried by short-lived client processes. A
client is created by running this binary with a --command
flag.
--cache-dir Directory to cache the index and other useful information. If
a previous cache is present, the database will try to reuse
it. If this flag is not present, the database will be
in-memory only.
--threads Number of threads to use for indexing and querying tasks.
This value is optional; a good estimate is computed by
default.
options.parse(argc, argv);
Client:
--command Execute a query command against the index. See
--command-help for a listing of valid commands and a
description of what they do. Presence of this flag indicates
that the indexer is in client mode; this flag is mutually
exclusive with --server.
--location Location of the query. Some commands require only a file,
other require a line and column as well. Format is
filename[:line:column]. For example, "foobar.cc" and
"foobar.cc:1:10" are valid inputs.
--preferred-symbol-location
When looking up symbols, try to return either the
'declaration' or the 'definition'. Defaults to 'definition'.
)help";
exit(0);
}
if (options.count("list-commands")) {
std::cout << R"(Available commands:
if (HasOption(options, "--help-commands")) {
std::cout << R"(Available commands:
callees:
callers:
@ -228,39 +276,28 @@ int main(int argc, char** argv) {
search:
Search for a symbol by name.
)";
exit(0);
exit(0);
}
if (HasOption(options, "--project")) {
std::vector<CompilationEntry> entries = LoadCompilationEntriesFromDirectory(options["--project"]);
std::vector<ParsingDatabase> dbs;
for (const CompilationEntry& entry : entries) {
std::cout << "Parsing " << entry.filename << std::endl;
ParsingDatabase db = Parse(entry.filename, entry.args);
dbs.emplace_back(db);
std::cout << db.ToString() << std::endl << std::endl;
}
if (argc == 1 || options.count("help")) {
std::cout << options.help({ "", "server", "client" }) << std::endl;
exit(0);
}
}
catch (cxxopts::OptionException exc) {
fail(exc.what());
std::cin.get();
exit(0);
}
/*
std::string command;
for (int i = 0; i < argc; ++i) {
if (strcmp(argv[i], "--command") == 0) {
if ((i + 1) >= argc)
fail("missing --command type");
command = argv[i + 1];
}
}
if (command == "")
fail("missing --command switch");
if (command == "query") {
}
std::cout << "Running command " << command;
*/
std::cout << "Invalid arguments. Try --help.";
exit(1);
return 0;
}