summaryrefslogtreecommitdiffstats
path: root/clangd/XRefs.cpp
diff options
context:
space:
mode:
authorJordan Rupprecht <rupprecht@google.com>2019-05-14 21:58:59 +0000
committerJordan Rupprecht <rupprecht@google.com>2019-05-14 21:58:59 +0000
commit46054fed6aeeabea27b9ba4a3ef81ab5ff9b9645 (patch)
treed12279f80b5729d0324f066002c838baa736fbd2 /clangd/XRefs.cpp
parent5026a9a16d10a2edf09be54c7225f49b5789c69e (diff)
parent0eb1ac6d1df856f065717226ef34d00679a211fe (diff)
Creating branches/google/stable and tags/google/stable/2019-05-14 from r360103upstream/stable
git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/branches/google/stable@360714 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'clangd/XRefs.cpp')
-rw-r--r--clangd/XRefs.cpp512
1 files changed, 349 insertions, 163 deletions
diff --git a/clangd/XRefs.cpp b/clangd/XRefs.cpp
index 29561dfe..c51631ad 100644
--- a/clangd/XRefs.cpp
+++ b/clangd/XRefs.cpp
@@ -1,19 +1,24 @@
//===--- XRefs.cpp -----------------------------------------------*- C++-*-===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "XRefs.h"
#include "AST.h"
+#include "FindSymbols.h"
#include "Logger.h"
#include "SourceCode.h"
#include "URI.h"
+#include "index/Merge.h"
+#include "index/SymbolCollector.h"
+#include "index/SymbolLocation.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Type.h"
#include "clang/Index/IndexDataConsumer.h"
+#include "clang/Index/IndexSymbol.h"
#include "clang/Index/IndexingAction.h"
#include "clang/Index/USRGeneration.h"
#include "llvm/Support/Path.h"
@@ -22,18 +27,27 @@ namespace clang {
namespace clangd {
namespace {
-// Get the definition from a given declaration `D`.
-// Return nullptr if no definition is found, or the declaration type of `D` is
-// not supported.
+// Returns the single definition of the entity declared by D, if visible.
+// In particular:
+// - for non-redeclarable kinds (e.g. local vars), return D
+// - for kinds that allow multiple definitions (e.g. namespaces), return nullptr
+// Kinds of nodes that always return nullptr here will not have definitions
+// reported by locateSymbolAt().
const Decl *getDefinition(const Decl *D) {
assert(D);
+ // Decl has one definition that we can find.
if (const auto *TD = dyn_cast<TagDecl>(D))
return TD->getDefinition();
- else if (const auto *VD = dyn_cast<VarDecl>(D))
+ if (const auto *VD = dyn_cast<VarDecl>(D))
return VD->getDefinition();
- else if (const auto *FD = dyn_cast<FunctionDecl>(D))
+ if (const auto *FD = dyn_cast<FunctionDecl>(D))
return FD->getDefinition();
- return nullptr;
+ // Only a single declaration is allowed.
+ if (isa<ValueDecl>(D) || isa<TemplateTypeParmDecl>(D) ||
+ isa<TemplateTemplateParmDecl>(D)) // except cases above
+ return D;
+ // Multiple definitions are allowed.
+ return nullptr; // except cases above
}
void logIfOverflow(const SymbolLocation &Loc) {
@@ -70,25 +84,40 @@ llvm::Optional<Location> toLSPLocation(const SymbolLocation &Loc,
return LSPLoc;
}
+SymbolLocation toIndexLocation(const Location &Loc, std::string &URIStorage) {
+ SymbolLocation SymLoc;
+ URIStorage = Loc.uri.uri();
+ SymLoc.FileURI = URIStorage.c_str();
+ SymLoc.Start.setLine(Loc.range.start.line);
+ SymLoc.Start.setColumn(Loc.range.start.character);
+ SymLoc.End.setLine(Loc.range.end.line);
+ SymLoc.End.setColumn(Loc.range.end.character);
+ return SymLoc;
+}
+
+// Returns the preferred location between an AST location and an index location.
+SymbolLocation getPreferredLocation(const Location &ASTLoc,
+ const SymbolLocation &IdxLoc,
+ std::string &Scratch) {
+ // Also use a dummy symbol for the index location so that other fields (e.g.
+ // definition) are not factored into the preferrence.
+ Symbol ASTSym, IdxSym;
+ ASTSym.ID = IdxSym.ID = SymbolID("dummy_id");
+ ASTSym.CanonicalDeclaration = toIndexLocation(ASTLoc, Scratch);
+ IdxSym.CanonicalDeclaration = IdxLoc;
+ auto Merged = mergeSymbol(ASTSym, IdxSym);
+ return Merged.CanonicalDeclaration;
+}
+
struct MacroDecl {
llvm::StringRef Name;
const MacroInfo *Info;
};
-struct DeclInfo {
- const Decl *D;
- // Indicates the declaration is referenced by an explicit AST node.
- bool IsReferencedExplicitly = false;
-};
-
/// Finds declarations locations that a given source location refers to.
class DeclarationAndMacrosFinder : public index::IndexDataConsumer {
std::vector<MacroDecl> MacroInfos;
- // The value of the map indicates whether the declaration has been referenced
- // explicitly in the code.
- // True means the declaration is explicitly referenced at least once; false
- // otherwise.
- llvm::DenseMap<const Decl *, bool> Decls;
+ llvm::DenseSet<const Decl *> Decls;
const SourceLocation &SearchedLocation;
const ASTContext &AST;
Preprocessor &PP;
@@ -98,22 +127,14 @@ public:
ASTContext &AST, Preprocessor &PP)
: SearchedLocation(SearchedLocation), AST(AST), PP(PP) {}
- // Get all DeclInfo of the found declarations.
- // The results are sorted by "IsReferencedExplicitly" and declaration
- // location.
- std::vector<DeclInfo> getFoundDecls() const {
- std::vector<DeclInfo> Result;
- for (auto It : Decls) {
- Result.emplace_back();
- Result.back().D = It.first;
- Result.back().IsReferencedExplicitly = It.second;
- }
+ // The results are sorted by declaration location.
+ std::vector<const Decl *> getFoundDecls() const {
+ std::vector<const Decl *> Result;
+ for (const Decl *D : Decls)
+ Result.push_back(D);
- // Sort results. Declarations being referenced explicitly come first.
- llvm::sort(Result, [](const DeclInfo &L, const DeclInfo &R) {
- if (L.IsReferencedExplicitly != R.IsReferencedExplicitly)
- return L.IsReferencedExplicitly > R.IsReferencedExplicitly;
- return L.D->getBeginLoc() < R.D->getBeginLoc();
+ llvm::sort(Result, [](const Decl *L, const Decl *R) {
+ return L->getBeginLoc() < R->getBeginLoc();
});
return Result;
}
@@ -137,29 +158,33 @@ public:
llvm::ArrayRef<index::SymbolRelation> Relations,
SourceLocation Loc,
index::IndexDataConsumer::ASTNodeInfo ASTNode) override {
+ // Skip non-semantic references.
+ if (Roles & static_cast<unsigned>(index::SymbolRole::NameReference))
+ return true;
+
if (Loc == SearchedLocation) {
- auto isImplicitExpr = [](const Expr *E) {
+ auto IsImplicitExpr = [](const Expr *E) {
if (!E)
return false;
// We assume that a constructor expression is implict (was inserted by
// clang) if it has an invalid paren/brace location, since such
// experssion is impossible to write down.
if (const auto *CtorExpr = dyn_cast<CXXConstructExpr>(E))
- return CtorExpr->getNumArgs() > 0 &&
- CtorExpr->getParenOrBraceRange().isInvalid();
+ return CtorExpr->getParenOrBraceRange().isInvalid();
return isa<ImplicitCastExpr>(E);
};
- bool IsExplicit = !isImplicitExpr(ASTNode.OrigE);
+ if (IsImplicitExpr(ASTNode.OrigE))
+ return true;
// Find and add definition declarations (for GoToDefinition).
// We don't use parameter `D`, as Parameter `D` is the canonical
// declaration, which is the first declaration of a redeclarable
// declaration, and it could be a forward declaration.
if (const auto *Def = getDefinition(D)) {
- Decls[Def] |= IsExplicit;
+ Decls.insert(Def);
} else {
// Couldn't find a definition, fall back to use `D`.
- Decls[D] |= IsExplicit;
+ Decls.insert(D);
}
}
return true;
@@ -197,7 +222,7 @@ private:
};
struct IdentifiedSymbol {
- std::vector<DeclInfo> Decls;
+ std::vector<const Decl *> Decls;
std::vector<MacroDecl> Macros;
};
@@ -208,6 +233,8 @@ IdentifiedSymbol getSymbolAtPosition(ParsedAST &AST, SourceLocation Pos) {
IndexOpts.SystemSymbolFilter =
index::IndexingOptions::SystemSymbolFilterKind::All;
IndexOpts.IndexFunctionLocals = true;
+ IndexOpts.IndexParametersInDeclarations = true;
+ IndexOpts.IndexTemplateParameters = true;
indexTopLevelDecls(AST.getASTContext(), AST.getPreprocessor(),
AST.getLocalTopLevelDecls(), DeclMacrosFinder, IndexOpts);
@@ -241,8 +268,8 @@ llvm::Optional<Location> makeLocation(ParsedAST &AST, SourceLocation TokLoc,
} // namespace
-std::vector<Location> findDefinitions(ParsedAST &AST, Position Pos,
- const SymbolIndex *Index) {
+std::vector<LocatedSymbol> locateSymbolAt(ParsedAST &AST, Position Pos,
+ const SymbolIndex *Index) {
const auto &SM = AST.getASTContext().getSourceManager();
auto MainFilePath =
getCanonicalPath(SM.getFileEntryForID(SM.getMainFileID()), SM);
@@ -251,116 +278,99 @@ std::vector<Location> findDefinitions(ParsedAST &AST, Position Pos,
return {};
}
- std::vector<Location> Result;
- // Handle goto definition for #include.
+ // Treat #included files as symbols, to enable go-to-definition on them.
for (auto &Inc : AST.getIncludeStructure().MainFileIncludes) {
- if (!Inc.Resolved.empty() && Inc.R.start.line == Pos.line)
- Result.push_back(
- Location{URIForFile::canonicalize(Inc.Resolved, *MainFilePath), {}});
+ if (!Inc.Resolved.empty() && Inc.R.start.line == Pos.line) {
+ LocatedSymbol File;
+ File.Name = llvm::sys::path::filename(Inc.Resolved);
+ File.PreferredDeclaration = {
+ URIForFile::canonicalize(Inc.Resolved, *MainFilePath), Range{}};
+ File.Definition = File.PreferredDeclaration;
+ // We're not going to find any further symbols on #include lines.
+ return {std::move(File)};
+ }
}
- if (!Result.empty())
- return Result;
- // Identified symbols at a specific position.
SourceLocation SourceLocationBeg =
getBeginningOfIdentifier(AST, Pos, SM.getMainFileID());
auto Symbols = getSymbolAtPosition(AST, SourceLocationBeg);
- for (auto Item : Symbols.Macros) {
- auto Loc = Item.Info->getDefinitionLoc();
- auto L = makeLocation(AST, Loc, *MainFilePath);
- if (L)
- Result.push_back(*L);
- }
-
- // Declaration and definition are different terms in C-family languages, and
- // LSP only defines the "GoToDefinition" specification, so we try to perform
- // the "most sensible" GoTo operation:
- //
- // - We use the location from AST and index (if available) to provide the
- // final results. When there are duplicate results, we prefer AST over
- // index because AST is more up-to-date.
- //
- // - For each symbol, we will return a location of the canonical declaration
- // (e.g. function declaration in header), and a location of definition if
- // they are available.
- //
- // So the work flow:
- //
- // 1. Identify the symbols being search for by traversing the AST.
- // 2. Populate one of the locations with the AST location.
- // 3. Use the AST information to query the index, and populate the index
- // location (if available).
- // 4. Return all populated locations for all symbols, definition first (
- // which we think is the users wants most often).
- struct CandidateLocation {
- llvm::Optional<Location> Def;
- llvm::Optional<Location> Decl;
- };
- // We respect the order in Symbols.Decls.
- llvm::SmallVector<CandidateLocation, 8> ResultCandidates;
- llvm::DenseMap<SymbolID, size_t> CandidatesIndex;
+ // Macros are simple: there's no declaration/definition distinction.
+ // As a consequence, there's no need to look them up in the index either.
+ std::vector<LocatedSymbol> Result;
+ for (auto M : Symbols.Macros) {
+ if (auto Loc =
+ makeLocation(AST, M.Info->getDefinitionLoc(), *MainFilePath)) {
+ LocatedSymbol Macro;
+ Macro.Name = M.Name;
+ Macro.PreferredDeclaration = *Loc;
+ Macro.Definition = Loc;
+ Result.push_back(std::move(Macro));
+ }
+ }
+
+ // Decls are more complicated.
+ // The AST contains at least a declaration, maybe a definition.
+ // These are up-to-date, and so generally preferred over index results.
+ // We perform a single batch index lookup to find additional definitions.
+
+ // Results follow the order of Symbols.Decls.
+ // Keep track of SymbolID -> index mapping, to fill in index data later.
+ llvm::DenseMap<SymbolID, size_t> ResultIndex;
// Emit all symbol locations (declaration or definition) from AST.
- for (const DeclInfo &DI : Symbols.Decls) {
- const Decl *D = DI.D;
- // Fake key for symbols don't have USR (no SymbolID).
- // Ideally, there should be a USR for each identified symbols. Symbols
- // without USR are rare and unimportant cases, we use the a fake holder to
- // minimize the invasiveness of these cases.
- SymbolID Key("");
+ for (const Decl *D : Symbols.Decls) {
+ auto Loc = makeLocation(AST, findNameLoc(D), *MainFilePath);
+ if (!Loc)
+ continue;
+
+ Result.emplace_back();
+ if (auto *ND = dyn_cast<NamedDecl>(D))
+ Result.back().Name = printName(AST.getASTContext(), *ND);
+ Result.back().PreferredDeclaration = *Loc;
+ // DeclInfo.D is always a definition if possible, so this check works.
+ if (getDefinition(D) == D)
+ Result.back().Definition = *Loc;
+
+ // Record SymbolID for index lookup later.
if (auto ID = getSymbolID(D))
- Key = *ID;
-
- auto R = CandidatesIndex.try_emplace(Key, ResultCandidates.size());
- if (R.second) // new entry
- ResultCandidates.emplace_back();
- auto &Candidate = ResultCandidates[R.first->second];
-
- auto Loc = findNameLoc(D);
- auto L = makeLocation(AST, Loc, *MainFilePath);
- // The declaration in the identified symbols is a definition if possible
- // otherwise it is declaration.
- bool IsDef = getDefinition(D) == D;
- // Populate one of the slots with location for the AST.
- if (!IsDef)
- Candidate.Decl = L;
- else
- Candidate.Def = L;
+ ResultIndex[*ID] = Result.size() - 1;
}
- if (Index) {
+ // Now query the index for all Symbol IDs we found in the AST.
+ if (Index && !ResultIndex.empty()) {
LookupRequest QueryRequest;
- // Build request for index query, using SymbolID.
- for (auto It : CandidatesIndex)
+ for (auto It : ResultIndex)
QueryRequest.IDs.insert(It.first);
- std::string TUPath;
- const FileEntry *FE = SM.getFileEntryForID(SM.getMainFileID());
- if (auto Path = getCanonicalPath(FE, SM))
- TUPath = *Path;
- // Query the index and populate the empty slot.
- Index->lookup(QueryRequest, [&TUPath, &ResultCandidates,
- &CandidatesIndex](const Symbol &Sym) {
- auto It = CandidatesIndex.find(Sym.ID);
- assert(It != CandidatesIndex.end());
- auto &Value = ResultCandidates[It->second];
-
- if (!Value.Def)
- Value.Def = toLSPLocation(Sym.Definition, TUPath);
- if (!Value.Decl)
- Value.Decl = toLSPLocation(Sym.CanonicalDeclaration, TUPath);
+ std::string Scratch;
+ Index->lookup(QueryRequest, [&](const Symbol &Sym) {
+ auto &R = Result[ResultIndex.lookup(Sym.ID)];
+
+ if (R.Definition) { // from AST
+ // Special case: if the AST yielded a definition, then it may not be
+ // the right *declaration*. Prefer the one from the index.
+ if (auto Loc = toLSPLocation(Sym.CanonicalDeclaration, *MainFilePath))
+ R.PreferredDeclaration = *Loc;
+
+ // We might still prefer the definition from the index, e.g. for
+ // generated symbols.
+ if (auto Loc = toLSPLocation(
+ getPreferredLocation(*R.Definition, Sym.Definition, Scratch),
+ *MainFilePath))
+ R.Definition = *Loc;
+ } else {
+ R.Definition = toLSPLocation(Sym.Definition, *MainFilePath);
+
+ // Use merge logic to choose AST or index declaration.
+ if (auto Loc = toLSPLocation(
+ getPreferredLocation(R.PreferredDeclaration,
+ Sym.CanonicalDeclaration, Scratch),
+ *MainFilePath))
+ R.PreferredDeclaration = *Loc;
+ }
});
}
- // Populate the results, definition first.
- for (const auto &Candidate : ResultCandidates) {
- if (Candidate.Def)
- Result.push_back(*Candidate.Def);
- if (Candidate.Decl &&
- Candidate.Decl != Candidate.Def) // Decl and Def might be the same
- Result.push_back(*Candidate.Decl);
- }
-
return Result;
}
@@ -424,6 +434,8 @@ findRefs(const std::vector<const Decl *> &Decls, ParsedAST &AST) {
IndexOpts.SystemSymbolFilter =
index::IndexingOptions::SystemSymbolFilterKind::All;
IndexOpts.IndexFunctionLocals = true;
+ IndexOpts.IndexParametersInDeclarations = true;
+ IndexOpts.IndexTemplateParameters = true;
indexTopLevelDecls(AST.getASTContext(), AST.getPreprocessor(),
AST.getLocalTopLevelDecls(), RefFinder, IndexOpts);
return std::move(RefFinder).take();
@@ -436,11 +448,7 @@ std::vector<DocumentHighlight> findDocumentHighlights(ParsedAST &AST,
const SourceManager &SM = AST.getASTContext().getSourceManager();
auto Symbols = getSymbolAtPosition(
AST, getBeginningOfIdentifier(AST, Pos, SM.getMainFileID()));
- std::vector<const Decl *> TargetDecls;
- for (const DeclInfo &DI : Symbols.Decls) {
- TargetDecls.push_back(DI.D);
- }
- auto References = findRefs(TargetDecls, AST);
+ auto References = findRefs(Symbols.Decls, AST);
std::vector<DocumentHighlight> Result;
for (const auto &Ref : References) {
@@ -561,13 +569,30 @@ static Hover getHoverContents(QualType T, ASTContext &ASTCtx) {
return H;
}
-/// Generate a \p Hover object given the macro \p MacroInf.
-static Hover getHoverContents(llvm::StringRef MacroName) {
- Hover H;
-
- H.contents.value = "#define ";
- H.contents.value += MacroName;
+/// Generate a \p Hover object given the macro \p MacroDecl.
+static Hover getHoverContents(MacroDecl Decl, ParsedAST &AST) {
+ SourceManager &SM = AST.getASTContext().getSourceManager();
+ std::string Definition = Decl.Name;
+
+ // Try to get the full definition, not just the name
+ SourceLocation StartLoc = Decl.Info->getDefinitionLoc();
+ SourceLocation EndLoc = Decl.Info->getDefinitionEndLoc();
+ if (EndLoc.isValid()) {
+ EndLoc = Lexer::getLocForEndOfToken(EndLoc, 0, SM,
+ AST.getASTContext().getLangOpts());
+ bool Invalid;
+ StringRef Buffer = SM.getBufferData(SM.getFileID(StartLoc), &Invalid);
+ if (!Invalid) {
+ unsigned StartOffset = SM.getFileOffset(StartLoc);
+ unsigned EndOffset = SM.getFileOffset(EndLoc);
+ if (EndOffset <= Buffer.size() && StartOffset < EndOffset)
+ Definition = Buffer.substr(StartOffset, EndOffset - StartOffset).str();
+ }
+ }
+ Hover H;
+ H.contents.kind = MarkupKind::PlainText;
+ H.contents.value = "#define " + Definition;
return H;
}
@@ -691,10 +716,10 @@ llvm::Optional<Hover> getHover(ParsedAST &AST, Position Pos) {
auto Symbols = getSymbolAtPosition(AST, SourceLocationBeg);
if (!Symbols.Macros.empty())
- return getHoverContents(Symbols.Macros[0].Name);
+ return getHoverContents(Symbols.Macros[0], AST);
if (!Symbols.Decls.empty())
- return getHoverContents(Symbols.Decls[0].D);
+ return getHoverContents(Symbols.Decls[0]);
auto DeducedType = getDeducedType(AST, SourceLocationBeg);
if (DeducedType && !DeducedType->isNull())
@@ -718,15 +743,9 @@ std::vector<Location> findReferences(ParsedAST &AST, Position Pos,
auto Loc = getBeginningOfIdentifier(AST, Pos, SM.getMainFileID());
auto Symbols = getSymbolAtPosition(AST, Loc);
- std::vector<const Decl *> TargetDecls;
- for (const DeclInfo &DI : Symbols.Decls) {
- if (DI.IsReferencedExplicitly)
- TargetDecls.push_back(DI.D);
- }
-
// We traverse the AST to find references in the main file.
// TODO: should we handle macros, too?
- auto MainFileRefs = findRefs(TargetDecls, AST);
+ auto MainFileRefs = findRefs(Symbols.Decls, AST);
for (const auto &Ref : MainFileRefs) {
Location Result;
Result.range = getTokenRange(AST, Ref.Loc);
@@ -739,7 +758,7 @@ std::vector<Location> findReferences(ParsedAST &AST, Position Pos,
RefsRequest Req;
Req.Limit = Limit;
- for (const Decl *D : TargetDecls) {
+ for (const Decl *D : Symbols.Decls) {
// Not all symbols can be referenced from outside (e.g. function-locals).
// TODO: we could skip TU-scoped symbols here (e.g. static functions) if
// we know this file isn't a header. The details might be tricky.
@@ -770,9 +789,9 @@ std::vector<SymbolDetails> getSymbolInfo(ParsedAST &AST, Position Pos) {
std::vector<SymbolDetails> Results;
- for (const auto &Sym : Symbols.Decls) {
+ for (const Decl *D : Symbols.Decls) {
SymbolDetails NewSymbol;
- if (const NamedDecl *ND = dyn_cast<NamedDecl>(Sym.D)) {
+ if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) {
std::string QName = printQualifiedName(*ND);
std::tie(NewSymbol.containerName, NewSymbol.name) =
splitQualifiedName(QName);
@@ -784,7 +803,7 @@ std::vector<SymbolDetails> getSymbolInfo(ParsedAST &AST, Position Pos) {
}
}
llvm::SmallString<32> USR;
- if (!index::generateUSRForDecl(Sym.D, USR)) {
+ if (!index::generateUSRForDecl(D, USR)) {
NewSymbol.USR = USR.str();
NewSymbol.ID = SymbolID(NewSymbol.USR);
}
@@ -795,7 +814,8 @@ std::vector<SymbolDetails> getSymbolInfo(ParsedAST &AST, Position Pos) {
SymbolDetails NewMacro;
NewMacro.name = Macro.Name;
llvm::SmallString<32> USR;
- if (!index::generateUSRForMacro(NewMacro.name, Loc, SM, USR)) {
+ if (!index::generateUSRForMacro(NewMacro.name,
+ Macro.Info->getDefinitionLoc(), SM, USR)) {
NewMacro.USR = USR.str();
NewMacro.ID = SymbolID(NewMacro.USR);
}
@@ -805,5 +825,171 @@ std::vector<SymbolDetails> getSymbolInfo(ParsedAST &AST, Position Pos) {
return Results;
}
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const LocatedSymbol &S) {
+ OS << S.Name << ": " << S.PreferredDeclaration;
+ if (S.Definition)
+ OS << " def=" << *S.Definition;
+ return OS;
+}
+
+// FIXME(nridge): Reduce duplication between this function and declToSym().
+static llvm::Optional<TypeHierarchyItem>
+declToTypeHierarchyItem(ASTContext &Ctx, const NamedDecl &ND) {
+ auto &SM = Ctx.getSourceManager();
+
+ SourceLocation NameLoc = findNameLoc(&ND);
+ // getFileLoc is a good choice for us, but we also need to make sure
+ // sourceLocToPosition won't switch files, so we call getSpellingLoc on top of
+ // that to make sure it does not switch files.
+ // FIXME: sourceLocToPosition should not switch files!
+ SourceLocation BeginLoc = SM.getSpellingLoc(SM.getFileLoc(ND.getBeginLoc()));
+ SourceLocation EndLoc = SM.getSpellingLoc(SM.getFileLoc(ND.getEndLoc()));
+ if (NameLoc.isInvalid() || BeginLoc.isInvalid() || EndLoc.isInvalid())
+ return llvm::None;
+
+ Position NameBegin = sourceLocToPosition(SM, NameLoc);
+ Position NameEnd = sourceLocToPosition(
+ SM, Lexer::getLocForEndOfToken(NameLoc, 0, SM, Ctx.getLangOpts()));
+
+ index::SymbolInfo SymInfo = index::getSymbolInfo(&ND);
+ // FIXME: this is not classifying constructors, destructors and operators
+ // correctly (they're all "methods").
+ SymbolKind SK = indexSymbolKindToSymbolKind(SymInfo.Kind);
+
+ TypeHierarchyItem THI;
+ THI.name = printName(Ctx, ND);
+ THI.kind = SK;
+ THI.deprecated = ND.isDeprecated();
+ THI.range =
+ Range{sourceLocToPosition(SM, BeginLoc), sourceLocToPosition(SM, EndLoc)};
+ THI.selectionRange = Range{NameBegin, NameEnd};
+ if (!THI.range.contains(THI.selectionRange)) {
+ // 'selectionRange' must be contained in 'range', so in cases where clang
+ // reports unrelated ranges we need to reconcile somehow.
+ THI.range = THI.selectionRange;
+ }
+
+ auto FilePath =
+ getCanonicalPath(SM.getFileEntryForID(SM.getFileID(BeginLoc)), SM);
+ auto TUPath = getCanonicalPath(SM.getFileEntryForID(SM.getMainFileID()), SM);
+ if (!FilePath || !TUPath)
+ return llvm::None; // Not useful without a uri.
+ THI.uri = URIForFile::canonicalize(*FilePath, *TUPath);
+
+ return THI;
+}
+
+using RecursionProtectionSet = llvm::SmallSet<const CXXRecordDecl *, 4>;
+
+static Optional<TypeHierarchyItem>
+getTypeAncestors(const CXXRecordDecl &CXXRD, ASTContext &ASTCtx,
+ RecursionProtectionSet &RPSet) {
+ Optional<TypeHierarchyItem> Result = declToTypeHierarchyItem(ASTCtx, CXXRD);
+ if (!Result)
+ return Result;
+
+ Result->parents.emplace();
+
+ // typeParents() will replace dependent template specializations
+ // with their class template, so to avoid infinite recursion for
+ // certain types of hierarchies, keep the templates encountered
+ // along the parent chain in a set, and stop the recursion if one
+ // starts to repeat.
+ auto *Pattern = CXXRD.getDescribedTemplate() ? &CXXRD : nullptr;
+ if (Pattern) {
+ if (!RPSet.insert(Pattern).second) {
+ return Result;
+ }
+ }
+
+ for (const CXXRecordDecl *ParentDecl : typeParents(&CXXRD)) {
+ if (Optional<TypeHierarchyItem> ParentSym =
+ getTypeAncestors(*ParentDecl, ASTCtx, RPSet)) {
+ Result->parents->emplace_back(std::move(*ParentSym));
+ }
+ }
+
+ if (Pattern) {
+ RPSet.erase(Pattern);
+ }
+
+ return Result;
+}
+
+const CXXRecordDecl *findRecordTypeAt(ParsedAST &AST, Position Pos) {
+ ASTContext &ASTCtx = AST.getASTContext();
+ const SourceManager &SourceMgr = ASTCtx.getSourceManager();
+ SourceLocation SourceLocationBeg =
+ getBeginningOfIdentifier(AST, Pos, SourceMgr.getMainFileID());
+ IdentifiedSymbol Symbols = getSymbolAtPosition(AST, SourceLocationBeg);
+ if (Symbols.Decls.empty())
+ return nullptr;
+
+ const Decl *D = Symbols.Decls[0];
+
+ if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
+ // If this is a variable, use the type of the variable.
+ return VD->getType().getTypePtr()->getAsCXXRecordDecl();
+ }
+
+ if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) {
+ // If this is a method, use the type of the class.
+ return Method->getParent();
+ }
+
+ // We don't handle FieldDecl because it's not clear what behaviour
+ // the user would expect: the enclosing class type (as with a
+ // method), or the field's type (as with a variable).
+
+ return dyn_cast<CXXRecordDecl>(D);
+}
+
+std::vector<const CXXRecordDecl *> typeParents(const CXXRecordDecl *CXXRD) {
+ std::vector<const CXXRecordDecl *> Result;
+
+ for (auto Base : CXXRD->bases()) {
+ const CXXRecordDecl *ParentDecl = nullptr;
+
+ const Type *Type = Base.getType().getTypePtr();
+ if (const RecordType *RT = Type->getAs<RecordType>()) {
+ ParentDecl = RT->getAsCXXRecordDecl();
+ }
+
+ if (!ParentDecl) {
+ // Handle a dependent base such as "Base<T>" by using the primary
+ // template.
+ if (const TemplateSpecializationType *TS =
+ Type->getAs<TemplateSpecializationType>()) {
+ TemplateName TN = TS->getTemplateName();
+ if (TemplateDecl *TD = TN.getAsTemplateDecl()) {
+ ParentDecl = dyn_cast<CXXRecordDecl>(TD->getTemplatedDecl());
+ }
+ }
+ }
+
+ if (ParentDecl)
+ Result.push_back(ParentDecl);
+ }
+
+ return Result;
+}
+
+llvm::Optional<TypeHierarchyItem>
+getTypeHierarchy(ParsedAST &AST, Position Pos, int ResolveLevels,
+ TypeHierarchyDirection Direction) {
+ const CXXRecordDecl *CXXRD = findRecordTypeAt(AST, Pos);
+ if (!CXXRD)
+ return llvm::None;
+
+ RecursionProtectionSet RPSet;
+ Optional<TypeHierarchyItem> Result =
+ getTypeAncestors(*CXXRD, AST.getASTContext(), RPSet);
+
+ // FIXME(nridge): Resolve type descendants if direction is Children or Both,
+ // and ResolveLevels > 0.
+
+ return Result;
+}
+
} // namespace clangd
} // namespace clang