diff --git a/clang-tools-extra/clangd/FindTarget.cpp b/clang-tools-extra/clangd/FindTarget.cpp --- a/clang-tools-extra/clangd/FindTarget.cpp +++ b/clang-tools-extra/clangd/FindTarget.cpp @@ -585,7 +585,7 @@ llvm::SmallVector refInExpr(const Expr *E) { struct Visitor : ConstStmtVisitor { - // FIXME: handle more complicated cases, e.g. ObjC, designated initializers. + // FIXME: handle more complicated cases: more ObjC, designated initializers. llvm::SmallVector Refs; void VisitDeclRefExpr(const DeclRefExpr *E) { @@ -616,6 +616,14 @@ /*IsDecl=*/false, {E->getPack()}}); } + + void VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *E) { + Refs.push_back(ReferenceLoc{ + NestedNameSpecifierLoc(), E->getLocation(), + /*IsDecl=*/false, + // Select the getter, setter, or @property depending on the call. + explicitReferenceTargets(DynTypedNode::create(*E), {})}); + } }; Visitor V; @@ -729,6 +737,19 @@ return true; } + bool TraverseOpaqueValueExpr(OpaqueValueExpr *OVE) { + visitNode(DynTypedNode::create(*OVE)); + // Not clear why the source expression is skipped by default... + return RecursiveASTVisitor::TraverseStmt(OVE->getSourceExpr()); + } + + bool TraversePseudoObjectExpr(PseudoObjectExpr *POE) { + visitNode(DynTypedNode::create(*POE)); + // Traverse only the syntactic form to find the *written* references. + // (The semantic form also contains lots of duplication) + return RecursiveASTVisitor::TraverseStmt(POE->getSyntacticForm()); + } + // We re-define Traverse*, since there's no corresponding Visit*. // TemplateArgumentLoc is the only way to get locations for references to // template template parameters. diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp --- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -511,8 +511,7 @@ [[f.x]].y = 0; } )cpp"; - EXPECT_DECLS("OpaqueValueExpr", - "@property(atomic, retain, readwrite) I *x"); + EXPECT_DECLS("OpaqueValueExpr", "@property(atomic, retain, readwrite) I *x"); Code = R"cpp( @protocol Foo @@ -565,8 +564,10 @@ // parsing. TU.ExtraArgs.push_back("-fno-delayed-template-parsing"); TU.ExtraArgs.push_back("-std=c++17"); + TU.ExtraArgs.push_back("-xobjective-c++"); auto AST = TU.build(); + EXPECT_THAT(AST.getDiagnostics(), ::testing::IsEmpty()) << Code; auto *TestDecl = &findDecl(AST, "foo"); if (auto *T = llvm::dyn_cast(TestDecl)) @@ -664,7 +665,7 @@ void foo() { $0^Struct $1^x; $2^Typedef $3^y; - static_cast<$4^Struct*>(0); + (void) static_cast<$4^Struct*>(0); } )cpp", "0: targets = {Struct}\n" @@ -675,10 +676,10 @@ // Name qualifiers. {R"cpp( namespace a { namespace b { struct S { typedef int type; }; } } - void foo() { + int foo() { $0^a::$1^b::$2^S $3^x; using namespace $4^a::$5^b; - $6^S::$7^type $8^y; + return $6^S::$7^type(5); } )cpp", "0: targets = {a}\n" @@ -688,8 +689,7 @@ "4: targets = {a}\n" "5: targets = {a::b}, qualifier = 'a::'\n" "6: targets = {a::b::S}\n" - "7: targets = {a::b::S::type}, qualifier = 'struct S::'\n" - "8: targets = {y}, decl\n"}, + "7: targets = {a::b::S::type}, qualifier = 'struct S::'\n"}, // Simple templates. {R"cpp( template struct vector { using value_type = T; }; @@ -719,7 +719,7 @@ "3: targets = {vb}, decl\n"}, // MemberExpr should know their using declaration. {R"cpp( - struct X { void func(int); } + struct X { void func(int); }; struct Y : X { using X::func; }; @@ -744,8 +744,8 @@ #define FOO a #define BAR b - void foo(int a, int b) { - $0^FOO+$1^BAR; + int foo(int a, int b) { + return $0^FOO+$1^BAR; } )cpp", "0: targets = {a}\n" @@ -825,7 +825,7 @@ void foo() { $0^TT $1^x; $2^foo<$3^TT>(); - $4^foo<$5^vector>() + $4^foo<$5^vector>(); $6^foo<$7^TP...>(); } )cpp", @@ -892,7 +892,7 @@ }; // delegating initializer class $10^Foo { - $11^Foo(int); + $11^Foo(int) {} $12^Foo(): $13^Foo(111) {} }; } @@ -925,7 +925,7 @@ // Namespace aliases should be handled properly. { R"cpp( - namespace ns { struct Type {} } + namespace ns { struct Type {}; } namespace alias = ns; namespace rec_alias = alias; @@ -966,7 +966,21 @@ } )cpp", "0: targets = {Test}\n" - "1: targets = {a}, decl\n"}}; + "1: targets = {a}, decl\n"}, + { + R"cpp( + @interface I {} + @property(retain) I* x; + @property(retain) I* y; + @end + I *f; + void foo() { + $0^f.$1^x.$2^y = 0; + } + )cpp", + "0: targets = {f}\n" + "1: targets = {I::x}\n" + "2: targets = {I::y}\n"}}; for (const auto &C : Cases) { llvm::StringRef ExpectedCode = C.first;