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 | |