| 1 | //===- unittest/Tooling/StandardLibrary.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/Tooling/Inclusions/StandardLibrary.h" |
| 10 | #include "clang/AST/ASTContext.h" |
| 11 | #include "clang/AST/Decl.h" |
| 12 | #include "clang/AST/DeclarationName.h" |
| 13 | #include "clang/Testing/TestAST.h" |
| 14 | #include "llvm/ADT/StringRef.h" |
| 15 | #include "llvm/Support/Casting.h" |
| 16 | #include "llvm/Support/ScopedPrinter.h" |
| 17 | |
| 18 | #include "gmock/gmock.h" |
| 19 | #include "gtest/gtest.h" |
| 20 | |
| 21 | using ::testing::Contains; |
| 22 | using ::testing::ElementsAre; |
| 23 | |
| 24 | namespace clang { |
| 25 | namespace tooling { |
| 26 | namespace { |
| 27 | |
| 28 | const NamedDecl &lookup(TestAST &AST, llvm::StringRef Name) { |
| 29 | TranslationUnitDecl *TU = AST.context().getTranslationUnitDecl(); |
| 30 | auto Result = TU->lookup(DeclarationName(&AST.context().Idents.get(Name))); |
| 31 | assert(!Result.empty() && "Lookup failed" ); |
| 32 | assert(Result.isSingleResult() && "Lookup returned multiple results" ); |
| 33 | return *Result.front(); |
| 34 | } |
| 35 | |
| 36 | TEST(StdlibTest, All) { |
| 37 | auto VectorH = stdlib::Header::named(Name: "<vector>" ); |
| 38 | EXPECT_TRUE(VectorH); |
| 39 | EXPECT_EQ(VectorH->name(), "<vector>" ); |
| 40 | EXPECT_EQ(llvm::to_string(*VectorH), "<vector>" ); |
| 41 | EXPECT_FALSE(stdlib::Header::named("HeadersTests.cpp" )); |
| 42 | |
| 43 | EXPECT_TRUE(stdlib::Header::named("<vector>" , stdlib::Lang::CXX)); |
| 44 | EXPECT_FALSE(stdlib::Header::named("<vector>" , stdlib::Lang::C)); |
| 45 | |
| 46 | auto Vector = stdlib::Symbol::named(Scope: "std::" , Name: "vector" ); |
| 47 | EXPECT_TRUE(Vector); |
| 48 | EXPECT_EQ(Vector->scope(), "std::" ); |
| 49 | EXPECT_EQ(Vector->name(), "vector" ); |
| 50 | EXPECT_EQ(Vector->qualifiedName(), "std::vector" ); |
| 51 | EXPECT_EQ(llvm::to_string(*Vector), "std::vector" ); |
| 52 | EXPECT_FALSE(stdlib::Symbol::named("std::" , "dongle" )); |
| 53 | EXPECT_FALSE(stdlib::Symbol::named("clang::" , "ASTContext" )); |
| 54 | |
| 55 | EXPECT_TRUE(stdlib::Symbol::named("std::" , "vector" , stdlib::Lang::CXX)); |
| 56 | EXPECT_FALSE(stdlib::Symbol::named("std::" , "vector" , stdlib::Lang::C)); |
| 57 | |
| 58 | EXPECT_EQ(Vector->header(), *VectorH); |
| 59 | EXPECT_THAT(Vector->headers(), ElementsAre(*VectorH)); |
| 60 | |
| 61 | EXPECT_TRUE(stdlib::Symbol::named("std::" , "get" )); |
| 62 | EXPECT_FALSE(stdlib::Symbol::named("std::" , "get" )->header()); |
| 63 | |
| 64 | EXPECT_THAT(stdlib::Symbol::named("std::" , "basic_iostream" )->headers(), |
| 65 | ElementsAre(stdlib::Header::named("<istream>" ), |
| 66 | stdlib::Header::named("<iostream>" ), |
| 67 | stdlib::Header::named("<iosfwd>" ))); |
| 68 | EXPECT_THAT(stdlib::Symbol::named("std::" , "size_t" )->headers(), |
| 69 | ElementsAre(stdlib::Header::named("<cstddef>" ), |
| 70 | stdlib::Header::named("<cstdlib>" ), |
| 71 | stdlib::Header::named("<cstring>" ), |
| 72 | stdlib::Header::named("<cwchar>" ), |
| 73 | stdlib::Header::named("<cuchar>" ), |
| 74 | stdlib::Header::named("<ctime>" ), |
| 75 | stdlib::Header::named("<cstdio>" ))); |
| 76 | EXPECT_EQ(stdlib::Symbol::named("std::" , "size_t" )->header(), |
| 77 | stdlib::Header::named("<cstddef>" )); |
| 78 | |
| 79 | EXPECT_THAT(stdlib::Header::all(), Contains(*VectorH)); |
| 80 | EXPECT_THAT(stdlib::Symbol::all(), Contains(*Vector)); |
| 81 | EXPECT_TRUE(stdlib::Header::named("<stdint.h>" , stdlib::Lang::CXX)); |
| 82 | EXPECT_FALSE(stdlib::Header::named("<ios646.h>" , stdlib::Lang::CXX)); |
| 83 | } |
| 84 | |
| 85 | TEST(StdlibTest, Experimental) { |
| 86 | EXPECT_FALSE( |
| 87 | stdlib::Header::named("<experimental/filesystem>" , stdlib::Lang::C)); |
| 88 | EXPECT_TRUE( |
| 89 | stdlib::Header::named("<experimental/filesystem>" , stdlib::Lang::CXX)); |
| 90 | |
| 91 | auto Symbol = stdlib::Symbol::named(Scope: "std::experimental::filesystem::" , |
| 92 | Name: "system_complete" ); |
| 93 | EXPECT_TRUE(Symbol); |
| 94 | EXPECT_EQ(Symbol->scope(), "std::experimental::filesystem::" ); |
| 95 | EXPECT_EQ(Symbol->name(), "system_complete" ); |
| 96 | EXPECT_EQ(Symbol->header(), |
| 97 | stdlib::Header::named("<experimental/filesystem>" )); |
| 98 | EXPECT_EQ(Symbol->qualifiedName(), |
| 99 | "std::experimental::filesystem::system_complete" ); |
| 100 | } |
| 101 | |
| 102 | TEST(StdlibTest, CCompat) { |
| 103 | EXPECT_THAT( |
| 104 | stdlib::Symbol::named("" , "int16_t" , stdlib::Lang::CXX)->headers(), |
| 105 | ElementsAre(stdlib::Header::named("<cstdint>" ), |
| 106 | stdlib::Header::named("<stdint.h>" ))); |
| 107 | EXPECT_THAT( |
| 108 | stdlib::Symbol::named("std::" , "int16_t" , stdlib::Lang::CXX)->headers(), |
| 109 | ElementsAre(stdlib::Header::named("<cstdint>" ))); |
| 110 | |
| 111 | EXPECT_TRUE(stdlib::Header::named("<stdint.h>" , stdlib::Lang::C)); |
| 112 | EXPECT_THAT( |
| 113 | stdlib::Symbol::named("" , "int16_t" , stdlib::Lang::C)->headers(), |
| 114 | ElementsAre(stdlib::Header::named("<stdint.h>" , stdlib::Lang::C))); |
| 115 | EXPECT_FALSE(stdlib::Symbol::named("std::" , "int16_t" , stdlib::Lang::C)); |
| 116 | } |
| 117 | |
| 118 | TEST(StdlibTest, Recognizer) { |
| 119 | TestAST AST(R"cpp( |
| 120 | namespace std { |
| 121 | inline namespace inl { |
| 122 | |
| 123 | template <typename> |
| 124 | struct vector { class nested {}; }; |
| 125 | |
| 126 | class secret {}; |
| 127 | |
| 128 | } // inl |
| 129 | |
| 130 | inline namespace __1 { |
| 131 | namespace chrono { |
| 132 | inline namespace chrono_inl { |
| 133 | class system_clock {}; |
| 134 | } // chrono_inl |
| 135 | } // chrono |
| 136 | } // __1 |
| 137 | |
| 138 | } // std |
| 139 | |
| 140 | // C Standard Library structure defined in <stdlib.h> |
| 141 | struct div_t {}; |
| 142 | |
| 143 | class vector {}; |
| 144 | std::vector<int> vec; |
| 145 | std::vector<int>::nested nest; |
| 146 | std::secret sec; |
| 147 | std::chrono::system_clock clock; |
| 148 | |
| 149 | div_t div; |
| 150 | )cpp" ); |
| 151 | |
| 152 | auto &VectorNonstd = lookup(AST, Name: "vector" ); |
| 153 | auto *Vec = cast<VarDecl>(Val: lookup(AST, Name: "vec" )).getType()->getAsCXXRecordDecl(); |
| 154 | auto *Nest = |
| 155 | cast<VarDecl>(Val: lookup(AST, Name: "nest" )).getType()->getAsCXXRecordDecl(); |
| 156 | auto *Clock = |
| 157 | cast<VarDecl>(Val: lookup(AST, Name: "clock" )).getType()->getAsCXXRecordDecl(); |
| 158 | auto *Sec = cast<VarDecl>(Val: lookup(AST, Name: "sec" )).getType()->getAsCXXRecordDecl(); |
| 159 | auto *CDivT = |
| 160 | cast<VarDecl>(Val: lookup(AST, Name: "div" )).getType()->getAsCXXRecordDecl(); |
| 161 | |
| 162 | stdlib::Recognizer Recognizer; |
| 163 | |
| 164 | EXPECT_EQ(Recognizer(&VectorNonstd), std::nullopt); |
| 165 | EXPECT_EQ(Recognizer(Vec), stdlib::Symbol::named("std::" , "vector" )); |
| 166 | EXPECT_EQ(Recognizer(Vec), |
| 167 | stdlib::Symbol::named("std::" , "vector" , stdlib::Lang::CXX)); |
| 168 | EXPECT_EQ(Recognizer(Nest), stdlib::Symbol::named("std::" , "vector" )); |
| 169 | EXPECT_EQ(Recognizer(Clock), |
| 170 | stdlib::Symbol::named("std::chrono::" , "system_clock" )); |
| 171 | auto DivT = stdlib::Symbol::named(Scope: "" , Name: "div_t" , Language: stdlib::Lang::CXX); |
| 172 | EXPECT_TRUE(DivT); |
| 173 | EXPECT_EQ(Recognizer(CDivT), DivT); |
| 174 | EXPECT_EQ(Recognizer(Sec), std::nullopt); |
| 175 | } |
| 176 | |
| 177 | TEST(StdlibTest, RecognizerForC99) { |
| 178 | TestInputs Input("typedef char uint8_t;" ); |
| 179 | Input.Language = TestLanguage::Lang_C99; |
| 180 | TestAST AST(Input); |
| 181 | |
| 182 | auto &Uint8T = lookup(AST, Name: "uint8_t" ); |
| 183 | stdlib::Recognizer Recognizer; |
| 184 | EXPECT_EQ(Recognizer(&Uint8T), |
| 185 | stdlib::Symbol::named("" , "uint8_t" , stdlib::Lang::C)); |
| 186 | } |
| 187 | |
| 188 | TEST(StdlibTest, SpecialCMappings) { |
| 189 | TestInputs Input("typedef char size_t;" ); |
| 190 | Input.Language = TestLanguage::Lang_C99; |
| 191 | TestAST AST(Input); |
| 192 | |
| 193 | auto &SizeT = lookup(AST, Name: "size_t" ); |
| 194 | stdlib::Recognizer Recognizer; |
| 195 | auto ActualSym = Recognizer(&SizeT); |
| 196 | assert(ActualSym); |
| 197 | EXPECT_EQ(ActualSym, stdlib::Symbol::named("" , "size_t" , stdlib::Lang::C)); |
| 198 | EXPECT_EQ(ActualSym->header()->name(), "<stddef.h>" ); |
| 199 | } |
| 200 | |
| 201 | } // namespace |
| 202 | } // namespace tooling |
| 203 | } // namespace clang |
| 204 | |