1 //===--- XRefs.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 "XRefs.h" 10 #include "Logger.h" 11 #include "SourceCode.h" 12 #include "URI.h" 13 #include "clang/AST/DeclTemplate.h" 14 #include "clang/Index/IndexDataConsumer.h" 15 #include "clang/Index/IndexingAction.h" 16 #include "llvm/Support/Path.h" 17 namespace clang { 18 namespace clangd { 19 using namespace llvm; 20 namespace { 21 22 // Get the definition from a given declaration `D`. 23 // Return nullptr if no definition is found, or the declaration type of `D` is 24 // not supported. 25 const Decl *GetDefinition(const Decl *D) { 26 assert(D); 27 if (const auto *TD = dyn_cast<TagDecl>(D)) 28 return TD->getDefinition(); 29 else if (const auto *VD = dyn_cast<VarDecl>(D)) 30 return VD->getDefinition(); 31 else if (const auto *FD = dyn_cast<FunctionDecl>(D)) 32 return FD->getDefinition(); 33 return nullptr; 34 } 35 36 struct MacroDecl { 37 StringRef Name; 38 const MacroInfo *Info; 39 }; 40 41 /// Finds declarations locations that a given source location refers to. 42 class DeclarationAndMacrosFinder : public index::IndexDataConsumer { 43 std::vector<const Decl *> Decls; 44 std::vector<MacroDecl> MacroInfos; 45 const SourceLocation &SearchedLocation; 46 const ASTContext &AST; 47 Preprocessor &PP; 48 49 public: 50 DeclarationAndMacrosFinder(raw_ostream &OS, 51 const SourceLocation &SearchedLocation, 52 ASTContext &AST, Preprocessor &PP) 53 : SearchedLocation(SearchedLocation), AST(AST), PP(PP) {} 54 55 std::vector<const Decl *> takeDecls() { 56 // Don't keep the same declaration multiple times. 57 // This can happen when nodes in the AST are visited twice. 58 std::sort(Decls.begin(), Decls.end()); 59 auto Last = std::unique(Decls.begin(), Decls.end()); 60 Decls.erase(Last, Decls.end()); 61 return std::move(Decls); 62 } 63 64 std::vector<MacroDecl> takeMacroInfos() { 65 // Don't keep the same Macro info multiple times. 66 std::sort(MacroInfos.begin(), MacroInfos.end(), 67 [](const MacroDecl &Left, const MacroDecl &Right) { 68 return Left.Info < Right.Info; 69 }); 70 71 auto Last = std::unique(MacroInfos.begin(), MacroInfos.end(), 72 [](const MacroDecl &Left, const MacroDecl &Right) { 73 return Left.Info == Right.Info; 74 }); 75 MacroInfos.erase(Last, MacroInfos.end()); 76 return std::move(MacroInfos); 77 } 78 79 bool 80 handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles, 81 ArrayRef<index::SymbolRelation> Relations, FileID FID, 82 unsigned Offset, 83 index::IndexDataConsumer::ASTNodeInfo ASTNode) override { 84 if (isSearchedLocation(FID, Offset)) { 85 // Find and add definition declarations (for GoToDefinition). 86 // We don't use parameter `D`, as Parameter `D` is the canonical 87 // declaration, which is the first declaration of a redeclarable 88 // declaration, and it could be a forward declaration. 89 if (const auto *Def = GetDefinition(D)) { 90 Decls.push_back(Def); 91 } else { 92 // Couldn't find a definition, fall back to use `D`. 93 Decls.push_back(D); 94 } 95 } 96 return true; 97 } 98 99 private: 100 bool isSearchedLocation(FileID FID, unsigned Offset) const { 101 const SourceManager &SourceMgr = AST.getSourceManager(); 102 return SourceMgr.getFileOffset(SearchedLocation) == Offset && 103 SourceMgr.getFileID(SearchedLocation) == FID; 104 } 105 106 void finish() override { 107 // Also handle possible macro at the searched location. 108 Token Result; 109 auto &Mgr = AST.getSourceManager(); 110 if (!Lexer::getRawToken(SearchedLocation, Result, Mgr, AST.getLangOpts(), 111 false)) { 112 if (Result.is(tok::raw_identifier)) { 113 PP.LookUpIdentifierInfo(Result); 114 } 115 IdentifierInfo *IdentifierInfo = Result.getIdentifierInfo(); 116 if (IdentifierInfo && IdentifierInfo->hadMacroDefinition()) { 117 std::pair<FileID, unsigned int> DecLoc = 118 Mgr.getDecomposedExpansionLoc(SearchedLocation); 119 // Get the definition just before the searched location so that a macro 120 // referenced in a '#undef MACRO' can still be found. 121 SourceLocation BeforeSearchedLocation = Mgr.getMacroArgExpandedLocation( 122 Mgr.getLocForStartOfFile(DecLoc.first) 123 .getLocWithOffset(DecLoc.second - 1)); 124 MacroDefinition MacroDef = 125 PP.getMacroDefinitionAtLoc(IdentifierInfo, BeforeSearchedLocation); 126 MacroInfo *MacroInf = MacroDef.getMacroInfo(); 127 if (MacroInf) { 128 MacroInfos.push_back(MacroDecl{IdentifierInfo->getName(), MacroInf}); 129 } 130 } 131 } 132 } 133 }; 134 135 llvm::Optional<Location> 136 getDeclarationLocation(ParsedAST &AST, const SourceRange &ValSourceRange) { 137 const SourceManager &SourceMgr = AST.getASTContext().getSourceManager(); 138 const LangOptions &LangOpts = AST.getASTContext().getLangOpts(); 139 SourceLocation LocStart = ValSourceRange.getBegin(); 140 141 const FileEntry *F = 142 SourceMgr.getFileEntryForID(SourceMgr.getFileID(LocStart)); 143 if (!F) 144 return llvm::None; 145 SourceLocation LocEnd = Lexer::getLocForEndOfToken(ValSourceRange.getEnd(), 0, 146 SourceMgr, LangOpts); 147 Position Begin = sourceLocToPosition(SourceMgr, LocStart); 148 Position End = sourceLocToPosition(SourceMgr, LocEnd); 149 Range R = {Begin, End}; 150 Location L; 151 152 SmallString<64> FilePath = F->tryGetRealPathName(); 153 if (FilePath.empty()) 154 FilePath = F->getName(); 155 if (!llvm::sys::path::is_absolute(FilePath)) { 156 if (!SourceMgr.getFileManager().makeAbsolutePath(FilePath)) { 157 log("Could not turn relative path to absolute: " + FilePath); 158 return llvm::None; 159 } 160 } 161 162 L.uri = URIForFile(FilePath.str()); 163 L.range = R; 164 return L; 165 } 166 167 } // namespace 168 169 std::vector<Location> findDefinitions(ParsedAST &AST, Position Pos) { 170 const SourceManager &SourceMgr = AST.getASTContext().getSourceManager(); 171 const FileEntry *FE = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()); 172 if (!FE) 173 return {}; 174 175 SourceLocation SourceLocationBeg = getBeginningOfIdentifier(AST, Pos, FE); 176 177 auto DeclMacrosFinder = std::make_shared<DeclarationAndMacrosFinder>( 178 llvm::errs(), SourceLocationBeg, AST.getASTContext(), 179 AST.getPreprocessor()); 180 index::IndexingOptions IndexOpts; 181 IndexOpts.SystemSymbolFilter = 182 index::IndexingOptions::SystemSymbolFilterKind::All; 183 IndexOpts.IndexFunctionLocals = true; 184 185 indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(), 186 DeclMacrosFinder, IndexOpts); 187 188 std::vector<const Decl *> Decls = DeclMacrosFinder->takeDecls(); 189 std::vector<MacroDecl> MacroInfos = DeclMacrosFinder->takeMacroInfos(); 190 std::vector<Location> Result; 191 192 for (auto Item : Decls) { 193 auto L = getDeclarationLocation(AST, Item->getSourceRange()); 194 if (L) 195 Result.push_back(*L); 196 } 197 198 for (auto Item : MacroInfos) { 199 SourceRange SR(Item.Info->getDefinitionLoc(), 200 Item.Info->getDefinitionEndLoc()); 201 auto L = getDeclarationLocation(AST, SR); 202 if (L) 203 Result.push_back(*L); 204 } 205 206 /// Process targets for paths inside #include directive. 207 for (auto &IncludeLoc : AST.getInclusionLocations()) { 208 Range R = IncludeLoc.first; 209 Position Pos = sourceLocToPosition(SourceMgr, SourceLocationBeg); 210 211 if (R.contains(Pos)) 212 Result.push_back(Location{URIForFile{IncludeLoc.second}, {}}); 213 } 214 215 return Result; 216 } 217 218 namespace { 219 220 /// Finds document highlights that a given list of declarations refers to. 221 class DocumentHighlightsFinder : public index::IndexDataConsumer { 222 std::vector<const Decl *> &Decls; 223 std::vector<DocumentHighlight> DocumentHighlights; 224 const ASTContext &AST; 225 226 public: 227 DocumentHighlightsFinder(raw_ostream &OS, ASTContext &AST, Preprocessor &PP, 228 std::vector<const Decl *> &Decls) 229 : Decls(Decls), AST(AST) {} 230 std::vector<DocumentHighlight> takeHighlights() { 231 // Don't keep the same highlight multiple times. 232 // This can happen when nodes in the AST are visited twice. 233 std::sort(DocumentHighlights.begin(), DocumentHighlights.end()); 234 auto Last = 235 std::unique(DocumentHighlights.begin(), DocumentHighlights.end()); 236 DocumentHighlights.erase(Last, DocumentHighlights.end()); 237 return std::move(DocumentHighlights); 238 } 239 240 bool 241 handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles, 242 ArrayRef<index::SymbolRelation> Relations, FileID FID, 243 unsigned Offset, 244 index::IndexDataConsumer::ASTNodeInfo ASTNode) override { 245 const SourceManager &SourceMgr = AST.getSourceManager(); 246 if (SourceMgr.getMainFileID() != FID || 247 std::find(Decls.begin(), Decls.end(), D) == Decls.end()) { 248 return true; 249 } 250 SourceLocation End; 251 const LangOptions &LangOpts = AST.getLangOpts(); 252 SourceLocation StartOfFileLoc = SourceMgr.getLocForStartOfFile(FID); 253 SourceLocation HightlightStartLoc = StartOfFileLoc.getLocWithOffset(Offset); 254 End = 255 Lexer::getLocForEndOfToken(HightlightStartLoc, 0, SourceMgr, LangOpts); 256 SourceRange SR(HightlightStartLoc, End); 257 258 DocumentHighlightKind Kind = DocumentHighlightKind::Text; 259 if (static_cast<index::SymbolRoleSet>(index::SymbolRole::Write) & Roles) 260 Kind = DocumentHighlightKind::Write; 261 else if (static_cast<index::SymbolRoleSet>(index::SymbolRole::Read) & Roles) 262 Kind = DocumentHighlightKind::Read; 263 264 DocumentHighlights.push_back(getDocumentHighlight(SR, Kind)); 265 return true; 266 } 267 268 private: 269 DocumentHighlight getDocumentHighlight(SourceRange SR, 270 DocumentHighlightKind Kind) { 271 const SourceManager &SourceMgr = AST.getSourceManager(); 272 Position Begin = sourceLocToPosition(SourceMgr, SR.getBegin()); 273 Position End = sourceLocToPosition(SourceMgr, SR.getEnd()); 274 Range R = {Begin, End}; 275 DocumentHighlight DH; 276 DH.range = R; 277 DH.kind = Kind; 278 return DH; 279 } 280 }; 281 282 } // namespace 283 284 std::vector<DocumentHighlight> findDocumentHighlights(ParsedAST &AST, 285 Position Pos) { 286 const SourceManager &SourceMgr = AST.getASTContext().getSourceManager(); 287 const FileEntry *FE = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()); 288 if (!FE) 289 return {}; 290 291 SourceLocation SourceLocationBeg = getBeginningOfIdentifier(AST, Pos, FE); 292 293 auto DeclMacrosFinder = std::make_shared<DeclarationAndMacrosFinder>( 294 llvm::errs(), SourceLocationBeg, AST.getASTContext(), 295 AST.getPreprocessor()); 296 index::IndexingOptions IndexOpts; 297 IndexOpts.SystemSymbolFilter = 298 index::IndexingOptions::SystemSymbolFilterKind::All; 299 IndexOpts.IndexFunctionLocals = true; 300 301 // Macro occurences are not currently handled. 302 indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(), 303 DeclMacrosFinder, IndexOpts); 304 305 std::vector<const Decl *> SelectedDecls = DeclMacrosFinder->takeDecls(); 306 307 auto DocHighlightsFinder = std::make_shared<DocumentHighlightsFinder>( 308 llvm::errs(), AST.getASTContext(), AST.getPreprocessor(), SelectedDecls); 309 310 indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(), 311 DocHighlightsFinder, IndexOpts); 312 313 return DocHighlightsFinder->takeHighlights(); 314 } 315 316 static PrintingPolicy PrintingPolicyForDecls(PrintingPolicy Base) { 317 PrintingPolicy Policy(Base); 318 319 Policy.AnonymousTagLocations = false; 320 Policy.TerseOutput = true; 321 Policy.PolishForDeclaration = true; 322 Policy.ConstantsAsWritten = true; 323 Policy.SuppressTagKeyword = false; 324 325 return Policy; 326 } 327 328 /// Return a string representation (e.g. "class MyNamespace::MyClass") of 329 /// the type declaration \p TD. 330 static std::string TypeDeclToString(const TypeDecl *TD) { 331 QualType Type = TD->getASTContext().getTypeDeclType(TD); 332 333 PrintingPolicy Policy = 334 PrintingPolicyForDecls(TD->getASTContext().getPrintingPolicy()); 335 336 std::string Name; 337 llvm::raw_string_ostream Stream(Name); 338 Type.print(Stream, Policy); 339 340 return Stream.str(); 341 } 342 343 /// Return a string representation (e.g. "namespace ns1::ns2") of 344 /// the named declaration \p ND. 345 static std::string NamedDeclQualifiedName(const NamedDecl *ND, 346 StringRef Prefix) { 347 PrintingPolicy Policy = 348 PrintingPolicyForDecls(ND->getASTContext().getPrintingPolicy()); 349 350 std::string Name; 351 llvm::raw_string_ostream Stream(Name); 352 Stream << Prefix << ' '; 353 ND->printQualifiedName(Stream, Policy); 354 355 return Stream.str(); 356 } 357 358 /// Given a declaration \p D, return a human-readable string representing the 359 /// scope in which it is declared. If the declaration is in the global scope, 360 /// return the string "global namespace". 361 static llvm::Optional<std::string> getScopeName(const Decl *D) { 362 const DeclContext *DC = D->getDeclContext(); 363 364 if (isa<TranslationUnitDecl>(DC)) 365 return std::string("global namespace"); 366 if (const TypeDecl *TD = dyn_cast<TypeDecl>(DC)) 367 return TypeDeclToString(TD); 368 else if (const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC)) 369 return NamedDeclQualifiedName(ND, "namespace"); 370 else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(DC)) 371 return NamedDeclQualifiedName(FD, "function"); 372 373 return llvm::None; 374 } 375 376 /// Generate a \p Hover object given the declaration \p D. 377 static Hover getHoverContents(const Decl *D) { 378 Hover H; 379 llvm::Optional<std::string> NamedScope = getScopeName(D); 380 381 // Generate the "Declared in" section. 382 if (NamedScope) { 383 assert(!NamedScope->empty()); 384 385 H.contents.value += "Declared in "; 386 H.contents.value += *NamedScope; 387 H.contents.value += "\n\n"; 388 } 389 390 // We want to include the template in the Hover. 391 if (TemplateDecl *TD = D->getDescribedTemplate()) 392 D = TD; 393 394 std::string DeclText; 395 llvm::raw_string_ostream OS(DeclText); 396 397 PrintingPolicy Policy = 398 PrintingPolicyForDecls(D->getASTContext().getPrintingPolicy()); 399 400 D->print(OS, Policy); 401 402 OS.flush(); 403 404 H.contents.value += DeclText; 405 return H; 406 } 407 408 /// Generate a \p Hover object given the macro \p MacroInf. 409 static Hover getHoverContents(StringRef MacroName) { 410 Hover H; 411 412 H.contents.value = "#define "; 413 H.contents.value += MacroName; 414 415 return H; 416 } 417 418 Hover getHover(ParsedAST &AST, Position Pos) { 419 const SourceManager &SourceMgr = AST.getASTContext().getSourceManager(); 420 const FileEntry *FE = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()); 421 if (FE == nullptr) 422 return Hover(); 423 424 SourceLocation SourceLocationBeg = getBeginningOfIdentifier(AST, Pos, FE); 425 auto DeclMacrosFinder = std::make_shared<DeclarationAndMacrosFinder>( 426 llvm::errs(), SourceLocationBeg, AST.getASTContext(), 427 AST.getPreprocessor()); 428 429 index::IndexingOptions IndexOpts; 430 IndexOpts.SystemSymbolFilter = 431 index::IndexingOptions::SystemSymbolFilterKind::All; 432 IndexOpts.IndexFunctionLocals = true; 433 434 indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(), 435 DeclMacrosFinder, IndexOpts); 436 437 std::vector<MacroDecl> Macros = DeclMacrosFinder->takeMacroInfos(); 438 if (!Macros.empty()) 439 return getHoverContents(Macros[0].Name); 440 441 std::vector<const Decl *> Decls = DeclMacrosFinder->takeDecls(); 442 if (!Decls.empty()) 443 return getHoverContents(Decls[0]); 444 445 return Hover(); 446 } 447 448 } // namespace clangd 449 } // namespace clang 450