From: Douglas Gregor Date: Wed, 15 Feb 2012 00:54:55 +0000 (+0000) Subject: Implement indexing support for lambdas in libclang (both kinds), as X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=011d8b93b7cfa8492b8a9c909a850d6577e08dca;p=clang Implement indexing support for lambdas in libclang (both kinds), as well as improving the RecursiveASTVisitor's walk of lambda expressions. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@150549 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index 0c6d6d1c76..018a316c89 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -1500,7 +1500,13 @@ enum CXCursorKind { */ CXCursor_OverloadedDeclRef = 49, - CXCursor_LastRef = CXCursor_OverloadedDeclRef, + /** + * \brief A reference to a variable that occurs in some non-expression + * context, e.g., a C++ lambda capture list. + */ + CXCursor_VariableRef = 50, + + CXCursor_LastRef = CXCursor_VariableRef, /* Error conditions */ CXCursor_FirstInvalid = 70, @@ -1746,7 +1752,21 @@ enum CXCursorKind { */ CXCursor_SizeOfPackExpr = 143, - CXCursor_LastExpr = CXCursor_SizeOfPackExpr, + /* \brief Represents a C++ lambda expression that produces a local function + * object. + * + * \code + * void abssort(float *x, unsigned N) { + * std::sort(x, x + N, + * [](float a, float b) { + * return std::abs(a) < std::abs(b); + * }); + * } + * \endcode + */ + CXCursor_LambdaExpr = 144, + + CXCursor_LastExpr = CXCursor_LambdaExpr, /* Statements */ CXCursor_FirstStmt = 200, diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index dcfe6bea98..a113a94846 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -228,6 +228,11 @@ public: /// \returns false if the visitation was terminated early, true otherwise. bool TraverseConstructorInitializer(CXXCtorInitializer *Init); + /// \brief Recursively visit a lambda capture. + /// + /// \returns false if the visitation was terminated early, true otherwise. + bool TraverseLambdaCapture(LambdaExpr::Capture C); + // ---- Methods on Stmts ---- // Declare Traverse*() for all concrete Stmt classes. @@ -675,6 +680,10 @@ bool RecursiveASTVisitor::TraverseConstructorInitializer( return true; } +template +bool RecursiveASTVisitor::TraverseLambdaCapture(LambdaExpr::Capture C){ + return true; +} // ----------------- Type traversal ----------------- @@ -1953,9 +1962,36 @@ DEF_TRAVERSE_STMT(CXXTemporaryObjectExpr, { TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc())); }) -DEF_TRAVERSE_STMT(LambdaExpr, { - TRY_TO(TraverseStmt(S->getBody())); - }) +// Walk only the visible parts of lambda expressions. +template +bool RecursiveASTVisitor::TraverseLambdaExpr(LambdaExpr *S) { + for (LambdaExpr::capture_iterator C = S->explicit_capture_begin(), + CEnd = S->explicit_capture_end(); + C != CEnd; ++C) { + TRY_TO(TraverseLambdaCapture(*C)); + } + + if (S->hasExplicitParameters() || S->hasExplicitResultType()) { + TypeLoc TL = S->getCallOperator()->getTypeSourceInfo()->getTypeLoc(); + if (S->hasExplicitParameters() && S->hasExplicitResultType()) { + // Visit the whole type. + TRY_TO(TraverseTypeLoc(TL)); + } else if (isa(TL)) { + FunctionProtoTypeLoc Proto = cast(TL); + if (S->hasExplicitParameters()) { + // Visit parameters. + for (unsigned I = 0, N = Proto.getNumArgs(); I != N; ++I) { + TRY_TO(TraverseDecl(Proto.getArg(I))); + } + } else { + TRY_TO(TraverseTypeLoc(Proto.getResultLoc())); + } + } + } + + TRY_TO(TraverseStmt(S->getBody())); + return true; +} DEF_TRAVERSE_STMT(CXXUnresolvedConstructExpr, { // This is called for code like 'T()', where T is a template argument. diff --git a/test/Index/cxx11-lambdas.cpp b/test/Index/cxx11-lambdas.cpp new file mode 100644 index 0000000000..93db022035 --- /dev/null +++ b/test/Index/cxx11-lambdas.cpp @@ -0,0 +1,33 @@ +// Test is line- and column-sensitive; see below. + +typedef int Integer; +struct X { + void f() { + int localA, localB; + auto lambda = [&localA, localB] (Integer x) -> Integer { + return localA + localB + x; + }; + } +}; + +// RUN: c-index-test -test-load-source all -std=c++11 %s | FileCheck -check-prefix=CHECK-LOAD %s +// CHECK-LOAD: cxx11-lambdas.cpp:7:19: LambdaExpr= Extent=[7:19 - 9:6] +// CHECK-LOAD: cxx11-lambdas.cpp:7:21: VariableRef=localA:6:9 Extent=[7:21 - 7:27] +// CHECK-LOAD: cxx11-lambdas.cpp:7:29: VariableRef=localB:6:17 Extent=[7:29 - 7:35] +// CHECK-LOAD: cxx11-lambdas.cpp:7:52: TypeRef=Integer:3:13 Extent=[7:52 - 7:59] +// CHECK-LOAD: cxx11-lambdas.cpp:7:46: ParmDecl=x:7:46 (Definition) Extent=[7:38 - 7:47] +// CHECK-LOAD: cxx11-lambdas.cpp:7:38: TypeRef=Integer:3:13 Extent=[7:38 - 7:45] +// CHECK-LOAD: cxx11-lambdas.cpp:7:60: CompoundStmt= Extent=[7:60 - 9:6] +// CHECK-LOAD: cxx11-lambdas.cpp:8:7: ReturnStmt= Extent=[8:7 - 8:33] +// CHECK-LOAD: cxx11-lambdas.cpp:8:14: DeclRefExpr=localA:6:9 Extent=[8:14 - 8:20] +// CHECK-LOAD: cxx11-lambdas.cpp:8:23: DeclRefExpr=localB:6:17 Extent=[8:23 - 8:29] +// CHECK-LOAD: cxx11-lambdas.cpp:8:32: DeclRefExpr=x:7:46 Extent=[8:32 - 8:33] + +// RUN: env CINDEXTEST_INDEXLOCALSYMBOLS=1 c-index-test -index-file -std=c++11 %s | FileCheck -check-prefix=CHECK-INDEX %s +// CHECK-INDEX: [indexEntityReference]: kind: variable | name: localA | USR: c:cxx11-lambdas.cpp@100@S@X@F@f#@localA | lang: C | cursor: VariableRef=localA:6:9 | loc: 7:21 +// CHECK-INDEX: [indexEntityReference]: kind: variable | name: localB | USR: c:cxx11-lambdas.cpp@100@S@X@F@f#@localB | lang: C | cursor: VariableRef=localB:6:17 | loc: 7:29 +// CHECK-INDEX: [indexEntityReference]: kind: typedef | name: Integer | USR: c:cxx11-lambdas.cpp@51@T@Integer | lang: C | cursor: TypeRef=Integer:3:13 | loc: 7:52 +// CHECK-INDEX: [indexEntityReference]: kind: typedef | name: Integer | USR: c:cxx11-lambdas.cpp@51@T@Integer | lang: C | cursor: TypeRef=Integer:3:13 | loc: 7:38 +// CHECK-INDEX: [indexEntityReference]: kind: variable | name: localA | USR: c:cxx11-lambdas.cpp@100@S@X@F@f#@localA | lang: C | cursor: DeclRefExpr=localA:6:9 | loc: 8:14 +// CHECK-INDEX: [indexEntityReference]: kind: variable | name: localB | USR: c:cxx11-lambdas.cpp@100@S@X@F@f#@localB | lang: C | cursor: DeclRefExpr=localB:6:17 | loc: 8:23 +// CHECK-INDEX: [indexEntityReference]: kind: variable | name: x | USR: c:cxx11-lambdas.cpp@157@S@X@F@f#@Ca@F@operator()#I#1@x | lang: C | cursor: DeclRefExpr=x:7:46 | loc: 8:32 diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 90bccc55e1..beec4d2352 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -1615,6 +1615,7 @@ DEF_JOB(OverloadExprParts, OverloadExpr, OverloadExprPartsKind) DEF_JOB(ExplicitTemplateArgsVisit, ASTTemplateArgumentListInfo, ExplicitTemplateArgsVisitKind) DEF_JOB(SizeOfPackExprParts, SizeOfPackExpr, SizeOfPackExprPartsKind) +DEF_JOB(LambdaExprParts, LambdaExpr, LambdaExprPartsKind) #undef DEF_JOB class DeclVisit : public VisitorJob { @@ -1761,6 +1762,7 @@ public: void VisitSizeOfPackExpr(SizeOfPackExpr *E); void VisitPseudoObjectExpr(PseudoObjectExpr *E); void VisitOpaqueValueExpr(OpaqueValueExpr *E); + void VisitLambdaExpr(LambdaExpr *E); private: void AddDeclarationNameInfo(Stmt *S); @@ -2081,6 +2083,10 @@ void EnqueueVisitor::VisitOpaqueValueExpr(OpaqueValueExpr *E) { if (Expr *SourceExpr = E->getSourceExpr()) return Visit(SourceExpr); } +void EnqueueVisitor::VisitLambdaExpr(LambdaExpr *E) { + AddStmt(E->getBody()); + WL.push_back(LambdaExprParts(E, Parent)); +} void EnqueueVisitor::VisitPseudoObjectExpr(PseudoObjectExpr *E) { // Treat the expression like its syntactic form. Visit(E->getSyntacticForm()); @@ -2258,6 +2264,45 @@ bool CursorVisitor::RunVisitorWorkList(VisitorWorkList &WL) { // treated like DeclRefExpr cursors. continue; } + + case VisitorJob::LambdaExprPartsKind: { + // Visit captures. + LambdaExpr *E = cast(&LI)->get(); + for (LambdaExpr::capture_iterator C = E->explicit_capture_begin(), + CEnd = E->explicit_capture_end(); + C != CEnd; ++C) { + if (C->capturesThis()) + continue; + + if (Visit(MakeCursorVariableRef(C->getCapturedVar(), + C->getLocation(), + TU))) + return true; + } + + // Visit parameters and return type, if present. + if (E->hasExplicitParameters() || E->hasExplicitResultType()) { + TypeLoc TL = E->getCallOperator()->getTypeSourceInfo()->getTypeLoc(); + if (E->hasExplicitParameters() && E->hasExplicitResultType()) { + // Visit the whole type. + if (Visit(TL)) + return true; + } else if (isa(TL)) { + FunctionProtoTypeLoc Proto = cast(TL); + if (E->hasExplicitParameters()) { + // Visit parameters. + for (unsigned I = 0, N = Proto.getNumArgs(); I != N; ++I) + if (Visit(MakeCXCursor(Proto.getArg(I), TU))) + return true; + } else { + // Visit result type. + if (Visit(Proto.getResultLoc())) + return true; + } + } + } + break; + } } } return false; @@ -2980,6 +3025,13 @@ CXString clang_getCursorSpelling(CXCursor C) { return createCXString((*Ovl->begin())->getNameAsString()); } + case CXCursor_VariableRef: { + VarDecl *Var = getCursorVariableRef(C).first; + assert(Var && "Missing variable decl"); + + return createCXString(Var->getNameAsString()); + } + default: return createCXString(""); } @@ -3173,6 +3225,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return createCXString("LabelRef"); case CXCursor_OverloadedDeclRef: return createCXString("OverloadedDeclRef"); + case CXCursor_VariableRef: + return createCXString("VariableRef"); case CXCursor_IntegerLiteral: return createCXString("IntegerLiteral"); case CXCursor_FloatingLiteral: @@ -3251,6 +3305,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return createCXString("PackExpansionExpr"); case CXCursor_SizeOfPackExpr: return createCXString("SizeOfPackExpr"); + case CXCursor_LambdaExpr: + return createCXString("LambdaExpr"); case CXCursor_UnexposedExpr: return createCXString("UnexposedExpr"); case CXCursor_DeclRefExpr: @@ -3626,6 +3682,11 @@ CXSourceLocation clang_getCursorLocation(CXCursor C) { return cxloc::translateSourceLocation(P.first->getASTContext(), P.second); } + case CXCursor_VariableRef: { + std::pair P = getCursorVariableRef(C); + return cxloc::translateSourceLocation(P.first->getASTContext(), P.second); + } + case CXCursor_CXXBaseSpecifier: { CXXBaseSpecifier *BaseSpec = getCursorCXXBaseSpecifier(C); if (!BaseSpec) @@ -3768,6 +3829,9 @@ static SourceRange getRawCursorExtent(CXCursor C) { case CXCursor_OverloadedDeclRef: return getCursorOverloadedDeclRef(C).second; + case CXCursor_VariableRef: + return getCursorVariableRef(C).second; + default: // FIXME: Need a way to enumerate all non-reference cases. llvm_unreachable("Missed a reference kind"); @@ -3975,6 +4039,9 @@ CXCursor clang_getCursorReferenced(CXCursor C) { case CXCursor_OverloadedDeclRef: return C; + + case CXCursor_VariableRef: + return MakeCXCursor(getCursorVariableRef(C).first, tu); default: // We would prefer to enumerate all non-reference cursor kinds here. diff --git a/tools/libclang/CXCursor.cpp b/tools/libclang/CXCursor.cpp index af640bd7ab..531e70e17a 100644 --- a/tools/libclang/CXCursor.cpp +++ b/tools/libclang/CXCursor.cpp @@ -226,7 +226,6 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, CXTranslationUnit TU, case Stmt::UnaryExprOrTypeTraitExprClass: case Stmt::UnaryTypeTraitExprClass: case Stmt::VAArgExprClass: - case Stmt::LambdaExprClass: K = CXCursor_UnexposedExpr; break; @@ -441,6 +440,10 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, CXTranslationUnit TU, K = CXCursor_CallExpr; break; + case Stmt::LambdaExprClass: + K = CXCursor_LambdaExpr; + break; + case Stmt::ObjCMessageExprClass: { K = CXCursor_ObjCMessageExpr; int SelectorIdIndex = -1; @@ -573,6 +576,23 @@ cxcursor::getCursorNamespaceRef(CXCursor C) { reinterpret_cast(C.data[1]))); } +CXCursor cxcursor::MakeCursorVariableRef(const VarDecl *Var, SourceLocation Loc, + CXTranslationUnit TU) { + + assert(Var && TU && "Invalid arguments!"); + void *RawLoc = reinterpret_cast(Loc.getRawEncoding()); + CXCursor C = { CXCursor_VariableRef, 0, { (void*)Var, RawLoc, TU } }; + return C; +} + +std::pair +cxcursor::getCursorVariableRef(CXCursor C) { + assert(C.kind == CXCursor_VariableRef); + return std::make_pair(static_cast(C.data[0]), + SourceLocation::getFromRawEncoding( + reinterpret_cast(C.data[1]))); +} + CXCursor cxcursor::MakeCursorMemberRef(const FieldDecl *Field, SourceLocation Loc, CXTranslationUnit TU) { diff --git a/tools/libclang/CXCursor.h b/tools/libclang/CXCursor.h index 7423560085..947b0a3eed 100644 --- a/tools/libclang/CXCursor.h +++ b/tools/libclang/CXCursor.h @@ -41,6 +41,7 @@ class Stmt; class TemplateDecl; class TemplateName; class TypeDecl; +class VarDecl; namespace cxcursor { @@ -111,6 +112,14 @@ CXCursor MakeCursorNamespaceRef(const NamedDecl *NS, SourceLocation Loc, /// it references and the location where the reference occurred. std::pair getCursorNamespaceRef(CXCursor C); +/// \brief Create a reference to a variable at the given location. +CXCursor MakeCursorVariableRef(const VarDecl *Var, SourceLocation Loc, + CXTranslationUnit TU); + +/// \brief Unpack a VariableRef cursor into the variable it references and the +/// location where the where the reference occurred. +std::pair getCursorVariableRef(CXCursor C); + /// \brief Create a reference to a field at the given location. CXCursor MakeCursorMemberRef(const FieldDecl *Field, SourceLocation Loc, CXTranslationUnit TU); diff --git a/tools/libclang/CXType.cpp b/tools/libclang/CXType.cpp index eba0405994..0d4a5195ab 100644 --- a/tools/libclang/CXType.cpp +++ b/tools/libclang/CXType.cpp @@ -160,12 +160,17 @@ CXType clang_getCursorType(CXCursor C) { case CXCursor_CXXBaseSpecifier: return cxtype::MakeCXType(getCursorCXXBaseSpecifier(C)->getType(), TU); - - case CXCursor_ObjCProtocolRef: + + case CXCursor_MemberRef: + return cxtype::MakeCXType(getCursorMemberRef(C).first->getType(), TU); + + case CXCursor_VariableRef: + return cxtype::MakeCXType(getCursorVariableRef(C).first->getType(), TU); + + case CXCursor_ObjCProtocolRef: case CXCursor_TemplateRef: case CXCursor_NamespaceRef: - case CXCursor_MemberRef: - case CXCursor_OverloadedDeclRef: + case CXCursor_OverloadedDeclRef: default: break; } diff --git a/tools/libclang/CursorVisitor.h b/tools/libclang/CursorVisitor.h index f1258cb666..88b70a490e 100644 --- a/tools/libclang/CursorVisitor.h +++ b/tools/libclang/CursorVisitor.h @@ -31,7 +31,8 @@ public: ExplicitTemplateArgsVisitKind, NestedNameSpecifierLocVisitKind, DeclarationNameInfoVisitKind, - MemberRefVisitKind, SizeOfPackExprPartsKind }; + MemberRefVisitKind, SizeOfPackExprPartsKind, + LambdaExprPartsKind }; protected: void *data[3]; CXCursor parent; diff --git a/tools/libclang/IndexBody.cpp b/tools/libclang/IndexBody.cpp index 684d5ce385..77ff843525 100644 --- a/tools/libclang/IndexBody.cpp +++ b/tools/libclang/IndexBody.cpp @@ -101,6 +101,17 @@ public: IndexCtx.indexDeclGroupRef(S->getDeclGroup()); return true; } + + bool TraverseLambdaCapture(LambdaExpr::Capture C) { + if (C.capturesThis()) + return true; + + if (IndexCtx.indexFunctionLocalSymbols()) + IndexCtx.handleReference(C.getCapturedVar(), C.getLocation(), + Parent, ParentDC); + return true; + } + }; } // anonymous namespace diff --git a/tools/libclang/IndexingContext.cpp b/tools/libclang/IndexingContext.cpp index 75184dd3b0..2963f3b945 100644 --- a/tools/libclang/IndexingContext.cpp +++ b/tools/libclang/IndexingContext.cpp @@ -1035,7 +1035,9 @@ CXCursor IndexingContext::getRefCursor(const NamedDecl *D, SourceLocation Loc) { return MakeCursorNamespaceRef(Namespace, Loc, CXTU); if (const FieldDecl *Field = dyn_cast(D)) return MakeCursorMemberRef(Field, Loc, CXTU); - + if (const VarDecl *Var = dyn_cast(D)) + return MakeCursorVariableRef(Var, Loc, CXTU); + return clang_getNullCursor(); }