Skip to content

Commit b3ae2bc

Browse files
author
Ivan Donchevskii
committedAug 23, 2018
[libclang] Fix cursors for arguments of Subscript and Call operators
The DeclRefExpr of CXXOperatorCallExpr refering to the custom operator is visited before the arguments to the operator call. For the Call and Subscript operator the range of this DeclRefExpr includes the whole call expression, so that all tokens in that range were mapped to the operator function, even the tokens of the arguments. Fix this by ensuring that this particular DeclRefExpr is visited last. Fixes PR25775. Fix by Nikolai Kosjar. Differential Revision: https://reviews.llvm.org/D40481 llvm-svn: 340521
1 parent 0dfbf6b commit b3ae2bc

File tree

2 files changed

+215
-1
lines changed

2 files changed

+215
-1
lines changed
 

Diff for: ‎clang/test/Index/annotate-operator-call-expr.cpp

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
struct Foo {
2+
int operator[](int key);
3+
int operator()(int key = 2);
4+
};
5+
6+
void testFoo(Foo foo, int index) {
7+
foo();
8+
foo(index);
9+
10+
foo[index];
11+
foo[index + index];
12+
13+
foo[foo[index]];
14+
foo[foo() + foo[index]];
15+
foo[foo(index) + foo[index]];
16+
}
17+
18+
// RUN: c-index-test -test-annotate-tokens=%s:7:1:7:100 %s -std=c++11 -Wno-unused-value | FileCheck %s -check-prefix=CHECK1
19+
// CHECK1: Identifier: "foo" [7:3 - 7:6] DeclRefExpr=foo:6:18
20+
// CHECK1: Punctuation: "(" [7:6 - 7:7] DeclRefExpr=operator():3:7 RefName=[7:6 - 7:7] RefName=[7:7 - 7:8]
21+
// CHECK1: Punctuation: ")" [7:7 - 7:8] DeclRefExpr=operator():3:7 RefName=[7:6 - 7:7] RefName=[7:7 - 7:8]
22+
// CHECK1: Punctuation: ";" [7:8 - 7:9] CompoundStmt=
23+
24+
// RUN: c-index-test -test-annotate-tokens=%s:8:1:8:100 %s -std=c++11 -Wno-unused-value | FileCheck %s -check-prefix=CHECK2
25+
// CHECK2: Punctuation: "(" [8:6 - 8:7] DeclRefExpr=operator():3:7 RefName=[8:6 - 8:7] RefName=[8:12 - 8:13]
26+
// CHECK2: Identifier: "index" [8:7 - 8:12] DeclRefExpr=index:6:27
27+
// CHECK2: Punctuation: ")" [8:12 - 8:13] DeclRefExpr=operator():3:7 RefName=[8:6 - 8:7] RefName=[8:12 - 8:13]
28+
// CHECK2: Punctuation: ";" [8:13 - 8:14] CompoundStmt=
29+
30+
// RUN: c-index-test -test-annotate-tokens=%s:10:1:10:100 %s -std=c++11 -Wno-unused-value | FileCheck %s -check-prefix=CHECK3
31+
// CHECK3: Identifier: "foo" [10:3 - 10:6] DeclRefExpr=foo:6:18
32+
// CHECK3: Punctuation: "[" [10:6 - 10:7] DeclRefExpr=operator[]:2:7 RefName=[10:6 - 10:7] RefName=[10:12 - 10:13]
33+
// CHECK3: Identifier: "index" [10:7 - 10:12] DeclRefExpr=index:6:27
34+
// CHECK3: Punctuation: "]" [10:12 - 10:13] DeclRefExpr=operator[]:2:7 RefName=[10:6 - 10:7] RefName=[10:12 - 10:13]
35+
// CHECK3: Punctuation: ";" [10:13 - 10:14] CompoundStmt=
36+
37+
// RUN: c-index-test -test-annotate-tokens=%s:11:1:11:100 %s -std=c++11 -Wno-unused-value | FileCheck %s -check-prefix=CHECK4
38+
// CHECK4: Identifier: "foo" [11:3 - 11:6] DeclRefExpr=foo:6:18
39+
// CHECK4: Punctuation: "[" [11:6 - 11:7] DeclRefExpr=operator[]:2:7 RefName=[11:6 - 11:7] RefName=[11:20 - 11:21]
40+
// CHECK4: Identifier: "index" [11:7 - 11:12] DeclRefExpr=index:6:27
41+
// CHECK4: Punctuation: "+" [11:13 - 11:14] BinaryOperator=
42+
// CHECK4: Identifier: "index" [11:15 - 11:20] DeclRefExpr=index:6:27
43+
// CHECK4: Punctuation: "]" [11:20 - 11:21] DeclRefExpr=operator[]:2:7 RefName=[11:6 - 11:7] RefName=[11:20 - 11:21]
44+
// CHECK4: Punctuation: ";" [11:21 - 11:22] CompoundStmt=
45+
46+
// RUN: c-index-test -test-annotate-tokens=%s:13:1:13:100 %s -std=c++11 -Wno-unused-value | FileCheck %s -check-prefix=CHECK5
47+
// CHECK5: Identifier: "foo" [13:3 - 13:6] DeclRefExpr=foo:6:18
48+
// CHECK5: Punctuation: "[" [13:6 - 13:7] DeclRefExpr=operator[]:2:7 RefName=[13:6 - 13:7] RefName=[13:17 - 13:18]
49+
// CHECK5: Identifier: "foo" [13:7 - 13:10] DeclRefExpr=foo:6:18
50+
// CHECK5: Punctuation: "[" [13:10 - 13:11] DeclRefExpr=operator[]:2:7 RefName=[13:10 - 13:11] RefName=[13:16 - 13:17]
51+
// CHECK5: Identifier: "index" [13:11 - 13:16] DeclRefExpr=index:6:27
52+
// CHECK5: Punctuation: "]" [13:16 - 13:17] DeclRefExpr=operator[]:2:7 RefName=[13:10 - 13:11] RefName=[13:16 - 13:17]
53+
// CHECK5: Punctuation: "]" [13:17 - 13:18] DeclRefExpr=operator[]:2:7 RefName=[13:6 - 13:7] RefName=[13:17 - 13:18]
54+
// CHECK5: Punctuation: ";" [13:18 - 13:19] CompoundStmt=
55+
56+
// RUN: c-index-test -test-annotate-tokens=%s:14:1:14:100 %s -std=c++11 -Wno-unused-value | FileCheck %s -check-prefix=CHECK6
57+
// CHECK6: Identifier: "foo" [14:3 - 14:6] DeclRefExpr=foo:6:18
58+
// CHECK6: Punctuation: "[" [14:6 - 14:7] DeclRefExpr=operator[]:2:7 RefName=[14:6 - 14:7] RefName=[14:25 - 14:26]
59+
// CHECK6: Identifier: "foo" [14:7 - 14:10] DeclRefExpr=foo:6:18
60+
// CHECK6: Punctuation: "(" [14:10 - 14:11] DeclRefExpr=operator():3:7 RefName=[14:10 - 14:11] RefName=[14:11 - 14:12]
61+
// CHECK6: Punctuation: ")" [14:11 - 14:12] DeclRefExpr=operator():3:7 RefName=[14:10 - 14:11] RefName=[14:11 - 14:12]
62+
// CHECK6: Punctuation: "+" [14:13 - 14:14] BinaryOperator=
63+
// CHECK6: Identifier: "foo" [14:15 - 14:18] DeclRefExpr=foo:6:18
64+
// CHECK6: Punctuation: "[" [14:18 - 14:19] DeclRefExpr=operator[]:2:7 RefName=[14:18 - 14:19] RefName=[14:24 - 14:25]
65+
// CHECK6: Identifier: "index" [14:19 - 14:24] DeclRefExpr=operator[]:2:7 RefName=[14:6 - 14:7] RefName=[14:25 - 14:26]
66+
// CHECK6: Punctuation: "]" [14:24 - 14:25] DeclRefExpr=operator[]:2:7 RefName=[14:18 - 14:19] RefName=[14:24 - 14:25]
67+
// CHECK6: Punctuation: "]" [14:25 - 14:26] DeclRefExpr=operator[]:2:7 RefName=[14:6 - 14:7] RefName=[14:25 - 14:26]
68+
// CHECK6: Punctuation: ";" [14:26 - 14:27] CompoundStmt=
69+
70+
// RUN: c-index-test -test-annotate-tokens=%s:15:1:15:100 %s -std=c++11 -Wno-unused-value | FileCheck %s -check-prefix=CHECK7
71+
// CHECK7: Identifier: "foo" [15:3 - 15:6] DeclRefExpr=foo:6:18
72+
// CHECK7: Punctuation: "[" [15:6 - 15:7] DeclRefExpr=operator[]:2:7 RefName=[15:6 - 15:7] RefName=[15:30 - 15:31]
73+
// CHECK7: Identifier: "foo" [15:7 - 15:10] DeclRefExpr=foo:6:18
74+
// CHECK7: Punctuation: "(" [15:10 - 15:11] DeclRefExpr=operator():3:7 RefName=[15:10 - 15:11] RefName=[15:16 - 15:17]
75+
// CHECK7: Identifier: "index" [15:11 - 15:16] DeclRefExpr=index:6:27
76+
// CHECK7: Punctuation: ")" [15:16 - 15:17] DeclRefExpr=operator():3:7 RefName=[15:10 - 15:11] RefName=[15:16 - 15:17]
77+
// CHECK7: Punctuation: "+" [15:18 - 15:19] BinaryOperator=
78+
// CHECK7: Identifier: "foo" [15:20 - 15:23] DeclRefExpr=foo:6:18
79+
// CHECK7: Punctuation: "[" [15:23 - 15:24] DeclRefExpr=operator[]:2:7 RefName=[15:23 - 15:24] RefName=[15:29 - 15:30]
80+
// CHECK7: Identifier: "index" [15:24 - 15:29] DeclRefExpr=index:6:27
81+
// CHECK7: Punctuation: "]" [15:29 - 15:30] DeclRefExpr=operator[]:2:7 RefName=[15:23 - 15:24] RefName=[15:29 - 15:30]
82+
// CHECK7: Punctuation: "]" [15:30 - 15:31] DeclRefExpr=operator[]:2:7 RefName=[15:6 - 15:7] RefName=[15:30 - 15:31]
83+
// CHECK7: Punctuation: ";" [15:31 - 15:32] CompoundStmt=
84+

