Skip to content

Commit 0384339

Browse files
feat: Emit defs & refs for functions & methods (#122)
1 parent 31bce53 commit 0384339

19 files changed

+1040
-30
lines changed

docs/Design.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,7 @@ There are 4 possible cases here:
566566
to the corresponding synthetic standard library package
567567
568568

569+
<!-- TODO(def: handle-forward-decls) -->
569570
For simplicity, an initial implementation
570571
can put fake `SymbolInformation` values
571572
in the `external_symbols` list

indexer/Indexer.cc

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,49 @@ void TuIndexer::saveEnumTypeLoc(const clang::EnumTypeLoc &enumTypeLoc) {
297297
this->saveTagTypeLoc(enumTypeLoc);
298298
}
299299

300+
void TuIndexer::saveFunctionDecl(const clang::FunctionDecl &functionDecl) {
301+
auto optSymbol = this->symbolFormatter.getFunctionSymbol(functionDecl);
302+
if (!optSymbol.has_value()) {
303+
return;
304+
}
305+
std::string_view symbol = optSymbol.value();
306+
307+
if (functionDecl.isPure() || functionDecl.isThisDeclarationADefinition()) {
308+
scip::SymbolInformation symbolInfo{};
309+
for (auto &docComment : this->tryGetDocComment(functionDecl)) {
310+
*symbolInfo.add_documentation() = std::move(docComment.Text);
311+
}
312+
if (auto *cxxMethodDecl =
313+
llvm::dyn_cast<clang::CXXMethodDecl>(&functionDecl)) {
314+
for (auto &overridenMethodDecl : cxxMethodDecl->overridden_methods()) {
315+
if (auto optOverridenSymbol =
316+
this->symbolFormatter.getFunctionSymbol(*overridenMethodDecl)) {
317+
scip::Relationship rel{};
318+
rel.set_symbol(optOverridenSymbol->data(),
319+
optOverridenSymbol->size());
320+
rel.set_is_implementation(true);
321+
rel.set_is_reference(true);
322+
*symbolInfo.add_relationships() = std::move(rel);
323+
}
324+
}
325+
}
326+
// FIXME(issue: https://github.com/sourcegraph/scip-clang/issues/123)
327+
// Kythe uses DeclarationNameInfo::getSourceRange(), which seems like
328+
// the right API for getting the right range for multi-token names like
329+
// 'operator<<' etc., but when I tried using that here, it returned
330+
// incorrect results for every kind of name.
331+
//
332+
// Specifically, for simple identifiers, it would return a blank range,
333+
// and for 'operator<<', it would exclude the range of '<<'.
334+
// So just rely on the single token implementation for now.
335+
this->saveDefinition(symbol, functionDecl.getLocation(),
336+
std::move(symbolInfo));
337+
} else {
338+
// See TODO(ref: handle-forward-decls)
339+
this->saveReference(symbol, functionDecl.getLocation());
340+
}
341+
}
342+
300343
void TuIndexer::saveNamespaceDecl(const clang::NamespaceDecl &namespaceDecl) {
301344
auto optSymbol = this->symbolFormatter.getNamespaceSymbol(namespaceDecl);
302345
if (!optSymbol.has_value()) {
@@ -420,9 +463,10 @@ void TuIndexer::saveTagDecl(const clang::TagDecl &tagDecl) {
420463
auto symbol = optSymbol.value();
421464

422465
if (!tagDecl.isThisDeclarationADefinition()) {
423-
// Forward declarations should be marked as references. In the future,
424-
// we should add an extra role to SCIP for forward declarations,
425-
// once https://github.com/sourcegraph/scip/issues/131 is addressed.
466+
// 1. We should emit an external symbol here in some situations
467+
// See TODO(ref: handle-forward-decls)
468+
// 2. Pass in a forward declaration SymbolRole here
469+
// once https://github.com/sourcegraph/scip/issues/131 is fixed.
426470
this->saveReference(symbol, tagDecl.getLocation());
427471
return;
428472
}
@@ -496,6 +540,25 @@ void TuIndexer::saveDeclRefExpr(const clang::DeclRefExpr &declRefExpr) {
496540
// ^ TODO: Add read-write access to the symbol role here
497541
}
498542

543+
void TuIndexer::saveMemberExpr(const clang::MemberExpr &memberExpr) {
544+
auto *namedDecl =
545+
llvm::dyn_cast<clang::NamedDecl>(memberExpr.getMemberDecl());
546+
if (!namedDecl) {
547+
return;
548+
}
549+
auto optSymbol = this->symbolFormatter.getNamedDeclSymbol(*namedDecl);
550+
if (!optSymbol.has_value()) {
551+
return;
552+
}
553+
// FIXME(issue: https://github.com/sourcegraph/scip-clang/issues/123)
554+
// The ideal API here is probably getMemberNameInfo().getSourceRange(),
555+
// similar to the operator case, but that doesn't give the expected result.
556+
if (!memberExpr.getMemberNameInfo().getName().isIdentifier()) {
557+
return;
558+
}
559+
this->saveReference(optSymbol.value(), memberExpr.getMemberLoc());
560+
}
561+
499562
void TuIndexer::emitDocumentOccurrencesAndSymbols(
500563
bool deterministic, clang::FileID fileId, scip::Document &scipDocument) {
501564
auto it = this->documentMap.find({fileId});

indexer/Indexer.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
#include "indexer/ScipExtras.h"
2222
#include "indexer/SymbolFormatter.h"
2323

24+
#define FOR_EACH_EXPR_TO_BE_INDEXED(F) \
25+
F(DeclRef) \
26+
F(Member)
27+
2428
#define FOR_EACH_TYPE_TO_BE_INDEXED(F) \
2529
F(Enum) \
2630
F(Record)
@@ -30,13 +34,16 @@ namespace clang {
3034
FOR_EACH_DECL_TO_BE_INDEXED(FORWARD_DECLARE)
3135
#undef FORWARD_DECLARE
3236

37+
#define FORWARD_DECLARE(ExprName) class ExprName##Expr;
38+
FOR_EACH_EXPR_TO_BE_INDEXED(FORWARD_DECLARE)
39+
#undef FORWARD_DECLARE
40+
3341
#define FORWARD_DECLARE(TypeName) class TypeName##TypeLoc;
3442
FOR_EACH_TYPE_TO_BE_INDEXED(FORWARD_DECLARE)
3543
#undef FORWARD_DECLARE
3644

3745
class ASTContext;
3846
class Decl;
39-
class DeclRefExpr;
4047
class MacroDefinition;
4148
class MacroInfo;
4249
class NestedNameSpecifierLoc;
@@ -213,7 +220,10 @@ class TuIndexer final {
213220
void saveTagDecl(const clang::TagDecl &);
214221
void saveTagTypeLoc(const clang::TagTypeLoc &);
215222

216-
void saveDeclRefExpr(const clang::DeclRefExpr &);
223+
#define SAVE_EXPR(ExprName) \
224+
void save##ExprName##Expr(const clang::ExprName##Expr &);
225+
FOR_EACH_EXPR_TO_BE_INDEXED(SAVE_EXPR)
226+
#undef SAVE_EXPR
217227
void saveNestedNameSpecifierLoc(const clang::NestedNameSpecifierLoc &);
218228

219229
#define SAVE_TYPE_LOC(TypeName) \

indexer/SymbolFormatter.cc

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -371,23 +371,38 @@ SymbolFormatter::getEnumSymbol(const clang::EnumDecl &enumDecl) {
371371
return this->getTagSymbol(enumDecl);
372372
}
373373

374+
std::optional<std::string_view>
375+
SymbolFormatter::getFunctionSymbol(const clang::FunctionDecl &functionDecl) {
376+
return this->getSymbolCached(
377+
functionDecl, [&]() -> std::optional<std::string> {
378+
auto optContextSymbol =
379+
this->getContextSymbol(*functionDecl.getDeclContext());
380+
if (!optContextSymbol.has_value()) {
381+
return {};
382+
}
383+
// See discussion in docs/Design.md for this choice of disambiguator.
384+
auto typeString =
385+
functionDecl.getType().getCanonicalType().getAsString();
386+
auto name = functionDecl.getNameAsString();
387+
return SymbolBuilder::formatContextual(
388+
optContextSymbol.value(),
389+
DescriptorBuilder{
390+
.name = name,
391+
.disambiguator = this->formatTemporary(
392+
"{:x}", HashValue::forText(typeString)),
393+
.suffix = scip::Descriptor::Method,
394+
});
395+
});
396+
}
397+
374398
std::optional<std::string_view>
375399
SymbolFormatter::getNamedDeclSymbol(const clang::NamedDecl &namedDecl) {
376-
using Kind = clang::Decl::Kind;
377-
#define HANDLE(kind_) \
378-
case clang::Decl::Kind::kind_: \
379-
return this->get##kind_##Symbol(llvm::cast<clang::kind_##Decl>(namedDecl));
380-
switch (namedDecl.getKind()) {
381-
FOR_EACH_DECL_TO_BE_INDEXED(HANDLE)
382-
case Kind::VarTemplateSpecialization:
383-
case Kind::VarTemplatePartialSpecialization:
384-
case Kind::OMPCapturedExpr:
385-
case Kind::Decomposition:
386-
case Kind::ParmVar:
387-
return this->getVarSymbol(*llvm::dyn_cast<clang::VarDecl>(&namedDecl));
388-
default:
389-
return {};
400+
#define HANDLE(kind_) \
401+
if (auto *decl = llvm::dyn_cast<clang::kind_##Decl>(&namedDecl)) { \
402+
return this->get##kind_##Symbol(*decl); \
390403
}
404+
FOR_EACH_DECL_TO_BE_INDEXED(HANDLE)
405+
return {};
391406
}
392407

393408
/// Returns nullopt for anonymous namespaces in files for which

indexer/SymbolFormatter.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
F(Binding) \
2121
F(EnumConstant) \
2222
F(Enum) \
23+
F(Function) \
2324
F(Namespace) \
2425
F(Record) \
2526
F(Var)

indexer/Worker.cc

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -705,10 +705,14 @@ class IndexerAstVisitor : public clang::RecursiveASTVisitor<IndexerAstVisitor> {
705705
FOR_EACH_DECL_TO_BE_INDEXED(VISIT_DECL)
706706
#undef VISIT_DECL
707707

708-
bool VisitDeclRefExpr(clang::DeclRefExpr *declRefExpr) {
709-
this->tuIndexer.saveDeclRefExpr(*declRefExpr);
710-
return true;
711-
}
708+
#define VISIT_EXPR(ExprName) \
709+
bool Visit##ExprName##Expr(clang::ExprName##Expr *expr) { \
710+
ENFORCE(expr, "expected " #ExprName "Expr to be null-null"); \
711+
this->tuIndexer.save##ExprName##Expr(*expr); \
712+
return true; \
713+
}
714+
FOR_EACH_EXPR_TO_BE_INDEXED(VISIT_EXPR)
715+
#undef VISIT_EXPR
712716

713717
#define VISIT_TYPE_LOC(TypeName) \
714718
bool Visit##TypeName##TypeLoc(clang::TypeName##TypeLoc typeLoc) { \

test/Snapshot.cc

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,9 @@ void formatSnapshot(const scip::Document &document,
167167
}
168168

169169
ENFORCE(range.start.column < range.end.column,
170-
"We shouldn't be emitting empty ranges 🙅");
170+
"Found empty range for {} at {}:{}:{}", occ.symbol(),
171+
sourceFilePath.asStringView(), range.start.line,
172+
range.start.column);
171173

172174
auto lineStart =
173175
absl::StrCat("//", std::string(range.start.column - 1, ' '));
@@ -293,13 +295,17 @@ void MultiTuSnapshotTest::run(SnapshotMode mode,
293295
absl::flat_hash_map<RootRelativePath, RootRelativePath> alreadyUsedFiles;
294296

295297
for (auto &io : this->inputOutputs) {
296-
auto &sourceFilePath = io.sourceFilePath.asStringRef();
297-
if (!test::isTuMainFilePath(sourceFilePath)) {
298+
auto &sourceFileRelPath = io.sourceFilePath.asStringRef();
299+
if (!test::isTuMainFilePath(sourceFileRelPath)) {
298300
continue;
299301
}
300-
std::vector<std::string> commandLine{"clang", "-I", ".", sourceFilePath};
302+
std::vector<std::string> commandLine{"clang", "-I", ".", sourceFileRelPath};
301303
{
302-
std::ifstream tuStream(sourceFilePath, std::ios::in | std::ios::binary);
304+
auto sourceFileAbsPath =
305+
this->rootPath.makeAbsolute(io.sourceFilePath.asRef());
306+
ENFORCE(std::filesystem::exists(sourceFileAbsPath.asStringRef()));
307+
std::ifstream tuStream(sourceFileAbsPath.asStringRef(),
308+
std::ios::in | std::ios::binary);
303309
std::string prefix = "// extra-args: ";
304310
for (std::string line; std::getline(tuStream, line);) {
305311
if (!line.starts_with(prefix)) {
@@ -314,8 +320,8 @@ void MultiTuSnapshotTest::run(SnapshotMode mode,
314320
}
315321
}
316322

317-
auto output =
318-
compute(rootPath, io.sourceFilePath.asRef(), std::move(commandLine));
323+
auto output = compute(this->rootPath, io.sourceFilePath.asRef(),
324+
std::move(commandLine));
319325

320326
for (auto &[filePath, _] : output) {
321327
ENFORCE(!alreadyUsedFiles.contains(filePath),

test/index/functions/ctors_dtors.cc

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// extra-args: -std=c++20
2+
3+
template<class T> struct remove_ref { typedef T type; };
4+
template<class T> struct remove_ref<T&> { typedef T type; };
5+
template<class T> struct remove_ref<T&&> { typedef T type; };
6+
7+
template <typename T>
8+
typename remove_ref<T>::type&& move(T&& arg) {
9+
return static_cast<typename remove_ref<T>::type&&>(arg);
10+
}
11+
12+
struct C {
13+
int x;
14+
int y;
15+
};
16+
17+
struct D {
18+
int x;
19+
int y;
20+
21+
D() = default;
22+
D(const D &) = default;
23+
D(D &&) = default;
24+
D &operator=(const D &) = default;
25+
D &operator=(D &&) = default;
26+
};
27+
28+
void test_ctors() {
29+
C c0;
30+
D d0;
31+
C c1{};
32+
D d1{};
33+
C c2{0, 1};
34+
// TODO: Figure out a minimal stub for std::initializer_list,
35+
// which we can use here, without running into Clang's
36+
// "cannot compile this weird std::initializer_list yet" error
37+
// D d2{0, 1};
38+
C c3{move(c1)};
39+
D d3{move(d1)};
40+
41+
C c4 = {};
42+
D d4 = {};
43+
C c5 = C();
44+
D d5 = D();
45+
C c6 = {0, 1};
46+
// Uncomment after adding initializer_list
47+
// D d6 = {0, 1};
48+
49+
C c7 = {.x = 0};
50+
C c8 = {.x = 0, .y = 1};
51+
C c9 = C{0, 1};
52+
C c10 = move(c1);
53+
D d10 = move(d1);
54+
}

0 commit comments

Comments
 (0)