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