Diff for: ‎clang/tools/libclang/CIndex.cpp

+131-1
Original file line numberDiff line numberDiff line change
@@ -6794,11 +6794,18 @@ class AnnotateTokensWorker {
67946794
SourceManager &SrcMgr;
67956795
bool HasContextSensitiveKeywords;
67966796

6797+
struct PostChildrenAction {
6798+
CXCursor cursor;
6799+
enum Action { Invalid, Ignore, Postpone } action;
6800+
};
6801+
using PostChildrenActions = SmallVector<PostChildrenAction, 0>;
6802+
67976803
struct PostChildrenInfo {
67986804
CXCursor Cursor;
67996805
SourceRange CursorRange;
68006806
unsigned BeforeReachingCursorIdx;
68016807
unsigned BeforeChildrenTokenIdx;
6808+
PostChildrenActions ChildActions;
68026809
};
68036810
SmallVector<PostChildrenInfo, 8> PostChildrenInfos;
68046811

@@ -6844,7 +6851,13 @@ class AnnotateTokensWorker {
68446851

68456852
void VisitChildren(CXCursor C) { AnnotateVis.VisitChildren(C); }
68466853
enum CXChildVisitResult Visit(CXCursor cursor, CXCursor parent);
6854+
bool IsIgnoredChildCursor(CXCursor cursor) const;
6855+
PostChildrenActions DetermineChildActions(CXCursor Cursor) const;
6856+
68476857
bool postVisitChildren(CXCursor cursor);
6858+
void HandlePostPonedChildCursors(const PostChildrenInfo &Info);
6859+
void HandlePostPonedChildCursor(CXCursor Cursor, unsigned StartTokenIndex);
6860+
68486861
void AnnotateTokens();
68496862

68506863
/// Determine whether the annotator saw any cursors that have
@@ -6865,6 +6878,67 @@ void AnnotateTokensWorker::AnnotateTokens() {
68656878
AnnotateVis.visitFileRegion();
68666879
}
68676880

6881+
bool AnnotateTokensWorker::IsIgnoredChildCursor(CXCursor cursor) const {
6882+
if (PostChildrenInfos.empty())
6883+
return false;
6884+
6885+
for (const auto &ChildAction : PostChildrenInfos.back().ChildActions) {
6886+
if (ChildAction.cursor == cursor &&
6887+
ChildAction.action == PostChildrenAction::Ignore) {
6888+
return true;
6889+
}
6890+
}
6891+
6892+
return false;
6893+
}
6894+
6895+
const CXXOperatorCallExpr *GetSubscriptOrCallOperator(CXCursor Cursor) {
6896+
if (!clang_isExpression(Cursor.kind))
6897+
return nullptr;
6898+
6899+
const Expr *E = getCursorExpr(Cursor);
6900+
if (const auto *OCE = dyn_cast<CXXOperatorCallExpr>(E)) {
6901+
const OverloadedOperatorKind Kind = OCE->getOperator();
6902+
if (Kind == OO_Call || Kind == OO_Subscript)
6903+
return OCE;
6904+
}
6905+
6906+
return nullptr;
6907+
}
6908+
6909+
AnnotateTokensWorker::PostChildrenActions
6910+
AnnotateTokensWorker::DetermineChildActions(CXCursor Cursor) const {
6911+
PostChildrenActions actions;
6912+
6913+
// The DeclRefExpr of CXXOperatorCallExpr refering to the custom operator is
6914+
// visited before the arguments to the operator call. For the Call and
6915+
// Subscript operator the range of this DeclRefExpr includes the whole call
6916+
// expression, so that all tokens in that range would be mapped to the
6917+
// operator function, including the tokens of the arguments. To avoid that,
6918+
// ensure to visit this DeclRefExpr as last node.
6919+
if (const auto *OCE = GetSubscriptOrCallOperator(Cursor)) {
6920+
const Expr *Callee = OCE->getCallee();
6921+
if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Callee)) {
6922+
const Expr *SubExpr = ICE->getSubExpr();
6923+
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(SubExpr)) {
6924+
const Decl *parentDecl = getCursorParentDecl(Cursor);
6925+
CXTranslationUnit TU = clang_Cursor_getTranslationUnit(Cursor);
6926+
6927+
// Visit the DeclRefExpr as last.
6928+
CXCursor cxChild = MakeCXCursor(DRE, parentDecl, TU);
6929+
actions.push_back({cxChild, PostChildrenAction::Postpone});
6930+
6931+
// The parent of the DeclRefExpr, an ImplicitCastExpr, has an equally
6932+
// wide range as the DeclRefExpr. We can skip visiting this entirely.
6933+
cxChild = MakeCXCursor(ICE, parentDecl, TU);
6934+
actions.push_back({cxChild, PostChildrenAction::Ignore});
6935+
}
6936+
}
6937+
}
6938+
6939+
return actions;
6940+
}
6941+
68686942
static inline void updateCursorAnnotation(CXCursor &Cursor,
68696943
const CXCursor &updateC) {
68706944
if (clang_isInvalid(updateC.kind) || !clang_isInvalid(Cursor.kind))
@@ -6941,7 +7015,10 @@ AnnotateTokensWorker::Visit(CXCursor cursor, CXCursor parent) {
69417015
SourceRange cursorRange = getRawCursorExtent(cursor);
69427016
if (cursorRange.isInvalid())
69437017
return CXChildVisit_Recurse;
6944-
7018+
7019+
if (IsIgnoredChildCursor(cursor))
7020+
return CXChildVisit_Continue;
7021+
69457022
if (!HasContextSensitiveKeywords) {
69467023
// Objective-C properties can have context-sensitive keywords.
69477024
if (cursor.kind == CXCursor_ObjCPropertyDecl) {
@@ -7089,6 +7166,7 @@ AnnotateTokensWorker::Visit(CXCursor cursor, CXCursor parent) {
70897166
Info.CursorRange = cursorRange;
70907167
Info.BeforeReachingCursorIdx = BeforeReachingCursorIdx;
70917168
Info.BeforeChildrenTokenIdx = NextToken();
7169+
Info.ChildActions = DetermineChildActions(cursor);
70927170
PostChildrenInfos.push_back(Info);
70937171

70947172
return CXChildVisit_Recurse;
@@ -7101,6 +7179,8 @@ bool AnnotateTokensWorker::postVisitChildren(CXCursor cursor) {
71017179
if (!clang_equalCursors(Info.Cursor, cursor))
71027180
return false;
71037181

7182+
HandlePostPonedChildCursors(Info);
7183+
71047184
const unsigned BeforeChildren = Info.BeforeChildrenTokenIdx;
71057185
const unsigned AfterChildren = NextToken();
71067186
SourceRange cursorRange = Info.CursorRange;
@@ -7127,6 +7207,56 @@ bool AnnotateTokensWorker::postVisitChildren(CXCursor cursor) {
71277207
return false;
71287208
}
71297209

7210+
void AnnotateTokensWorker::HandlePostPonedChildCursors(
7211+
const PostChildrenInfo &Info) {
7212+
for (const auto &ChildAction : Info.ChildActions) {
7213+
if (ChildAction.action == PostChildrenAction::Postpone) {
7214+
HandlePostPonedChildCursor(ChildAction.cursor,
7215+
Info.BeforeChildrenTokenIdx);
7216+
}
7217+
}
7218+
}
7219+
7220+
void AnnotateTokensWorker::HandlePostPonedChildCursor(
7221+
CXCursor Cursor, unsigned StartTokenIndex) {
7222+
const auto flags = CXNameRange_WantQualifier | CXNameRange_WantQualifier;
7223+
unsigned I = StartTokenIndex;
7224+
7225+
// The bracket tokens of a Call or Subscript operator are mapped to
7226+
// CallExpr/CXXOperatorCallExpr because we skipped visiting the corresponding
7227+
// DeclRefExpr. Remap these tokens to the DeclRefExpr cursors.
7228+
for (unsigned RefNameRangeNr = 0; I < NumTokens; RefNameRangeNr++) {
7229+
const CXSourceRange CXRefNameRange =
7230+
clang_getCursorReferenceNameRange(Cursor, flags, RefNameRangeNr);
7231+
if (clang_Range_isNull(CXRefNameRange))
7232+
break; // All ranges handled.
7233+
7234+
SourceRange RefNameRange = cxloc::translateCXSourceRange(CXRefNameRange);
7235+
while (I < NumTokens) {
7236+
const SourceLocation TokenLocation = GetTokenLoc(I);
7237+
if (!TokenLocation.isValid())
7238+
break;
7239+
7240+
// Adapt the end range, because LocationCompare() reports
7241+
// RangeOverlap even for the not-inclusive end location.
7242+
const SourceLocation fixedEnd =
7243+
RefNameRange.getEnd().getLocWithOffset(-1);
7244+
RefNameRange = SourceRange(RefNameRange.getBegin(), fixedEnd);
7245+
7246+
const RangeComparisonResult ComparisonResult =
7247+
LocationCompare(SrcMgr, TokenLocation, RefNameRange);
7248+
7249+
if (ComparisonResult == RangeOverlap) {
7250+
Cursors[I++] = Cursor;
7251+
} else if (ComparisonResult == RangeBefore) {
7252+
++I; // Not relevant token, check next one.
7253+
} else if (ComparisonResult == RangeAfter) {
7254+
break; // All tokens updated for current range, check next.
7255+
}
7256+
}
7257+
}
7258+
}
7259+
71307260
static enum CXChildVisitResult AnnotateTokensVisitor(CXCursor cursor,
71317261
CXCursor parent,
71327262
CXClientData client_data) {

0 commit comments

Comments
 (0)
Please sign in to comment.