From: Douglas Gregor Date: Tue, 19 Jan 2010 00:34:46 +0000 (+0000) Subject: Implement clang_getCursorExtent, which provides a source range for the X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a7bde20f8c6334ccc3a7ef4dd77243d0921a8497;p=clang Implement clang_getCursorExtent, which provides a source range for the cursor itself. In particular, for references this returns the source range of the reference rather than the source range of the thing it refers to. Switch c-index-test from clang_getDeclExtent (which will eventually be deprecated and removed) over to clang_getCursorExtent. The source ranges we print for references now make sense; fix up the tests appropriately. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@93823 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index 80febb7b33..50ce14a15a 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -407,6 +407,17 @@ CINDEX_LINKAGE CXString clang_getCursorSpelling(CXCursor); */ CINDEX_LINKAGE CXSourceLocation clang_getCursorLocation(CXCursor); +/** \brief Retrieve the physical extent of the source construct referenced by + * the given cursor. + * + * The extent of a cursor starts with the file/line/column pointing at the + * first character within the source construct that the cursor refers to and + * ends with the last character withinin that source construct. For a + * declaration, the extent covers the declaration itself. For a reference, + * the extent covers the location of the reference (e.g., where the referenced + * entity was actually used). + */ +CINDEX_LINKAGE CXSourceRange clang_getCursorExtent(CXCursor); /* for debug/testing */ CINDEX_LINKAGE const char *clang_getCursorKindSpelling(enum CXCursorKind Kind); diff --git a/test/Index/c-index-api-loadTU-test.m b/test/Index/c-index-api-loadTU-test.m index 9d126c8a97..db7f09866f 100644 --- a/test/Index/c-index-api-loadTU-test.m +++ b/test/Index/c-index-api-loadTU-test.m @@ -57,19 +57,19 @@ int main (int argc, const char * argv[]) { // CHECK: c-index-api-loadTU-test.m:8:1: ObjCInstanceMethodDecl=foo:8:1 [Extent=8:1:8:6] // CHECK: c-index-api-loadTU-test.m:9:1: ObjCClassMethodDecl=fooC:9:1 [Extent=9:1:9:7] // CHECK: c-index-api-loadTU-test.m:13:12: ObjCInterfaceDecl=Bar:13:1 [Extent=13:1:17:4] -// CHECK: c-index-api-loadTU-test.m:13:18: ObjCSuperClassRef=Foo:4:1 [Extent=4:1:11:4] +// CHECK: c-index-api-loadTU-test.m:13:18: ObjCSuperClassRef=Foo:4:1 [Extent=13:18:13:20] // CHECK: c-index-api-loadTU-test.m:19:12: ObjCCategoryDecl=FooCat:19:12 [Extent=19:1:22:4] -// CHECK: c-index-api-loadTU-test.m:19:12: ObjCClassRef=Foo:4:1 [Extent=4:1:11:4] +// CHECK: c-index-api-loadTU-test.m:19:12: ObjCClassRef=Foo:4:1 [Extent=19:12:19:14] // CHECK: c-index-api-loadTU-test.m:20:1: ObjCInstanceMethodDecl=catMethodWithFloat::20:1 [Extent=20:1:20:40] // CHECK: c-index-api-loadTU-test.m:21:1: ObjCInstanceMethodDecl=floatMethod:21:1 [Extent=21:1:21:22] // CHECK: c-index-api-loadTU-test.m:24:1: ObjCProtocolDecl=Proto:24:1 [Extent=24:1:26:4] // CHECK: c-index-api-loadTU-test.m:25:1: ObjCInstanceMethodDecl=pMethod:25:1 [Extent=25:1:25:10] // CHECK: c-index-api-loadTU-test.m:28:1: ObjCProtocolDecl=SubP:28:1 [Extent=28:1:30:4] -// CHECK: c-index-api-loadTU-test.m:28:17: ObjCProtocolRef=Proto:24:1 [Extent=24:1:26:4] +// CHECK: c-index-api-loadTU-test.m:28:17: ObjCProtocolRef=Proto:24:1 [Extent=28:17:28:21] // CHECK: c-index-api-loadTU-test.m:29:1: ObjCInstanceMethodDecl=spMethod:29:1 [Extent=29:1:29:11] // CHECK: c-index-api-loadTU-test.m:32:12: ObjCInterfaceDecl=Baz:32:1 [Extent=32:1:39:4] -// CHECK: c-index-api-loadTU-test.m:32:18: ObjCSuperClassRef=Bar:13:1 [Extent=13:1:17:4] -// CHECK: c-index-api-loadTU-test.m:32:23: ObjCProtocolRef=SubP:28:1 [Extent=28:1:30:4] +// CHECK: c-index-api-loadTU-test.m:32:18: ObjCSuperClassRef=Bar:13:1 [Extent=32:18:32:20] +// CHECK: c-index-api-loadTU-test.m:32:23: ObjCProtocolRef=SubP:28:1 [Extent=32:23:32:26] // CHECK: c-index-api-loadTU-test.m:34:9: ObjCIvarDecl=_anIVar:34:9 [Extent=34:9:34:15] // CHECK: c-index-api-loadTU-test.m:37:1: ObjCInstanceMethodDecl=bazMethod:37:1 [Extent=37:1:37:20] // CHECK: c-index-api-loadTU-test.m:41:1: EnumDecl=:41:1 [Extent=41:1:43:1] diff --git a/tools/CIndex/CIndex.cpp b/tools/CIndex/CIndex.cpp index e48581ac0a..9e7408375f 100644 --- a/tools/CIndex/CIndex.cpp +++ b/tools/CIndex/CIndex.cpp @@ -130,6 +130,55 @@ static CXSourceLocation translateSourceLocation(SourceManager &SourceMgr, return Result; } +/// \brief Translate a Clang source range into a CIndex source range. +static CXSourceRange translateSourceRange(ASTContext &Context, + SourceRange R) { + if (R.isInvalid()) { + CXSourceRange extent = { { 0, 0, 0 }, { 0, 0, 0 } }; + return extent; + } + + // FIXME: This is largely copy-paste from + ///TextDiagnosticPrinter::HighlightRange. When it is clear that this is + // what we want the two routines should be refactored. + + SourceManager &SM = Context.getSourceManager(); + SourceLocation Begin = SM.getInstantiationLoc(R.getBegin()); + SourceLocation End = SM.getInstantiationLoc(R.getEnd()); + + // If the End location and the start location are the same and are a macro + // location, then the range was something that came from a macro expansion + // or _Pragma. If this is an object-like macro, the best we can do is to + // get the range. If this is a function-like macro, we'd also like to + // get the arguments. + if (Begin == End && R.getEnd().isMacroID()) + End = SM.getInstantiationRange(R.getEnd()).second; + + unsigned StartLineNo = SM.getInstantiationLineNumber(Begin); + unsigned EndLineNo = SM.getInstantiationLineNumber(End); + + // Compute the column number of the start. Keep the column based at 1. + unsigned StartColNo = SM.getInstantiationColumnNumber(Begin); + + // Compute the column number of the end. + unsigned EndColNo = SM.getInstantiationColumnNumber(End); + if (EndColNo) { + // Offset the end column by 1 so that we point to the last character + // in the last token. + --EndColNo; + + // Add in the length of the token, so that we cover multi-char tokens. + EndColNo += Lexer::MeasureTokenLength(End, SM, Context.getLangOptions()); + } + + // Package up the line/column data and return to the caller. + const FileEntry *BeginFile = SM.getFileEntryForID(SM.getFileID(Begin)); + const FileEntry *EndFile = SM.getFileEntryForID(SM.getFileID(End)); + CXSourceRange extent = { { (void *)BeginFile, StartLineNo, StartColNo }, + { (void *)EndFile, EndLineNo, EndColNo } }; + return extent; +} + //===----------------------------------------------------------------------===// // Visitors. //===----------------------------------------------------------------------===// @@ -609,57 +658,7 @@ unsigned clang_getDeclColumn(CXDecl AnonDecl) { } CXSourceRange clang_getDeclExtent(CXDecl AnonDecl) { - assert(AnonDecl && "Passed null CXDecl"); - NamedDecl *ND = static_cast(AnonDecl); - SourceManager &SM = ND->getASTContext().getSourceManager(); - SourceRange R = ND->getSourceRange(); - - SourceLocation Begin = SM.getInstantiationLoc(R.getBegin()); - SourceLocation End = SM.getInstantiationLoc(R.getEnd()); - - if (!Begin.isValid()) { - CXSourceRange extent = { { 0, 0, 0 }, { 0, 0, 0 } }; - return extent; - } - - // FIXME: This is largely copy-paste from - ///TextDiagnosticPrinter::HighlightRange. When it is clear that this is - // what we want the two routines should be refactored. - - // If the End location and the start location are the same and are a macro - // location, then the range was something that came from a macro expansion - // or _Pragma. If this is an object-like macro, the best we can do is to - // get the range. If this is a function-like macro, we'd also like to - // get the arguments. - if (Begin == End && R.getEnd().isMacroID()) - End = SM.getInstantiationRange(R.getEnd()).second; - - assert(SM.getFileID(Begin) == SM.getFileID(End)); - unsigned StartLineNo = SM.getInstantiationLineNumber(Begin); - unsigned EndLineNo = SM.getInstantiationLineNumber(End); - - // Compute the column number of the start. Keep the column based at 1. - unsigned StartColNo = SM.getInstantiationColumnNumber(Begin); - - // Compute the column number of the end. - unsigned EndColNo = SM.getInstantiationColumnNumber(End); - if (EndColNo) { - // Offset the end column by 1 so that we point to the last character - // in the last token. - --EndColNo; - - // Add in the length of the token, so that we cover multi-char tokens. - ASTContext &Ctx = ND->getTranslationUnitDecl()->getASTContext(); - const LangOptions &LOpts = Ctx.getLangOptions(); - - EndColNo += Lexer::MeasureTokenLength(End, SM, LOpts); - } - - // Package up the line/column data and return to the caller. - const FileEntry *FEntry = SM.getFileEntryForID(SM.getFileID(Begin)); - CXSourceRange extent = { { (void *)FEntry, StartLineNo, StartColNo }, - { (void *)FEntry, EndLineNo, EndColNo } }; - return extent; + return clang_getCursorExtent(clang_getCursorFromDecl(AnonDecl)); } const char *clang_getDeclSource(CXDecl AnonDecl) { @@ -961,6 +960,52 @@ CXSourceLocation clang_getCursorLocation(CXCursor C) { Loc = Class->getClassLoc(); return translateSourceLocation(SM, Loc); } + +CXSourceRange clang_getCursorExtent(CXCursor C) { + if (clang_isReference(C.kind)) { + switch (C.kind) { + case CXCursor_ObjCSuperClassRef: { + std::pair P + = getCursorObjCSuperClassRef(C); + return translateSourceRange(P.first->getASTContext(), P.second); + } + + case CXCursor_ObjCProtocolRef: { + std::pair P + = getCursorObjCProtocolRef(C); + return translateSourceRange(P.first->getASTContext(), P.second); + } + + case CXCursor_ObjCClassRef: { + std::pair P + = getCursorObjCClassRef(C); + + return translateSourceRange(P.first->getASTContext(), P.second); + } + + case CXCursor_ObjCSelectorRef: + case CXCursor_ObjCIvarRef: + case CXCursor_VarRef: + case CXCursor_FunctionRef: + case CXCursor_EnumConstantRef: + case CXCursor_MemberRef: + return translateSourceRange(getCursorContext(C), + getCursorExpr(C)->getSourceRange()); + + default: + // FIXME: Need a way to enumerate all non-reference cases. + llvm_unreachable("Missed a reference kind"); + } + } + + if (!getCursorDecl(C)) { + CXSourceRange empty = { { 0, 0, 0 }, { 0, 0, 0 } }; + return empty; + } + + Decl *D = getCursorDecl(C); + return translateSourceRange(D->getASTContext(), D->getSourceRange()); +} void clang_getDefinitionSpellingAndExtent(CXCursor C, const char **startBuf, diff --git a/tools/CIndex/CIndex.exports b/tools/CIndex/CIndex.exports index e3985c1a01..0272f37e50 100644 --- a/tools/CIndex/CIndex.exports +++ b/tools/CIndex/CIndex.exports @@ -13,6 +13,7 @@ _clang_getCompletionChunkKind _clang_getCompletionChunkText _clang_getCursor _clang_getCursorDecl +_clang_getCursorExtent _clang_getCursorFromDecl _clang_getCursorKind _clang_getCursorKindSpelling diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c index a2d4fe9808..2f113cfada 100644 --- a/tools/c-index-test/c-index-test.c +++ b/tools/c-index-test/c-index-test.c @@ -73,11 +73,11 @@ static const char* GetCursorSource(CXCursor Cursor) { static const char *FileCheckPrefix = "CHECK"; -static void PrintDeclExtent(CXDecl Dcl) { - CXSourceRange extent; - if (!Dcl) +static void PrintCursorExtent(CXCursor C) { + CXSourceRange extent = clang_getCursorExtent(C); + /* FIXME: Better way to check for empty extents? */ + if (!extent.begin.file) return; - extent = clang_getDeclExtent(Dcl); printf(" [Extent=%d:%d:%d:%d]", extent.begin.line, extent.begin.column, extent.end.line, extent.end.column); } @@ -89,8 +89,8 @@ static void DeclVisitor(CXDecl Dcl, CXCursor Cursor, CXClientData Filter) { if (!source) source = ""; printf("// %s: %s:%d:%d: ", FileCheckPrefix, source, Loc.line, Loc.column); - PrintCursor(Cursor); - PrintDeclExtent(clang_getCursorDecl(Cursor)); + PrintCursor(Cursor); + PrintCursorExtent(Cursor); printf("\n"); } @@ -111,7 +111,7 @@ static void TranslationUnitVisitor(CXTranslationUnit Unit, CXCursor Cursor, return; } - PrintDeclExtent(D); + PrintCursorExtent(Cursor); printf("\n"); clang_loadDeclaration(D, DeclVisitor, 0); } @@ -173,7 +173,7 @@ static void USRDeclVisitor(CXDecl D, CXCursor C, CXClientData Filter) { return; } printf("// %s: %s %s", FileCheckPrefix, GetCursorSource(C), USR.Spelling); - PrintDeclExtent(clang_getCursorDecl(C)); + PrintCursorExtent(C); printf("\n"); clang_disposeString(USR); }