CIndexHigh.cpp revision 5c2a1f741e133fcad4782b855a18d3b220243b11
1//===- CIndexHigh.cpp - Higher level API functions ------------------------===//
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
10#include "CursorVisitor.h"
11#include "CXCursor.h"
12#include "CXSourceLocation.h"
13#include "CXTranslationUnit.h"
14
15#include "clang/Frontend/ASTUnit.h"
16#include "clang/AST/DeclObjC.h"
17
18using namespace clang;
19using namespace cxcursor;
20
21static void getTopOverriddenMethods(CXTranslationUnit TU,
22                                    Decl *D,
23                                    SmallVectorImpl<Decl *> &Methods) {
24  if (!isa<ObjCMethodDecl>(D) && !isa<CXXMethodDecl>(D))
25    return;
26
27  SmallVector<CXCursor, 8> Overridden;
28  cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D, TU), Overridden);
29
30  if (Overridden.empty()) {
31    Methods.push_back(D->getCanonicalDecl());
32    return;
33  }
34
35  for (SmallVector<CXCursor, 8>::iterator
36         I = Overridden.begin(), E = Overridden.end(); I != E; ++I)
37    getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods);
38}
39
40namespace {
41
42struct FindFileIdRefVisitData {
43  CXTranslationUnit TU;
44  FileID FID;
45  Decl *Dcl;
46  int SelectorIdIdx;
47  CXCursorAndRangeVisitor visitor;
48
49  typedef SmallVector<Decl *, 8> TopMethodsTy;
50  TopMethodsTy TopMethods;
51
52  FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID,
53                         Decl *D, int selectorIdIdx,
54                         CXCursorAndRangeVisitor visitor)
55    : TU(TU), FID(FID), SelectorIdIdx(selectorIdIdx), visitor(visitor) {
56    Dcl = getCanonical(D);
57    getTopOverriddenMethods(TU, Dcl, TopMethods);
58  }
59
60  ASTContext &getASTContext() const {
61    return static_cast<ASTUnit *>(TU->TUData)->getASTContext();
62  }
63
64  /// \brief We are looking to find all semantically relevant identifiers,
65  /// so the definition of "canonical" here is different than in the AST, e.g.
66  ///
67  /// \code
68  ///   class C {
69  ///     C() {}
70  ///   };
71  /// \endcode
72  ///
73  /// we consider the canonical decl of the constructor decl to be the class
74  /// itself, so both 'C' can be highlighted.
75  Decl *getCanonical(Decl *D) const {
76    D = D->getCanonicalDecl();
77
78    if (ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D))
79      return getCanonical(ImplD->getClassInterface());
80    if (CXXConstructorDecl *CXXCtorD = dyn_cast<CXXConstructorDecl>(D))
81      return getCanonical(CXXCtorD->getParent());
82
83    return D;
84  }
85
86  bool isHit(Decl *D) const {
87    D = getCanonical(D);
88    if (D == Dcl)
89      return true;
90
91    if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D))
92      return isOverriddingMethod(D);
93
94    return false;
95  }
96
97private:
98  bool isOverriddingMethod(Decl *D) const {
99    if (std::find(TopMethods.begin(), TopMethods.end(), D) !=
100          TopMethods.end())
101      return true;
102
103    TopMethodsTy methods;
104    getTopOverriddenMethods(TU, D, methods);
105    for (TopMethodsTy::iterator
106           I = methods.begin(), E = methods.end(); I != E; ++I) {
107      if (std::find(TopMethods.begin(), TopMethods.end(), *I) !=
108            TopMethods.end())
109        return true;
110    }
111
112    return false;
113  }
114};
115
116} // end anonymous namespace.
117
118/// \brief For a macro \arg Loc, returns the file spelling location and sets
119/// to \arg isMacroArg whether the spelling resides inside a macro definition or
120/// a macro argument.
121static SourceLocation getFileSpellingLoc(SourceManager &SM,
122                                         SourceLocation Loc,
123                                         bool &isMacroArg) {
124  assert(Loc.isMacroID());
125  SourceLocation SpellLoc = SM.getImmediateSpellingLoc(Loc);
126  if (SpellLoc.isMacroID())
127    return getFileSpellingLoc(SM, SpellLoc, isMacroArg);
128
129  isMacroArg = SM.isMacroArgExpansion(Loc);
130  return SpellLoc;
131}
132
133static enum CXChildVisitResult findFileIdRefVisit(CXCursor cursor,
134                                                  CXCursor parent,
135                                                  CXClientData client_data) {
136  CXCursor declCursor = clang_getCursorReferenced(cursor);
137  if (!clang_isDeclaration(declCursor.kind))
138    return CXChildVisit_Recurse;
139
140  Decl *D = cxcursor::getCursorDecl(declCursor);
141  FindFileIdRefVisitData *data = (FindFileIdRefVisitData *)client_data;
142  if (data->isHit(D)) {
143    cursor = cxcursor::getSelectorIdentifierCursor(data->SelectorIdIdx, cursor);
144
145    // We are looking for identifiers to highlight so for objc methods (and
146    // not a parameter) we can only highlight the selector identifiers.
147    if ((cursor.kind == CXCursor_ObjCClassMethodDecl ||
148         cursor.kind == CXCursor_ObjCInstanceMethodDecl) &&
149         cxcursor::getSelectorIdentifierIndex(cursor) == -1)
150      return CXChildVisit_Recurse;
151
152    if (clang_isExpression(cursor.kind)) {
153      if (cursor.kind == CXCursor_DeclRefExpr ||
154          cursor.kind == CXCursor_MemberRefExpr) {
155        // continue..
156
157      } else if (cursor.kind == CXCursor_ObjCMessageExpr &&
158                 cxcursor::getSelectorIdentifierIndex(cursor) != -1) {
159        // continue..
160
161      } else
162        return CXChildVisit_Recurse;
163    }
164
165    SourceLocation
166      Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
167    SourceLocation SelIdLoc = cxcursor::getSelectorIdentifierLoc(cursor);
168    if (SelIdLoc.isValid())
169      Loc = SelIdLoc;
170
171    ASTContext &Ctx = data->getASTContext();
172    SourceManager &SM = Ctx.getSourceManager();
173    bool isInMacroDef = false;
174    if (Loc.isMacroID()) {
175      bool isMacroArg;
176      Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
177      isInMacroDef = !isMacroArg;
178    }
179
180    // We are looking for identifiers in a specific file.
181    std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
182    if (LocInfo.first != data->FID)
183      return CXChildVisit_Recurse;
184
185    if (isInMacroDef) {
186      // FIXME: For a macro definition make sure that all expansions
187      // of it expand to the same reference before allowing to point to it.
188      return CXChildVisit_Recurse;
189    }
190
191    data->visitor.visit(data->visitor.context, cursor,
192                        cxloc::translateSourceRange(Ctx, Loc));
193  }
194  return CXChildVisit_Recurse;
195}
196
197static void findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor,
198                           const FileEntry *File,
199                           CXCursorAndRangeVisitor Visitor) {
200  assert(clang_isDeclaration(declCursor.kind));
201  ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData);
202  SourceManager &SM = Unit->getSourceManager();
203
204  FileID FID = SM.translateFile(File);
205  Decl *Dcl = cxcursor::getCursorDecl(declCursor);
206  FindFileIdRefVisitData data(TU, FID, Dcl,
207                              cxcursor::getSelectorIdentifierIndex(declCursor),
208                              Visitor);
209
210  if (DeclContext *DC = Dcl->getParentFunctionOrMethod()) {
211    clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU),
212                        findFileIdRefVisit, &data);
213    return;
214  }
215
216  SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
217  CursorVisitor FindIdRefsVisitor(TU,
218                                  findFileIdRefVisit, &data,
219                                  /*VisitPreprocessorLast=*/true,
220                                  /*VisitIncludedEntities=*/false,
221                                  Range,
222                                  /*VisitDeclsOnly=*/true);
223  FindIdRefsVisitor.visitFileRegion();
224}
225
226namespace {
227
228struct FindFileMacroRefVisitData {
229  ASTUnit &Unit;
230  const FileEntry *File;
231  const IdentifierInfo *Macro;
232  CXCursorAndRangeVisitor visitor;
233
234  FindFileMacroRefVisitData(ASTUnit &Unit, const FileEntry *File,
235                            const IdentifierInfo *Macro,
236                            CXCursorAndRangeVisitor visitor)
237    : Unit(Unit), File(File), Macro(Macro), visitor(visitor) { }
238
239  ASTContext &getASTContext() const {
240    return Unit.getASTContext();
241  }
242};
243
244} // anonymous namespace
245
246static enum CXChildVisitResult findFileMacroRefVisit(CXCursor cursor,
247                                                     CXCursor parent,
248                                                     CXClientData client_data) {
249  const IdentifierInfo *Macro = 0;
250  if (cursor.kind == CXCursor_MacroDefinition)
251    Macro = getCursorMacroDefinition(cursor)->getName();
252  else if (cursor.kind == CXCursor_MacroExpansion)
253    Macro = getCursorMacroExpansion(cursor)->getName();
254  if (!Macro)
255    return CXChildVisit_Continue;
256
257  FindFileMacroRefVisitData *data = (FindFileMacroRefVisitData *)client_data;
258  if (data->Macro != Macro)
259    return CXChildVisit_Continue;
260
261  SourceLocation
262    Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
263
264  ASTContext &Ctx = data->getASTContext();
265  SourceManager &SM = Ctx.getSourceManager();
266  bool isInMacroDef = false;
267  if (Loc.isMacroID()) {
268    bool isMacroArg;
269    Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
270    isInMacroDef = !isMacroArg;
271  }
272
273  // We are looking for identifiers in a specific file.
274  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
275  if (SM.getFileEntryForID(LocInfo.first) != data->File)
276    return CXChildVisit_Continue;
277
278  if (isInMacroDef) {
279    // FIXME: For a macro definition make sure that all expansions
280    // of it expand to the same reference before allowing to point to it.
281    return CXChildVisit_Continue;
282  }
283
284  data->visitor.visit(data->visitor.context, cursor,
285                      cxloc::translateSourceRange(Ctx, Loc));
286  return CXChildVisit_Continue;
287}
288
289static void findMacroRefsInFile(CXTranslationUnit TU, CXCursor Cursor,
290                                const FileEntry *File,
291                                CXCursorAndRangeVisitor Visitor) {
292  if (Cursor.kind != CXCursor_MacroDefinition &&
293      Cursor.kind != CXCursor_MacroExpansion)
294    return;
295
296  ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData);
297  SourceManager &SM = Unit->getSourceManager();
298
299  FileID FID = SM.translateFile(File);
300  const IdentifierInfo *Macro = 0;
301  if (Cursor.kind == CXCursor_MacroDefinition)
302    Macro = getCursorMacroDefinition(Cursor)->getName();
303  else
304    Macro = getCursorMacroExpansion(Cursor)->getName();
305  if (!Macro)
306    return;
307
308  FindFileMacroRefVisitData data(*Unit, File, Macro, Visitor);
309
310  SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
311  CursorVisitor FindMacroRefsVisitor(TU,
312                                  findFileMacroRefVisit, &data,
313                                  /*VisitPreprocessorLast=*/false,
314                                  /*VisitIncludedEntities=*/false,
315                                  Range);
316  FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion();
317}
318
319
320//===----------------------------------------------------------------------===//
321// libclang public APIs.
322//===----------------------------------------------------------------------===//
323
324extern "C" {
325
326void clang_findReferencesInFile(CXCursor cursor, CXFile file,
327                                CXCursorAndRangeVisitor visitor) {
328  bool Logging = ::getenv("LIBCLANG_LOGGING");
329
330  if (clang_Cursor_isNull(cursor)) {
331    if (Logging)
332      llvm::errs() << "clang_findReferencesInFile: Null cursor\n";
333    return;
334  }
335  if (!file) {
336    if (Logging)
337      llvm::errs() << "clang_findReferencesInFile: Null file\n";
338    return;
339  }
340  if (!visitor.visit) {
341    if (Logging)
342      llvm::errs() << "clang_findReferencesInFile: Null visitor\n";
343    return;
344  }
345
346  ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor);
347  if (!CXXUnit)
348    return;
349
350  ASTUnit::ConcurrencyCheck Check(*CXXUnit);
351
352  if (cursor.kind == CXCursor_MacroDefinition ||
353      cursor.kind == CXCursor_MacroExpansion) {
354    findMacroRefsInFile(cxcursor::getCursorTU(cursor),
355                        cursor,
356                        static_cast<const FileEntry *>(file),
357                        visitor);
358    return;
359  }
360
361  // We are interested in semantics of identifiers so for C++ constructor exprs
362  // prefer type references, e.g.:
363  //
364  //  return MyStruct();
365  //
366  // for 'MyStruct' we'll have a cursor pointing at the constructor decl but
367  // we are actually interested in the type declaration.
368  cursor = cxcursor::getTypeRefCursor(cursor);
369
370  CXCursor refCursor = clang_getCursorReferenced(cursor);
371
372  if (!clang_isDeclaration(refCursor.kind)) {
373    if (Logging)
374      llvm::errs() << "clang_findReferencesInFile: cursor is not referencing a "
375                      "declaration\n";
376    return;
377  }
378
379  findIdRefsInFile(cxcursor::getCursorTU(cursor),
380                   refCursor,
381                   static_cast<const FileEntry *>(file),
382                   visitor);
383}
384
385static enum CXVisitorResult _visitCursorAndRange(void *context,
386                                                 CXCursor cursor,
387                                                 CXSourceRange range) {
388  CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context;
389  return INVOKE_BLOCK2(block, cursor, range);
390}
391
392void clang_findReferencesInFileWithBlock(CXCursor cursor,
393                                         CXFile file,
394                                         CXCursorAndRangeVisitorBlock block) {
395  CXCursorAndRangeVisitor visitor = { block,
396                                      block ? _visitCursorAndRange : 0 };
397  return clang_findReferencesInFile(cursor, file, visitor);
398}
399
400} // end: extern "C"
401
402