//===-- FindTargetTests.cpp --------------------------*- C++ -*------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "FindTarget.h" #include "Selection.h" #include "TestTU.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclTemplate.h" #include "clang/Basic/SourceLocation.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Testing/Support/Annotations.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include namespace clang { namespace clangd { namespace { // A referenced Decl together with its DeclRelationSet, for assertions. // // There's no great way to assert on the "content" of a Decl in the general case // that's both expressive and unambiguous (e.g. clearly distinguishes between // templated decls and their specializations). // // We use the result of pretty-printing the decl, with the {body} truncated. struct PrintedDecl { PrintedDecl(const char *Name, DeclRelationSet Relations = {}) : Name(Name), Relations(Relations) {} PrintedDecl(const NamedDecl *D, DeclRelationSet Relations = {}) : Relations(Relations) { std::string S; llvm::raw_string_ostream OS(S); D->print(OS); llvm::StringRef FirstLine = llvm::StringRef(OS.str()).take_until([](char C) { return C == '\n'; }); FirstLine = FirstLine.rtrim(" {"); Name = std::string(FirstLine.rtrim(" {")); } std::string Name; DeclRelationSet Relations; }; bool operator==(const PrintedDecl &L, const PrintedDecl &R) { return std::tie(L.Name, L.Relations) == std::tie(R.Name, R.Relations); } llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const PrintedDecl &D) { return OS << D.Name << " Rel=" << D.Relations; } // The test cases in for targetDecl() take the form // - a piece of code (Code = "...") // - Code should have a single AST node marked as a [[range]] // - an EXPECT_DECLS() assertion that verify the type of node selected, and // all the decls that targetDecl() considers it to reference // Despite the name, these cases actually test allTargetDecls() for brevity. class TargetDeclTest : public ::testing::Test { protected: using Rel = DeclRelation; std::string Code; std::vector Flags; // Asserts that `Code` has a marked selection of a node `NodeType`, // and returns allTargetDecls() as PrintedDecl structs. // Use via EXPECT_DECLS(). std::vector assertNodeAndPrintDecls(const char *NodeType) { llvm::Annotations A(Code); auto TU = TestTU::withCode(A.code()); TU.ExtraArgs = Flags; auto AST = TU.build(); llvm::Annotations::Range R = A.range(); auto Selection = SelectionTree::createRight( AST.getASTContext(), AST.getTokens(), R.Begin, R.End); const SelectionTree::Node *N = Selection.commonAncestor(); if (!N) { ADD_FAILURE() << "No node selected!\n" << Code; return {}; } EXPECT_EQ(N->kind(), NodeType) << Selection; std::vector ActualDecls; for (const auto &Entry : allTargetDecls(N->ASTNode)) ActualDecls.emplace_back(Entry.first, Entry.second); return ActualDecls; } }; // This is a macro to preserve line numbers in assertion failures. // It takes the expected decls as varargs to work around comma-in-macro issues. #define EXPECT_DECLS(NodeType, ...) \ EXPECT_THAT(assertNodeAndPrintDecls(NodeType), \ ::testing::UnorderedElementsAreArray( \ std::vector({__VA_ARGS__}))) \ << Code using ExpectedDecls = std::vector; TEST_F(TargetDeclTest, Exprs) { Code = R"cpp( int f(); int x = [[f]](); )cpp"; EXPECT_DECLS("DeclRefExpr", "int f()"); Code = R"cpp( struct S { S operator+(S) const; }; auto X = S() [[+]] S(); )cpp"; EXPECT_DECLS("DeclRefExpr", "S operator+(S) const"); Code = R"cpp( int foo(); int s = foo[[()]]; )cpp"; EXPECT_DECLS("CallExpr", "int foo()"); Code = R"cpp( struct X { void operator()(int n); }; void test() { X x; x[[(123)]]; } )cpp"; EXPECT_DECLS("CXXOperatorCallExpr", "void operator()(int n)"); Code = R"cpp( void test() { goto [[label]]; label: return; } )cpp"; EXPECT_DECLS("GotoStmt", "label:"); Code = R"cpp( void test() { [[label]]: return; } )cpp"; EXPECT_DECLS("LabelStmt", "label:"); } TEST_F(TargetDeclTest, RecoveryForC) { Flags = {"-xc", "-Xclang", "-frecovery-ast"}; Code = R"cpp( // error-ok: testing behavior on broken code // int f(); int f(int); int x = [[f]](); )cpp"; EXPECT_DECLS("DeclRefExpr", "int f(int)"); } TEST_F(TargetDeclTest, Recovery) { Code = R"cpp( // error-ok: testing behavior on broken code int f(); int f(int, int); int x = [[f]](42); )cpp"; EXPECT_DECLS("UnresolvedLookupExpr", "int f()", "int f(int, int)"); } TEST_F(TargetDeclTest, RecoveryType) { Code = R"cpp( // error-ok: testing behavior on broken code struct S { int member; }; S overloaded(int); void foo() { // No overload matches, but we have recovery-expr with the correct type. overloaded().[[member]]; } )cpp"; EXPECT_DECLS("MemberExpr", "int member"); } TEST_F(TargetDeclTest, UsingDecl) { Code = R"cpp( namespace foo { int f(int); int f(char); } using foo::f; int x = [[f]](42); )cpp"; // f(char) is not referenced! EXPECT_DECLS("DeclRefExpr", {"using foo::f", Rel::Alias}, {"int f(int)"}); Code = R"cpp( namespace foo { int f(int); int f(char); } [[using foo::f]]; )cpp"; // All overloads are referenced. EXPECT_DECLS("UsingDecl", {"using foo::f", Rel::Alias}, {"int f(int)"}, {"int f(char)"}); Code = R"cpp( struct X { int foo(); }; struct Y : X { using X::foo; }; int x = Y().[[foo]](); )cpp"; EXPECT_DECLS("MemberExpr", {"using X::foo", Rel::Alias}, {"int foo()"}); Code = R"cpp( template struct Base { void waldo() {} }; template struct Derived : Base { using Base::[[waldo]]; }; )cpp"; EXPECT_DECLS("UnresolvedUsingValueDecl", {"using Base::waldo", Rel::Alias}, {"void waldo()"}); } TEST_F(TargetDeclTest, ConstructorInitList) { Code = R"cpp( struct X { int a; X() : [[a]](42) {} }; )cpp"; EXPECT_DECLS("CXXCtorInitializer", "int a"); Code = R"cpp( struct X { X() : [[X]](1) {} X(int); }; )cpp"; EXPECT_DECLS("RecordTypeLoc", "struct X"); } TEST_F(TargetDeclTest, DesignatedInit) { Flags = {"-xc"}; // array designators are a C99 extension. Code = R"c( struct X { int a; }; struct Y { int b; struct X c[2]; }; struct Y y = { .c[0].[[a]] = 1 }; )c"; EXPECT_DECLS("DesignatedInitExpr", "int a"); } TEST_F(TargetDeclTest, NestedNameSpecifier) { Code = R"cpp( namespace a { namespace b { int c; } } int x = a::[[b::]]c; )cpp"; EXPECT_DECLS("NestedNameSpecifierLoc", "namespace b"); Code = R"cpp( namespace a { struct X { enum { y }; }; } int x = a::[[X::]]y; )cpp"; EXPECT_DECLS("NestedNameSpecifierLoc", "struct X"); Code = R"cpp( template int x = [[T::]]y; )cpp"; EXPECT_DECLS("NestedNameSpecifierLoc", "typename T"); Code = R"cpp( namespace a { int x; } namespace b = a; int y = [[b]]::x; )cpp"; EXPECT_DECLS("NestedNameSpecifierLoc", {"namespace b = a", Rel::Alias}, {"namespace a", Rel::Underlying}); } TEST_F(TargetDeclTest, Types) { Code = R"cpp( struct X{}; [[X]] x; )cpp"; EXPECT_DECLS("RecordTypeLoc", "struct X"); Code = R"cpp( struct S{}; typedef S X; [[X]] x; )cpp"; EXPECT_DECLS("TypedefTypeLoc", {"typedef S X", Rel::Alias}, {"struct S", Rel::Underlying}); Code = R"cpp( namespace ns { struct S{}; } typedef ns::S X; [[X]] x; )cpp"; EXPECT_DECLS("TypedefTypeLoc", {"typedef ns::S X", Rel::Alias}, {"struct S", Rel::Underlying}); // FIXME: Auto-completion in a template requires disabling delayed template // parsing. Flags = {"-fno-delayed-template-parsing"}; Code = R"cpp( template void foo() { [[T]] x; } )cpp"; EXPECT_DECLS("TemplateTypeParmTypeLoc", "class T"); Flags.clear(); // FIXME: Auto-completion in a template requires disabling delayed template // parsing. Flags = {"-fno-delayed-template-parsing"}; Code = R"cpp( template class T> void foo() { [[T]] x; } )cpp"; EXPECT_DECLS("TemplateSpecializationTypeLoc", "template class T"); Flags.clear(); Code = R"cpp( struct S{}; S X; [[decltype]](X) Y; )cpp"; EXPECT_DECLS("DecltypeTypeLoc", {"struct S", Rel::Underlying}); Code = R"cpp( struct S{}; [[auto]] X = S{}; )cpp"; // FIXME: deduced type missing in AST. https://llvm.org/PR42914 EXPECT_DECLS("AutoTypeLoc"); Code = R"cpp( template struct S { static const int size = sizeof...([[E]]); }; )cpp"; EXPECT_DECLS("SizeOfPackExpr", "typename ...E"); Code = R"cpp( template class Foo { void f([[Foo]] x); }; )cpp"; EXPECT_DECLS("InjectedClassNameTypeLoc", "class Foo"); } TEST_F(TargetDeclTest, ClassTemplate) { Code = R"cpp( // Implicit specialization. template class Foo{}; [[Foo<42>]] B; )cpp"; EXPECT_DECLS("TemplateSpecializationTypeLoc", {"template<> class Foo<42>", Rel::TemplateInstantiation}, {"class Foo", Rel::TemplatePattern}); Code = R"cpp( template class Foo {}; // The "Foo" SpecializationDecl is incomplete, there is no // instantiation happening. void func([[Foo]] *); )cpp"; EXPECT_DECLS("TemplateSpecializationTypeLoc", {"class Foo", Rel::TemplatePattern}, {"template<> class Foo", Rel::TemplateInstantiation}); Code = R"cpp( // Explicit specialization. template class Foo{}; template<> class Foo<42>{}; [[Foo<42>]] B; )cpp"; EXPECT_DECLS("TemplateSpecializationTypeLoc", "template<> class Foo<42>"); Code = R"cpp( // Partial specialization. template class Foo{}; template class Foo{}; [[Foo]] B; )cpp"; EXPECT_DECLS("TemplateSpecializationTypeLoc", {"template<> class Foo", Rel::TemplateInstantiation}, {"template class Foo", Rel::TemplatePattern}); Code = R"cpp( // Template template argument. template struct Vector {}; template