From 664b06f9bd2ea83443ddc8553feba8ba48880752 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Mon, 7 Jan 2013 19:16:25 +0000 Subject: [PATCH] [libclang] When getting the cursor for an identifier inside a macro definition, check if this was ever a macro name and return a specific CXCursor_MacroExpansion cursor in such a case, instead of the generic CXCursor_MacroDefinition. Checking for macro name makes sure the identifier is not part of the identifier list in a function macro. While, in general, resolving identifiers in macro definitions to other macros may not be completely accurate, it greatly improves functionality such as give-me-the-definition-of-this, which was not working at all inside macro definitions. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@171773 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/Index/c-index-getCursor-pp.c | 13 +++- tools/libclang/CIndex.cpp | 117 ++++++++++++++++++++++++++++-- tools/libclang/CIndexHigh.cpp | 4 +- tools/libclang/CIndexer.h | 28 +++++++ tools/libclang/CXCursor.cpp | 25 ++++++- tools/libclang/CXCursor.h | 49 ++++++++++++- 6 files changed, 222 insertions(+), 14 deletions(-) diff --git a/test/Index/c-index-getCursor-pp.c b/test/Index/c-index-getCursor-pp.c index 48d07adf22..8efaaf26c1 100644 --- a/test/Index/c-index-getCursor-pp.c +++ b/test/Index/c-index-getCursor-pp.c @@ -1,6 +1,6 @@ #define OBSCURE(X) X #define DECORATION - +#define FNM(X) OBSCURE(X) typedef int T; void OBSCURE(func)(int x) { OBSCURE(T) DECORATION value; @@ -23,6 +23,8 @@ const char *fname = __FILE__; #if defined(OBSCURE) #endif +#define C(A) A + // RUN: c-index-test -cursor-at=%s:1:11 -I%S/Inputs %s | FileCheck -check-prefix=CHECK-1 %s // CHECK-1: macro definition=OBSCURE // RUN: c-index-test -cursor-at=%s:2:14 -I%S/Inputs %s | FileCheck -check-prefix=CHECK-2 %s @@ -45,8 +47,17 @@ const char *fname = __FILE__; // CHECK-10: 20:8 macro expansion=OBSCURE // CHECK-10: 23:13 macro expansion=OBSCURE +// RUN: c-index-test -cursor-at=%s:3:20 -cursor-at=%s:12:14 \ +// RUN: -cursor-at=%s:26:11 -cursor-at=%s:26:14 -I%S/Inputs %s | FileCheck -check-prefix=CHECK-IN-MACRODEF %s +// CHECK-IN-MACRODEF: 3:16 macro expansion=OBSCURE +// CHECK-IN-MACRODEF: 12:14 macro expansion=A +// CHECK-IN-MACRODEF: 26:9 macro definition=C +// CHECK-IN-MACRODEF: 26:9 macro definition=C + // Same tests, but with "editing" optimizations // RUN: env CINDEXTEST_EDITING=1 c-index-test -cursor-at=%s:1:11 -I%S/Inputs %s | FileCheck -check-prefix=CHECK-1 %s // RUN: env CINDEXTEST_EDITING=1 c-index-test -cursor-at=%s:2:14 -I%S/Inputs %s | FileCheck -check-prefix=CHECK-2 %s // RUN: env CINDEXTEST_EDITING=1 c-index-test -cursor-at=%s:5:7 -I%S/Inputs %s | FileCheck -check-prefix=CHECK-3 %s // RUN: env CINDEXTEST_EDITING=1 c-index-test -cursor-at=%s:9:10 -I%S/Inputs %s | FileCheck -check-prefix=CHECK-6 %s +// RUN: env CINDEXTEST_EDITING=1 c-index-test -cursor-at=%s:3:20 -cursor-at=%s:12:14 \ +// RUN: -cursor-at=%s:26:11 -cursor-at=%s:26:14 -I%S/Inputs %s | FileCheck -check-prefix=CHECK-IN-MACRODEF %s diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 177b3a6d51..96c7c586fa 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -520,6 +520,19 @@ bool CursorVisitor::VisitChildren(CXCursor Cursor) { A->getInterfaceLoc(), TU)); } + // If pointing inside a macro definition, check if the token is an identifier + // that was ever defined as a macro. In such a case, create a "pseudo" macro + // expansion cursor for that token. + SourceLocation BeginLoc = RegionOfInterest.getBegin(); + if (Cursor.kind == CXCursor_MacroDefinition && + BeginLoc == RegionOfInterest.getEnd()) { + SourceLocation Loc = AU->mapLocationToPreamble(BeginLoc); + MacroInfo *MI = getMacroInfo(cxcursor::getCursorMacroDefinition(Cursor),TU); + if (MacroDefinition *MacroDef = + checkForMacroInMacroDefinition(MI, Loc, TU)) + return Visit(cxcursor::MakeMacroExpansionCursor(MacroDef, BeginLoc, TU)); + } + // Nothing to visit at the moment. return false; } @@ -3185,7 +3198,7 @@ CXString clang_getCursorSpelling(CXCursor C) { } if (C.kind == CXCursor_MacroExpansion) - return createCXString(getCursorMacroExpansion(C)->getName() + return createCXString(getCursorMacroExpansion(C).getName() ->getNameStart()); if (C.kind == CXCursor_MacroDefinition) @@ -3999,7 +4012,7 @@ CXSourceLocation clang_getCursorLocation(CXCursor C) { if (C.kind == CXCursor_MacroExpansion) { SourceLocation L - = cxcursor::getCursorMacroExpansion(C)->getSourceRange().getBegin(); + = cxcursor::getCursorMacroExpansion(C).getSourceRange().getBegin(); return cxloc::translateSourceLocation(getCursorContext(C), L); } @@ -4125,7 +4138,7 @@ static SourceRange getRawCursorExtent(CXCursor C) { if (C.kind == CXCursor_MacroExpansion) { ASTUnit *TU = getCursorASTUnit(C); - SourceRange Range = cxcursor::getCursorMacroExpansion(C)->getSourceRange(); + SourceRange Range = cxcursor::getCursorMacroExpansion(C).getSourceRange(); return TU->mapRangeFromPreamble(Range); } @@ -4265,7 +4278,7 @@ CXCursor clang_getCursorReferenced(CXCursor C) { } if (C.kind == CXCursor_MacroExpansion) { - if (MacroDefinition *Def = getCursorMacroExpansion(C)->getDefinition()) + if (MacroDefinition *Def = getCursorMacroExpansion(C).getDefinition()) return MakeMacroDefinitionCursor(Def, tu); } @@ -5265,7 +5278,7 @@ public: if (cursor.kind != CXCursor_MacroExpansion) return CXChildVisit_Continue; - SourceRange macroRange = getCursorMacroExpansion(cursor)->getSourceRange(); + SourceRange macroRange = getCursorMacroExpansion(cursor).getSourceRange(); if (macroRange.getBegin() == macroRange.getEnd()) return CXChildVisit_Continue; // it's not a function macro. @@ -6169,6 +6182,100 @@ void cxindex::printDiagsToStderr(ASTUnit *Unit) { #endif } +MacroInfo *cxindex::getMacroInfo(const IdentifierInfo &II, + SourceLocation MacroDefLoc, + CXTranslationUnit TU){ + if (MacroDefLoc.isInvalid() || !TU) + return 0; + + ASTUnit *Unit = static_cast(TU->TUData); + Preprocessor &PP = Unit->getPreprocessor(); + if (!II.hadMacroDefinition()) + return 0; + + MacroInfo *MI = PP.getMacroInfoHistory(const_cast(&II)); + while (MI) { + if (MacroDefLoc == MI->getDefinitionLoc()) + return MI; + MI = MI->getPreviousDefinition(); + } + + return 0; +} + +MacroInfo *cxindex::getMacroInfo(MacroDefinition *MacroDef, + CXTranslationUnit TU) { + if (!MacroDef || !TU) + return 0; + const IdentifierInfo *II = MacroDef->getName(); + if (!II) + return 0; + + return getMacroInfo(*II, MacroDef->getLocation(), TU); +} + +MacroDefinition *cxindex::checkForMacroInMacroDefinition(const MacroInfo *MI, + const Token &Tok, + CXTranslationUnit TU) { + if (!MI || !TU) + return 0; + if (Tok.isNot(tok::raw_identifier)) + return 0; + + if (MI->getNumTokens() == 0) + return 0; + SourceRange DefRange(MI->getReplacementToken(0).getLocation(), + MI->getDefinitionEndLoc()); + ASTUnit *Unit = static_cast(TU->TUData); + + // Check that the token is inside the definition and not its argument list. + SourceManager &SM = Unit->getSourceManager(); + if (SM.isBeforeInTranslationUnit(Tok.getLocation(), DefRange.getBegin())) + return 0; + if (SM.isBeforeInTranslationUnit(DefRange.getEnd(), Tok.getLocation())) + return 0; + + Preprocessor &PP = Unit->getPreprocessor(); + PreprocessingRecord *PPRec = PP.getPreprocessingRecord(); + if (!PPRec) + return 0; + + StringRef Name(Tok.getRawIdentifierData(), Tok.getLength()); + IdentifierInfo &II = PP.getIdentifierTable().get(Name); + if (!II.hadMacroDefinition()) + return 0; + + // Check that the identifier is not one of the macro arguments. + if (std::find(MI->arg_begin(), MI->arg_end(), &II) != MI->arg_end()) + return 0; + + MacroInfo *InnerMI = PP.getMacroInfoHistory(&II); + if (!InnerMI) + return 0; + + return PPRec->findMacroDefinition(InnerMI); +} + +MacroDefinition *cxindex::checkForMacroInMacroDefinition(const MacroInfo *MI, + SourceLocation Loc, + CXTranslationUnit TU) { + if (Loc.isInvalid() || !MI || !TU) + return 0; + + if (MI->getNumTokens() == 0) + return 0; + ASTUnit *Unit = static_cast(TU->TUData); + Preprocessor &PP = Unit->getPreprocessor(); + if (!PP.getPreprocessingRecord()) + return 0; + Loc = Unit->getSourceManager().getSpellingLoc(Loc); + Token Tok; + if (PP.getRawToken(Loc, Tok)) + return 0; + + return checkForMacroInMacroDefinition(MI, Tok, TU); +} + extern "C" { CXString clang_getClangVersion() { diff --git a/tools/libclang/CIndexHigh.cpp b/tools/libclang/CIndexHigh.cpp index 43f2024079..f4d6bc564e 100644 --- a/tools/libclang/CIndexHigh.cpp +++ b/tools/libclang/CIndexHigh.cpp @@ -266,7 +266,7 @@ static enum CXChildVisitResult findFileMacroRefVisit(CXCursor cursor, if (cursor.kind == CXCursor_MacroDefinition) Macro = getCursorMacroDefinition(cursor)->getName(); else if (cursor.kind == CXCursor_MacroExpansion) - Macro = getCursorMacroExpansion(cursor)->getName(); + Macro = getCursorMacroExpansion(cursor).getName(); if (!Macro) return CXChildVisit_Continue; @@ -317,7 +317,7 @@ static void findMacroRefsInFile(CXTranslationUnit TU, CXCursor Cursor, if (Cursor.kind == CXCursor_MacroDefinition) Macro = getCursorMacroDefinition(Cursor)->getName(); else - Macro = getCursorMacroExpansion(Cursor)->getName(); + Macro = getCursorMacroExpansion(Cursor).getName(); if (!Macro) return; diff --git a/tools/libclang/CIndexer.h b/tools/libclang/CIndexer.h index 5cc1bf648d..d34547dffd 100644 --- a/tools/libclang/CIndexer.h +++ b/tools/libclang/CIndexer.h @@ -26,6 +26,11 @@ namespace llvm { namespace clang { class ASTUnit; + class MacroInfo; + class MacroDefinition; + class SourceLocation; + class Token; + class IdentifierInfo; class CIndexer { bool OnlyLocalDecls; @@ -94,6 +99,29 @@ public: namespace cxindex { void printDiagsToStderr(ASTUnit *Unit); + + /// \brief If \c MacroDefLoc points at a macro definition with \c II as + /// its name, this retrieves its MacroInfo. + MacroInfo *getMacroInfo(const IdentifierInfo &II, + SourceLocation MacroDefLoc, + CXTranslationUnit TU); + + /// \brief Retrieves the corresponding MacroInfo of a MacroDefinition. + MacroInfo *getMacroInfo(MacroDefinition *MacroDef, CXTranslationUnit TU); + + /// \brief If \c Loc resides inside the definition of \c MI and it points at + /// an identifier that has ever been a macro name, this returns the latest + /// MacroDefinition for that name, otherwise it returns NULL. + MacroDefinition *checkForMacroInMacroDefinition(const MacroInfo *MI, + SourceLocation Loc, + CXTranslationUnit TU); + + /// \brief If \c Tok resides inside the definition of \c MI and it points at + /// an identifier that has ever been a macro name, this returns the latest + /// MacroDefinition for that name, otherwise it returns NULL. + MacroDefinition *checkForMacroInMacroDefinition(const MacroInfo *MI, + const Token &Tok, + CXTranslationUnit TU); } } diff --git a/tools/libclang/CXCursor.cpp b/tools/libclang/CXCursor.cpp index 6615e4a6f4..9508962122 100644 --- a/tools/libclang/CXCursor.cpp +++ b/tools/libclang/CXCursor.cpp @@ -677,9 +677,28 @@ CXCursor cxcursor::MakeMacroExpansionCursor(MacroExpansion *MI, return C; } -MacroExpansion *cxcursor::getCursorMacroExpansion(CXCursor C) { - assert(C.kind == CXCursor_MacroExpansion); - return static_cast(C.data[0]); +CXCursor cxcursor::MakeMacroExpansionCursor(MacroDefinition *MI, + SourceLocation Loc, + CXTranslationUnit TU) { + assert(Loc.isValid()); + CXCursor C = { CXCursor_MacroExpansion, 0, { MI, Loc.getPtrEncoding(), TU } }; + return C; +} + +const IdentifierInfo *cxcursor::MacroExpansionCursor::getName() const { + if (isPseudo()) + return getAsMacroDefinition()->getName(); + return getAsMacroExpansion()->getName(); +} +MacroDefinition *cxcursor::MacroExpansionCursor::getDefinition() const { + if (isPseudo()) + return getAsMacroDefinition(); + return getAsMacroExpansion()->getDefinition(); +} +SourceRange cxcursor::MacroExpansionCursor::getSourceRange() const { + if (isPseudo()) + return getPseudoLoc(); + return getAsMacroExpansion()->getSourceRange(); } CXCursor cxcursor::MakeInclusionDirectiveCursor(InclusionDirective *ID, diff --git a/tools/libclang/CXCursor.h b/tools/libclang/CXCursor.h index 120b881069..86fb975243 100644 --- a/tools/libclang/CXCursor.h +++ b/tools/libclang/CXCursor.h @@ -42,6 +42,7 @@ class TemplateDecl; class TemplateName; class TypeDecl; class VarDecl; +class IdentifierInfo; namespace cxcursor { @@ -153,9 +154,51 @@ MacroDefinition *getCursorMacroDefinition(CXCursor C); CXCursor MakeMacroExpansionCursor(MacroExpansion *, CXTranslationUnit TU); -/// \brief Unpack a given macro expansion cursor to retrieve its -/// source range. -MacroExpansion *getCursorMacroExpansion(CXCursor C); +/// \brief Create a "pseudo" macro expansion cursor, using a macro definition +/// and a source location. +CXCursor MakeMacroExpansionCursor(MacroDefinition *, SourceLocation Loc, + CXTranslationUnit TU); + +/// \brief Wraps a macro expansion cursor and provides a common interface +/// for a normal macro expansion cursor or a "pseudo" one. +/// +/// "Pseudo" macro expansion cursors (essentially a macro definition along with +/// a source location) are created in special cases, for example they can be +/// created for identifiers inside macro definitions, if these identifiers are +/// macro names. +class MacroExpansionCursor { + CXCursor C; + + bool isPseudo() const { + return C.data[1] != 0; + } + MacroDefinition *getAsMacroDefinition() const { + assert(isPseudo()); + return static_cast(C.data[0]); + } + MacroExpansion *getAsMacroExpansion() const { + assert(!isPseudo()); + return static_cast(C.data[0]); + } + SourceLocation getPseudoLoc() const { + assert(isPseudo()); + return SourceLocation::getFromPtrEncoding(C.data[1]); + } + +public: + MacroExpansionCursor(CXCursor C) : C(C) { + assert(C.kind == CXCursor_MacroExpansion); + } + + const IdentifierInfo *getName() const; + MacroDefinition *getDefinition() const; + SourceRange getSourceRange() const; +}; + +/// \brief Unpack a given macro expansion cursor to retrieve its info. +static inline MacroExpansionCursor getCursorMacroExpansion(CXCursor C) { + return C; +} /// \brief Create an inclusion directive cursor. CXCursor MakeInclusionDirectiveCursor(InclusionDirective *, -- 2.40.0