| Marc-Andre Laperle | b387b6e | 2018-04-23 20:00:52 +0000 | [diff] [blame] | 1 | //===--- FindSymbols.cpp ------------------------------------*- C++-*------===// | 
|  | 2 | // | 
|  | 3 | //                     The LLVM Compiler Infrastructure | 
|  | 4 | // | 
|  | 5 | // This file is distributed under the University of Illinois Open Source | 
|  | 6 | // License. See LICENSE.TXT for details. | 
|  | 7 | // | 
|  | 8 | //===----------------------------------------------------------------------===// | 
|  | 9 | #include "FindSymbols.h" | 
|  | 10 |  | 
| Marc-Andre Laperle | 1be6970 | 2018-07-05 19:35:01 +0000 | [diff] [blame] | 11 | #include "AST.h" | 
|  | 12 | #include "ClangdUnit.h" | 
| Sam McCall | 203cdee | 2018-06-07 06:55:59 +0000 | [diff] [blame] | 13 | #include "FuzzyMatch.h" | 
| Marc-Andre Laperle | 1be6970 | 2018-07-05 19:35:01 +0000 | [diff] [blame] | 14 | #include "Logger.h" | 
| Sam McCall | 203cdee | 2018-06-07 06:55:59 +0000 | [diff] [blame] | 15 | #include "Quality.h" | 
| Marc-Andre Laperle | 1be6970 | 2018-07-05 19:35:01 +0000 | [diff] [blame] | 16 | #include "SourceCode.h" | 
| Marc-Andre Laperle | b387b6e | 2018-04-23 20:00:52 +0000 | [diff] [blame] | 17 | #include "index/Index.h" | 
| Marc-Andre Laperle | 1be6970 | 2018-07-05 19:35:01 +0000 | [diff] [blame] | 18 | #include "clang/Index/IndexDataConsumer.h" | 
| Marc-Andre Laperle | b387b6e | 2018-04-23 20:00:52 +0000 | [diff] [blame] | 19 | #include "clang/Index/IndexSymbol.h" | 
| Marc-Andre Laperle | 1be6970 | 2018-07-05 19:35:01 +0000 | [diff] [blame] | 20 | #include "clang/Index/IndexingAction.h" | 
| Marc-Andre Laperle | b387b6e | 2018-04-23 20:00:52 +0000 | [diff] [blame] | 21 | #include "llvm/Support/FormatVariadic.h" | 
|  | 22 | #include "llvm/Support/Path.h" | 
|  | 23 |  | 
| Sam McCall | 203cdee | 2018-06-07 06:55:59 +0000 | [diff] [blame] | 24 | #define DEBUG_TYPE "FindSymbols" | 
|  | 25 |  | 
| Sam McCall | c008af6 | 2018-10-20 15:30:37 +0000 | [diff] [blame] | 26 | using namespace llvm; | 
| Marc-Andre Laperle | b387b6e | 2018-04-23 20:00:52 +0000 | [diff] [blame] | 27 | namespace clang { | 
|  | 28 | namespace clangd { | 
| Marc-Andre Laperle | b387b6e | 2018-04-23 20:00:52 +0000 | [diff] [blame] | 29 | namespace { | 
|  | 30 |  | 
|  | 31 | // Convert a index::SymbolKind to clangd::SymbolKind (LSP) | 
|  | 32 | // Note, some are not perfect matches and should be improved when this LSP | 
|  | 33 | // issue is addressed: | 
|  | 34 | // https://github.com/Microsoft/language-server-protocol/issues/344 | 
|  | 35 | SymbolKind indexSymbolKindToSymbolKind(index::SymbolKind Kind) { | 
|  | 36 | switch (Kind) { | 
|  | 37 | case index::SymbolKind::Unknown: | 
|  | 38 | return SymbolKind::Variable; | 
|  | 39 | case index::SymbolKind::Module: | 
|  | 40 | return SymbolKind::Module; | 
|  | 41 | case index::SymbolKind::Namespace: | 
|  | 42 | return SymbolKind::Namespace; | 
|  | 43 | case index::SymbolKind::NamespaceAlias: | 
|  | 44 | return SymbolKind::Namespace; | 
|  | 45 | case index::SymbolKind::Macro: | 
|  | 46 | return SymbolKind::String; | 
|  | 47 | case index::SymbolKind::Enum: | 
|  | 48 | return SymbolKind::Enum; | 
|  | 49 | case index::SymbolKind::Struct: | 
|  | 50 | return SymbolKind::Struct; | 
|  | 51 | case index::SymbolKind::Class: | 
|  | 52 | return SymbolKind::Class; | 
|  | 53 | case index::SymbolKind::Protocol: | 
|  | 54 | return SymbolKind::Interface; | 
|  | 55 | case index::SymbolKind::Extension: | 
|  | 56 | return SymbolKind::Interface; | 
|  | 57 | case index::SymbolKind::Union: | 
|  | 58 | return SymbolKind::Class; | 
|  | 59 | case index::SymbolKind::TypeAlias: | 
|  | 60 | return SymbolKind::Class; | 
|  | 61 | case index::SymbolKind::Function: | 
|  | 62 | return SymbolKind::Function; | 
|  | 63 | case index::SymbolKind::Variable: | 
|  | 64 | return SymbolKind::Variable; | 
|  | 65 | case index::SymbolKind::Field: | 
|  | 66 | return SymbolKind::Field; | 
|  | 67 | case index::SymbolKind::EnumConstant: | 
|  | 68 | return SymbolKind::EnumMember; | 
|  | 69 | case index::SymbolKind::InstanceMethod: | 
|  | 70 | case index::SymbolKind::ClassMethod: | 
|  | 71 | case index::SymbolKind::StaticMethod: | 
|  | 72 | return SymbolKind::Method; | 
|  | 73 | case index::SymbolKind::InstanceProperty: | 
|  | 74 | case index::SymbolKind::ClassProperty: | 
|  | 75 | case index::SymbolKind::StaticProperty: | 
|  | 76 | return SymbolKind::Property; | 
|  | 77 | case index::SymbolKind::Constructor: | 
|  | 78 | case index::SymbolKind::Destructor: | 
|  | 79 | return SymbolKind::Method; | 
|  | 80 | case index::SymbolKind::ConversionFunction: | 
|  | 81 | return SymbolKind::Function; | 
|  | 82 | case index::SymbolKind::Parameter: | 
|  | 83 | return SymbolKind::Variable; | 
|  | 84 | case index::SymbolKind::Using: | 
|  | 85 | return SymbolKind::Namespace; | 
|  | 86 | } | 
|  | 87 | llvm_unreachable("invalid symbol kind"); | 
|  | 88 | } | 
|  | 89 |  | 
| Sam McCall | 203cdee | 2018-06-07 06:55:59 +0000 | [diff] [blame] | 90 | using ScoredSymbolInfo = std::pair<float, SymbolInformation>; | 
|  | 91 | struct ScoredSymbolGreater { | 
|  | 92 | bool operator()(const ScoredSymbolInfo &L, const ScoredSymbolInfo &R) { | 
|  | 93 | if (L.first != R.first) | 
|  | 94 | return L.first > R.first; | 
|  | 95 | return L.second.name < R.second.name; // Earlier name is better. | 
|  | 96 | } | 
|  | 97 | }; | 
|  | 98 |  | 
| Marc-Andre Laperle | b387b6e | 2018-04-23 20:00:52 +0000 | [diff] [blame] | 99 | } // namespace | 
|  | 100 |  | 
| Sam McCall | c008af6 | 2018-10-20 15:30:37 +0000 | [diff] [blame] | 101 | Expected<std::vector<SymbolInformation>> | 
| Eric Liu | 53425f29 | 2018-06-19 09:33:53 +0000 | [diff] [blame] | 102 | getWorkspaceSymbols(StringRef Query, int Limit, const SymbolIndex *const Index, | 
|  | 103 | StringRef HintPath) { | 
| Marc-Andre Laperle | b387b6e | 2018-04-23 20:00:52 +0000 | [diff] [blame] | 104 | std::vector<SymbolInformation> Result; | 
|  | 105 | if (Query.empty() || !Index) | 
|  | 106 | return Result; | 
|  | 107 |  | 
|  | 108 | auto Names = splitQualifiedName(Query); | 
|  | 109 |  | 
|  | 110 | FuzzyFindRequest Req; | 
|  | 111 | Req.Query = Names.second; | 
|  | 112 |  | 
|  | 113 | // FuzzyFind doesn't want leading :: qualifier | 
|  | 114 | bool IsGlobalQuery = Names.first.consume_front("::"); | 
|  | 115 | // Restrict results to the scope in the query string if present (global or | 
|  | 116 | // not). | 
|  | 117 | if (IsGlobalQuery || !Names.first.empty()) | 
|  | 118 | Req.Scopes = {Names.first}; | 
| Eric Liu | b04869a | 2018-11-06 11:08:17 +0000 | [diff] [blame] | 119 | else | 
|  | 120 | Req.AnyScope = true; | 
| Marc-Andre Laperle | b387b6e | 2018-04-23 20:00:52 +0000 | [diff] [blame] | 121 | if (Limit) | 
| Kirill Bobyrev | e6dd080 | 2018-09-13 14:27:03 +0000 | [diff] [blame] | 122 | Req.Limit = Limit; | 
|  | 123 | TopN<ScoredSymbolInfo, ScoredSymbolGreater> Top( | 
|  | 124 | Req.Limit ? *Req.Limit : std::numeric_limits<size_t>::max()); | 
| Sam McCall | 203cdee | 2018-06-07 06:55:59 +0000 | [diff] [blame] | 125 | FuzzyMatcher Filter(Req.Query); | 
| Eric Liu | 53425f29 | 2018-06-19 09:33:53 +0000 | [diff] [blame] | 126 | Index->fuzzyFind(Req, [HintPath, &Top, &Filter](const Symbol &Sym) { | 
| Marc-Andre Laperle | b387b6e | 2018-04-23 20:00:52 +0000 | [diff] [blame] | 127 | // Prefer the definition over e.g. a function declaration in a header | 
|  | 128 | auto &CD = Sym.Definition ? Sym.Definition : Sym.CanonicalDeclaration; | 
|  | 129 | auto Uri = URI::parse(CD.FileURI); | 
|  | 130 | if (!Uri) { | 
| Sam McCall | bed5885 | 2018-07-11 10:35:11 +0000 | [diff] [blame] | 131 | log("Workspace symbol: Could not parse URI '{0}' for symbol '{1}'.", | 
|  | 132 | CD.FileURI, Sym.Name); | 
| Marc-Andre Laperle | b387b6e | 2018-04-23 20:00:52 +0000 | [diff] [blame] | 133 | return; | 
|  | 134 | } | 
| Eric Liu | 53425f29 | 2018-06-19 09:33:53 +0000 | [diff] [blame] | 135 | auto Path = URI::resolve(*Uri, HintPath); | 
| Marc-Andre Laperle | b387b6e | 2018-04-23 20:00:52 +0000 | [diff] [blame] | 136 | if (!Path) { | 
| Sam McCall | bed5885 | 2018-07-11 10:35:11 +0000 | [diff] [blame] | 137 | log("Workspace symbol: Could not resolve path for URI '{0}' for symbol " | 
|  | 138 | "'{1}'.", | 
|  | 139 | Uri->toString(), Sym.Name); | 
| Marc-Andre Laperle | b387b6e | 2018-04-23 20:00:52 +0000 | [diff] [blame] | 140 | return; | 
|  | 141 | } | 
|  | 142 | Location L; | 
|  | 143 | L.uri = URIForFile((*Path)); | 
|  | 144 | Position Start, End; | 
| Haojian Wu | b515fab | 2018-10-18 10:43:50 +0000 | [diff] [blame] | 145 | Start.line = CD.Start.line(); | 
|  | 146 | Start.character = CD.Start.column(); | 
|  | 147 | End.line = CD.End.line(); | 
|  | 148 | End.character = CD.End.column(); | 
| Marc-Andre Laperle | b387b6e | 2018-04-23 20:00:52 +0000 | [diff] [blame] | 149 | L.range = {Start, End}; | 
|  | 150 | SymbolKind SK = indexSymbolKindToSymbolKind(Sym.SymInfo.Kind); | 
|  | 151 | std::string Scope = Sym.Scope; | 
|  | 152 | StringRef ScopeRef = Scope; | 
|  | 153 | ScopeRef.consume_back("::"); | 
| Sam McCall | 203cdee | 2018-06-07 06:55:59 +0000 | [diff] [blame] | 154 | SymbolInformation Info = {Sym.Name, SK, L, ScopeRef}; | 
|  | 155 |  | 
|  | 156 | SymbolQualitySignals Quality; | 
|  | 157 | Quality.merge(Sym); | 
|  | 158 | SymbolRelevanceSignals Relevance; | 
|  | 159 | Relevance.Query = SymbolRelevanceSignals::Generic; | 
|  | 160 | if (auto NameMatch = Filter.match(Sym.Name)) | 
|  | 161 | Relevance.NameMatch = *NameMatch; | 
|  | 162 | else { | 
| Sam McCall | bed5885 | 2018-07-11 10:35:11 +0000 | [diff] [blame] | 163 | log("Workspace symbol: {0} didn't match query {1}", Sym.Name, | 
|  | 164 | Filter.pattern()); | 
| Sam McCall | 203cdee | 2018-06-07 06:55:59 +0000 | [diff] [blame] | 165 | return; | 
|  | 166 | } | 
|  | 167 | Relevance.merge(Sym); | 
|  | 168 | auto Score = | 
|  | 169 | evaluateSymbolAndRelevance(Quality.evaluate(), Relevance.evaluate()); | 
| Sam McCall | bed5885 | 2018-07-11 10:35:11 +0000 | [diff] [blame] | 170 | dlog("FindSymbols: {0}{1} = {2}\n{3}{4}\n", Sym.Scope, Sym.Name, Score, | 
|  | 171 | Quality, Relevance); | 
| Sam McCall | 203cdee | 2018-06-07 06:55:59 +0000 | [diff] [blame] | 172 |  | 
|  | 173 | Top.push({Score, std::move(Info)}); | 
| Marc-Andre Laperle | b387b6e | 2018-04-23 20:00:52 +0000 | [diff] [blame] | 174 | }); | 
| Sam McCall | 203cdee | 2018-06-07 06:55:59 +0000 | [diff] [blame] | 175 | for (auto &R : std::move(Top).items()) | 
|  | 176 | Result.push_back(std::move(R.second)); | 
| Marc-Andre Laperle | b387b6e | 2018-04-23 20:00:52 +0000 | [diff] [blame] | 177 | return Result; | 
|  | 178 | } | 
|  | 179 |  | 
| Marc-Andre Laperle | 1be6970 | 2018-07-05 19:35:01 +0000 | [diff] [blame] | 180 | namespace { | 
|  | 181 | /// Finds document symbols in the main file of the AST. | 
|  | 182 | class DocumentSymbolsConsumer : public index::IndexDataConsumer { | 
|  | 183 | ASTContext &AST; | 
|  | 184 | std::vector<SymbolInformation> Symbols; | 
|  | 185 | // We are always list document for the same file, so cache the value. | 
| Sam McCall | c008af6 | 2018-10-20 15:30:37 +0000 | [diff] [blame] | 186 | Optional<URIForFile> MainFileUri; | 
| Marc-Andre Laperle | 1be6970 | 2018-07-05 19:35:01 +0000 | [diff] [blame] | 187 |  | 
|  | 188 | public: | 
|  | 189 | DocumentSymbolsConsumer(ASTContext &AST) : AST(AST) {} | 
|  | 190 | std::vector<SymbolInformation> takeSymbols() { return std::move(Symbols); } | 
|  | 191 |  | 
|  | 192 | void initialize(ASTContext &Ctx) override { | 
|  | 193 | // Compute the absolute path of the main file which we will use for all | 
|  | 194 | // results. | 
|  | 195 | const SourceManager &SM = AST.getSourceManager(); | 
|  | 196 | const FileEntry *F = SM.getFileEntryForID(SM.getMainFileID()); | 
|  | 197 | if (!F) | 
|  | 198 | return; | 
| Simon Marchi | 25f1f73 | 2018-08-10 22:27:53 +0000 | [diff] [blame] | 199 | auto FilePath = getRealPath(F, SM); | 
| Marc-Andre Laperle | 1be6970 | 2018-07-05 19:35:01 +0000 | [diff] [blame] | 200 | if (FilePath) | 
|  | 201 | MainFileUri = URIForFile(*FilePath); | 
|  | 202 | } | 
|  | 203 |  | 
|  | 204 | bool shouldIncludeSymbol(const NamedDecl *ND) { | 
|  | 205 | if (!ND || ND->isImplicit()) | 
|  | 206 | return false; | 
|  | 207 | // Skip anonymous declarations, e.g (anonymous enum/class/struct). | 
|  | 208 | if (ND->getDeclName().isEmpty()) | 
|  | 209 | return false; | 
|  | 210 | return true; | 
|  | 211 | } | 
|  | 212 |  | 
|  | 213 | bool | 
|  | 214 | handleDeclOccurence(const Decl *, index::SymbolRoleSet Roles, | 
|  | 215 | ArrayRef<index::SymbolRelation> Relations, | 
|  | 216 | SourceLocation Loc, | 
|  | 217 | index::IndexDataConsumer::ASTNodeInfo ASTNode) override { | 
|  | 218 | assert(ASTNode.OrigD); | 
|  | 219 | // No point in continuing the index consumer if we could not get the | 
|  | 220 | // absolute path of the main file. | 
|  | 221 | if (!MainFileUri) | 
|  | 222 | return false; | 
|  | 223 | // We only want declarations and definitions, i.e. no references. | 
|  | 224 | if (!(Roles & static_cast<unsigned>(index::SymbolRole::Declaration) || | 
|  | 225 | Roles & static_cast<unsigned>(index::SymbolRole::Definition))) | 
|  | 226 | return true; | 
|  | 227 | SourceLocation NameLoc = findNameLoc(ASTNode.OrigD); | 
|  | 228 | const SourceManager &SourceMgr = AST.getSourceManager(); | 
|  | 229 | // We should be only be looking at "local" decls in the main file. | 
|  | 230 | if (!SourceMgr.isWrittenInMainFile(NameLoc)) { | 
|  | 231 | // Even thought we are visiting only local (non-preamble) decls, | 
| Fangrui Song | 445bdd1 | 2018-09-05 08:01:37 +0000 | [diff] [blame] | 232 | // we can get here when in the presence of "extern" decls. | 
| Marc-Andre Laperle | 1be6970 | 2018-07-05 19:35:01 +0000 | [diff] [blame] | 233 | return true; | 
|  | 234 | } | 
| Sam McCall | c008af6 | 2018-10-20 15:30:37 +0000 | [diff] [blame] | 235 | const NamedDecl *ND = dyn_cast<NamedDecl>(ASTNode.OrigD); | 
| Marc-Andre Laperle | 1be6970 | 2018-07-05 19:35:01 +0000 | [diff] [blame] | 236 | if (!shouldIncludeSymbol(ND)) | 
|  | 237 | return true; | 
|  | 238 |  | 
|  | 239 | SourceLocation EndLoc = | 
|  | 240 | Lexer::getLocForEndOfToken(NameLoc, 0, SourceMgr, AST.getLangOpts()); | 
|  | 241 | Position Begin = sourceLocToPosition(SourceMgr, NameLoc); | 
|  | 242 | Position End = sourceLocToPosition(SourceMgr, EndLoc); | 
|  | 243 | Range R = {Begin, End}; | 
|  | 244 | Location L; | 
|  | 245 | L.uri = *MainFileUri; | 
|  | 246 | L.range = R; | 
|  | 247 |  | 
|  | 248 | std::string QName = printQualifiedName(*ND); | 
|  | 249 | StringRef Scope, Name; | 
|  | 250 | std::tie(Scope, Name) = splitQualifiedName(QName); | 
|  | 251 | Scope.consume_back("::"); | 
|  | 252 |  | 
|  | 253 | index::SymbolInfo SymInfo = index::getSymbolInfo(ND); | 
|  | 254 | SymbolKind SK = indexSymbolKindToSymbolKind(SymInfo.Kind); | 
|  | 255 |  | 
|  | 256 | SymbolInformation SI; | 
|  | 257 | SI.name = Name; | 
|  | 258 | SI.kind = SK; | 
|  | 259 | SI.location = L; | 
|  | 260 | SI.containerName = Scope; | 
|  | 261 | Symbols.push_back(std::move(SI)); | 
|  | 262 | return true; | 
|  | 263 | } | 
|  | 264 | }; | 
|  | 265 | } // namespace | 
|  | 266 |  | 
| Sam McCall | c008af6 | 2018-10-20 15:30:37 +0000 | [diff] [blame] | 267 | Expected<std::vector<SymbolInformation>> getDocumentSymbols(ParsedAST &AST) { | 
| Marc-Andre Laperle | 1be6970 | 2018-07-05 19:35:01 +0000 | [diff] [blame] | 268 | DocumentSymbolsConsumer DocumentSymbolsCons(AST.getASTContext()); | 
|  | 269 |  | 
|  | 270 | index::IndexingOptions IndexOpts; | 
|  | 271 | IndexOpts.SystemSymbolFilter = | 
|  | 272 | index::IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly; | 
|  | 273 | IndexOpts.IndexFunctionLocals = false; | 
| Eric Liu | f736766 | 2018-09-18 08:52:14 +0000 | [diff] [blame] | 274 | indexTopLevelDecls(AST.getASTContext(), AST.getPreprocessor(), | 
|  | 275 | AST.getLocalTopLevelDecls(), DocumentSymbolsCons, | 
|  | 276 | IndexOpts); | 
| Marc-Andre Laperle | 1be6970 | 2018-07-05 19:35:01 +0000 | [diff] [blame] | 277 |  | 
|  | 278 | return DocumentSymbolsCons.takeSymbols(); | 
|  | 279 | } | 
|  | 280 |  | 
| Marc-Andre Laperle | b387b6e | 2018-04-23 20:00:52 +0000 | [diff] [blame] | 281 | } // namespace clangd | 
|  | 282 | } // namespace clang |