Try to have better behavior when types are defined across multiple files.

This commit is contained in:
Jacob Dufault 2017-04-18 23:56:37 -07:00
parent eb83ba26e1
commit cbe308c0f9
9 changed files with 374 additions and 257 deletions

View File

@ -1,5 +1 @@
#if false
struct Parent {
virtual void foo() {}
};
#endif
a,b,c

View File

@ -1,17 +1,4 @@
struct Parent {
virtual void Method() = 0;
enum Foo {
#include "a.h"
};
struct Derived : public Parent {
void Method() override {}
};
void Caller() {
Derived d;
Parent* p = &d;
p->Method();
d->Method();
}

View File

@ -563,7 +563,7 @@ optional<lsLocation> GetLsLocation(QueryDatabase* db, WorkingFiles* working_file
}
// Returns a symbol. The symbol will have *NOT* have a location assigned.
lsSymbolInformation GetSymbolInfo(QueryDatabase* db, WorkingFiles* working_files, SymbolIdx symbol) {
optional<lsSymbolInformation> GetSymbolInfo(QueryDatabase* db, WorkingFiles* working_files, SymbolIdx symbol) {
lsSymbolInformation info;
switch (symbol.kind) {
@ -599,8 +599,7 @@ lsSymbolInformation GetSymbolInfo(QueryDatabase* db, WorkingFiles* working_files
break;
}
case SymbolKind::Invalid: {
assert(false && "unexpected");
break;
return nullopt;
}
};
@ -1397,12 +1396,15 @@ void QueryDbMainLoop(
std::cerr << "File outline size is " << file->def.outline.size() << std::endl;
for (SymbolRef ref : file->def.outline) {
lsSymbolInformation info = GetSymbolInfo(db, working_files, ref.idx);
optional<lsSymbolInformation> info = GetSymbolInfo(db, working_files, ref.idx);
if (!info)
continue;
optional<lsLocation> location = GetLsLocation(db, working_files, ref.loc);
if (!location)
continue;
info.location = *location;
response.result.push_back(info);
info->location = *location;
response.result.push_back(*info);
}
ipc->SendOutMessageToClient(response);
@ -1520,7 +1522,10 @@ void QueryDbMainLoop(
}
if (db->detailed_names[i].find(query) != std::string::npos) {
lsSymbolInformation info = GetSymbolInfo(db, working_files, db->symbols[i]);
optional<lsSymbolInformation> info = GetSymbolInfo(db, working_files, db->symbols[i]);
if (!info)
continue;
optional<QueryLocation> location = GetDefinitionExtentOfSymbol(db, db->symbols[i]);
if (!location) {
auto decls = GetDeclarationsOfSymbolForGotoDefinition(db, db->symbols[i]);
@ -1532,8 +1537,8 @@ void QueryDbMainLoop(
optional<lsLocation> ls_location = GetLsLocation(db, working_files, *location);
if (!ls_location)
continue;
info.location = *ls_location;
response.result.push_back(info);
info->location = *ls_location;
response.result.push_back(*info);
}
}

View File

@ -146,6 +146,19 @@ struct TypeDefDefinitionData {
TypeDefDefinitionData() {} // For reflection.
TypeDefDefinitionData(const std::string& usr) : usr(usr) {}
bool HasInterestingState() const {
return
!short_name.empty() ||
!detailed_name.empty() ||
definition_spelling ||
definition_extent ||
alias_of ||
!parents.empty() ||
!types.empty() ||
!funcs.empty() ||
!vars.empty();
}
bool operator==(const TypeDefDefinitionData<TypeId, FuncId, VarId, Range>&
other) const {
return usr == other.usr && short_name == other.short_name &&
@ -206,8 +219,7 @@ struct IndexedTypeDef {
bool HasInterestingState() const {
return
def.definition_spelling ||
def.definition_extent ||
def.HasInterestingState() ||
!derived.empty() ||
!instantiations.empty() ||
!uses.empty();
@ -250,6 +262,18 @@ struct FuncDefDefinitionData {
// assert(usr.size() > 0);
}
bool HasInterestingState() const {
return
!short_name.empty() ||
!detailed_name.empty() ||
definition_spelling ||
definition_extent ||
declaring_type ||
base ||
!locals.empty() ||
!callees.empty();
}
bool operator==(
const FuncDefDefinitionData<TypeId, FuncId, VarId, FuncRef, Range>&
other) const {
@ -314,8 +338,7 @@ struct IndexedFuncDef {
bool HasInterestingState() const {
return
def.definition_spelling ||
def.definition_extent ||
def.HasInterestingState() ||
!def.callees.empty() ||
!declarations.empty() ||
!derived.empty() ||
@ -352,6 +375,16 @@ struct VarDefDefinitionData {
VarDefDefinitionData() {} // For reflection.
VarDefDefinitionData(const std::string& usr) : usr(usr) {}
bool HasInterestingState() const {
return
!short_name.empty() ||
!detailed_name.empty() ||
declaration ||
definition_spelling ||
definition_extent ||
variable_type ||
declaring_type;
}
bool operator==(const VarDefDefinitionData<TypeId, FuncId, VarId, Range>&
other) const {
return usr == other.usr && short_name == other.short_name &&
@ -403,8 +436,7 @@ struct IndexedVarDef {
bool HasInterestingState() const {
return
def.definition_spelling ||
def.definition_extent ||
def.HasInterestingState() ||
!uses.empty();
}

View File

@ -18,7 +18,6 @@
namespace {
QueryType::DefUpdate ToQuery(const IdMap& id_map, const IndexedTypeDef::Def& type) {
assert(!type.short_name.empty());
QueryType::DefUpdate result(type.usr);
result.short_name = type.short_name;
result.detailed_name = type.detailed_name;
@ -33,7 +32,6 @@ QueryType::DefUpdate ToQuery(const IdMap& id_map, const IndexedTypeDef::Def& typ
}
QueryFunc::DefUpdate ToQuery(const IdMap& id_map, const IndexedFuncDef::Def& func) {
assert(!func.short_name.empty());
QueryFunc::DefUpdate result(func.usr);
result.short_name = func.short_name;
result.detailed_name = func.detailed_name;
@ -47,7 +45,6 @@ QueryFunc::DefUpdate ToQuery(const IdMap& id_map, const IndexedFuncDef::Def& fun
}
QueryVar::DefUpdate ToQuery(const IdMap& id_map, const IndexedVarDef::Def& var) {
assert(!var.short_name.empty());
QueryVar::DefUpdate result(var.usr);
result.short_name = var.short_name;
result.detailed_name = var.detailed_name;
@ -91,131 +88,6 @@ void AddMergeableRange(
}
}
}
} // namespace
QueryFile::Def BuildFileDef(const IdMap& id_map, const IndexedFile& indexed) {
QueryFile::Def def;
def.path = indexed.path;
auto add_outline = [&def, &id_map](SymbolIdx idx, Range range) {
def.outline.push_back(SymbolRef(idx, id_map.ToQuery(range)));
};
auto add_all_symbols = [&def, &id_map](SymbolIdx idx, Range range) {
def.all_symbols.push_back(SymbolRef(idx, id_map.ToQuery(range)));
};
for (const IndexedTypeDef& def : indexed.types) {
if (def.def.definition_spelling.has_value())
add_all_symbols(id_map.ToSymbol(def.id), def.def.definition_spelling.value());
if (def.def.definition_extent.has_value())
add_outline(id_map.ToSymbol(def.id), def.def.definition_extent.value());
for (const Range& use : def.uses)
add_all_symbols(id_map.ToSymbol(def.id), use);
}
for (const IndexedFuncDef& def : indexed.funcs) {
if (def.def.definition_spelling.has_value())
add_all_symbols(id_map.ToSymbol(def.id), def.def.definition_spelling.value());
if (def.def.definition_extent.has_value())
add_outline(id_map.ToSymbol(def.id), def.def.definition_extent.value());
for (Range decl : def.declarations) {
add_all_symbols(id_map.ToSymbol(def.id), decl);
add_outline(id_map.ToSymbol(def.id), decl);
}
for (const IndexFuncRef& caller : def.callers)
add_all_symbols(id_map.ToSymbol(def.id), caller.loc);
}
for (const IndexedVarDef& def : indexed.vars) {
if (def.def.definition_spelling.has_value())
add_all_symbols(id_map.ToSymbol(def.id), def.def.definition_spelling.value());
if (def.def.definition_extent.has_value())
add_outline(id_map.ToSymbol(def.id), def.def.definition_extent.value());
for (const Range& use : def.uses)
add_all_symbols(id_map.ToSymbol(def.id), use);
}
std::sort(def.outline.begin(), def.outline.end(), [](const SymbolRef& a, const SymbolRef& b) {
return a.loc.range.start < b.loc.range.start;
});
std::sort(def.all_symbols.begin(), def.all_symbols.end(), [](const SymbolRef& a, const SymbolRef& b) {
return a.loc.range.start < b.loc.range.start;
});
return def;
}
QueryFile* SymbolIdx::ResolveFile(QueryDatabase* db) const {
assert(kind == SymbolKind::File);
return &db->files[idx];
}
QueryType* SymbolIdx::ResolveType(QueryDatabase* db) const {
assert(kind == SymbolKind::Type);
return &db->types[idx];
}
QueryFunc* SymbolIdx::ResolveFunc(QueryDatabase* db) const {
assert(kind == SymbolKind::Func);
return &db->funcs[idx];
}
QueryVar* SymbolIdx::ResolveVar(QueryDatabase* db) const {
assert(kind == SymbolKind::Var);
return &db->vars[idx];
}
// Compares |previous| and |current|, adding all elements that are
// in |previous| but not |current| to |removed|, and all elements
@ -288,6 +160,127 @@ void CompareGroups(
++curr_it;
}
}
QueryFile::Def BuildFileDef(const IdMap& id_map, const IndexedFile& indexed) {
QueryFile::Def def;
def.path = indexed.path;
auto add_outline = [&def, &id_map](SymbolIdx idx, Range range) {
def.outline.push_back(SymbolRef(idx, id_map.ToQuery(range)));
};
auto add_all_symbols = [&def, &id_map](SymbolIdx idx, Range range) {
def.all_symbols.push_back(SymbolRef(idx, id_map.ToQuery(range)));
};
for (const IndexedTypeDef& def : indexed.types) {
if (def.def.definition_spelling.has_value())
add_all_symbols(id_map.ToSymbol(def.id), def.def.definition_spelling.value());
if (def.def.definition_extent.has_value())
add_outline(id_map.ToSymbol(def.id), def.def.definition_extent.value());
for (const Range& use : def.uses)
add_all_symbols(id_map.ToSymbol(def.id), use);
}
for (const IndexedFuncDef& def : indexed.funcs) {
if (def.def.definition_spelling.has_value())
add_all_symbols(id_map.ToSymbol(def.id), def.def.definition_spelling.value());
if (def.def.definition_extent.has_value())
add_outline(id_map.ToSymbol(def.id), def.def.definition_extent.value());
for (Range decl : def.declarations) {
add_all_symbols(id_map.ToSymbol(def.id), decl);
add_outline(id_map.ToSymbol(def.id), decl);
}
for (const IndexFuncRef& caller : def.callers)
add_all_symbols(id_map.ToSymbol(def.id), caller.loc);
}
for (const IndexedVarDef& def : indexed.vars) {
if (def.def.definition_spelling.has_value())
add_all_symbols(id_map.ToSymbol(def.id), def.def.definition_spelling.value());
if (def.def.definition_extent.has_value())
add_outline(id_map.ToSymbol(def.id), def.def.definition_extent.value());
for (const Range& use : def.uses)
add_all_symbols(id_map.ToSymbol(def.id), use);
}
std::sort(def.outline.begin(), def.outline.end(), [](const SymbolRef& a, const SymbolRef& b) {
return a.loc.range.start < b.loc.range.start;
});
std::sort(def.all_symbols.begin(), def.all_symbols.end(), [](const SymbolRef& a, const SymbolRef& b) {
return a.loc.range.start < b.loc.range.start;
});
return def;
}
} // namespace
QueryFile* SymbolIdx::ResolveFile(QueryDatabase* db) const {
assert(kind == SymbolKind::File);
return &db->files[idx];
}
QueryType* SymbolIdx::ResolveType(QueryDatabase* db) const {
assert(kind == SymbolKind::Type);
return &db->types[idx];
}
QueryFunc* SymbolIdx::ResolveFunc(QueryDatabase* db) const {
assert(kind == SymbolKind::Func);
return &db->funcs[idx];
}
QueryVar* SymbolIdx::ResolveVar(QueryDatabase* db) const {
assert(kind == SymbolKind::Var);
return &db->vars[idx];
}
@ -296,7 +289,6 @@ void CompareGroups(
// TODO: consider having separate lookup maps so they are smaller (maybe
// lookups will go faster).
// TODO: Figure out where the invalid SymbolKinds are coming from.
QueryFileId GetQueryFileIdFromPath(QueryDatabase* query_db, const std::string& path) {
auto it = query_db->usr_to_symbol.find(path);
if (it != query_db->usr_to_symbol.end() && it->second.kind != SymbolKind::Invalid) {
@ -470,8 +462,18 @@ SymbolIdx IdMap::ToSymbol(IndexVarId id) const {
// ----------------------
// INDEX THREAD FUNCTIONS
// ----------------------
// static
IndexUpdate IndexUpdate::CreateDelta(const IdMap* previous_id_map, const IdMap* current_id_map, IndexedFile* previous, IndexedFile* current) {
// This function runs on an indexer thread.
if (!previous_id_map) {
assert(!previous);
IndexedFile previous(current->path);
@ -481,6 +483,8 @@ IndexUpdate IndexUpdate::CreateDelta(const IdMap* previous_id_map, const IdMap*
}
IndexUpdate::IndexUpdate(const IdMap& previous_id_map, const IdMap& current_id_map, IndexedFile& previous_file, IndexedFile& current_file) {
// This function runs on an indexer thread.
// |query_name| is the name of the variable on the query type.
// |index_name| is the name of the variable on the index type.
// |type| is the type of the variable.
@ -507,7 +511,7 @@ IndexUpdate::IndexUpdate(const IdMap& previous_id_map, const IdMap& current_id_m
types_removed.push_back(def->def.usr);
},
/*onAdded:*/[this, &current_id_map](IndexedTypeDef* type) {
if (!type->def.short_name.empty())
if (!type->def.detailed_name.empty())
types_def_update.push_back(ToQuery(current_id_map, type->def));
if (!type->derived.empty())
types_derived.push_back(QueryType::DerivedUpdate(current_id_map.ToQuery(type->id), current_id_map.ToQuery(type->derived)));
@ -533,7 +537,7 @@ IndexUpdate::IndexUpdate(const IdMap& previous_id_map, const IdMap& current_id_m
funcs_removed.push_back(def->def.usr);
},
/*onAdded:*/[this, &current_id_map](IndexedFuncDef* func) {
if (!func->def.short_name.empty())
if (!func->def.detailed_name.empty())
funcs_def_update.push_back(ToQuery(current_id_map, func->def));
if (!func->declarations.empty())
funcs_declarations.push_back(QueryFunc::DeclarationsUpdate(current_id_map.ToQuery(func->id), current_id_map.ToQuery(func->declarations)));
@ -559,7 +563,7 @@ IndexUpdate::IndexUpdate(const IdMap& previous_id_map, const IdMap& current_id_m
vars_removed.push_back(def->def.usr);
},
/*onAdded:*/[this, &current_id_map](IndexedVarDef* var) {
if (!var->def.short_name.empty())
if (!var->def.detailed_name.empty())
vars_def_update.push_back(ToQuery(current_id_map, var->def));
if (!var->uses.empty())
vars_uses.push_back(QueryVar::UsesUpdate(current_id_map.ToQuery(var->id), current_id_map.ToQuery(var->uses)));
@ -577,6 +581,8 @@ IndexUpdate::IndexUpdate(const IdMap& previous_id_map, const IdMap& current_id_m
}
void IndexUpdate::Merge(const IndexUpdate& update) {
// This function runs on an indexer thread.
#define INDEX_UPDATE_APPEND(name) \
AddRange(&name, update.name);
#define INDEX_UPDATE_MERGE(name) \
@ -623,92 +629,38 @@ void IndexUpdate::Merge(const IndexUpdate& update) {
void SetDetailedNameForWorkspaceSearch(QueryDatabase* db, size_t* qualified_name_index, SymbolKind kind, size_t symbol_index, const std::string& name) {
if (*qualified_name_index == -1) {
db->detailed_names.push_back(name);
db->symbols.push_back(SymbolIdx(kind, symbol_index));
*qualified_name_index = db->detailed_names.size() - 1;
}
else {
db->detailed_names[*qualified_name_index] = name;
}
}
// ------------------------
// QUERYDB THREAD FUNCTIONS
// ------------------------
void QueryDatabase::RemoveUsrs(const std::vector<Usr>& to_remove) {
// TODO: Removing usrs is tricky because it means we will have to rebuild idx locations. I'm thinking we just nullify
// the entry instead of actually removing the data. The index could be massive.
// This function runs on the querydb thread.
// Actually removing data is extremely slow because every offset/index would
// have to be updated. Instead, we just accept the memory overhead and mark
// the symbol as invalid.
//
// If the user wants to reduce memory usage, they will have to restart the
// indexer and load it from cache. Luckily, this doesn't take too long even
// on large projects (1-2 minutes).
for (Usr usr : to_remove)
usr_to_symbol[usr].kind = SymbolKind::Invalid;
// TODO: also remove from qualified_names?
}
void QueryDatabase::ImportOrUpdate(const std::vector<QueryFile::DefUpdate>& updates) {
for (auto& def : updates) {
auto it = usr_to_symbol.find(def.path);
assert(it != usr_to_symbol.end());
QueryFile& existing = files[it->second.idx];
existing.def = def;
//SetQualifiedNameForWorkspaceSearch(this, &existing.qualified_name_idx, SymbolKind::File, it->second.idx, def.qualified_name);
}
}
void QueryDatabase::ImportOrUpdate(const std::vector<QueryType::DefUpdate>& updates) {
for (auto& def : updates) {
if (def.detailed_name.empty())
continue;
auto it = usr_to_symbol.find(def.usr);
assert(it != usr_to_symbol.end());
QueryType& existing = types[it->second.idx];
if (existing.def.definition_spelling && !def.definition_spelling)
continue;
existing.def = def;
SetDetailedNameForWorkspaceSearch(this, &existing.detailed_name_idx, SymbolKind::Type, it->second.idx, def.detailed_name);
}
}
void QueryDatabase::ImportOrUpdate(const std::vector<QueryFunc::DefUpdate>& updates) {
for (auto& def : updates) {
if (def.detailed_name.empty())
continue;
auto it = usr_to_symbol.find(def.usr);
assert(it != usr_to_symbol.end());
QueryFunc& existing = funcs[it->second.idx];
if (existing.def.definition_spelling && !def.definition_spelling)
continue;
existing.def = def;
SetDetailedNameForWorkspaceSearch(this, &existing.detailed_name_idx, SymbolKind::Func, it->second.idx, def.detailed_name);
}
}
void QueryDatabase::ImportOrUpdate(const std::vector<QueryVar::DefUpdate>& updates) {
for (auto& def : updates) {
if (def.detailed_name.empty())
continue;
auto it = usr_to_symbol.find(def.usr);
assert(it != usr_to_symbol.end());
QueryVar& existing = vars[it->second.idx];
if (existing.def.definition_spelling && !def.definition_spelling)
continue;
existing.def = def;
if (def.declaring_type)
SetDetailedNameForWorkspaceSearch(this, &existing.detailed_name_idx, SymbolKind::Var, it->second.idx, def.detailed_name);
}
}
void QueryDatabase::ApplyIndexUpdate(IndexUpdate* update) {
// This function runs on the querydb thread.
#define HANDLE_MERGEABLE(update_var_name, def_var_name, storage_name) \
for (auto merge_update : update->update_var_name) { \
auto* def = &storage_name[merge_update.id.id]; \
@ -737,6 +689,90 @@ void QueryDatabase::ApplyIndexUpdate(IndexUpdate* update) {
#undef HANDLE_MERGEABLE
}
void QueryDatabase::ImportOrUpdate(const std::vector<QueryFile::DefUpdate>& updates) {
// This function runs on the querydb thread.
for (auto& def : updates) {
auto it = usr_to_symbol.find(def.path);
assert(it != usr_to_symbol.end());
QueryFile& existing = files[it->second.idx];
existing.def = def;
UpdateDetailedNames(&existing.detailed_name_idx, SymbolKind::File, it->second.idx, def.path);
}
}
void QueryDatabase::ImportOrUpdate(const std::vector<QueryType::DefUpdate>& updates) {
// This function runs on the querydb thread.
for (auto& def : updates) {
assert(!def.detailed_name.empty());
auto it = usr_to_symbol.find(def.usr);
assert(it != usr_to_symbol.end());
QueryType& existing = types[it->second.idx];
// Keep the existing definition if it is higher quality.
if (existing.def.definition_spelling && !def.definition_spelling)
continue;
existing.def = def;
UpdateDetailedNames(&existing.detailed_name_idx, SymbolKind::Type, it->second.idx, def.detailed_name);
}
}
void QueryDatabase::ImportOrUpdate(const std::vector<QueryFunc::DefUpdate>& updates) {
// This function runs on the querydb thread.
for (auto& def : updates) {
assert(!def.detailed_name.empty());
auto it = usr_to_symbol.find(def.usr);
assert(it != usr_to_symbol.end());
QueryFunc& existing = funcs[it->second.idx];
// Keep the existing definition if it is higher quality.
if (existing.def.definition_spelling && !def.definition_spelling)
continue;
existing.def = def;
UpdateDetailedNames(&existing.detailed_name_idx, SymbolKind::Func, it->second.idx, def.detailed_name);
}
}
void QueryDatabase::ImportOrUpdate(const std::vector<QueryVar::DefUpdate>& updates) {
// This function runs on the querydb thread.
for (auto& def : updates) {
assert(!def.detailed_name.empty());
auto it = usr_to_symbol.find(def.usr);
assert(it != usr_to_symbol.end());
QueryVar& existing = vars[it->second.idx];
// Keep the existing definition if it is higher quality.
if (existing.def.definition_spelling && !def.definition_spelling)
continue;
existing.def = def;
if (def.declaring_type)
UpdateDetailedNames(&existing.detailed_name_idx, SymbolKind::Var, it->second.idx, def.detailed_name);
}
}
void QueryDatabase::UpdateDetailedNames(size_t* qualified_name_index, SymbolKind kind, size_t symbol_index, const std::string& name) {
if (*qualified_name_index == -1) {
detailed_names.push_back(name);
symbols.push_back(SymbolIdx(kind, symbol_index));
*qualified_name_index = detailed_names.size() - 1;
}
else {
detailed_names[*qualified_name_index] = name;
}
}
@ -744,12 +780,4 @@ void QueryDatabase::ApplyIndexUpdate(IndexUpdate* update) {
// TODO: Idea: when indexing and joining to the main db, allow many dbs that
// are joined to. So that way even if the main db is busy we can
// still be joining. Joining the partially joined db to the main
// db should be faster since we will have larger data lanes to use.
// TODO: allow user to store configuration as json? file in home dir; also
// allow local overrides (scan up dirs)
// TODO: add opt to dump config when starting (--dump-config)
// TODO: allow user to decide some indexer choices, ie, do we mark prototype parameters as usages?

View File

@ -265,14 +265,15 @@ struct QueryDatabase {
}
//std::unordered_map<Usr, SymbolIdx> usr_to_symbol;
// Marks the given Usrs as invalid.
void RemoveUsrs(const std::vector<Usr>& to_remove);
// Insert the contents of |update| into |db|.
void ApplyIndexUpdate(IndexUpdate* update);
void RemoveUsrs(const std::vector<Usr>& to_remove);
void ImportOrUpdate(const std::vector<QueryFile::DefUpdate>& updates);
void ImportOrUpdate(const std::vector<QueryType::DefUpdate>& updates);
void ImportOrUpdate(const std::vector<QueryFunc::DefUpdate>& updates);
void ImportOrUpdate(const std::vector<QueryVar::DefUpdate>& updates);
void UpdateDetailedNames(size_t* qualified_name_index, SymbolKind kind, size_t symbol_index, const std::string& name);
};

View File

@ -118,7 +118,7 @@ void RunTests() {
for (std::string path : GetFilesInFolder("tests", true /*recursive*/, true /*add_folder_to_path*/)) {
//if (path != "tests/templates/specialized_func_definition.cc") continue;
//if (path != "tests/templates/namespace_template_class_template_func_usage_folded_into_one.cc") continue;
//if (path != "tests/multi_file/header.h") continue;
//if (path != "tests/multi_file/funky_enum.cc") continue;
//if (path != "tests/multi_file/simple_impl.cc") continue;
//if (path != "tests/usage/func_called_implicit_ctor.cc") continue;
//if (path != "tests/templates/implicit_variable_instantiation.cc") continue;

View File

@ -0,0 +1,62 @@
enum Foo {
#include "funky_enum.h"
};
/*
// TODO: In the future try to have better support for types defined across
// multiple files.
OUTPUT: funky_enum.h
{
"types": [{
"id": 0,
"usr": "c:@E@Foo",
"vars": [0, 1, 2]
}],
"vars": [{
"id": 0,
"usr": "c:@E@Foo@A",
"short_name": "A",
"detailed_name": "Foo Foo::A",
"definition_spelling": "4:1-4:2",
"definition_extent": "4:1-4:2",
"variable_type": 0,
"declaring_type": 0,
"uses": ["4:1-4:2"]
}, {
"id": 1,
"usr": "c:@E@Foo@B",
"short_name": "B",
"detailed_name": "Foo Foo::B",
"definition_spelling": "5:1-5:2",
"definition_extent": "5:1-5:2",
"variable_type": 0,
"declaring_type": 0,
"uses": ["5:1-5:2"]
}, {
"id": 2,
"usr": "c:@E@Foo@C",
"short_name": "C",
"detailed_name": "Foo Foo::C",
"definition_spelling": "6:1-6:2",
"definition_extent": "6:1-6:2",
"variable_type": 0,
"declaring_type": 0,
"uses": ["6:1-6:2"]
}]
}
OUTPUT: funky_enum.cc
{
"dependencies": ["C:/Users/jacob/Desktop/superindex/indexer/tests/multi_file/funky_enum.h"],
"types": [{
"id": 0,
"usr": "c:@E@Foo",
"short_name": "Foo",
"detailed_name": "Foo",
"definition_spelling": "1:6-1:9",
"definition_extent": "1:1-3:2",
"uses": ["1:6-1:9"]
}]
}
*/

View File

@ -0,0 +1,6 @@
// This file cannot be built directory. It is included in an enum definition of
// another file.
A,
B,
C