| //== unittests/Sema/SemaNoloadLookupTest.cpp -------------------------========// |
| // |
| // 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 "clang/AST/DeclLookups.h" |
| #include "clang/AST/DeclarationName.h" |
| #include "clang/ASTMatchers/ASTMatchFinder.h" |
| #include "clang/ASTMatchers/ASTMatchers.h" |
| #include "clang/Frontend/CompilerInstance.h" |
| #include "clang/Frontend/FrontendAction.h" |
| #include "clang/Frontend/FrontendActions.h" |
| #include "clang/Parse/ParseAST.h" |
| #include "clang/Sema/Lookup.h" |
| #include "clang/Sema/Sema.h" |
| #include "clang/Sema/SemaConsumer.h" |
| #include "clang/Tooling/Tooling.h" |
| #include "gtest/gtest.h" |
| |
| using namespace llvm; |
| using namespace clang; |
| using namespace clang::tooling; |
| |
| namespace { |
| |
| class NoloadLookupTest : public ::testing::Test { |
| void SetUp() override { |
| ASSERT_FALSE( |
| sys::fs::createUniqueDirectory("modules-no-comments-test", TestDir)); |
| } |
| |
| void TearDown() override { sys::fs::remove_directories(TestDir); } |
| |
| public: |
| SmallString<256> TestDir; |
| |
| void addFile(StringRef Path, StringRef Contents) { |
| ASSERT_FALSE(sys::path::is_absolute(Path)); |
| |
| SmallString<256> AbsPath(TestDir); |
| sys::path::append(AbsPath, Path); |
| |
| ASSERT_FALSE( |
| sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath))); |
| |
| std::error_code EC; |
| llvm::raw_fd_ostream OS(AbsPath, EC); |
| ASSERT_FALSE(EC); |
| OS << Contents; |
| } |
| |
| std::string GenerateModuleInterface(StringRef ModuleName, |
| StringRef Contents) { |
| std::string FileName = llvm::Twine(ModuleName + ".cppm").str(); |
| addFile(FileName, Contents); |
| |
| IntrusiveRefCntPtr<DiagnosticsEngine> Diags = |
| CompilerInstance::createDiagnostics(new DiagnosticOptions()); |
| CreateInvocationOptions CIOpts; |
| CIOpts.Diags = Diags; |
| CIOpts.VFS = llvm::vfs::createPhysicalFileSystem(); |
| |
| std::string CacheBMIPath = |
| llvm::Twine(TestDir + "/" + ModuleName + " .pcm").str(); |
| std::string PrebuiltModulePath = |
| "-fprebuilt-module-path=" + TestDir.str().str(); |
| const char *Args[] = {"clang++", |
| "-std=c++20", |
| "--precompile", |
| PrebuiltModulePath.c_str(), |
| "-working-directory", |
| TestDir.c_str(), |
| "-I", |
| TestDir.c_str(), |
| FileName.c_str(), |
| "-o", |
| CacheBMIPath.c_str()}; |
| std::shared_ptr<CompilerInvocation> Invocation = |
| createInvocation(Args, CIOpts); |
| EXPECT_TRUE(Invocation); |
| |
| CompilerInstance Instance; |
| Instance.setDiagnostics(Diags.get()); |
| Instance.setInvocation(Invocation); |
| GenerateModuleInterfaceAction Action; |
| EXPECT_TRUE(Instance.ExecuteAction(Action)); |
| EXPECT_FALSE(Diags->hasErrorOccurred()); |
| |
| return CacheBMIPath; |
| } |
| }; |
| |
| struct TrivialVisibleDeclConsumer : public VisibleDeclConsumer { |
| TrivialVisibleDeclConsumer() {} |
| void EnteredContext(DeclContext *Ctx) override {} |
| void FoundDecl(NamedDecl *ND, NamedDecl *Hiding, DeclContext *Ctx, |
| bool InBaseClass) override { |
| FoundNum++; |
| } |
| |
| int FoundNum = 0; |
| }; |
| |
| class NoloadLookupConsumer : public SemaConsumer { |
| public: |
| void InitializeSema(Sema &S) override { SemaPtr = &S; } |
| |
| bool HandleTopLevelDecl(DeclGroupRef D) override { |
| if (!D.isSingleDecl()) |
| return true; |
| |
| Decl *TD = D.getSingleDecl(); |
| |
| auto *ID = dyn_cast<ImportDecl>(TD); |
| if (!ID) |
| return true; |
| |
| Module *M = ID->getImportedModule(); |
| assert(M); |
| if (M->Name != "R") |
| return true; |
| |
| auto *Std = SemaPtr->getStdNamespace(); |
| EXPECT_TRUE(Std); |
| TrivialVisibleDeclConsumer Consumer; |
| SemaPtr->LookupVisibleDecls(Std, Sema::LookupNameKind::LookupOrdinaryName, |
| Consumer, |
| /*IncludeGlobalScope=*/true, |
| /*IncludeDependentBases=*/false, |
| /*LoadExternal=*/false); |
| EXPECT_EQ(Consumer.FoundNum, 1); |
| return true; |
| } |
| |
| private: |
| Sema *SemaPtr = nullptr; |
| }; |
| |
| class NoloadLookupAction : public ASTFrontendAction { |
| std::unique_ptr<ASTConsumer> |
| CreateASTConsumer(CompilerInstance &CI, StringRef /*Unused*/) override { |
| return std::make_unique<NoloadLookupConsumer>(); |
| } |
| }; |
| |
| TEST_F(NoloadLookupTest, NonModulesTest) { |
| GenerateModuleInterface("M", R"cpp( |
| module; |
| namespace std { |
| int What(); |
| |
| void bar(int x = What()) { |
| } |
| } |
| export module M; |
| export using std::bar; |
| )cpp"); |
| |
| GenerateModuleInterface("R", R"cpp( |
| module; |
| namespace std { |
| class Another; |
| int What(Another); |
| int What(); |
| } |
| export module R; |
| )cpp"); |
| |
| const char *test_file_contents = R"cpp( |
| import M; |
| namespace std { |
| void use() { |
| bar(); |
| } |
| } |
| import R; |
| )cpp"; |
| std::string DepArg = "-fprebuilt-module-path=" + TestDir.str().str(); |
| EXPECT_TRUE(runToolOnCodeWithArgs(std::make_unique<NoloadLookupAction>(), |
| test_file_contents, |
| { |
| "-std=c++20", |
| DepArg.c_str(), |
| "-I", |
| TestDir.c_str(), |
| }, |
| "test.cpp")); |
| } |
| |
| } // namespace |