Skip to content

Commit 1ee0894

Browse files
feat: Add code nav for includes (#166)
1 parent f9a5cb7 commit 1ee0894

27 files changed

+278
-10
lines changed

indexer/Indexer.cc

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,15 @@ FileLocalSourceRange::fromNonEmpty(const clang::SourceManager &sourceManager,
5252
fileId};
5353
}
5454

55+
// static
56+
FileLocalSourceRange
57+
FileLocalSourceRange::makeEmpty(const clang::SourceManager &sourceManager,
58+
clang::SourceLocation loc) {
59+
auto presumedLoc = sourceManager.getPresumedLoc(loc);
60+
return {presumedLoc.getLine(), presumedLoc.getColumn(), presumedLoc.getLine(),
61+
presumedLoc.getColumn()};
62+
}
63+
5564
void FileLocalSourceRange::addToOccurrence(scip::Occurrence &occ) const {
5665
occ.add_range(this->startLine - 1);
5766
occ.add_range(this->startColumn - 1);
@@ -195,6 +204,19 @@ void MacroIndexer::saveReference(
195204
this->saveOccurrence(refFileId, macroNameToken, defMacroInfo,
196205
Role::Reference);
197206
}
207+
void MacroIndexer::saveInclude(clang::FileID containingFileId,
208+
clang::SourceRange pathRange,
209+
AbsolutePathRef includedFilePath) {
210+
auto it = this->includeRanges.find({containingFileId});
211+
if (it != this->includeRanges.end()) {
212+
it->second->emplace_back(pathRange, includedFilePath);
213+
return;
214+
}
215+
PerFileIncludeInfo vec{std::make_pair(pathRange, includedFilePath)};
216+
this->includeRanges.insert(
217+
{{containingFileId},
218+
std::make_shared<PerFileIncludeInfo>(std::move(vec))});
219+
}
198220

199221
void MacroIndexer::emitDocumentOccurrencesAndSymbols(
200222
bool deterministic, SymbolFormatter &symbolFormatter, clang::FileID fileId,
@@ -257,6 +279,19 @@ void MacroIndexer::emitExternalSymbols(bool deterministic,
257279
}));
258280
}
259281

