CIndexHigh.cpp 17 KB

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