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