282+
void MacroIndexer::forEachIncludeInFile(
283+
clang::FileID fileId,
284+
absl::FunctionRef<void(clang::SourceRange, AbsolutePathRef)> callback)
285+
const {
286+
auto it = this->includeRanges.find({fileId});
287+
if (it == this->includeRanges.end()) {
288+
return;
289+
}
290+
for (auto &[range, path] : *it->second) {
291+
callback(range, path);
292+
}
293+
}
294+
260295
TuIndexer::TuIndexer(const clang::SourceManager &sourceManager,
261296
const clang::LangOptions &langOptions,
262297
const clang::ASTContext &astContext,
@@ -267,6 +302,43 @@ TuIndexer::TuIndexer(const clang::SourceManager &sourceManager,
267302
getStableFileId(getStableFileId), externalSymbols(),
268303
forwardDeclarations() {}
269304

305+
void TuIndexer::saveSyntheticFileDefinition(clang::FileID fileId,
306+
StableFileId stableFileId) {
307+
if (stableFileId.isSynthetic || !stableFileId.isInProject) {
308+
return;
309+
}
310+
// Strictly speaking, the token at the start of the file could be any
311+
// token, including a reference to a built-in type or a keyword. In
312+
// such a situation, there would be no natural place to put the definition.
313+
// We could use a zero-length occurrence, which would be more "correct"
314+
// but less useful, since you couldn't trigger Find references.
315+
auto fileStartLoc = this->sourceManager.getLocForStartOfFile(fileId);
316+
auto symbol = this->symbolFormatter.getFileSymbol(stableFileId);
317+
auto tokenLength = clang::Lexer::MeasureTokenLength(
318+
fileStartLoc, this->sourceManager, this->langOptions);
319+
if (tokenLength > 0) {
320+
this->saveDefinition(symbol, fileStartLoc, scip::SymbolInformation{});
321+
return;
322+
}
323+
auto range =
324+
FileLocalSourceRange::makeEmpty(this->sourceManager, fileStartLoc);
325+
this->saveOccurrenceImpl(symbol, range, fileId, scip::SymbolRole::Definition);
326+
}
327+
328+
void TuIndexer::saveInclude(clang::SourceRange sourceRange,
329+
StableFileId stableFileId) {
330+
if (stableFileId.isSynthetic) {
331+
return;
332+
}
333+
auto symbol = this->symbolFormatter.getFileSymbol(stableFileId);
334+
// #include can't come from macro expansions, so instead of having
335+
// to write a generic saveReference method which needs to handle
336+
// ranges in macro expansions, directly call saveOccurrenceImpl.
337+
auto [range, fileId] =
338+
FileLocalSourceRange::fromNonEmpty(this->sourceManager, sourceRange);
339+
this->saveOccurrenceImpl(symbol, range, fileId, 0);
340+
}
341+
270342
void TuIndexer::saveBindingDecl(const clang::BindingDecl &bindingDecl) {
271343
auto optSymbol = this->symbolFormatter.getBindingSymbol(bindingDecl);
272344
if (!optSymbol.has_value()) {
@@ -795,6 +867,13 @@ PartialDocument &TuIndexer::saveOccurrence(std::string_view symbol,
795867
clang::SourceLocation expansionLoc,
796868
int32_t allRoles) {
797869
auto [range, fileId] = this->getTokenExpansionRange(expansionLoc);
870+
return this->saveOccurrenceImpl(symbol, range, fileId, allRoles);
871+
}
872+
873+
PartialDocument &TuIndexer::saveOccurrenceImpl(std::string_view symbol,
874+
FileLocalSourceRange range,
875+
clang::FileID fileId,
876+
int32_t allRoles) {
798877
scip::Occurrence occ;
799878
range.addToOccurrence(occ);
800879
occ.set_symbol(symbol.data(), symbol.size());

indexer/Indexer.h

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
#include <cstdint>
66
#include <string>
77
#include <string_view>
8+
#include <utility>
89
#include <vector>
910

1011
#include "absl/container/flat_hash_map.h"
1112
#include "absl/container/flat_hash_set.h"
13+
#include "absl/functional/function_ref.h"
1214

1315
#include "clang/AST/RawCommentList.h"
1416
#include "clang/Basic/SourceLocation.h"
@@ -86,6 +88,9 @@ struct FileLocalSourceRange {
8688
static std::pair<FileLocalSourceRange, clang::FileID>
8789
fromNonEmpty(const clang::SourceManager &, clang::SourceRange inclusiveRange);
8890

91+
static FileLocalSourceRange makeEmpty(const clang::SourceManager &,
92+
clang::SourceLocation);
93+
8994
void addToOccurrence(scip::Occurrence &occ) const;
9095

9196
std::string debugToString() const;
@@ -160,9 +165,25 @@ class MacroIndexer final {
160165

161166
absl::flat_hash_set<NonFileBasedMacro> nonFileBasedMacros;
162167

168+
using PerFileIncludeInfo =
169+
std::vector<std::pair<clang::SourceRange, AbsolutePathRef>>;
170+
171+
/// Map storing #include information on a per-FileID basis.
172+
///
173+
/// On seeing `#include "some/dir/header.h"` in A.cpp, the map
174+
/// will have a key for A.cpp and the AbsolutePathRef in the value
175+
/// corresponds to the absolute path for header.h.
176+
/// (Ideally, we'd use a clang::FileID instead of an AbsolutePathRef,
177+
/// but it's not clear if that's possible with the existing APIs
178+
/// during #include processing).
179+
absl::flat_hash_map<llvm_ext::AbslHashAdapter<clang::FileID>,
180+
// shared_ptr for copy-ability required by flat_hash_map
181+
std::shared_ptr<PerFileIncludeInfo>>
182+
includeRanges;
183+
163184
public:
164185
MacroIndexer(clang::SourceManager &m)
165-
: sourceManager(&m), table(), nonFileBasedMacros() {}
186+
: sourceManager(&m), table(), nonFileBasedMacros(), includeRanges() {}
166187
MacroIndexer(MacroIndexer &&) = default;
167188
MacroIndexer &operator=(MacroIndexer &&other) = default;
168189
MacroIndexer(const MacroIndexer &) = delete;
@@ -176,11 +197,19 @@ class MacroIndexer final {
176197
void saveDefinition(const clang::Token &macroNameToken,
177198
const clang::MacroInfo *);
178199

200+
void saveInclude(clang::FileID fileContainingInclude,
201+
clang::SourceRange pathRange,
202+
AbsolutePathRef includedFilePath);
203+
179204
void emitDocumentOccurrencesAndSymbols(bool deterministic, SymbolFormatter &,
180205
clang::FileID, scip::Document &);
181206
void emitExternalSymbols(bool deterministic, SymbolFormatter &,
182207
scip::Index &);
183208

209+
void forEachIncludeInFile(
210+
clang::FileID,
211+
absl::FunctionRef<void(clang::SourceRange, AbsolutePathRef)>) const;
212+
184213
private:
185214
/// Pre-condition: all arguments are valid/non-null.
186215
void saveOccurrence(clang::FileID occFileId, const clang::Token &macroToken,
@@ -224,6 +253,15 @@ class TuIndexer final {
224253
TuIndexer(const clang::SourceManager &, const clang::LangOptions &,
225254
const clang::ASTContext &, SymbolFormatter &, GetStableFileId);
226255

256+
/// Emit a fake 'definition' for a file, which can be used as a target
257+
/// of Go to definition from #include, as well as the source for
258+
// Find references to see where a header has been included.
259+
void saveSyntheticFileDefinition(clang::FileID, StableFileId);
260+
261+
/// Emit a reference to the fake 'definition' for a file, allowing Go to
262+
/// Definition from '#include'.
263+
void saveInclude(clang::SourceRange, StableFileId);
264+
227265
// See NOTE(ref: emit-vs-save) for naming conventions.
228266
#define SAVE_DECL(DeclName) \
229267
void save##DeclName##Decl(const clang::DeclName##Decl &);
@@ -289,6 +327,11 @@ class TuIndexer final {
289327
clang::SourceLocation loc,
290328
int32_t allRoles = 0);
291329

330+
PartialDocument &saveOccurrenceImpl(std::string_view symbol,
331+
FileLocalSourceRange range,
332+
clang::FileID fileId,
333+
int32_t allRoles = 0);
334+
292335
DocComment tryGetDocComment(const clang::Decl &) const;
293336
};
294337

indexer/SymbolFormatter.cc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,27 @@ std::string_view SymbolFormatter::getMacroSymbol(clang::SourceLocation defLoc) {
160160
return std::string_view(newIt->second);
161161
}
162162

163+
std::string_view SymbolFormatter::getFileSymbol(StableFileId stableFileId) {
164+
auto it = this->fileSymbolCache.find(stableFileId);
165+
if (it != this->fileSymbolCache.end()) {
166+
return std::string_view(it->second);
167+
}
168+
auto name =
169+
this->formatTemporary("<file>/{}", stableFileId.path.asStringView());
170+
std::string out{};
171+
SymbolBuilder{.packageName = "todo-pkg",
172+
.packageVersion = "todo-version",
173+
.descriptors = {DescriptorBuilder{
174+
.name = name, .suffix = scip::Descriptor::Namespace}}}
175+
.formatTo(out);
176+
auto [newIt, inserted] =
177+
this->fileSymbolCache.emplace(stableFileId, std::move(out));
178+
ENFORCE(
179+
inserted,
180+
"StableFileId key was missing earlier, so insert should've succeeded");
181+
return std::string_view(newIt->second);
182+
}
183+
163184
// NOTE(def: canonical-decl):
164185
// It is a little subtle as to why using getCanonicalDecl will
165186
// give correct results. In particular, the result of getCanonicalDecl

indexer/SymbolFormatter.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212
#include "clang/Basic/SourceManager.h"
1313
#include "llvm/ADT/SmallVector.h"
1414

15+
#include "scip/scip.pb.h"
16+
17+
#include "indexer/Derive.h"
1518
#include "indexer/LlvmAdapter.h"
1619
#include "indexer/Path.h"
17-
#include "scip/scip.pb.h"
1820

1921
#define FOR_EACH_DECL_TO_BE_INDEXED(F) \
2022
F(Binding) \
@@ -112,6 +114,14 @@ struct StableFileId {
112114
bool isInProject;
113115
/// Track this for debugging.
114116
bool isSynthetic;
117+
118+
template <typename H>
119+
friend H AbslHashValue(H h, const StableFileId &stableFileId) {
120+
return H::combine(std::move(h), stableFileId.path, stableFileId.isInProject,
121+
stableFileId.isSynthetic);
122+
}
123+
124+
DERIVE_EQ_ALL(StableFileId)
115125
};
116126

117127
using GetStableFileId =
@@ -128,6 +138,7 @@ class SymbolFormatter final {
128138
std::string>
129139
locationBasedCache;
130140
absl::flat_hash_map<const clang::Decl *, std::string> declBasedCache;
141+
absl::flat_hash_map<StableFileId, std::string> fileSymbolCache;
131142
absl::flat_hash_map<llvm_ext::AbslHashAdapter<clang::FileID>, uint32_t>
132143
anonymousTypeCounters;
133144
absl::flat_hash_map<llvm_ext::AbslHashAdapter<clang::FileID>, uint32_t>
@@ -138,12 +149,15 @@ class SymbolFormatter final {
138149
SymbolFormatter(const clang::SourceManager &sourceManager,
139150
GetStableFileId getStableFileId)
140151
: sourceManager(sourceManager), getStableFileId(getStableFileId),
141-
locationBasedCache(), declBasedCache(), scratchBuffer() {}
152+
locationBasedCache(), declBasedCache(), fileSymbolCache(),
153+
localVariableCounters(), scratchBuffer() {}
142154
SymbolFormatter(const SymbolFormatter &) = delete;
143155
SymbolFormatter &operator=(const SymbolFormatter &) = delete;
144156

145157
std::string_view getMacroSymbol(clang::SourceLocation defLoc);
146158

159+
std::string_view getFileSymbol(StableFileId);
160+
147161
#define DECLARE_GET_SYMBOL(DeclName) \
148162
std::optional<std::string_view> get##DeclName##Symbol( \
149163
const clang::DeclName##Decl &);

0 commit comments

Comments
 (0)