ccls/src/query.h
Fangrui Song a33f4df404 index.multiVersion: int
Before, QueryFile::Def::{all_symbols,outline} were built by indexers.
Now, {symbol,outline}2refcnt are used instead, built by main thread.
*_removed are augmented with Query*:Def to allow removal of old {symbol,outline}2refcnt entries.
2018-09-03 12:27:26 -07:00

197 lines
6.0 KiB
C++

/* Copyright 2017-2018 ccls Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#pragma once
#include "indexer.h"
#include "serializer.h"
#include <llvm/ADT/DenseMap.h>
#include <llvm/ADT/SmallVector.h>
namespace llvm {
template <> struct DenseMapInfo<SymbolRef> {
static inline SymbolRef getEmptyKey() { return {}; }
static inline SymbolRef getTombstoneKey() {
SymbolRef ret{};
ret.usr = -1;
return ret;
}
static unsigned getHashValue(SymbolRef sym) {
return std::hash<SymbolRef>()(sym);
}
static bool isEqual(SymbolRef l, SymbolRef r) { return l == r; }
};
}
struct QueryFile {
struct Def {
std::string path;
std::vector<std::string> args;
LanguageId language;
// Includes in the file.
std::vector<IndexInclude> includes;
// Parts of the file which are disabled.
std::vector<Range> skipped_ranges;
// Used by |$ccls/freshenIndex|.
std::vector<std::string> dependencies;
};
using DefUpdate = std::pair<Def, std::string>;
int id = -1;
std::optional<Def> def;
llvm::DenseMap<SymbolRef, int> symbol2refcnt;
llvm::DenseMap<SymbolRef, int> outline2refcnt;
};
template <typename Q, typename QDef> struct QueryEntity {
using Def = QDef;
Def *AnyDef() {
Def *ret = nullptr;
for (auto &i : static_cast<Q *>(this)->def) {
ret = &i;
if (i.spell)
break;
}
return ret;
}
const Def *AnyDef() const {
return const_cast<QueryEntity *>(this)->AnyDef();
}
};
using UseUpdate =
std::unordered_map<Usr, std::pair<std::vector<Use>, std::vector<Use>>>;
using UsrUpdate =
std::unordered_map<Usr, std::pair<std::vector<Usr>, std::vector<Usr>>>;
struct QueryFunc : QueryEntity<QueryFunc, FuncDef> {
Usr usr;
llvm::SmallVector<Def, 1> def;
std::vector<Use> declarations;
std::vector<Use> uses;
std::vector<Usr> derived;
};
struct QueryType : QueryEntity<QueryType, TypeDef> {
Usr usr;
llvm::SmallVector<Def, 1> def;
std::vector<Use> declarations;
std::vector<Use> uses;
std::vector<Usr> derived;
std::vector<Usr> instances;
};
struct QueryVar : QueryEntity<QueryVar, VarDef> {
Usr usr;
llvm::SmallVector<Def, 1> def;
std::vector<Use> declarations;
std::vector<Use> uses;
};
struct IndexUpdate {
// Creates a new IndexUpdate based on the delta from previous to current. If
// no delta computation should be done just pass null for previous.
static IndexUpdate CreateDelta(IndexFile *previous, IndexFile *current);
int file_id;
// Dummy one to refresh all semantic highlight.
bool refresh = false;
decltype(IndexFile::lid2path) prev_lid2path;
decltype(IndexFile::lid2path) lid2path;
// File updates.
std::optional<std::string> files_removed;
std::optional<QueryFile::DefUpdate> files_def_update;
// Function updates.
int funcs_hint;
std::vector<std::pair<Usr, QueryFunc::Def>> funcs_removed;
std::vector<std::pair<Usr, QueryFunc::Def>> funcs_def_update;
UseUpdate funcs_declarations;
UseUpdate funcs_uses;
UsrUpdate funcs_derived;
// Type updates.
int types_hint;
std::vector<std::pair<Usr, QueryType::Def>> types_removed;
std::vector<std::pair<Usr, QueryType::Def>> types_def_update;
UseUpdate types_declarations;
UseUpdate types_uses;
UsrUpdate types_derived;
UsrUpdate types_instances;
// Variable updates.
int vars_hint;
std::vector<std::pair<Usr, QueryVar::Def>> vars_removed;
std::vector<std::pair<Usr, QueryVar::Def>> vars_def_update;
UseUpdate vars_declarations;
UseUpdate vars_uses;
};
struct WrappedUsr {
Usr usr;
};
template <> struct llvm::DenseMapInfo<WrappedUsr> {
static inline WrappedUsr getEmptyKey() { return {0}; }
static inline WrappedUsr getTombstoneKey() { return {~0ULL}; }
static unsigned getHashValue(WrappedUsr w) { return w.usr; }
static bool isEqual(WrappedUsr l, WrappedUsr r) { return l.usr == r.usr; }
};
using Lid2file_id = std::unordered_map<int, int>;
// The query database is heavily optimized for fast queries. It is stored
// in-memory.
struct DB {
std::vector<QueryFile> files;
llvm::StringMap<int> name2file_id;
llvm::DenseMap<WrappedUsr, int> func_usr, type_usr, var_usr;
std::vector<QueryFunc> funcs;
std::vector<QueryType> types;
std::vector<QueryVar> vars;
template <typename Def>
void RemoveUsrs(SymbolKind kind, int file_id,
const std::vector<std::pair<Usr, Def>> &to_remove);
// Insert the contents of |update| into |db|.
void ApplyIndexUpdate(IndexUpdate *update);
int GetFileId(const std::string &path);
int Update(QueryFile::DefUpdate &&u);
void Update(const Lid2file_id &, int file_id,
std::vector<std::pair<Usr, QueryType::Def>> &&us);
void Update(const Lid2file_id &, int file_id,
std::vector<std::pair<Usr, QueryFunc::Def>> &&us);
void Update(const Lid2file_id &, int file_id,
std::vector<std::pair<Usr, QueryVar::Def>> &&us);
std::string_view GetSymbolName(SymbolIdx sym, bool qualified);
bool HasFunc(Usr usr) const { return func_usr.count({usr}); }
bool HasType(Usr usr) const { return type_usr.count({usr}); }
bool HasVar(Usr usr) const { return var_usr.count({usr}); }
QueryFunc &Func(Usr usr) { return funcs[func_usr[{usr}]]; }
QueryType &Type(Usr usr) { return types[type_usr[{usr}]]; }
QueryVar &Var(Usr usr) { return vars[var_usr[{usr}]]; }
QueryFile &GetFile(SymbolIdx ref) { return files[ref.usr]; }
QueryFunc &GetFunc(SymbolIdx ref) { return Func(ref.usr); }
QueryType &GetType(SymbolIdx ref) { return Type(ref.usr); }
QueryVar &GetVar(SymbolIdx ref) { return Var(ref.usr); }
};