diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -16,6 +16,8 @@ #include "Selection.h" #include "SourceCode.h" #include "URI.h" +#include "clang-include-cleaner/Analysis.h" +#include "clang-include-cleaner/Types.h" #include "index/Index.h" #include "index/Merge.h" #include "index/Relation.h" @@ -1310,6 +1312,28 @@ return printQualifiedName(*ND); return {}; } + +// FIXME(bakalova): Remove after merging https://reviews.llvm.org/D143496 +std::vector +collectMacroReferences(ParsedAST &AST) { + const auto &SM = AST.getSourceManager(); + // FIXME: !!this is a hacky way to collect macro references. + std::vector Macros; + auto &PP = AST.getPreprocessor(); + for (const syntax::Token &Tok : + AST.getTokens().spelledTokens(SM.getMainFileID())) { + auto Macro = locateMacroAt(Tok, PP); + if (!Macro) + continue; + if (auto DefLoc = Macro->Info->getDefinitionLoc(); DefLoc.isValid()) + Macros.push_back( + {Tok.location(), + include_cleaner::Macro{/*Name=*/PP.getIdentifierInfo(Tok.text(SM)), + DefLoc}, + include_cleaner::RefType::Explicit}); + } + return Macros; +} } // namespace ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit, @@ -1324,6 +1348,74 @@ return {}; } + // TODO(bakalova) Look closer at the proper directive kind handling. + std::vector Includes = AST.getIncludeStructure().MainFileIncludes; + for (auto &Inc : Includes) { + if (Inc.Directive != tok::PPKeywordKind::pp_include) { + // Ignore non-C++ include directives. + continue; + } + // It is enough to compare line numbers, since one include per line is a + // standard. + if (Pos.line != Inc.HashLine) { + continue; + } + + const auto Macros = collectMacroReferences(AST); + include_cleaner::walkUsed( + AST.getLocalTopLevelDecls(), Macros, + AST.getPragmaIncludes(), SM, + [&](const include_cleaner::SymbolReference &Ref, + llvm::ArrayRef Providers) { + if (!Ref.RefLocation.isFileID() || + Ref.RT != include_cleaner::RefType::Explicit || + !SM.isWrittenInMainFile(SM.getSpellingLoc(Ref.RefLocation))) { + return; + } + const syntax::Token *Tok = + AST.getTokens().spelledTokenAt(Ref.RefLocation); + if (Tok == nullptr) { + elog("Can't find spelled token at the symbol reference location."); + return; + } + const SourceLocation &RefEndLocation = Tok->endLocation(); + clangd::Range Range = + clangd::Range{sourceLocToPosition(SM, Ref.RefLocation), + sourceLocToPosition(SM, RefEndLocation)}; + ReferencesResult::Reference Result; + Result.Loc.range = Range; + Result.Loc.uri = URIMainFile; + + // TODO(bakalova) does this differentiation make any sense in this + // context? Possibly not if (Ref.IsDefinition) { + // Result.Attributes |= ReferencesResult::Declaration; + // Result.Attributes |= ReferencesResult::Definition; + // } + for (const include_cleaner::Header &H : Providers) { + switch (H.kind()) { + case include_cleaner::Header::Physical: + if (H.physical()->tryGetRealPathName() == Inc.Resolved) { + Results.References.push_back(std::move(Result)); + } + break; + case include_cleaner::Header::Standard: + if (Inc.Written == H.standard().name()) { + Results.References.push_back(std::move(Result)); + } + break; + case include_cleaner::Header::Verbatim: + if (Inc.Written == H.verbatim()) { + Results.References.push_back(std::move(Result)); + } + break; + } + } + }); + if (!Results.References.empty()) { + return Results; + } + } + llvm::DenseSet IDsToQuery, OverriddenMethods; const auto *IdentifierAtCursor =