Clang Project

clang_source_code/tools/libclang/CIndexHigh.cpp
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<CXCursor8Overridden;
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 *, 8TopMethodsTy;
53  TopMethodsTy TopMethods;
54
55  FindFileIdRefVisitData(CXTranslationUnit TUFileID FID,
56                         const Decl *Dint 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 *Dconst {
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 *Dconst {
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 *Dconst {
112    if (std::find(TopMethods.begin(), TopMethods.end(), D) !=
113          TopMethods.end())
114      return true;
115
116    TopMethodsTy methods;
117    getTopOverriddenMethods(TU, D, methods);
118    for (TopMethodsTy::iterator
119           I = methods.begin(), E = methods.end(); I != E; ++I) {
120      if (std::find(TopMethods.begin(), TopMethods.end(), *I) !=
121            TopMethods.end())
122        return true;
123    }
124
125    return false;
126  }
127};
128
129// end anonymous namespace.
130
131/// For a macro \arg Loc, returns the file spelling location and sets
132/// to \arg isMacroArg whether the spelling resides inside a macro definition or
133/// a macro argument.
134static SourceLocation getFileSpellingLoc(SourceManager &SM,
135                                         SourceLocation Loc,
136                                         bool &isMacroArg) {
137  assert(Loc.isMacroID());
138  SourceLocation SpellLoc = SM.getImmediateSpellingLoc(Loc);
139  if (SpellLoc.isMacroID())
140    return getFileSpellingLoc(SMSpellLocisMacroArg);
141  
142  isMacroArg = SM.isMacroArgExpansion(Loc);
143  return SpellLoc;
144}
145
146static enum CXChildVisitResult findFileIdRefVisit(CXCursor cursor,
147                                                  CXCursor parent,
148                                                  CXClientData client_data) {
149  CXCursor declCursor = clang_getCursorReferenced(cursor);
150  if (!clang_isDeclaration(declCursor.kind))
151    return CXChildVisit_Recurse;
152
153  const Decl *D = cxcursor::getCursorDecl(declCursor);
154  if (!D)
155    return CXChildVisit_Continue;
156
157  FindFileIdRefVisitData *data = (FindFileIdRefVisitData *)client_data;
158  if (data->isHit(D)) {
159    cursor = cxcursor::getSelectorIdentifierCursor(data->SelectorIdIdxcursor);
160
161    // We are looking for identifiers to highlight so for objc methods (and
162    // not a parameter) we can only highlight the selector identifiers.
163    if ((cursor.kind == CXCursor_ObjCClassMethodDecl ||
164         cursor.kind == CXCursor_ObjCInstanceMethodDecl) &&
165         cxcursor::getSelectorIdentifierIndex(cursor) == -1)
166      return CXChildVisit_Recurse;
167
168    if (clang_isExpression(cursor.kind)) {
169      if (cursor.kind == CXCursor_DeclRefExpr ||
170          cursor.kind == CXCursor_MemberRefExpr) {
171        // continue..
172
173      } else if (cursor.kind == CXCursor_ObjCMessageExpr &&
174                 cxcursor::getSelectorIdentifierIndex(cursor) != -1) {
175        // continue..
176                
177      } else
178        return CXChildVisit_Recurse;
179    }
180
181    SourceLocation
182      Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
183    SourceLocation SelIdLoc = cxcursor::getSelectorIdentifierLoc(cursor);
184    if (SelIdLoc.isValid())
185      Loc = SelIdLoc;
186
187    ASTContext &Ctx = data->getASTContext();
188    SourceManager &SM = Ctx.getSourceManager();
189    bool isInMacroDef = false;
190    if (Loc.isMacroID()) {
191      bool isMacroArg;
192      Loc = getFileSpellingLoc(SMLocisMacroArg);
193      isInMacroDef = !isMacroArg;
194    }
195
196    // We are looking for identifiers in a specific file.
197    std::pair<FileIDunsignedLocInfo = SM.getDecomposedLoc(Loc);
198    if (LocInfo.first != data->FID)
199      return CXChildVisit_Recurse;
200
201    if (isInMacroDef) {
202      // FIXME: For a macro definition make sure that all expansions
203      // of it expand to the same reference before allowing to point to it.
204      return CXChildVisit_Recurse;
205    }
206
207    if (data->visitor.visit(data->visitor.contextcursor,
208                        cxloc::translateSourceRange(CtxLoc)) == CXVisit_Break)
209      return CXChildVisit_Break;
210  }
211  return CXChildVisit_Recurse;
212}
213
214static bool findIdRefsInFile(CXTranslationUnit TUCXCursor declCursor,
215                             const FileEntry *File,
216                             CXCursorAndRangeVisitor Visitor) {
217  assert(clang_isDeclaration(declCursor.kind));
218  SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager();
219
220  FileID FID = SM.translateFile(File);
221  const Decl *Dcl = cxcursor::getCursorDecl(declCursor);
222  if (!Dcl)
223    return false;
224
225  FindFileIdRefVisitData data(TUFIDDcl,
226                              cxcursor::getSelectorIdentifierIndex(declCursor),
227                              Visitor);
228
229  if (const DeclContext *DC = Dcl->getParentFunctionOrMethod()) {
230    return clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU),
231                               findFileIdRefVisit, &data);
232  }
233
234  SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
235  CursorVisitor FindIdRefsVisitor(TU,
236                                  findFileIdRefVisit, &data,
237                                  /*VisitPreprocessorLast=*/true,
238                                  /*VisitIncludedEntities=*/false,
239                                  Range,
240                                  /*VisitDeclsOnly=*/true);
241  return FindIdRefsVisitor.visitFileRegion();
242}
243
244namespace {
245
246struct FindFileMacroRefVisitData {
247  ASTUnit &Unit;
248  const FileEntry *File;
249  const IdentifierInfo *Macro;
250  CXCursorAndRangeVisitor visitor;
251
252  FindFileMacroRefVisitData(ASTUnit &Unitconst FileEntry *File,
253                            const IdentifierInfo *Macro,
254                            CXCursorAndRangeVisitor visitor)
255    : Unit(Unit), File(File), Macro(Macro), visitor(visitor) { }
256
257  ASTContext &getASTContext() const {
258    return Unit.getASTContext();
259  }
260};
261
262// anonymous namespace
263
264static enum CXChildVisitResult findFileMacroRefVisit(CXCursor cursor,
265                                                     CXCursor parent,
266                                                     CXClientData client_data) {
267  const IdentifierInfo *Macro = nullptr;
268  if (cursor.kind == CXCursor_MacroDefinition)
269    Macro = getCursorMacroDefinition(cursor)->getName();
270  else if (cursor.kind == CXCursor_MacroExpansion)
271    Macro = getCursorMacroExpansion(cursor).getName();
272  if (!Macro)
273    return CXChildVisit_Continue;
274
275  FindFileMacroRefVisitData *data = (FindFileMacroRefVisitData *)client_data;
276  if (data->Macro != Macro)
277    return CXChildVisit_Continue;
278
279  SourceLocation
280    Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
281
282  ASTContext &Ctx = data->getASTContext();
283  SourceManager &SM = Ctx.getSourceManager();
284  bool isInMacroDef = false;
285  if (Loc.isMacroID()) {
286    bool isMacroArg;
287    Loc = getFileSpellingLoc(SMLocisMacroArg);
288    isInMacroDef = !isMacroArg;
289  }
290
291  // We are looking for identifiers in a specific file.
292  std::pair<FileIDunsignedLocInfo = SM.getDecomposedLoc(Loc);
293  if (SM.getFileEntryForID(LocInfo.first) != data->File)
294    return CXChildVisit_Continue;
295
296  if (isInMacroDef) {
297    // FIXME: For a macro definition make sure that all expansions
298    // of it expand to the same reference before allowing to point to it.
299    return CXChildVisit_Continue;
300  }
301
302  if (data->visitor.visit(data->visitor.contextcursor,
303                        cxloc::translateSourceRange(CtxLoc)) == CXVisit_Break)
304    return CXChildVisit_Break;
305  return CXChildVisit_Continue;
306}
307
308static bool findMacroRefsInFile(CXTranslationUnit TUCXCursor Cursor,
309                                const FileEntry *File,
310                                CXCursorAndRangeVisitor Visitor) {
311  if (Cursor.kind != CXCursor_MacroDefinition &&
312      Cursor.kind != CXCursor_MacroExpansion)
313    return false;
314
315  ASTUnit *Unit = cxtu::getASTUnit(TU);
316  SourceManager &SM = Unit->getSourceManager();
317
318  FileID FID = SM.translateFile(File);
319  const IdentifierInfo *Macro = nullptr;
320  if (Cursor.kind == CXCursor_MacroDefinition)
321    Macro = getCursorMacroDefinition(Cursor)->getName();
322  else
323    Macro = getCursorMacroExpansion(Cursor).getName();
324  if (!Macro)
325    return false;
326
327  FindFileMacroRefVisitData data(*UnitFileMacroVisitor);
328
329  SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
330  CursorVisitor FindMacroRefsVisitor(TU,
331                                  findFileMacroRefVisit, &data,
332                                  /*VisitPreprocessorLast=*/false,
333                                  /*VisitIncludedEntities=*/false,
334                                  Range);
335  return FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion();
336}
337
338namespace {
339
340struct FindFileIncludesVisitor {
341  ASTUnit &Unit;
342  const FileEntry *File;
343  CXCursorAndRangeVisitor visitor;
344
345  FindFileIncludesVisitor(ASTUnit &Unitconst FileEntry *File,
346                          CXCursorAndRangeVisitor visitor)
347    : Unit(Unit), File(File), visitor(visitor) { }
348
349  ASTContext &getASTContext() const {
350    return Unit.getASTContext();
351  }
352
353  enum CXChildVisitResult visit(CXCursor cursorCXCursor parent) {
354    if (cursor.kind != CXCursor_InclusionDirective)
355      return CXChildVisit_Continue;
356
357    SourceLocation
358      Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
359
360    ASTContext &Ctx = getASTContext();
361    SourceManager &SM = Ctx.getSourceManager();
362
363    // We are looking for includes in a specific file.
364    std::pair<FileIDunsignedLocInfo = SM.getDecomposedLoc(Loc);
365    if (SM.getFileEntryForID(LocInfo.first) != File)
366      return CXChildVisit_Continue;
367
368    if (visitor.visit(visitor.contextcursor,
369                      cxloc::translateSourceRange(CtxLoc)) == CXVisit_Break)
370      return CXChildVisit_Break;
371    return CXChildVisit_Continue;
372  }
373
374  static enum CXChildVisitResult visit(CXCursor cursorCXCursor parent,
375                                       CXClientData client_data) {
376    return static_cast<FindFileIncludesVisitor*>(client_data)->
377                                                          visit(cursorparent);
378  }
379};
380
381// anonymous namespace
382
383static bool findIncludesInFile(CXTranslationUnit TUconst FileEntry *File,
384                               CXCursorAndRangeVisitor Visitor) {
385  assert(TU && File && Visitor.visit);
386
387  ASTUnit *Unit = cxtu::getASTUnit(TU);
388  SourceManager &SM = Unit->getSourceManager();
389
390  FileID FID = SM.translateFile(File);
391
392  FindFileIncludesVisitor IncludesVisitor(*UnitFileVisitor);
393
394  SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
395  CursorVisitor InclusionCursorsVisitor(TU,
396                                        FindFileIncludesVisitor::visit,
397                                        &IncludesVisitor,
398                                        /*VisitPreprocessorLast=*/false,
399                                        /*VisitIncludedEntities=*/false,
400                                        Range);
401  return InclusionCursorsVisitor.visitPreprocessedEntitiesInRegion();
402}
403
404
405//===----------------------------------------------------------------------===//
406// libclang public APIs.
407//===----------------------------------------------------------------------===//
408
409extern "C" {
410
411CXResult clang_findReferencesInFile(CXCursor cursorCXFile file,
412                                    CXCursorAndRangeVisitor visitor) {
413  LogRef Log = Logger::make(__func__);
414
415  if (clang_Cursor_isNull(cursor)) {
416    if (Log)
417      *Log << "Null cursor";
418    return CXResult_Invalid;
419  }
420  if (cursor.kind == CXCursor_NoDeclFound) {
421    if (Log)
422      *Log << "Got CXCursor_NoDeclFound";
423    return CXResult_Invalid;
424  }
425  if (!file) {
426    if (Log)
427      *Log << "Null file";
428    return CXResult_Invalid;
429  }
430  if (!visitor.visit) {
431    if (Log)
432      *Log << "Null visitor";
433    return CXResult_Invalid;
434  }
435
436  if (Log)
437    *Log << cursor << " @" << static_cast<const FileEntry *>(file);
438
439  ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor);
440  if (!CXXUnit)
441    return CXResult_Invalid;
442
443  ASTUnit::ConcurrencyCheck Check(*CXXUnit);
444
445  if (cursor.kind == CXCursor_MacroDefinition ||
446      cursor.kind == CXCursor_MacroExpansion) {
447    if (findMacroRefsInFile(cxcursor::getCursorTU(cursor),
448                            cursor,
449                            static_cast<const FileEntry *>(file),
450                            visitor))
451      return CXResult_VisitBreak;
452    return CXResult_Success;
453  }
454
455  // We are interested in semantics of identifiers so for C++ constructor exprs
456  // prefer type references, e.g.:
457  //
458  //  return MyStruct();
459  //
460  // for 'MyStruct' we'll have a cursor pointing at the constructor decl but
461  // we are actually interested in the type declaration.
462  cursor = cxcursor::getTypeRefCursor(cursor);
463
464  CXCursor refCursor = clang_getCursorReferenced(cursor);
465
466  if (!clang_isDeclaration(refCursor.kind)) {
467    if (Log)
468      *Log << "cursor is not referencing a declaration";
469    return CXResult_Invalid;
470  }
471
472  if (findIdRefsInFile(cxcursor::getCursorTU(cursor),
473                       refCursor,
474                       static_cast<const FileEntry *>(file),
475                       visitor))
476    return CXResult_VisitBreak;
477  return CXResult_Success;
478}
479
480CXResult clang_findIncludesInFile(CXTranslationUnit TUCXFile file,
481                             CXCursorAndRangeVisitor visitor) {
482  if (cxtu::isNotUsableTU(TU)) {
483    LOG_BAD_TU(TU);
484    return CXResult_Invalid;
485  }
486
487  LogRef Log = Logger::make(__func__);
488  if (!file) {
489    if (Log)
490      *Log << "Null file";
491    return CXResult_Invalid;
492  }
493  if (!visitor.visit) {
494    if (Log)
495      *Log << "Null visitor";
496    return CXResult_Invalid;
497  }
498
499  if (Log)
500    *Log << TU << " @" << static_cast<const FileEntry *>(file);
501
502  ASTUnit *CXXUnit = cxtu::getASTUnit(TU);
503  if (!CXXUnit)
504    return CXResult_Invalid;
505
506  ASTUnit::ConcurrencyCheck Check(*CXXUnit);
507
508  if (findIncludesInFile(TUstatic_cast<const FileEntry *>(file), visitor))
509    return CXResult_VisitBreak;
510  return CXResult_Success;
511}
512
513static enum CXVisitorResult _visitCursorAndRange(void *context,
514                                                 CXCursor cursor,
515                                                 CXSourceRange range) {
516  CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context;
517  return INVOKE_BLOCK2(block, cursor, range);
518}
519
520CXResult clang_findReferencesInFileWithBlock(CXCursor cursor,
521                                             CXFile file,
522                                           CXCursorAndRangeVisitorBlock block) {
523  CXCursorAndRangeVisitor visitor = { block,
524                                      block ? _visitCursorAndRange : nullptr };
525  return clang_findReferencesInFile(cursorfilevisitor);
526}
527
528CXResult clang_findIncludesInFileWithBlock(CXTranslationUnit TU,
529                                           CXFile file,
530                                           CXCursorAndRangeVisitorBlock block) {
531  CXCursorAndRangeVisitor visitor = { block,
532                                      block ? _visitCursorAndRange : nullptr };
533  return clang_findIncludesInFile(TUfilevisitor);
534}
535
536// end: extern "C"
537