ccls/src/indexer.cc

1451 lines
48 KiB
C++
Raw Normal View History

2018-08-21 05:27:52 +00:00
/* 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.
==============================================================================*/
2017-02-22 08:52:00 +00:00
#include "indexer.h"
2017-02-17 09:57:44 +00:00
#include "clang_complete.hh"
2018-09-23 20:31:06 +00:00
#include "clang_tu.hh"
2018-05-27 19:24:56 +00:00
#include "log.hh"
#include "match.h"
2018-09-21 01:04:55 +00:00
#include "pipeline.hh"
2017-04-08 22:54:36 +00:00
#include "platform.h"
#include "serializer.h"
#include <clang/AST/AST.h>
#include <clang/Frontend/FrontendAction.h>
#include <clang/Index/IndexDataConsumer.h>
#include <clang/Index/IndexingAction.h>
#include <clang/Index/USRGeneration.h>
#include <clang/Lex/PreprocessorOptions.h>
#include <llvm/ADT/DenseSet.h>
#include <llvm/Support/CrashRecoveryContext.h>
#include <llvm/Support/Path.h>
2018-08-09 17:08:14 +00:00
#include <algorithm>
#include <inttypes.h>
2018-07-07 23:56:47 +00:00
#include <map>
#include <unordered_set>
2017-04-22 07:39:55 +00:00
using namespace ccls;
using namespace clang;
namespace {
constexpr int kInitializerMaxLines = 3;
GroupMatch *multiVersionMatcher;
2018-09-21 01:04:55 +00:00
struct File {
std::string path;
int64_t mtime;
std::string content;
std::unique_ptr<IndexFile> db;
};
struct IndexParam {
2018-09-21 01:04:55 +00:00
std::unordered_map<llvm::sys::fs::UniqueID, File> UID2File;
std::unordered_map<llvm::sys::fs::UniqueID, bool> UID2multi;
2018-07-08 19:49:27 +00:00
struct DeclInfo {
Usr usr;
std::string short_name;
std::string qualified;
};
2018-08-09 17:08:14 +00:00
std::unordered_map<const Decl *, DeclInfo> Decl2Info;
2017-04-12 07:57:12 +00:00
2018-09-21 01:04:55 +00:00
VFS &vfs;
2018-08-09 17:08:14 +00:00
ASTContext *Ctx;
2018-09-21 01:04:55 +00:00
IndexParam(VFS &vfs) : vfs(vfs) {}
void SeenFile(const FileEntry &File) {
2018-07-07 23:56:47 +00:00
// If this is the first time we have seen the file (ignoring if we are
// generating an index for it):
auto [it, inserted] = UID2File.try_emplace(File.getUniqueID());
2018-07-10 06:40:26 +00:00
if (inserted) {
2018-09-23 20:31:06 +00:00
std::string path = PathFromFileEntry(File);
it->second.path = path;
it->second.mtime = File.getModificationTime();
if (!it->second.mtime)
if (auto tim = LastWriteTime(path))
it->second.mtime = *tim;
if (std::optional<std::string> content = ReadContent(path))
it->second.content = *content;
2018-09-21 01:04:55 +00:00
if (!vfs.Stamp(path, it->second.mtime, 1))
return;
it->second.db = std::make_unique<IndexFile>(File.getUniqueID(), path,
it->second.content);
2018-07-07 23:56:47 +00:00
}
}
IndexFile *ConsumeFile(const FileEntry &FE) {
SeenFile(FE);
2018-09-21 01:04:55 +00:00
return UID2File[FE.getUniqueID()].db.get();
2018-07-07 23:56:47 +00:00
}
bool UseMultiVersion(const FileEntry &FE) {
auto it = UID2multi.try_emplace(FE.getUniqueID());
if (it.second)
2018-09-23 20:31:06 +00:00
it.first->second = multiVersionMatcher->IsMatch(PathFromFileEntry(FE));
return it.first->second;
}
2018-07-07 23:56:47 +00:00
};
2018-01-07 09:07:39 +00:00
2018-07-08 19:49:27 +00:00
StringRef GetSourceInRange(const SourceManager &SM, const LangOptions &LangOpts,
SourceRange R) {
SourceLocation BLoc = R.getBegin(), ELoc = R.getEnd();
std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc),
EInfo = SM.getDecomposedLoc(ELoc);
bool invalid = false;
StringRef Buf = SM.getBufferData(BInfo.first, &invalid);
if (invalid)
return "";
2018-08-09 17:08:14 +00:00
return Buf.substr(BInfo.second,
EInfo.second +
Lexer::MeasureTokenLength(ELoc, SM, LangOpts) -
BInfo.second);
2018-07-08 19:49:27 +00:00
}
SymbolKind GetSymbolKind(const Decl *D, lsSymbolKind &kind) {
2018-07-07 23:56:47 +00:00
switch (D->getKind()) {
case Decl::LinkageSpec:
return SymbolKind::Invalid;
case Decl::Namespace:
kind = lsSymbolKind::Namespace;
return SymbolKind::Type;
case Decl::NamespaceAlias:
kind = lsSymbolKind::TypeAlias;
return SymbolKind::Type;
2018-07-09 07:05:56 +00:00
case Decl::ObjCCategory:
case Decl::ObjCImplementation:
2018-07-09 07:05:56 +00:00
case Decl::ObjCInterface:
case Decl::ObjCProtocol:
kind = lsSymbolKind::Interface;
return SymbolKind::Type;
case Decl::ObjCMethod:
kind = lsSymbolKind::Method;
return SymbolKind::Func;
case Decl::ObjCProperty:
kind = lsSymbolKind::Property;
return SymbolKind::Type;
case Decl::ClassTemplate:
kind = lsSymbolKind::Class;
return SymbolKind::Type;
case Decl::FunctionTemplate:
kind = lsSymbolKind::Function;
return SymbolKind::Func;
case Decl::TypeAliasTemplate:
kind = lsSymbolKind::TypeAlias;
return SymbolKind::Type;
case Decl::VarTemplate:
kind = lsSymbolKind::Variable;
return SymbolKind::Var;
2018-07-08 19:49:27 +00:00
case Decl::TemplateTemplateParm:
kind = lsSymbolKind::TypeParameter;
return SymbolKind::Type;
case Decl::Enum:
kind = lsSymbolKind::Enum;
return SymbolKind::Type;
case Decl::CXXRecord:
case Decl::Record:
kind = lsSymbolKind::Class;
// spec has no Union, use Class
if (auto *RD = dyn_cast<RecordDecl>(D))
if (RD->getTagKind() == TTK_Struct)
kind = lsSymbolKind::Struct;
return SymbolKind::Type;
2018-07-07 23:56:47 +00:00
case Decl::ClassTemplateSpecialization:
2018-07-08 19:49:27 +00:00
case Decl::ClassTemplatePartialSpecialization:
kind = lsSymbolKind::Class;
return SymbolKind::Type;
case Decl::TypeAlias:
case Decl::Typedef:
case Decl::UnresolvedUsingTypename:
kind = lsSymbolKind::TypeAlias;
return SymbolKind::Type;
2018-07-08 19:49:27 +00:00
case Decl::Binding:
kind = lsSymbolKind::Variable;
return SymbolKind::Var;
case Decl::Field:
2018-07-09 07:05:56 +00:00
case Decl::ObjCIvar:
kind = lsSymbolKind::Field;
return SymbolKind::Var;
case Decl::Function:
kind = lsSymbolKind::Function;
return SymbolKind::Func;
case Decl::CXXMethod: {
const auto *MD = cast<CXXMethodDecl>(D);
kind = MD->isStatic() ? lsSymbolKind::StaticMethod : lsSymbolKind::Method;
return SymbolKind::Func;
}
case Decl::CXXConstructor:
kind = lsSymbolKind::Constructor;
return SymbolKind::Func;
case Decl::CXXConversion:
case Decl::CXXDestructor:
kind = lsSymbolKind::Method;
return SymbolKind::Func;
case Decl::Var:
case Decl::Decomposition:
kind = lsSymbolKind::Variable;
return SymbolKind::Var;
case Decl::ImplicitParam:
case Decl::ParmVar:
// ccls extension
kind = lsSymbolKind::Parameter;
return SymbolKind::Var;
case Decl::VarTemplateSpecialization:
case Decl::VarTemplatePartialSpecialization:
kind = lsSymbolKind::Variable;
return SymbolKind::Var;
case Decl::EnumConstant:
kind = lsSymbolKind::EnumMember;
return SymbolKind::Var;
2018-07-08 19:49:27 +00:00
case Decl::UnresolvedUsingValue:
kind = lsSymbolKind::Variable;
return SymbolKind::Var;
case Decl::TranslationUnit:
return SymbolKind::Invalid;
default:
LOG_S(INFO) << "unhandled " << int(D->getKind());
return SymbolKind::Invalid;
}
}
2018-07-09 07:05:56 +00:00
LanguageId GetDeclLanguage(const Decl *D) {
switch (D->getKind()) {
2018-08-09 17:08:14 +00:00
default:
return LanguageId::C;
case Decl::ImplicitParam:
case Decl::ObjCAtDefsField:
case Decl::ObjCCategory:
case Decl::ObjCCategoryImpl:
case Decl::ObjCCompatibleAlias:
case Decl::ObjCImplementation:
case Decl::ObjCInterface:
case Decl::ObjCIvar:
case Decl::ObjCMethod:
case Decl::ObjCProperty:
case Decl::ObjCPropertyImpl:
case Decl::ObjCProtocol:
case Decl::ObjCTypeParam:
return LanguageId::ObjC;
case Decl::CXXConstructor:
case Decl::CXXConversion:
case Decl::CXXDestructor:
case Decl::CXXMethod:
case Decl::CXXRecord:
case Decl::ClassTemplate:
case Decl::ClassTemplatePartialSpecialization:
case Decl::ClassTemplateSpecialization:
case Decl::Friend:
case Decl::FriendTemplate:
case Decl::FunctionTemplate:
case Decl::LinkageSpec:
case Decl::Namespace:
case Decl::NamespaceAlias:
case Decl::NonTypeTemplateParm:
case Decl::StaticAssert:
case Decl::TemplateTemplateParm:
case Decl::TemplateTypeParm:
case Decl::UnresolvedUsingTypename:
case Decl::UnresolvedUsingValue:
case Decl::Using:
case Decl::UsingDirective:
case Decl::UsingShadow:
return LanguageId::Cpp;
2018-07-09 07:05:56 +00:00
}
}
2018-07-08 19:49:27 +00:00
// clang/lib/AST/DeclPrinter.cpp
QualType GetBaseType(QualType T, bool deduce_auto) {
QualType BaseType = T;
while (!BaseType.isNull() && !BaseType->isSpecifierType()) {
if (const PointerType *PTy = BaseType->getAs<PointerType>())
BaseType = PTy->getPointeeType();
else if (const BlockPointerType *BPy = BaseType->getAs<BlockPointerType>())
BaseType = BPy->getPointeeType();
2018-08-09 17:08:14 +00:00
else if (const ArrayType *ATy = dyn_cast<ArrayType>(BaseType))
2018-07-08 19:49:27 +00:00
BaseType = ATy->getElementType();
else if (const VectorType *VTy = BaseType->getAs<VectorType>())
BaseType = VTy->getElementType();
else if (const ReferenceType *RTy = BaseType->getAs<ReferenceType>())
BaseType = RTy->getPointeeType();
else if (const ParenType *PTy = BaseType->getAs<ParenType>())
BaseType = PTy->desugar();
else if (deduce_auto) {
if (const AutoType *ATy = BaseType->getAs<AutoType>())
BaseType = ATy->getDeducedType();
else
break;
2018-08-09 17:08:14 +00:00
} else
2018-07-08 19:49:27 +00:00
break;
}
2018-07-08 19:49:27 +00:00
return BaseType;
}
const Decl *GetTypeDecl(QualType T, bool *specialization = nullptr) {
2018-07-08 19:49:27 +00:00
Decl *D = nullptr;
T = GetBaseType(T.getUnqualifiedType(), true);
2018-08-09 17:08:14 +00:00
const Type *TP = T.getTypePtrOrNull();
2018-07-08 19:49:27 +00:00
if (!TP)
return nullptr;
try_again:
switch (TP->getTypeClass()) {
case Type::Typedef:
D = cast<TypedefType>(TP)->getDecl();
break;
case Type::ObjCObject:
D = cast<ObjCObjectType>(TP)->getInterface();
break;
case Type::ObjCInterface:
D = cast<ObjCInterfaceType>(TP)->getDecl();
break;
case Type::Record:
case Type::Enum:
D = cast<TagType>(TP)->getDecl();
break;
2018-07-08 19:49:27 +00:00
case Type::TemplateTypeParm:
D = cast<TemplateTypeParmType>(TP)->getDecl();
break;
case Type::TemplateSpecialization:
if (specialization)
*specialization = true;
if (const RecordType *Record = TP->getAs<RecordType>())
D = Record->getDecl();
else
D = cast<TemplateSpecializationType>(TP)
->getTemplateName()
.getAsTemplateDecl();
break;
case Type::Auto:
case Type::DeducedTemplateSpecialization:
TP = cast<DeducedType>(TP)->getDeducedType().getTypePtrOrNull();
if (TP)
goto try_again;
break;
case Type::InjectedClassName:
D = cast<InjectedClassNameType>(TP)->getDecl();
break;
2018-08-09 17:08:14 +00:00
// FIXME: Template type parameters!
case Type::Elaborated:
TP = cast<ElaboratedType>(TP)->getNamedType().getTypePtrOrNull();
goto try_again;
default:
break;
}
return D;
}
const Decl *GetAdjustedDecl(const Decl *D) {
while (D) {
if (auto *R = dyn_cast<CXXRecordDecl>(D)) {
if (auto *S = dyn_cast<ClassTemplateSpecializationDecl>(R)) {
if (!S->getTypeAsWritten()) {
llvm::PointerUnion<ClassTemplateDecl *,
ClassTemplatePartialSpecializationDecl *>
Result = S->getSpecializedTemplateOrPartial();
if (Result.is<ClassTemplateDecl *>())
D = Result.get<ClassTemplateDecl *>();
else
D = Result.get<ClassTemplatePartialSpecializationDecl *>();
continue;
}
} else if (auto *D1 = R->getInstantiatedFromMemberClass()) {
D = D1;
continue;
}
} else if (auto *ED = dyn_cast<EnumDecl>(D)) {
if (auto *D1 = ED->getInstantiatedFromMemberEnum()) {
D = D1;
continue;
}
}
break;
}
return D;
}
2018-08-03 23:11:32 +00:00
bool ValidateRecord(const RecordDecl *RD) {
2018-08-09 17:08:14 +00:00
for (const auto *I : RD->fields()) {
2018-08-03 23:11:32 +00:00
QualType FQT = I->getType();
if (FQT->isIncompleteType() || FQT->isDependentType())
return false;
if (const RecordType *ChildType = I->getType()->getAs<RecordType>())
if (const RecordDecl *Child = ChildType->getDecl())
if (!ValidateRecord(Child))
return false;
}
return true;
}
class IndexDataConsumer : public index::IndexDataConsumer {
2018-07-07 23:56:47 +00:00
public:
ASTContext *Ctx;
2018-08-09 17:08:14 +00:00
IndexParam &param;
2018-08-09 17:08:14 +00:00
std::string GetComment(const Decl *D) {
2018-07-07 22:25:25 +00:00
SourceManager &SM = Ctx->getSourceManager();
const RawComment *RC = Ctx->getRawCommentForAnyRedecl(D);
2018-08-09 17:08:14 +00:00
if (!RC)
return "";
2018-07-07 22:25:25 +00:00
StringRef Raw = RC->getRawText(Ctx->getSourceManager());
SourceRange R = RC->getSourceRange();
std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(R.getBegin());
unsigned start_column = SM.getLineNumber(BInfo.first, BInfo.second);
std::string ret;
int pad = -1;
for (const char *p = Raw.data(), *E = Raw.end(); p < E;) {
// The first line starts with a comment marker, but the rest needs
// un-indenting.
unsigned skip = start_column - 1;
for (; skip > 0 && p < E && (*p == ' ' || *p == '\t'); p++)
skip--;
const char *q = p;
while (q < E && *q != '\n')
q++;
if (q < E)
q++;
// A minimalist approach to skip Doxygen comment markers.
// See https://www.stack.nl/~dimitri/doxygen/manual/docblocks.html
if (pad < 0) {
// First line, detect the length of comment marker and put into |pad|
const char *begin = p;
while (p < E && (*p == '/' || *p == '*' || *p == '-' || *p == '='))
2018-07-07 22:25:25 +00:00
p++;
if (p < E && (*p == '<' || *p == '!'))
p++;
if (p < E && *p == ' ')
p++;
2018-07-25 17:36:30 +00:00
if (p + 1 == q)
p++;
else
pad = int(p - begin);
2018-07-07 22:25:25 +00:00
} else {
// Other lines, skip |pad| bytes
int prefix = pad;
while (prefix > 0 && p < E &&
(*p == ' ' || *p == '/' || *p == '*' || *p == '<' || *p == '!'))
prefix--, p++;
}
ret.insert(ret.end(), p, q);
p = q;
}
while (ret.size() && isspace(ret.back()))
ret.pop_back();
if (EndsWith(ret, "*/")) {
ret.resize(ret.size() - 2);
} else if (EndsWith(ret, "\n/")) {
ret.resize(ret.size() - 2);
}
while (ret.size() && isspace(ret.back()))
ret.pop_back();
return ret;
}
2018-07-08 19:49:27 +00:00
Usr GetUsr(const Decl *D, IndexParam::DeclInfo **info = nullptr) const {
D = D->getCanonicalDecl();
2018-07-10 06:40:26 +00:00
auto [it, inserted] = param.Decl2Info.try_emplace(D);
if (inserted) {
SmallString<256> USR;
index::generateUSRForDecl(D, USR);
2018-07-10 06:40:26 +00:00
auto &info = it->second;
2018-07-08 19:49:27 +00:00
info.usr = HashUsr(USR);
if (auto *ND = dyn_cast<NamedDecl>(D)) {
info.short_name = ND->getNameAsString();
info.qualified = ND->getQualifiedNameAsString();
SimplifyAnonymous(info.qualified);
}
}
2018-07-08 19:49:27 +00:00
if (info)
2018-07-10 06:40:26 +00:00
*info = &it->second;
return it->second.usr;
}
2018-07-07 23:56:47 +00:00
PrintingPolicy GetDefaultPolicy() const {
PrintingPolicy PP(Ctx->getLangOpts());
PP.AnonymousTagLocations = false;
PP.TerseOutput = true;
PP.PolishForDeclaration = true;
PP.ConstantsAsWritten = true;
PP.SuppressTagKeyword = true;
PP.SuppressInitializers = true;
PP.FullyQualifiedName = false;
return PP;
}
2018-08-09 17:08:14 +00:00
static void SimplifyAnonymous(std::string &name) {
for (std::string::size_type i = 0;;) {
if ((i = name.find("(anonymous ", i)) == std::string::npos)
break;
i++;
2018-07-07 23:56:47 +00:00
if (name.size() - i > 19 && name.compare(i + 10, 9, "namespace") == 0)
name.replace(i, 19, "anon ns");
else
2018-07-07 23:56:47 +00:00
name.replace(i, 9, "anon");
}
2018-07-07 23:56:47 +00:00
}
template <typename Def>
void SetName(const Decl *D, std::string_view short_name,
2018-07-08 19:49:27 +00:00
std::string_view qualified, Def &def) {
2018-07-07 23:56:47 +00:00
SmallString<256> Str;
llvm::raw_svector_ostream OS(Str);
2018-07-08 19:49:27 +00:00
D->print(OS, GetDefaultPolicy());
2018-07-07 23:56:47 +00:00
std::string name = OS.str();
SimplifyAnonymous(name);
// Remove \n in DeclPrinter.cpp "{\n" + if(!TerseOutput)something + "}"
for (std::string::size_type i = 0;;) {
if ((i = name.find("{\n}", i)) == std::string::npos)
break;
name.replace(i, 3, "{}");
}
auto i = name.find(short_name);
2018-07-27 07:21:57 +00:00
if (short_name.size())
while (i != std::string::npos && ((i && isalnum(name[i - 1])) ||
isalnum(name[i + short_name.size()])))
i = name.find(short_name, i + short_name.size());
if (i == std::string::npos) {
// e.g. operator type-parameter-1
i = 0;
2018-07-08 19:49:27 +00:00
def.short_name_offset = 0;
} else if (short_name.size() && (!i || name[i - 1] != ':')) {
name.replace(i, short_name.size(), qualified);
2018-07-08 19:49:27 +00:00
def.short_name_offset = i + qualified.size() - short_name.size();
} else {
2018-07-08 19:49:27 +00:00
def.short_name_offset = i;
}
2018-07-08 19:49:27 +00:00
def.short_name_size = short_name.size();
for (int paren = 0; i; i--) {
// Skip parentheses in "(anon struct)::name"
if (name[i - 1] == ')')
paren++;
else if (name[i - 1] == '(')
paren--;
else if (!(paren > 0 || isalnum(name[i - 1]) || name[i - 1] == '_' ||
name[i - 1] == ':'))
break;
}
def.qual_name_offset = i;
def.detailed_name = Intern(name);
}
void SetVarName(const Decl *D, std::string_view short_name,
std::string_view qualified, IndexVar::Def &def) {
QualType T;
2018-08-09 17:08:14 +00:00
const Expr *init = nullptr;
bool binding = false;
2018-07-08 19:49:27 +00:00
if (auto *VD = dyn_cast<VarDecl>(D)) {
T = VD->getType();
init = VD->getAnyInitializer();
def.storage = VD->getStorageClass();
} else if (auto *FD = dyn_cast<FieldDecl>(D)) {
T = FD->getType();
init = FD->getInClassInitializer();
} else if (auto *BD = dyn_cast<BindingDecl>(D)) {
T = BD->getType();
binding = true;
2018-07-08 19:49:27 +00:00
}
auto BT = GetBaseType(T, false);
if (!BT.isNull() &&
(binding || BT.getUnqualifiedType()->getAs<AutoType>())) {
2018-07-08 19:49:27 +00:00
SmallString<256> Str;
llvm::raw_svector_ostream OS(Str);
PrintingPolicy PP = GetDefaultPolicy();
T.print(OS, PP);
if (Str.size() &&
2018-08-09 17:08:14 +00:00
(Str.back() != ' ' && Str.back() != '*' && Str.back() != '&'))
2018-07-08 19:49:27 +00:00
Str += ' ';
def.qual_name_offset = Str.size();
def.short_name_offset = Str.size() + qualified.size() - short_name.size();
2018-07-07 23:56:47 +00:00
def.short_name_size = short_name.size();
2018-07-08 19:49:27 +00:00
Str += StringRef(qualified.data(), qualified.size());
def.detailed_name = Intern(Str);
2018-07-08 19:49:27 +00:00
} else {
SetName(D, short_name, qualified, def);
}
if (init) {
SourceManager &SM = Ctx->getSourceManager();
2018-08-09 17:08:14 +00:00
const LangOptions &Lang = Ctx->getLangOpts();
2018-07-27 07:21:57 +00:00
SourceRange R = SM.getExpansionRange(init->getSourceRange())
#if LLVM_VERSION_MAJOR >= 7
.getAsRange()
#endif
;
2018-07-08 19:49:27 +00:00
SourceLocation L = D->getLocation();
2018-07-17 06:22:34 +00:00
if (L.isMacroID() || !SM.isBeforeInTranslationUnit(L, R.getBegin()))
2018-07-08 19:49:27 +00:00
return;
StringRef Buf = GetSourceInRange(SM, Lang, R);
Twine Init = Buf.count('\n') <= kInitializerMaxLines - 1
? Buf.size() && Buf[0] == ':' ? Twine(" ", Buf)
: Twine(" = ", Buf)
: Twine();
Twine T = def.detailed_name + Init;
2018-07-08 19:49:27 +00:00
def.hover =
def.storage == SC_Static && strncmp(def.detailed_name, "static ", 7)
? Intern(("static " + T).str())
2018-07-08 19:49:27 +00:00
: Intern(T.str());
}
2017-07-30 04:24:02 +00:00
}
static int GetFileLID(IndexFile *db, SourceManager &SM, const FileEntry &FE) {
auto [it, inserted] = db->uid2lid_and_path.try_emplace(FE.getUniqueID());
if (inserted) {
it->second.first = db->uid2lid_and_path.size() - 1;
SmallString<256> Path = FE.tryGetRealPathName();
if (Path.empty())
Path = FE.getName();
if (!llvm::sys::path::is_absolute(Path) &&
!SM.getFileManager().makeAbsolutePath(Path))
return -1;
it->second.second = llvm::sys::path::convert_to_slash(Path.str());
}
return it->second.first;
}
void AddMacroUse(IndexFile *db, SourceManager &SM, Usr usr, SymbolKind kind,
SourceLocation Spell) const {
const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(Spell));
if (!FE)
return;
int lid = GetFileLID(db, SM, *FE);
if (lid < 0)
return;
2018-07-08 19:49:27 +00:00
Range spell =
FromTokenRange(SM, Ctx->getLangOpts(), SourceRange(Spell, Spell));
Use use{{spell, Role::Dynamic}, lid};
2018-07-08 19:49:27 +00:00
switch (kind) {
case SymbolKind::Func:
db->ToFunc(usr).uses.push_back(use);
break;
case SymbolKind::Type:
db->ToType(usr).uses.push_back(use);
break;
case SymbolKind::Var:
db->ToVar(usr).uses.push_back(use);
break;
default:
llvm_unreachable("");
}
}
void CollectRecordMembers(IndexType &type, const RecordDecl *RD) {
SmallVector<std::pair<const RecordDecl *, int>, 2> Stack{{RD, 0}};
llvm::DenseSet<const RecordDecl *> Seen;
Seen.insert(RD);
while (Stack.size()) {
int offset;
std::tie(RD, offset) = Stack.back();
Stack.pop_back();
if (!RD->isCompleteDefinition() || RD->isDependentType() ||
RD->isInvalidDecl() || !ValidateRecord(RD))
offset = -1;
for (FieldDecl *FD : RD->fields()) {
int offset1 = offset < 0 ? -1 : offset + Ctx->getFieldOffset(FD);
if (FD->getIdentifier())
type.def.vars.emplace_back(GetUsr(FD), offset1);
else if (const auto *RT1 = FD->getType()->getAs<RecordType>()) {
if (const RecordDecl *RD1 = RT1->getDecl())
if (Seen.insert(RD1).second)
Stack.push_back({RD1, offset1});
}
}
}
}
public:
2018-08-09 17:08:14 +00:00
IndexDataConsumer(IndexParam &param) : param(param) {}
void initialize(ASTContext &Ctx) override { this->Ctx = param.Ctx = &Ctx; }
bool handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles,
ArrayRef<index::SymbolRelation> Relations,
#if LLVM_VERSION_MAJOR >= 7
SourceLocation Loc,
#else
FileID LocFID, unsigned LocOffset,
#endif
ASTNodeInfo ASTNode) override {
SourceManager &SM = Ctx->getSourceManager();
const LangOptions &Lang = Ctx->getLangOpts();
#if LLVM_VERSION_MAJOR < 7
SourceLocation Loc;
{
const SrcMgr::SLocEntry &Entry = SM.getSLocEntry(LocFID);
unsigned off = Entry.getOffset() + LocOffset;
if (!Entry.isFile())
off |= 1u << 31;
Loc = SourceLocation::getFromRawEncoding(off);
}
#else
FileID LocFID;
#endif
SourceLocation Spell = SM.getSpellingLoc(Loc);
const FileEntry *FE;
Range loc;
#if LLVM_VERSION_MAJOR < 7
2018-07-16 16:49:32 +00:00
CharSourceRange R;
if (SM.isMacroArgExpansion(Loc))
R = CharSourceRange::getTokenRange(Spell);
else {
auto P = SM.getExpansionRange(Loc);
R = CharSourceRange::getTokenRange(P.first, P.second);
}
#else
auto R = SM.isMacroArgExpansion(Loc) ? CharSourceRange::getTokenRange(Spell)
: SM.getExpansionRange(Loc);
2018-07-16 16:49:32 +00:00
#endif
loc = FromCharSourceRange(SM, Lang, R);
LocFID = SM.getFileID(R.getBegin());
FE = SM.getFileEntryForID(LocFID);
if (!FE)
return true;
int lid = -1;
IndexFile *db;
if (g_config->index.multiVersion && param.UseMultiVersion(*FE)) {
db = param.ConsumeFile(*SM.getFileEntryForID(SM.getMainFileID()));
if (!db)
return true;
param.SeenFile(*FE);
if (!SM.isInMainFile(R.getBegin()))
lid = GetFileLID(db, SM, *FE);
} else {
db = param.ConsumeFile(*FE);
if (!db)
return true;
}
// spell, extent, comments use OrigD while most others use adjusted |D|.
2018-08-09 17:08:14 +00:00
const Decl *OrigD = ASTNode.OrigD;
const DeclContext *SemDC = OrigD->getDeclContext()->getRedeclContext();
const DeclContext *LexDC = ASTNode.ContainerDC->getRedeclContext();
{
const NamespaceDecl *ND;
while ((ND = dyn_cast<NamespaceDecl>(cast<Decl>(SemDC))) &&
ND->isAnonymousNamespace())
SemDC = ND->getDeclContext()->getRedeclContext();
while ((ND = dyn_cast<NamespaceDecl>(cast<Decl>(LexDC))) &&
ND->isAnonymousNamespace())
LexDC = ND->getDeclContext()->getRedeclContext();
}
Role role = static_cast<Role>(Roles);
db->language = LanguageId((int)db->language | (int)GetDeclLanguage(D));
bool is_decl = Roles & uint32_t(index::SymbolRole::Declaration);
bool is_def = Roles & uint32_t(index::SymbolRole::Definition);
if (is_decl && D->getKind() == Decl::Binding)
is_def = true;
IndexFunc *func = nullptr;
IndexType *type = nullptr;
IndexVar *var = nullptr;
lsSymbolKind ls_kind;
SymbolKind kind = GetSymbolKind(D, ls_kind);
2018-07-07 23:56:47 +00:00
if (is_def)
switch (D->getKind()) {
case Decl::CXXConversion: // *operator* int => *operator int*
case Decl::CXXDestructor: // *~*A => *~A*
case Decl::CXXMethod: // *operator*= => *operator=*
2018-09-23 05:05:24 +00:00
case Decl::Function: // operator delete
if (Loc.isFileID()) {
SourceRange R =
cast<FunctionDecl>(OrigD)->getNameInfo().getSourceRange();
if (R.getEnd().isFileID())
loc = FromTokenRange(SM, Lang, R);
}
break;
default:
break;
}
else {
// e.g. typedef Foo<int> gg; => Foo has an unadjusted `D`
const Decl *D1 = GetAdjustedDecl(D);
if (D1 && D1 != D)
D = D1;
}
IndexParam::DeclInfo *info;
Usr usr = GetUsr(D, &info);
2018-07-07 23:56:47 +00:00
auto do_def_decl = [&](auto *entity) {
Use use{{loc, role}, lid};
2018-07-07 23:56:47 +00:00
if (is_def) {
2018-07-25 17:36:30 +00:00
SourceRange R = OrigD->getSourceRange();
entity->def.spell = {use,
FromTokenRangeDefaulted(SM, Lang, R, FE, loc)};
GetSymbolKind(cast<Decl>(SemDC), entity->def.parent_kind);
2018-07-07 23:56:47 +00:00
} else if (is_decl) {
SourceRange R = OrigD->getSourceRange();
entity->declarations.push_back(
{use, FromTokenRangeDefaulted(SM, Lang, R, FE, loc)});
2018-07-07 23:56:47 +00:00
} else {
entity->uses.push_back(use);
2018-07-08 19:49:27 +00:00
return;
2018-07-07 23:56:47 +00:00
}
2018-07-08 19:49:27 +00:00
if (entity->def.comments[0] == '\0' && g_config->index.comments)
entity->def.comments = Intern(GetComment(OrigD));
2018-07-07 23:56:47 +00:00
};
switch (kind) {
case SymbolKind::Invalid:
2018-07-08 19:49:27 +00:00
LOG_S(INFO) << "Unhandled " << int(D->getKind()) << " " << info->qualified
<< " in " << db->path << ":" << loc.start.line + 1;
return true;
case SymbolKind::File:
return true;
case SymbolKind::Func:
func = &db->ToFunc(usr);
func->def.kind = ls_kind;
// Mark as Role::Implicit to span one more column to the left/right.
if (!is_def && !is_decl &&
(D->getKind() == Decl::CXXConstructor ||
D->getKind() == Decl::CXXConversion))
role = Role(role | Role::Implicit);
2018-07-07 23:56:47 +00:00
do_def_decl(func);
2018-07-08 19:49:27 +00:00
if (Spell != Loc)
AddMacroUse(db, SM, usr, SymbolKind::Func, Spell);
2018-07-08 19:49:27 +00:00
if (func->def.detailed_name[0] == '\0')
SetName(D, info->short_name, info->qualified, func->def);
2018-07-07 23:56:47 +00:00
if (is_def || is_decl) {
const Decl *DC = cast<Decl>(SemDC);
if (GetSymbolKind(DC, ls_kind) == SymbolKind::Type)
2018-07-07 23:56:47 +00:00
db->ToType(GetUsr(DC)).def.funcs.push_back(usr);
} else {
const Decl *DC = cast<Decl>(LexDC);
if (GetSymbolKind(DC, ls_kind) == SymbolKind::Func)
db->ToFunc(GetUsr(DC))
.def.callees.push_back({loc, usr, SymbolKind::Func, role});
}
break;
case SymbolKind::Type:
type = &db->ToType(usr);
type->def.kind = ls_kind;
2018-07-07 23:56:47 +00:00
do_def_decl(type);
2018-07-08 19:49:27 +00:00
if (Spell != Loc)
AddMacroUse(db, SM, usr, SymbolKind::Type, Spell);
if (type->def.detailed_name[0] == '\0' && info->short_name.size())
SetName(D, info->short_name, info->qualified, type->def);
2018-07-07 23:56:47 +00:00
if (is_def || is_decl) {
2018-08-09 17:08:14 +00:00
const Decl *DC = cast<Decl>(SemDC);
if (GetSymbolKind(DC, ls_kind) == SymbolKind::Type)
2018-07-07 23:56:47 +00:00
db->ToType(GetUsr(DC)).def.types.push_back(usr);
}
break;
case SymbolKind::Var:
var = &db->ToVar(usr);
var->def.kind = ls_kind;
2018-07-07 23:56:47 +00:00
do_def_decl(var);
2018-07-08 19:49:27 +00:00
if (Spell != Loc)
AddMacroUse(db, SM, usr, SymbolKind::Var, Spell);
2018-07-08 19:49:27 +00:00
if (var->def.detailed_name[0] == '\0')
SetVarName(D, info->short_name, info->qualified, var->def);
2018-07-08 19:49:27 +00:00
QualType T;
if (auto *VD = dyn_cast<VarDecl>(D))
T = VD->getType();
else if (auto *FD = dyn_cast<FieldDecl>(D))
T = FD->getType();
2018-07-07 23:56:47 +00:00
if (is_def || is_decl) {
const Decl *DC = cast<Decl>(SemDC);
if (GetSymbolKind(DC, ls_kind) == SymbolKind::Func)
2018-07-07 23:56:47 +00:00
db->ToFunc(GetUsr(DC)).def.vars.push_back(usr);
else if (auto *ND = dyn_cast<NamespaceDecl>(SemDC))
db->ToType(GetUsr(ND)).def.vars.emplace_back(usr, -1);
if (!T.isNull()) {
if (auto *BT = T->getAs<BuiltinType>()) {
Usr usr1 = static_cast<Usr>(BT->getKind());
var->def.type = usr1;
db->ToType(usr1).instances.push_back(usr);
} else if (const Decl *D1 = GetAdjustedDecl(GetTypeDecl(T))) {
if (isa<TemplateTypeParmDecl>(D1)) {
// e.g. TemplateTypeParmDecl is not handled by
// handleDeclOccurence.
SourceRange R1 = D1->getSourceRange();
if (SM.getFileID(R1.getBegin()) == LocFID) {
IndexParam::DeclInfo *info1;
Usr usr1 = GetUsr(D1, &info1);
IndexType &type1 = db->ToType(usr1);
SourceLocation L1 = D1->getLocation();
type1.def.spell = {
Use{{FromTokenRange(SM, Lang, {L1, L1}), Role::Definition},
lid},
FromTokenRange(SM, Lang, R1)};
type1.def.detailed_name = Intern(info1->short_name);
type1.def.short_name_size = int16_t(info1->short_name.size());
type1.def.kind = lsSymbolKind::TypeParameter;
type1.def.parent_kind = lsSymbolKind::Class;
var->def.type = usr1;
type1.instances.push_back(usr);
break;
}
2018-07-07 23:56:47 +00:00
}
IndexParam::DeclInfo *info1;
Usr usr1 = GetUsr(D1, &info1);
var->def.type = usr1;
db->ToType(usr1).instances.push_back(usr);
}
}
2018-07-08 19:49:27 +00:00
} else if (!var->def.spell && var->declarations.empty()) {
// e.g. lambda parameter
SourceLocation L = D->getLocation();
2018-07-08 19:49:27 +00:00
if (SM.getFileID(L) == LocFID) {
var->def.spell = {
Use{{FromTokenRange(SM, Lang, {L, L}), Role::Definition}, lid},
FromTokenRange(SM, Lang, D->getSourceRange())};
var->def.parent_kind = lsSymbolKind::Method;
2018-07-08 19:49:27 +00:00
}
2018-07-07 23:56:47 +00:00
}
break;
}
switch (D->getKind()) {
case Decl::Namespace:
if (D->isFirstDecl()) {
auto *ND = cast<NamespaceDecl>(D);
2018-07-17 06:22:34 +00:00
auto *ND1 = cast<Decl>(ND->getParent());
if (isa<NamespaceDecl>(ND1)) {
Usr usr1 = GetUsr(ND1);
type->def.bases.push_back(usr1);
db->ToType(usr1).derived.push_back(usr);
}
}
break;
case Decl::NamespaceAlias: {
2018-07-17 06:22:34 +00:00
auto *NAD = cast<NamespaceAliasDecl>(D);
if (const NamespaceDecl *ND = NAD->getNamespace()) {
Usr usr1 = GetUsr(ND);
2018-07-17 06:22:34 +00:00
type->def.alias_of = usr1;
(void)db->ToType(usr1);
}
break;
}
case Decl::CXXRecord:
if (is_def) {
auto *RD = dyn_cast<CXXRecordDecl>(D);
if (RD && RD->hasDefinition())
for (const CXXBaseSpecifier &Base : RD->bases())
if (const Decl *BaseD =
GetAdjustedDecl(GetTypeDecl(Base.getType()))) {
Usr usr1 = GetUsr(BaseD);
2018-07-17 06:22:34 +00:00
type->def.bases.push_back(usr1);
db->ToType(usr1).derived.push_back(usr);
2018-07-07 23:56:47 +00:00
}
}
[[fallthrough]];
case Decl::Record:
if (auto *RD = dyn_cast<RecordDecl>(D)) {
if (type->def.detailed_name[0] == '\0' && info->short_name.empty()) {
2018-09-16 23:32:06 +00:00
StringRef Tag;
switch (RD->getTagKind()) {
case TTK_Struct: Tag = "struct"; break;
case TTK_Interface: Tag = "__interface"; break;
case TTK_Union: Tag = "union"; break;
case TTK_Class: Tag = "class"; break;
case TTK_Enum: Tag = "enum"; break;
}
if (TypedefNameDecl *TD = RD->getTypedefNameForAnonDecl()) {
StringRef Name = TD->getName();
2018-09-16 23:32:06 +00:00
std::string name = ("anon " + Tag + " " + Name).str();
type->def.detailed_name = Intern(name);
type->def.short_name_size = name.size();
} else {
2018-09-16 23:32:06 +00:00
std::string name = ("anon " + Tag).str();
type->def.detailed_name = Intern(name);
type->def.short_name_size = name.size();
}
}
if (is_def)
if (auto *ORD = dyn_cast<RecordDecl>(OrigD))
CollectRecordMembers(*type, ORD);
}
break;
2018-07-07 23:56:47 +00:00
case Decl::ClassTemplateSpecialization:
2018-07-08 19:49:27 +00:00
case Decl::ClassTemplatePartialSpecialization:
2018-07-07 23:56:47 +00:00
type->def.kind = lsSymbolKind::Class;
if (is_def) {
if (auto *ORD = dyn_cast<RecordDecl>(OrigD))
CollectRecordMembers(*type, ORD);
2018-07-07 23:56:47 +00:00
if (auto *RD = dyn_cast<CXXRecordDecl>(D)) {
Decl *D1 = nullptr;
if (auto *SD = dyn_cast<ClassTemplatePartialSpecializationDecl>(RD))
D1 = SD->getSpecializedTemplate();
else if (auto *SD = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
llvm::PointerUnion<ClassTemplateDecl *,
ClassTemplatePartialSpecializationDecl *>
Result = SD->getSpecializedTemplateOrPartial();
if (Result.is<ClassTemplateDecl *>())
D1 = Result.get<ClassTemplateDecl *>();
else
D1 = Result.get<ClassTemplatePartialSpecializationDecl *>();
} else
D1 = RD->getInstantiatedFromMemberClass();
if (D1) {
Usr usr1 = GetUsr(D1);
2018-07-17 06:22:34 +00:00
type->def.bases.push_back(usr1);
db->ToType(usr1).derived.push_back(usr);
2018-07-07 23:56:47 +00:00
}
}
}
break;
case Decl::TypeAlias:
case Decl::Typedef:
case Decl::UnresolvedUsingTypename:
if (auto *TD = dyn_cast<TypedefNameDecl>(D)) {
bool specialization = false;
QualType T = TD->getUnderlyingType();
if (const Decl *D1 = GetAdjustedDecl(GetTypeDecl(T, &specialization))) {
Usr usr1 = GetUsr(D1);
IndexType &type1 = db->ToType(usr1);
type->def.alias_of = usr1;
// Not visited template<class T> struct B {typedef A<T> t;};
if (specialization) {
const TypeSourceInfo *TSI = TD->getTypeSourceInfo();
SourceLocation L1 = TSI->getTypeLoc().getBeginLoc();
if (SM.getFileID(L1) == LocFID)
type1.uses.push_back(
{{FromTokenRange(SM, Lang, {L1, L1}), Role::Reference}, lid});
}
}
}
break;
case Decl::CXXMethod:
2018-07-07 23:56:47 +00:00
if (is_def || is_decl) {
if (auto *ND = dyn_cast<NamedDecl>(D)) {
SmallVector<const NamedDecl *, 8> OverDecls;
Ctx->getOverriddenMethods(ND, OverDecls);
2018-08-09 17:08:14 +00:00
for (const auto *ND1 : OverDecls) {
2018-07-07 23:56:47 +00:00
Usr usr1 = GetUsr(ND1);
2018-07-17 06:22:34 +00:00
func->def.bases.push_back(usr1);
db->ToFunc(usr1).derived.push_back(usr);
2018-07-07 23:56:47 +00:00
}
}
}
break;
2018-07-07 23:56:47 +00:00
case Decl::EnumConstant:
2018-07-08 07:46:53 +00:00
if (is_def && strchr(var->def.detailed_name, '=') == nullptr) {
2018-07-07 23:56:47 +00:00
auto *ECD = cast<EnumConstantDecl>(D);
const auto &Val = ECD->getInitVal();
std::string init =
" = " + (Val.isSigned() ? std::to_string(Val.getSExtValue())
: std::to_string(Val.getZExtValue()));
var->def.hover = Intern(var->def.detailed_name + init);
}
break;
default:
break;
}
return true;
}
};
2018-07-07 23:56:47 +00:00
class IndexPPCallbacks : public PPCallbacks {
2018-08-09 17:08:14 +00:00
SourceManager &SM;
IndexParam &param;
2018-07-07 23:56:47 +00:00
2018-08-09 17:08:14 +00:00
std::pair<StringRef, Usr> GetMacro(const Token &Tok) const {
2018-07-07 23:56:47 +00:00
StringRef Name = Tok.getIdentifierInfo()->getName();
SmallString<256> USR("@macro@");
USR += Name;
return {Name, HashUsr(USR)};
}
public:
2018-07-08 19:49:27 +00:00
IndexPPCallbacks(SourceManager &SM, IndexParam &param)
: SM(SM), param(param) {}
2018-07-08 07:46:53 +00:00
void InclusionDirective(SourceLocation HashLoc, const Token &Tok,
StringRef Included, bool IsAngled,
CharSourceRange FilenameRange, const FileEntry *File,
StringRef SearchPath, StringRef RelativePath,
2018-07-08 19:49:27 +00:00
const Module *Imported
#if LLVM_VERSION_MAJOR >= 7
,
SrcMgr::CharacteristicKind FileType
#endif
) override {
2018-07-08 07:46:53 +00:00
if (!File)
return;
llvm::sys::fs::UniqueID UniqueID;
auto spell = FromCharSourceRange(SM, param.Ctx->getLangOpts(),
FilenameRange, &UniqueID);
const FileEntry *FE =
SM.getFileEntryForID(SM.getFileID(FilenameRange.getBegin()));
2018-07-08 07:46:53 +00:00
if (!FE)
return;
if (IndexFile *db = param.ConsumeFile(*FE)) {
2018-09-23 20:31:06 +00:00
std::string path = PathFromFileEntry(*File);
if (path.size())
db->includes.push_back({spell.start.line, Intern(path)});
2018-07-08 07:46:53 +00:00
}
}
2018-07-07 23:56:47 +00:00
void MacroDefined(const Token &Tok, const MacroDirective *MD) override {
llvm::sys::fs::UniqueID UniqueID;
2018-08-09 17:08:14 +00:00
const LangOptions &Lang = param.Ctx->getLangOpts();
2018-07-07 23:56:47 +00:00
SourceLocation L = MD->getLocation();
const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(L));
if (!FE)
return;
if (IndexFile *db = param.ConsumeFile(*FE)) {
2018-08-09 17:08:14 +00:00
auto [Name, usr] = GetMacro(Tok);
2018-07-07 23:56:47 +00:00
IndexVar &var = db->ToVar(usr);
Range range = FromTokenRange(SM, Lang, {L, L}, &UniqueID);
2018-07-08 19:49:27 +00:00
var.def.kind = lsSymbolKind::Macro;
var.def.parent_kind = lsSymbolKind::File;
if (var.def.spell)
var.declarations.push_back(*var.def.spell);
2018-07-08 19:49:27 +00:00
const MacroInfo *MI = MD->getMacroInfo();
SourceRange R(MI->getDefinitionLoc(), MI->getDefinitionEndLoc());
Range extent = FromTokenRange(SM, param.Ctx->getLangOpts(), R);
var.def.spell = {Use{{range, Role::Definition}}, extent};
2018-07-08 19:49:27 +00:00
if (var.def.detailed_name[0] == '\0') {
2018-07-07 23:56:47 +00:00
var.def.detailed_name = Intern(Name);
var.def.short_name_size = Name.size();
StringRef Buf = GetSourceInRange(SM, Lang, R);
var.def.hover =
Intern(Buf.count('\n') <= kInitializerMaxLines - 1
? Twine("#define ", GetSourceInRange(SM, Lang, R)).str()
: Twine("#define ", Name).str());
2018-07-07 23:56:47 +00:00
}
}
}
2018-08-09 17:08:14 +00:00
void MacroExpands(const Token &Tok, const MacroDefinition &MD, SourceRange R,
const MacroArgs *Args) override {
2018-07-07 23:56:47 +00:00
llvm::sys::fs::UniqueID UniqueID;
SourceLocation L = SM.getSpellingLoc(R.getBegin());
const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(L));
2018-07-07 23:56:47 +00:00
if (!FE)
return;
if (IndexFile *db = param.ConsumeFile(*FE)) {
IndexVar &var = db->ToVar(GetMacro(Tok).second);
var.uses.push_back(
{{FromTokenRange(SM, param.Ctx->getLangOpts(), {L, L}, &UniqueID),
Role::Dynamic}});
2018-07-07 23:56:47 +00:00
}
}
void MacroUndefined(const Token &Tok, const MacroDefinition &MD,
const MacroDirective *UD) override {
2018-07-08 07:46:53 +00:00
if (UD) {
SourceLocation L = UD->getLocation();
MacroExpands(Tok, MD, {L, L}, nullptr);
}
2018-07-07 23:56:47 +00:00
}
void SourceRangeSkipped(SourceRange Range, SourceLocation EndifLoc) override {
llvm::sys::fs::UniqueID UniqueID;
auto range = FromCharRange(SM, param.Ctx->getLangOpts(), Range, &UniqueID);
if (const FileEntry *FE =
SM.getFileEntryForID(SM.getFileID(Range.getBegin())))
if (IndexFile *db = param.ConsumeFile(*FE))
db->skipped_ranges.push_back(range);
2018-07-07 23:56:47 +00:00
}
};
class IndexFrontendAction : public ASTFrontendAction {
2018-08-09 17:08:14 +00:00
IndexParam &param;
2018-07-07 23:56:47 +00:00
public:
2018-08-09 17:08:14 +00:00
IndexFrontendAction(IndexParam &param) : param(param) {}
2018-07-07 23:56:47 +00:00
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
StringRef InFile) override {
Preprocessor &PP = CI.getPreprocessor();
2018-08-09 17:08:14 +00:00
PP.addPPCallbacks(
std::make_unique<IndexPPCallbacks>(PP.getSourceManager(), param));
2018-07-07 23:56:47 +00:00
return std::make_unique<ASTConsumer>();
}
};
2018-08-09 17:08:14 +00:00
} // namespace
const int IndexFile::kMajorVersion = 19;
const int IndexFile::kMinorVersion = 0;
2018-07-07 23:56:47 +00:00
IndexFile::IndexFile(llvm::sys::fs::UniqueID UniqueID, const std::string &path,
const std::string &contents)
2018-07-07 23:56:47 +00:00
: UniqueID(UniqueID), path(path), file_contents(contents) {}
2017-02-20 00:56:56 +00:00
2018-08-09 17:08:14 +00:00
IndexFunc &IndexFile::ToFunc(Usr usr) {
2018-07-10 06:40:26 +00:00
auto [it, inserted] = usr2func.try_emplace(usr);
if (inserted)
it->second.usr = usr;
return it->second;
2017-02-20 00:56:56 +00:00
}
2018-08-09 17:08:14 +00:00
IndexType &IndexFile::ToType(Usr usr) {
2018-07-10 06:40:26 +00:00
auto [it, inserted] = usr2type.try_emplace(usr);
if (inserted)
it->second.usr = usr;
return it->second;
2017-02-20 00:56:56 +00:00
}
2018-08-09 17:08:14 +00:00
IndexVar &IndexFile::ToVar(Usr usr) {
2018-07-10 06:40:26 +00:00
auto [it, inserted] = usr2var.try_emplace(usr);
if (inserted)
it->second.usr = usr;
return it->second;
2017-02-17 09:57:44 +00:00
}
std::string IndexFile::ToString() {
return ccls::Serialize(SerializeFormat::Json, *this);
2017-02-17 09:57:44 +00:00
}
2018-08-09 17:08:14 +00:00
template <typename T> void Uniquify(std::vector<T> &a) {
2018-07-17 06:22:34 +00:00
std::unordered_set<T> seen;
size_t n = 0;
2018-07-17 06:22:34 +00:00
for (size_t i = 0; i < a.size(); i++)
if (seen.insert(a[i]).second)
a[n++] = a[i];
a.resize(n);
2017-02-24 08:39:25 +00:00
}
2017-02-20 06:40:55 +00:00
2018-07-08 07:46:53 +00:00
namespace ccls::idx {
void Init() {
multiVersionMatcher = new GroupMatch(g_config->index.multiVersionWhitelist,
g_config->index.multiVersionBlacklist);
}
2018-08-09 17:08:14 +00:00
std::vector<std::unique_ptr<IndexFile>>
Index(CompletionManager *completion, WorkingFiles *wfiles, VFS *vfs,
const std::string &opt_wdir, const std::string &file,
2018-09-19 16:31:45 +00:00
const std::vector<const char *> &args,
2018-09-23 01:00:50 +00:00
const std::vector<std::pair<std::string, std::string>> &remapped, bool &ok) {
ok = true;
auto PCH = std::make_shared<PCHContainerOperations>();
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = llvm::vfs::getRealFileSystem();
std::shared_ptr<CompilerInvocation> CI = BuildCompilerInvocation(args, FS);
2018-09-23 01:00:50 +00:00
// e.g. .s
if (!CI)
return {};
2018-09-23 01:00:50 +00:00
ok = false;
2018-07-08 07:46:53 +00:00
// -fparse-all-comments enables documentation in the indexer and in
// code completion.
if (g_config->index.comments > 1)
CI->getLangOpts()->CommentOpts.ParseAllComments = true;
{
// FileSystemOptions& FSOpts = CI->getFileSystemOpts();
// if (FSOpts.WorkingDir.empty())
// FSOpts.WorkingDir = opt_wdir;
// HeaderSearchOptions &HSOpts = CI->getHeaderSearchOpts();
// llvm::errs() << HSOpts.ResourceDir << "\n";
// // lib/clang/7.0.0 is incorrect
// if (HSOpts.ResourceDir.compare(0, 3, "lib") == 0 &&
// HSOpts.UseBuiltinIncludes)
// HSOpts.ResourceDir = g_config->clang.resourceDir;
}
std::string buf = wfiles->GetContent(file);
std::vector<std::unique_ptr<llvm::MemoryBuffer>> Bufs;
2018-09-18 01:03:59 +00:00
if (buf.size()) {
// If there is a completion session, reuse its preamble if exists.
bool done_remap = false;
2018-09-18 01:03:59 +00:00
#if 0
std::shared_ptr<CompletionSession> session =
completion->TryGetSession(file, false, false);
if (session)
if (auto preamble = session->GetPreamble()) {
Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(buf));
auto Bounds = ComputePreambleBounds(*CI->getLangOpts(), Bufs.back().get(), 0);
if (preamble->Preamble.CanReuse(*CI, Bufs.back().get(), Bounds,
FS.get())) {
preamble->Preamble.AddImplicitPreamble(*CI, FS, Bufs.back().get());
done_remap = true;
}
}
2018-09-18 01:03:59 +00:00
#endif
for (auto &[filename, content] : remapped) {
if (filename == file && done_remap)
continue;
Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(content));
CI->getPreprocessorOpts().addRemappedFile(
filename == file ? CI->getFrontendOpts().Inputs[0].getFile()
: StringRef(filename),
Bufs.back().get());
}
}
DiagnosticConsumer DC;
auto Clang = std::make_unique<CompilerInstance>(PCH);
Clang->setInvocation(std::move(CI));
Clang->setVirtualFileSystem(FS);
Clang->createDiagnostics(&DC, false);
Clang->setTarget(TargetInfo::CreateTargetInfo(
Clang->getDiagnostics(), Clang->getInvocation().TargetOpts));
if (!Clang->hasTarget())
2018-04-02 07:22:12 +00:00
return {};
2017-02-16 09:35:30 +00:00
2018-09-21 01:04:55 +00:00
IndexParam param(*vfs);
auto DataConsumer = std::make_shared<IndexDataConsumer>(param);
index::IndexingOptions IndexOpts;
IndexOpts.SystemSymbolFilter =
index::IndexingOptions::SystemSymbolFilterKind::All;
IndexOpts.IndexFunctionLocals = true;
2018-07-10 06:40:26 +00:00
#if LLVM_VERSION_MAJOR >= 7
IndexOpts.IndexImplicitInstantiation = true;
#endif
std::unique_ptr<FrontendAction> Action = createIndexingAction(
2018-07-07 23:56:47 +00:00
DataConsumer, IndexOpts, std::make_unique<IndexFrontendAction>(param));
{
llvm::CrashRecoveryContext CRC;
auto parse = [&]() {
if (!Action->BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]))
return;
if (!Action->Execute())
return;
Action->EndSourceFile();
ok = true;
};
if (!CRC.RunSafely(parse)) {
LOG_S(ERROR) << "clang crashed for " << file;
return {};
}
2018-07-08 07:46:53 +00:00
}
if (!ok) {
LOG_S(ERROR) << "failed to index " << file;
2018-04-02 07:22:12 +00:00
return {};
2017-12-01 17:57:03 +00:00
}
for (auto &Buf : Bufs)
Buf.release();
2018-09-21 01:04:55 +00:00
std::vector<std::unique_ptr<IndexFile>> result;
for (auto &it : param.UID2File) {
if (!it.second.db)
continue;
std::unique_ptr<IndexFile> &entry = it.second.db;
entry->import_file = file;
entry->args = args;
for (auto &[_, it] : entry->uid2lid_and_path)
entry->lid2path.emplace_back(it.first, std::move(it.second));
entry->uid2lid_and_path.clear();
2018-08-09 17:08:14 +00:00
for (auto &it : entry->usr2func) {
2018-03-07 23:08:50 +00:00
// e.g. declaration + out-of-line definition
Uniquify(it.second.derived);
Uniquify(it.second.uses);
2018-03-07 23:08:50 +00:00
}
2018-08-09 17:08:14 +00:00
for (auto &it : entry->usr2type) {
Uniquify(it.second.derived);
Uniquify(it.second.uses);
// e.g. declaration + out-of-line definition
Uniquify(it.second.def.bases);
Uniquify(it.second.def.funcs);
}
2018-08-09 17:08:14 +00:00
for (auto &it : entry->usr2var)
Uniquify(it.second.uses);
2018-01-06 17:28:55 +00:00
// Update dependencies for the file.
for (auto &[_, file] : param.UID2File) {
const std::string &path = file.path;
if (path == entry->path)
entry->mtime = file.mtime;
else if (path != entry->import_file)
entry->dependencies[llvm::CachedHashStringRef(Intern(path))] =
file.mtime;
}
2018-09-21 01:04:55 +00:00
result.push_back(std::move(entry));
2017-04-08 22:54:36 +00:00
}
2017-04-19 00:05:14 +00:00
2018-04-02 07:22:12 +00:00
return result;
2017-03-22 17:16:09 +00:00
}
2018-08-09 17:08:14 +00:00
} // namespace ccls::idx
void Reflect(Reader &vis, SymbolRef &v) {
if (vis.Format() == SerializeFormat::Json) {
std::string t = vis.GetString();
char *s = const_cast<char *>(t.c_str());
v.range = Range::FromString(s);
s = strchr(s, '|');
v.usr = strtoull(s + 1, &s, 10);
v.kind = static_cast<SymbolKind>(strtol(s + 1, &s, 10));
v.role = static_cast<Role>(strtol(s + 1, &s, 10));
} else {
Reflect(vis, v.range);
Reflect(vis, v.usr);
Reflect(vis, v.kind);
Reflect(vis, v.role);
}
}
void Reflect(Writer &vis, SymbolRef &v) {
if (vis.Format() == SerializeFormat::Json) {
char buf[99];
snprintf(buf, sizeof buf, "%s|%" PRIu64 "|%d|%d",
v.range.ToString().c_str(), v.usr, int(v.kind), int(v.role));
std::string s(buf);
Reflect(vis, s);
} else {
Reflect(vis, v.range);
Reflect(vis, v.usr);
Reflect(vis, v.kind);
Reflect(vis, v.role);
}
}
2018-08-09 17:08:14 +00:00
void Reflect(Reader &vis, Use &v) {
if (vis.Format() == SerializeFormat::Json) {
std::string t = vis.GetString();
2018-08-09 17:08:14 +00:00
char *s = const_cast<char *>(t.c_str());
v.range = Range::FromString(s);
s = strchr(s, '|');
v.role = static_cast<Role>(strtol(s + 1, &s, 10));
v.file_id = static_cast<int>(strtol(s + 1, &s, 10));
} else {
Reflect(vis, v.range);
Reflect(vis, v.role);
Reflect(vis, v.file_id);
}
}
2018-08-09 17:08:14 +00:00
void Reflect(Writer &vis, Use &v) {
if (vis.Format() == SerializeFormat::Json) {
char buf[99];
snprintf(buf, sizeof buf, "%s|%d|%d", v.range.ToString().c_str(),
int(v.role), v.file_id);
std::string s(buf);
Reflect(vis, s);
} else {
Reflect(vis, v.range);
Reflect(vis, v.role);
Reflect(vis, v.file_id);
}
}
void Reflect(Reader &vis, DeclRef &v) {
if (vis.Format() == SerializeFormat::Json) {
std::string t = vis.GetString();
char *s = const_cast<char *>(t.c_str());
v.range = Range::FromString(s);
s = strchr(s, '|') + 1;
v.extent = Range::FromString(s);
s = strchr(s, '|');
v.role = static_cast<Role>(strtol(s + 1, &s, 10));
v.file_id = static_cast<int>(strtol(s + 1, &s, 10));
} else {
Reflect(vis, static_cast<Use &>(v));
Reflect(vis, v.extent);
}
}
void Reflect(Writer &vis, DeclRef &v) {
if (vis.Format() == SerializeFormat::Json) {
char buf[99];
snprintf(buf, sizeof buf, "%s|%s|%d|%d", v.range.ToString().c_str(),
v.extent.ToString().c_str(), int(v.role), v.file_id);
std::string s(buf);
Reflect(vis, s);
} else {
Reflect(vis, static_cast<Use &>(v));
Reflect(vis, v.extent);
}
}