1 //== unittests/Sema/SemaNoloadLookupTest.cpp -------------------------========// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "clang/AST/DeclLookups.h" 10 #include "clang/AST/DeclarationName.h" 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 12 #include "clang/ASTMatchers/ASTMatchers.h" 13 #include "clang/Frontend/CompilerInstance.h" 14 #include "clang/Frontend/FrontendAction.h" 15 #include "clang/Frontend/FrontendActions.h" 16 #include "clang/Parse/ParseAST.h" 17 #include "clang/Sema/Lookup.h" 18 #include "clang/Sema/Sema.h" 19 #include "clang/Sema/SemaConsumer.h" 20 #include "clang/Tooling/Tooling.h" 21 #include "gtest/gtest.h" 22 23 using namespace llvm; 24 using namespace clang; 25 using namespace clang::tooling; 26 27 namespace { 28 29 class NoloadLookupTest : public ::testing::Test { 30 void SetUp() override { 31 ASSERT_FALSE( 32 sys::fs::createUniqueDirectory("modules-no-comments-test", TestDir)); 33 } 34 35 void TearDown() override { sys::fs::remove_directories(TestDir); } 36 37 public: 38 SmallString<256> TestDir; 39 40 void addFile(StringRef Path, StringRef Contents) { 41 ASSERT_FALSE(sys::path::is_absolute(Path)); 42 43 SmallString<256> AbsPath(TestDir); 44 sys::path::append(AbsPath, Path); 45 46 ASSERT_FALSE( 47 sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath))); 48 49 std::error_code EC; 50 llvm::raw_fd_ostream OS(AbsPath, EC); 51 ASSERT_FALSE(EC); 52 OS << Contents; 53 } 54 55 std::string GenerateModuleInterface(StringRef ModuleName, 56 StringRef Contents) { 57 std::string FileName = llvm::Twine(ModuleName + ".cppm").str(); 58 addFile(FileName, Contents); 59 60 CreateInvocationOptions CIOpts; 61 CIOpts.VFS = llvm::vfs::createPhysicalFileSystem(); 62 IntrusiveRefCntPtr<DiagnosticsEngine> Diags = 63 CompilerInstance::createDiagnostics(*CIOpts.VFS, 64 new DiagnosticOptions()); 65 CIOpts.Diags = Diags; 66 67 std::string CacheBMIPath = 68 llvm::Twine(TestDir + "/" + ModuleName + ".pcm").str(); 69 std::string PrebuiltModulePath = 70 "-fprebuilt-module-path=" + TestDir.str().str(); 71 const char *Args[] = {"clang++", 72 "-std=c++20", 73 "--precompile", 74 PrebuiltModulePath.c_str(), 75 "-working-directory", 76 TestDir.c_str(), 77 "-I", 78 TestDir.c_str(), 79 FileName.c_str()}; 80 std::shared_ptr<CompilerInvocation> Invocation = 81 createInvocation(Args, CIOpts); 82 EXPECT_TRUE(Invocation); 83 84 CompilerInstance Instance; 85 Instance.setDiagnostics(Diags.get()); 86 Instance.setInvocation(Invocation); 87 Instance.getFrontendOpts().OutputFile = CacheBMIPath; 88 GenerateReducedModuleInterfaceAction Action; 89 EXPECT_TRUE(Instance.ExecuteAction(Action)); 90 EXPECT_FALSE(Diags->hasErrorOccurred()); 91 92 return CacheBMIPath; 93 } 94 }; 95 96 struct TrivialVisibleDeclConsumer : public VisibleDeclConsumer { 97 TrivialVisibleDeclConsumer() {} 98 void EnteredContext(DeclContext *Ctx) override {} 99 void FoundDecl(NamedDecl *ND, NamedDecl *Hiding, DeclContext *Ctx, 100 bool InBaseClass) override { 101 FoundNum++; 102 } 103 104 int FoundNum = 0; 105 }; 106 107 class NoloadLookupConsumer : public SemaConsumer { 108 public: 109 void InitializeSema(Sema &S) override { SemaPtr = &S; } 110 111 bool HandleTopLevelDecl(DeclGroupRef D) override { 112 if (!D.isSingleDecl()) 113 return true; 114 115 Decl *TD = D.getSingleDecl(); 116 117 auto *ID = dyn_cast<ImportDecl>(TD); 118 if (!ID) 119 return true; 120 121 clang::Module *M = ID->getImportedModule(); 122 assert(M); 123 if (M->Name != "R") 124 return true; 125 126 auto *Std = SemaPtr->getStdNamespace(); 127 EXPECT_TRUE(Std); 128 TrivialVisibleDeclConsumer Consumer; 129 SemaPtr->LookupVisibleDecls(Std, Sema::LookupNameKind::LookupOrdinaryName, 130 Consumer, 131 /*IncludeGlobalScope=*/true, 132 /*IncludeDependentBases=*/false, 133 /*LoadExternal=*/false); 134 EXPECT_EQ(Consumer.FoundNum, 1); 135 return true; 136 } 137 138 private: 139 Sema *SemaPtr = nullptr; 140 }; 141 142 class NoloadLookupAction : public ASTFrontendAction { 143 std::unique_ptr<ASTConsumer> 144 CreateASTConsumer(CompilerInstance &CI, StringRef /*Unused*/) override { 145 return std::make_unique<NoloadLookupConsumer>(); 146 } 147 }; 148 149 TEST_F(NoloadLookupTest, NonModulesTest) { 150 GenerateModuleInterface("M", R"cpp( 151 module; 152 namespace std { 153 int What(); 154 155 void bar(int x = What()) { 156 } 157 } 158 export module M; 159 export using std::bar; 160 )cpp"); 161 162 GenerateModuleInterface("R", R"cpp( 163 module; 164 namespace std { 165 class Another; 166 int What(Another); 167 int What(); 168 } 169 export module R; 170 )cpp"); 171 172 const char *test_file_contents = R"cpp( 173 import M; 174 namespace std { 175 void use() { 176 bar(); 177 } 178 } 179 import R; 180 )cpp"; 181 std::string DepArg = "-fprebuilt-module-path=" + TestDir.str().str(); 182 EXPECT_TRUE(runToolOnCodeWithArgs(std::make_unique<NoloadLookupAction>(), 183 test_file_contents, 184 { 185 "-std=c++20", 186 DepArg.c_str(), 187 "-I", 188 TestDir.c_str(), 189 }, 190 "test.cpp")); 191 } 192 193 } // namespace 194