//===-- ASTTests.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 "AST.h" #include "Annotations.h" #include "ParsedAST.h" #include "TestTU.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/Basic/SourceManager.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include #include #include namespace clang { namespace clangd { namespace { TEST(GetDeducedType, KwAutoExpansion) { struct Test { StringRef AnnotatedCode; const char *DeducedType; } Tests[] = { {"^auto i = 0;", "int"}, {"^auto f(){ return 1;};", "int"}, }; for (Test T : Tests) { Annotations File(T.AnnotatedCode); auto AST = TestTU::withCode(File.code()).build(); SourceManagerForFile SM("foo.cpp", File.code()); for (Position Pos : File.points()) { auto Location = sourceLocationInMainFile(SM.get(), Pos); ASSERT_TRUE(!!Location) << llvm::toString(Location.takeError()); auto DeducedType = getDeducedType(AST.getASTContext(), *Location); EXPECT_EQ(DeducedType->getAsString(), T.DeducedType); } } } TEST(ClangdAST, GetQualification) { // Tries to insert the decl `Foo` into position of each decl named `insert`. // This is done to get an appropriate DeclContext for the insertion location. // Qualifications are the required nested name specifier to spell `Foo` at the // `insert`ion location. // VisibleNamespaces are assumed to be visible at every insertion location. const struct { llvm::StringRef Test; std::vector Qualifications; std::vector VisibleNamespaces; } Cases[] = { { R"cpp( namespace ns1 { namespace ns2 { class Foo {}; } } void insert(); // ns1::ns2::Foo namespace ns1 { void insert(); // ns2::Foo namespace ns2 { void insert(); // Foo } using namespace ns2; void insert(); // Foo } using namespace ns1; void insert(); // ns2::Foo using namespace ns2; void insert(); // Foo )cpp", {"ns1::ns2::", "ns2::", "", "", "ns2::", ""}, {}, }, { R"cpp( namespace ns1 { namespace ns2 { class Bar { void Foo(); }; } } void insert(); // ns1::ns2::Bar::Foo namespace ns1 { void insert(); // ns2::Bar::Foo namespace ns2 { void insert(); // Bar::Foo } using namespace ns2; void insert(); // Bar::Foo } using namespace ns1; void insert(); // ns2::Bar::Foo using namespace ns2; void insert(); // Bar::Foo )cpp", {"ns1::ns2::Bar::", "ns2::Bar::", "Bar::", "Bar::", "ns2::Bar::", "Bar::"}, {}, }, { R"cpp( namespace ns1 { namespace ns2 { void Foo(); } } void insert(); // ns2::Foo namespace ns1 { void insert(); // ns2::Foo namespace ns2 { void insert(); // Foo } } )cpp", {"ns2::", "ns2::", ""}, {"ns1::"}, }, }; for (const auto &Case : Cases) { Annotations Test(Case.Test); TestTU TU = TestTU::withCode(Test.code()); ParsedAST AST = TU.build(); std::vector InsertionPoints; const NamedDecl *TargetDecl; findDecl(AST, [&](const NamedDecl &ND) { if (ND.getNameAsString() == "Foo") { TargetDecl = &ND; return true; } if (ND.getNameAsString() == "insert") InsertionPoints.push_back(&ND); return false; }); ASSERT_EQ(InsertionPoints.size(), Case.Qualifications.size()); for (size_t I = 0, E = InsertionPoints.size(); I != E; ++I) { const Decl *D = InsertionPoints[I]; if (Case.VisibleNamespaces.empty()) { EXPECT_EQ(getQualification(AST.getASTContext(), D->getLexicalDeclContext(), D->getBeginLoc(), TargetDecl), Case.Qualifications[I]); } else { EXPECT_EQ(getQualification(AST.getASTContext(), D->getLexicalDeclContext(), TargetDecl, Case.VisibleNamespaces), Case.Qualifications[I]); } } } } TEST(ClangdAST, PrintType) { const struct { llvm::StringRef Test; std::vector Types; } Cases[] = { { R"cpp( namespace ns1 { namespace ns2 { class Foo {}; } } void insert(); // ns1::ns2::Foo namespace ns1 { void insert(); // ns2::Foo namespace ns2 { void insert(); // Foo } } )cpp", {"ns1::ns2::Foo", "ns2::Foo", "Foo"}, }, { R"cpp( namespace ns1 { typedef int Foo; } void insert(); // ns1::Foo namespace ns1 { void insert(); // Foo } )cpp", {"ns1::Foo", "Foo"}, }, }; for (const auto &Case : Cases) { Annotations Test(Case.Test); TestTU TU = TestTU::withCode(Test.code()); ParsedAST AST = TU.build(); std::vector InsertionPoints; const TypeDecl *TargetDecl = nullptr; findDecl(AST, [&](const NamedDecl &ND) { if (ND.getNameAsString() == "Foo") { if (const auto *TD = llvm::dyn_cast(&ND)) { TargetDecl = TD; return true; } } else if (ND.getNameAsString() == "insert") InsertionPoints.push_back(ND.getDeclContext()); return false; }); ASSERT_EQ(InsertionPoints.size(), Case.Types.size()); for (size_t I = 0, E = InsertionPoints.size(); I != E; ++I) { const auto *DC = InsertionPoints[I]; EXPECT_EQ(printType(AST.getASTContext().getTypeDeclType(TargetDecl), *DC), Case.Types[I]); } } } } // namespace } // namespace clangd } // namespace clang