@@ -289,21 +289,103 @@ llvm::Optional<DocumentSymbol> declToSym(ASTContext &Ctx, const NamedDecl &ND) {
289
289
// / - visiting decls is actually simple, so we don't hit the complicated
290
290
// / cases that RAV mostly helps with (types, expressions, etc.)
291
291
class DocumentOutline {
292
+ // A DocumentSymbol we're constructing.
293
+ // We use this instead of DocumentSymbol directly so that we can keep track
294
+ // of the nodes we insert for macros.
295
+ class SymBuilder {
296
+ std::vector<SymBuilder> Children;
297
+ DocumentSymbol Symbol; // Symbol.children is empty, use Children instead.
298
+ // Macro expansions that this node or its parents are associated with.
299
+ // (Thus we will never create further children for these expansions).
300
+ llvm::SmallVector<SourceLocation> EnclosingMacroLoc;
301
+
302
+ public:
303
+ DocumentSymbol build () && {
304
+ for (SymBuilder &C : Children) {
305
+ Symbol.children .push_back (std::move (C).build ());
306
+ // Expand range to ensure children nest properly, which editors expect.
307
+ // This can fix some edge-cases in the AST, but is vital for macros.
308
+ // A macro expansion "contains" AST node if it covers the node's primary
309
+ // location, but it may not span the node's whole range.
310
+ Symbol.range .start =
311
+ std::min (Symbol.range .start , Symbol.children .back ().range .start );
312
+ Symbol.range .end =
313
+ std::max (Symbol.range .end , Symbol.children .back ().range .end );
314
+ }
315
+ return std::move (Symbol);
316
+ }
317
+
318
+ // Add a symbol as a child of the current one.
319
+ SymBuilder &addChild (DocumentSymbol S) {
320
+ Children.emplace_back ();
321
+ Children.back ().EnclosingMacroLoc = EnclosingMacroLoc;
322
+ Children.back ().Symbol = std::move (S);
323
+ return Children.back ();
324
+ }
325
+
326
+ // Get an appropriate container for children of this symbol that were
327
+ // expanded from a macro (whose spelled name is Tok).
328
+ //
329
+ // This may return:
330
+ // - a macro symbol child of this (either new or previously created)
331
+ // - this scope itself, if it *is* the macro symbol or is nested within it
332
+ SymBuilder &inMacro (const syntax::Token &Tok, const SourceManager &SM,
333
+ llvm::Optional<syntax::TokenBuffer::Expansion> Exp) {
334
+ if (llvm::is_contained (EnclosingMacroLoc, Tok.location ()))
335
+ return *this ;
336
+ // If there's an existing child for this macro, we expect it to be last.
337
+ if (!Children.empty () && !Children.back ().EnclosingMacroLoc .empty () &&
338
+ Children.back ().EnclosingMacroLoc .back () == Tok.location ())
339
+ return Children.back ();
340
+
341
+ DocumentSymbol Sym;
342
+ Sym.name = Tok.text (SM).str ();
343
+ Sym.kind = SymbolKind::Null; // There's no suitable kind!
344
+ Sym.range = Sym.selectionRange =
345
+ halfOpenToRange (SM, Tok.range (SM).toCharRange (SM));
346
+
347
+ // FIXME: Exp is currently unavailable for nested expansions.
348
+ if (Exp) {
349
+ // Full range covers the macro args.
350
+ Sym.range = halfOpenToRange (SM, CharSourceRange::getCharRange (
351
+ Exp->Spelled .front ().location (),
352
+ Exp->Spelled .back ().endLocation ()));
353
+ // Show macro args as detail.
354
+ llvm::raw_string_ostream OS (Sym.detail );
355
+ const syntax::Token *Prev = nullptr ;
356
+ for (const auto &Tok : Exp->Spelled .drop_front ()) {
357
+ // Don't dump arbitrarily long macro args.
358
+ if (OS.tell () > 80 ) {
359
+ OS << " ...)" ;
360
+ break ;
361
+ }
362
+ if (Prev && Prev->endLocation () != Tok.location ())
363
+ OS << ' ' ;
364
+ OS << Tok.text (SM);
365
+ Prev = &Tok;
366
+ }
367
+ }
368
+ SymBuilder &Child = addChild (std::move (Sym));
369
+ Child.EnclosingMacroLoc .push_back (Tok.location ());
370
+ return Child;
371
+ }
372
+ };
373
+
292
374
public:
293
375
DocumentOutline (ParsedAST &AST) : AST(AST) {}
294
376
295
377
// / Builds the document outline for the generated AST.
296
378
std::vector<DocumentSymbol> build () {
297
- std::vector<DocumentSymbol> Results ;
379
+ SymBuilder DummyRoot ;
298
380
for (auto &TopLevel : AST.getLocalTopLevelDecls ())
299
- traverseDecl (TopLevel, Results );
300
- return Results ;
381
+ traverseDecl (TopLevel, DummyRoot );
382
+ return std::move ( std::move (DummyRoot). build (). children ) ;
301
383
}
302
384
303
385
private:
304
386
enum class VisitKind { No, OnlyDecl, OnlyChildren, DeclAndChildren };
305
387
306
- void traverseDecl (Decl *D, std::vector<DocumentSymbol> &Results ) {
388
+ void traverseDecl (Decl *D, SymBuilder &Parent ) {
307
389
// Skip symbols which do not originate from the main file.
308
390
if (!isInsideMainFile (D->getLocation (), AST.getSourceManager ()))
309
391
return ;
@@ -319,27 +401,76 @@ class DocumentOutline {
319
401
return ;
320
402
321
403
if (Visit == VisitKind::OnlyChildren)
322
- return traverseChildren (D, Results );
404
+ return traverseChildren (D, Parent );
323
405
324
406
auto *ND = llvm::cast<NamedDecl>(D);
325
407
auto Sym = declToSym (AST.getASTContext (), *ND);
326
408
if (!Sym)
327
409
return ;
328
- Results.push_back (std::move (*Sym));
410
+ SymBuilder &MacroParent = possibleMacroContainer (D->getLocation (), Parent);
411
+ SymBuilder &Child = MacroParent.addChild (std::move (*Sym));
329
412
330
413
if (Visit == VisitKind::OnlyDecl)
331
414
return ;
332
415
333
416
assert (Visit == VisitKind::DeclAndChildren && " Unexpected VisitKind" );
334
- traverseChildren (ND, Results.back ().children );
417
+ traverseChildren (ND, Child);
418
+ }
419
+
420
+ // Determines where a decl should appear in the DocumentSymbol hierarchy.
421
+ //
422
+ // This is usually a direct child of the relevant AST parent.
423
+ // But we may also insert nodes for macros. Given:
424
+ // #define DECLARE_INT(V) int v;
425
+ // namespace a { DECLARE_INT(x) }
426
+ // We produce:
427
+ // Namespace a
428
+ // Macro DECLARE_INT(x)
429
+ // Variable x
430
+ //
431
+ // In the absence of macros, this method simply returns Parent.
432
+ // Otherwise it may return a macro expansion node instead.
433
+ // Each macro only has at most one node in the hierarchy, even if it expands
434
+ // to multiple decls.
435
+ SymBuilder &possibleMacroContainer (SourceLocation TargetLoc,
436
+ SymBuilder &Parent) {
437
+ const auto &SM = AST.getSourceManager ();
438
+ // Look at the path of macro-callers from the token to the main file.
439
+ // Note that along these paths we see the "outer" macro calls first.
440
+ SymBuilder *CurParent = &Parent;
441
+ for (SourceLocation Loc = TargetLoc; Loc.isMacroID ();
442
+ Loc = SM.getImmediateMacroCallerLoc (Loc)) {
443
+ // Find the virtual macro body that our token is being substituted into.
444
+ FileID MacroBody;
445
+ if (SM.isMacroArgExpansion (Loc)) {
446
+ // Loc is part of a macro arg being substituted into a macro body.
447
+ MacroBody = SM.getFileID (SM.getImmediateExpansionRange (Loc).getBegin ());
448
+ } else {
449
+ // Loc is already in the macro body.
450
+ MacroBody = SM.getFileID (Loc);
451
+ }
452
+ // The macro body is being substituted for a macro expansion, whose
453
+ // first token is the name of the macro.
454
+ SourceLocation MacroName =
455
+ SM.getSLocEntry (MacroBody).getExpansion ().getExpansionLocStart ();
456
+ // Only include the macro expansion in the outline if it was written
457
+ // directly in the main file, rather than expanded from another macro.
458
+ if (!MacroName.isValid () || !MacroName.isFileID ())
459
+ continue ;
460
+ // All conditions satisfied, add the macro.
461
+ if (auto *Tok = AST.getTokens ().spelledTokenAt (MacroName))
462
+ CurParent = &CurParent->inMacro (
463
+ *Tok, SM, AST.getTokens ().expansionStartingAt (Tok));
464
+ }
465
+ return *CurParent;
335
466
}
336
467
337
- void traverseChildren (Decl *D, std::vector<DocumentSymbol> &Results ) {
468
+ void traverseChildren (Decl *D, SymBuilder &Builder ) {
338
469
auto *Scope = llvm::dyn_cast<DeclContext>(D);
339
470
if (!Scope)
340
471
return ;
341
472
for (auto *C : Scope->decls ())
342
- traverseDecl (C, Results );
473
+ traverseDecl (C, Builder );
343
474
}
344
475
345
476
VisitKind shouldVisit (Decl *D) {
0 commit comments