1//===- CIndexHigh.cpp - Higher level API functions ------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "CursorVisitor.h"
10#include "CLog.h"
11#include "CXCursor.h"
12#include "CXSourceLocation.h"
13#include "CXTranslationUnit.h"
14#include "clang/AST/DeclObjC.h"
15#include "clang/Frontend/ASTUnit.h"
16#include "llvm/Support/Compiler.h"
17
18using namespace clang;
19using namespace cxcursor;
20using namespace cxindex;
21
22static void getTopOverriddenMethods(CXTranslationUnit TU,
23                                    const Decl *D,
24                                    SmallVectorImpl<const Decl *> &Methods) {
25  if (!D)
26    return;
27  if (!isa<ObjCMethodDecl>(D) && !isa<CXXMethodDecl>(D))
28    return;
29
30  SmallVector<CXCursor, 8> Overridden;
31  cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D, TU), Overridden);
32
33  if (Overridden.empty()) {
34    Methods.push_back(D->getCanonicalDecl());
35    return;
36  }
37
38  for (SmallVectorImpl<CXCursor>::iterator
39         I = Overridden.begin(), E = Overridden.end(); I != E; ++I)
40    getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods);
41}
42
43namespace {
44
45struct FindFileIdRefVisitData {
46  CXTranslationUnit TU;
47  FileID FID;
48  const Decl *Dcl;
49  int SelectorIdIdx;
50  CXCursorAndRangeVisitor visitor;
51
52  typedef SmallVector<const Decl *, 8> TopMethodsTy;
53  TopMethodsTy TopMethods;
54
55  FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID,
56                         const Decl *D, int selectorIdIdx,
57                         CXCursorAndRangeVisitor visitor)
58    : TU(TU), FID(FID), SelectorIdIdx(selectorIdIdx), visitor(visitor) {
59    Dcl = getCanonical(D);
60    getTopOverriddenMethods(TU, Dcl, TopMethods);
61  }
62
63  ASTContext &getASTContext() const {
64    return cxtu::getASTUnit(TU)->getASTContext();
65  }
66
67  /// We are looking to find all semantically relevant identifiers,
68  /// so the definition of "canonical" here is different than in the AST, e.g.
69  ///
70  /// \code
71  ///   class C {
72  ///     C() {}
73  ///   };
74  /// \endcode
75  ///
76  /// we consider the canonical decl of the constructor decl to be the class
77  /// itself, so both 'C' can be highlighted.
78  const Decl *getCanonical(const Decl *D) const {
79    if (!D)
80      return nullptr;
81
82    D = D->getCanonicalDecl();
83
84    if (const ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) {
85      if (ImplD->getClassInterface())
86        return getCanonical(ImplD->getClassInterface());
87
88    } else if (const CXXConstructorDecl *CXXCtorD =
89                   dyn_cast<CXXConstructorDecl>(D)) {
90      return getCanonical(CXXCtorD->getParent());
91    }
92
93    return D;
94  }
95
96  bool isHit(const Decl *D) const {
97    if (!D)
98      return false;
99
100    D = getCanonical(D);
101    if (D == Dcl)
102      return true;
103
104    if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D))
105      return isOverriddingMethod(D);
106
107    return false;
108  }
109
110private:
111  bool isOverriddingMethod(const Decl *D) const {
112    if (llvm::is_contained(TopMethods, D))
113      return true;
114
115    TopMethodsTy methods;
116    getTopOverriddenMethods(TU, D, methods);
117    for (TopMethodsTy::iterator
118           I = methods.begin(), E = methods.end(); I != E; ++I) {
119      if (llvm::is_contained(TopMethods, *I))
120        return true;
121    }
122
123    return false;
124  }
125};
126
127} // end anonymous namespace.
128
129/// For a macro \arg Loc, returns the file spelling location and sets
130/// to \arg isMacroArg whether the spelling resides inside a macro definition or
131/// a macro argument.
132static SourceLocation getFileSpellingLoc(SourceManager &SM,
133                                         SourceLocation Loc,
134                                         bool &isMacroArg) {
135  assert(Loc.isMacroID());
136  SourceLocation SpellLoc = SM.getImmediateSpellingLoc(Loc);
137  if (SpellLoc.isMacroID())
138    return getFileSpellingLoc(SM, SpellLoc, isMacroArg);
139
140  isMacroArg = SM.isMacroArgExpansion(Loc);
141  return SpellLoc;
142}
143
144static enum CXChildVisitResult findFileIdRefVisit(CXCursor cursor,
145                                                  CXCursor parent,
146                                                  CXClientData client_data) {
147  CXCursor declCursor = clang_getCursorReferenced(cursor);
148  if (!clang_isDeclaration(declCursor.kind))
149    return CXChildVisit_Recurse;
150
151  const Decl *D = cxcursor::getCursorDecl(declCursor);
152  if (!D)
153    return CXChildVisit_Continue;
154
155  FindFileIdRefVisitData *data = (FindFileIdRefVisitData *)client_data;
156  if (data->isHit(D)) {
157    cursor = cxcursor::getSelectorIdentifierCursor(data->SelectorIdIdx, cursor);
158
159    // We are looking for identifiers to highlight so for objc methods (and
160    // not a parameter) we can only highlight the selector identifiers.
161    if ((cursor.kind == CXCursor_ObjCClassMethodDecl ||
162         cursor.kind == CXCursor_ObjCInstanceMethodDecl) &&
163         cxcursor::getSelectorIdentifierIndex(cursor) == -1)
164      return CXChildVisit_Recurse;
165
166    if (clang_isExpression(cursor.kind)) {
167      if (cursor.kind == CXCursor_DeclRefExpr ||
168          cursor.kind == CXCursor_MemberRefExpr) {
169        // continue..
170
171      } else if (cursor.kind == CXCursor_ObjCMessageExpr &&
172                 cxcursor::getSelectorIdentifierIndex(cursor) != -1) {
173        // continue..
174
175      } else
176        return CXChildVisit_Recurse;
177    }
178
179    SourceLocation
180      Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
181    SourceLocation SelIdLoc = cxcursor::getSelectorIdentifierLoc(cursor);
182    if (SelIdLoc.isValid())
183      Loc = SelIdLoc;
184
185    ASTContext &Ctx = data->getASTContext();
186    SourceManager &SM = Ctx.getSourceManager();
187    bool isInMacroDef = false;
188    if (Loc.isMacroID()) {
189      bool isMacroArg;
190      Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
191      isInMacroDef = !isMacroArg;
192    }
193
194    // We are looking for identifiers in a specific file.
195    std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
196    if (LocInfo.first != data->FID)
197      return CXChildVisit_Recurse;
198
199    if (isInMacroDef) {
200      // FIXME: For a macro definition make sure that all expansions
201      // of it expand to the same reference before allowing to point to it.
202      return CXChildVisit_Recurse;
203    }
204
205    if (data->visitor.visit(data->visitor.context, cursor,
206                        cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
207      return CXChildVisit_Break;
208  }
209  return CXChildVisit_Recurse;
210}
211
212static bool findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor,
213                             const FileEntry *File,
214                             CXCursorAndRangeVisitor Visitor) {
215  assert(clang_isDeclaration(declCursor.kind));
216  SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager();
217
218  FileID FID = SM.translateFile(File);
219  const Decl *Dcl = cxcursor::getCursorDecl(declCursor);
220  if (!Dcl)
221    return false;
222
223  FindFileIdRefVisitData data(TU, FID, Dcl,
224                              cxcursor::getSelectorIdentifierIndex(declCursor),
225                              Visitor);
226
227  if (const DeclContext *DC = Dcl->getParentFunctionOrMethod()) {
228    return clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU),
229                               findFileIdRefVisit, &data);
230  }
231
232  SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
233  CursorVisitor FindIdRefsVisitor(TU,
234                                  findFileIdRefVisit, &data,
235                                  /*VisitPreprocessorLast=*/true,
236                                  /*VisitIncludedEntities=*/false,
237                                  Range,
238                                  /*VisitDeclsOnly=*/true);
239  return FindIdRefsVisitor.visitFileRegion();
240}
241
242namespace {
243
244struct FindFileMacroRefVisitData {
245  ASTUnit &Unit;
246  const FileEntry *File;
247  const IdentifierInfo *Macro;
248  CXCursorAndRangeVisitor visitor;
249
250  FindFileMacroRefVisitData(ASTUnit &Unit, const FileEntry *File,
251                            const IdentifierInfo *Macro,
252                            CXCursorAndRangeVisitor visitor)
253    : Unit(Unit), File(File), Macro(Macro), visitor(visitor) { }
254
255  ASTContext &getASTContext() const {
256    return Unit.getASTContext();
257  }
258};
259
260} // anonymous namespace
261
262static enum CXChildVisitResult findFileMacroRefVisit(CXCursor cursor,
263                                                     CXCursor parent,
264                                                     CXClientData client_data) {
265  const IdentifierInfo *Macro = nullptr;
266  if (cursor.kind == CXCursor_MacroDefinition)
267    Macro = getCursorMacroDefinition(cursor)->getName();
268  else if (cursor.kind == CXCursor_MacroExpansion)
269    Macro = getCursorMacroExpansion(cursor).getName();
270  if (!Macro)
271    return CXChildVisit_Continue;
272
273  FindFileMacroRefVisitData *data = (FindFileMacroRefVisitData *)client_data;
274  if (data->Macro != Macro)
275    return CXChildVisit_Continue;
276
277  SourceLocation
278    Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
279
280  ASTContext &Ctx = data->getASTContext();
281  SourceManager &SM = Ctx.getSourceManager();
282  bool isInMacroDef = false;
283  if (Loc.isMacroID()) {
284    bool isMacroArg;
285    Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
286    isInMacroDef = !isMacroArg;
287  }
288
289  // We are looking for identifiers in a specific file.
290  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
291  if (SM.getFileEntryForID(LocInfo.first) != data->File)
292    return CXChildVisit_Continue;
293
294  if (isInMacroDef) {
295    // FIXME: For a macro definition make sure that all expansions
296    // of it expand to the same reference before allowing to point to it.
297    return CXChildVisit_Continue;
298  }
299
300  if (data->visitor.visit(data->visitor.context, cursor,
301                        cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
302    return CXChildVisit_Break;
303  return CXChildVisit_Continue;
304}
305
306static bool findMacroRefsInFile(CXTranslationUnit TU, CXCursor Cursor,
307                                const FileEntry *File,
308                                CXCursorAndRangeVisitor Visitor) {
309  if (Cursor.kind != CXCursor_MacroDefinition &&
310      Cursor.kind != CXCursor_MacroExpansion)
311    return false;
312
313  ASTUnit *Unit = cxtu::getASTUnit(TU);
314  SourceManager &SM = Unit->getSourceManager();
315
316  FileID FID = SM.translateFile(File);
317  const IdentifierInfo *Macro = nullptr;
318  if (Cursor.kind == CXCursor_MacroDefinition)
319    Macro = getCursorMacroDefinition(Cursor)->getName();
320  else
321    Macro = getCursorMacroExpansion(Cursor).getName();
322  if (!Macro)
323    return false;
324
325  FindFileMacroRefVisitData data(*Unit, File, Macro, Visitor);
326
327  SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
328  CursorVisitor FindMacroRefsVisitor(TU,
329                                  findFileMacroRefVisit, &data,
330                                  /*VisitPreprocessorLast=*/false,
331                                  /*VisitIncludedEntities=*/false,
332                                  Range);
333  return FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion();
334}
335
336namespace {
337
338struct FindFileIncludesVisitor {
339  ASTUnit &Unit;
340  const FileEntry *File;
341  CXCursorAndRangeVisitor visitor;
342
343  FindFileIncludesVisitor(ASTUnit &Unit, const FileEntry *File,
344                          CXCursorAndRangeVisitor visitor)
345    : Unit(Unit), File(File), visitor(visitor) { }
346
347  ASTContext &getASTContext() const {
348    return Unit.getASTContext();
349  }
350
351  enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent) {
352    if (cursor.kind != CXCursor_InclusionDirective)
353      return CXChildVisit_Continue;
354
355    SourceLocation
356      Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
357
358    ASTContext &Ctx = getASTContext();
359    SourceManager &SM = Ctx.getSourceManager();
360
361    // We are looking for includes in a specific file.
362    std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
363    if (SM.getFileEntryForID(LocInfo.first) != File)
364      return CXChildVisit_Continue;
365
366    if (visitor.visit(visitor.context, cursor,
367                      cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
368      return CXChildVisit_Break;
369    return CXChildVisit_Continue;
370  }
371
372  static enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent,
373                                       CXClientData client_data) {
374    return static_cast<FindFileIncludesVisitor*>(client_data)->
375                                                          visit(cursor, parent);
376  }
377};
378
379} // anonymous namespace
380
381static bool findIncludesInFile(CXTranslationUnit TU, const FileEntry *File,
382                               CXCursorAndRangeVisitor Visitor) {
383  assert(TU && File && Visitor.visit);
384
385  ASTUnit *Unit = cxtu::getASTUnit(TU);
386  SourceManager &SM = Unit->getSourceManager();
387
388  FileID FID = SM.translateFile(File);
389
390  FindFileIncludesVisitor IncludesVisitor(*Unit, File, Visitor);
391
392  SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
393  CursorVisitor InclusionCursorsVisitor(TU,
394                                        FindFileIncludesVisitor::visit,
395                                        &IncludesVisitor,
396                                        /*VisitPreprocessorLast=*/false,
397                                        /*VisitIncludedEntities=*/false,
398                                        Range);
399  return InclusionCursorsVisitor.visitPreprocessedEntitiesInRegion();
400}
401
402
403//===----------------------------------------------------------------------===//
404// libclang public APIs.
405//===----------------------------------------------------------------------===//
406
407extern "C" {
408
409CXResult clang_findReferencesInFile(CXCursor cursor, CXFile file,
410                                    CXCursorAndRangeVisitor visitor) {
411  LogRef Log = Logger::make(__func__);
412
413  if (clang_Cursor_isNull(cursor)) {
414    if (Log)
415      *Log << "Null cursor";
416    return CXResult_Invalid;
417  }
418  if (cursor.kind == CXCursor_NoDeclFound) {
419    if (Log)
420      *Log << "Got CXCursor_NoDeclFound";
421    return CXResult_Invalid;
422  }
423  if (!file) {
424    if (Log)
425      *Log << "Null file";
426    return CXResult_Invalid;
427  }
428  if (!visitor.visit) {
429    if (Log)
430      *Log << "Null visitor";
431    return CXResult_Invalid;
432  }
433
434  if (Log)
435    *Log << cursor << " @" << static_cast<const FileEntry *>(file);
436
437  ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor);
438  if (!CXXUnit)
439    return CXResult_Invalid;
440
441  ASTUnit::ConcurrencyCheck Check(*CXXUnit);
442
443  if (cursor.kind == CXCursor_MacroDefinition ||
444      cursor.kind == CXCursor_MacroExpansion) {
445    if (findMacroRefsInFile(cxcursor::getCursorTU(cursor),
446                            cursor,
447                            static_cast<const FileEntry *>(file),
448                            visitor))
449      return CXResult_VisitBreak;
450    return CXResult_Success;
451  }
452
453  // We are interested in semantics of identifiers so for C++ constructor exprs
454  // prefer type references, e.g.:
455  //
456  //  return MyStruct();
457  //
458  // for 'MyStruct' we'll have a cursor pointing at the constructor decl but
459  // we are actually interested in the type declaration.
460  cursor = cxcursor::getTypeRefCursor(cursor);
461
462  CXCursor refCursor = clang_getCursorReferenced(cursor);
463
464  if (!clang_isDeclaration(refCursor.kind)) {
465    if (Log)
466      *Log << "cursor is not referencing a declaration";
467    return CXResult_Invalid;
468  }
469
470  if (findIdRefsInFile(cxcursor::getCursorTU(cursor),
471                       refCursor,
472                       static_cast<const FileEntry *>(file),
473                       visitor))
474    return CXResult_VisitBreak;
475  return CXResult_Success;
476}
477
478CXResult clang_findIncludesInFile(CXTranslationUnit TU, CXFile file,
479                             CXCursorAndRangeVisitor visitor) {
480  if (cxtu::isNotUsableTU(TU)) {
481    LOG_BAD_TU(TU);
482    return CXResult_Invalid;
483  }
484
485  LogRef Log = Logger::make(__func__);
486  if (!file) {
487    if (Log)
488      *Log << "Null file";
489    return CXResult_Invalid;
490  }
491  if (!visitor.visit) {
492    if (Log)
493      *Log << "Null visitor";
494    return CXResult_Invalid;
495  }
496
497  if (Log)
498    *Log << TU << " @" << static_cast<const FileEntry *>(file);
499
500  ASTUnit *CXXUnit = cxtu::getASTUnit(TU);
501  if (!CXXUnit)
502    return CXResult_Invalid;
503
504  ASTUnit::ConcurrencyCheck Check(*CXXUnit);
505
506  if (findIncludesInFile(TU, static_cast<const FileEntry *>(file), visitor))
507    return CXResult_VisitBreak;
508  return CXResult_Success;
509}
510
511static enum CXVisitorResult _visitCursorAndRange(void *context,
512                                                 CXCursor cursor,
513                                                 CXSourceRange range) {
514  CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context;
515  return INVOKE_BLOCK2(block, cursor, range);
516}
517
518CXResult clang_findReferencesInFileWithBlock(CXCursor cursor,
519                                             CXFile file,
520                                           CXCursorAndRangeVisitorBlock block) {
521  CXCursorAndRangeVisitor visitor = { block,
522                                      block ? _visitCursorAndRange : nullptr };
523  return clang_findReferencesInFile(cursor, file, visitor);
524}
525
526CXResult clang_findIncludesInFileWithBlock(CXTranslationUnit TU,
527                                           CXFile file,
528                                           CXCursorAndRangeVisitorBlock block) {
529  CXCursorAndRangeVisitor visitor = { block,
530                                      block ? _visitCursorAndRange : nullptr };
531  return clang_findIncludesInFile(TU, file, visitor);
532}
533
534} // end: extern "C"
535