1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <ostream>
6 #include <vector>
7
8 #include "gn/config.h"
9 #include "gn/header_checker.h"
10 #include "gn/scheduler.h"
11 #include "gn/target.h"
12 #include "gn/test_with_scheduler.h"
13 #include "gn/test_with_scope.h"
14 #include "util/test/test.h"
15
16 namespace {
17
18 class HeaderCheckerTest : public TestWithScheduler {
19 public:
HeaderCheckerTest()20 HeaderCheckerTest()
21 : a_(setup_.settings(), Label(SourceDir("//a/"), "a")),
22 b_(setup_.settings(), Label(SourceDir("//b/"), "b")),
23 c_(setup_.settings(), Label(SourceDir("//c/"), "c")),
24 d_(setup_.settings(), Label(SourceDir("//d/"), "d")) {
25 a_.set_output_type(Target::SOURCE_SET);
26 b_.set_output_type(Target::SOURCE_SET);
27 c_.set_output_type(Target::SOURCE_SET);
28 d_.set_output_type(Target::SOURCE_SET);
29
30 Err err;
31 a_.SetToolchain(setup_.toolchain(), &err);
32 b_.SetToolchain(setup_.toolchain(), &err);
33 c_.SetToolchain(setup_.toolchain(), &err);
34 d_.SetToolchain(setup_.toolchain(), &err);
35
36 a_.public_deps().push_back(LabelTargetPair(&b_));
37 b_.public_deps().push_back(LabelTargetPair(&c_));
38
39 // Start with all public visibility.
40 a_.visibility().SetPublic();
41 b_.visibility().SetPublic();
42 c_.visibility().SetPublic();
43 d_.visibility().SetPublic();
44
45 d_.OnResolved(&err);
46 c_.OnResolved(&err);
47 b_.OnResolved(&err);
48 a_.OnResolved(&err);
49
50 targets_.push_back(&a_);
51 targets_.push_back(&b_);
52 targets_.push_back(&c_);
53 targets_.push_back(&d_);
54 }
55
56 protected:
CreateChecker()57 scoped_refptr<HeaderChecker> CreateChecker() {
58 bool check_generated = false;
59 bool check_system = true;
60 return base::MakeRefCounted<HeaderChecker>(
61 setup_.build_settings(), targets_, check_generated, check_system);
62 }
63
64 TestWithScope setup_;
65
66 // Some headers that are automatically set up with a public dependency chain.
67 // a -> b -> c. D is unconnected.
68 Target a_;
69 Target b_;
70 Target c_;
71 Target d_;
72
73 std::vector<const Target*> targets_;
74 };
75
76 } // namespace
77
PrintTo(const SourceFile & source_file,::std::ostream * os)78 void PrintTo(const SourceFile& source_file, ::std::ostream* os) {
79 *os << source_file.value();
80 }
81
TEST_F(HeaderCheckerTest,IsDependencyOf)82 TEST_F(HeaderCheckerTest, IsDependencyOf) {
83 auto checker = CreateChecker();
84
85 // Add a target P ("private") that privately depends on C, and hook up the
86 // chain so that A -> P -> C. A will depend on C via two different paths.
87 Err err;
88 Target p(setup_.settings(), Label(SourceDir("//p/"), "p"));
89 p.set_output_type(Target::SOURCE_SET);
90 p.SetToolchain(setup_.toolchain(), &err);
91 EXPECT_FALSE(err.has_error());
92 p.private_deps().push_back(LabelTargetPair(&c_));
93 p.visibility().SetPublic();
94 p.OnResolved(&err);
95
96 a_.public_deps().push_back(LabelTargetPair(&p));
97
98 // A does not depend on itself.
99 bool is_permitted = false;
100 HeaderChecker::Chain chain;
101 EXPECT_FALSE(checker->IsDependencyOf(&a_, &a_, &chain, &is_permitted));
102
103 // A depends publicly on B.
104 chain.clear();
105 is_permitted = false;
106 EXPECT_TRUE(checker->IsDependencyOf(&b_, &a_, &chain, &is_permitted));
107 ASSERT_EQ(2u, chain.size());
108 EXPECT_EQ(HeaderChecker::ChainLink(&b_, true), chain[0]);
109 EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[1]);
110 EXPECT_TRUE(is_permitted);
111
112 // A indirectly depends on C. The "public" dependency path through B should
113 // be identified.
114 chain.clear();
115 is_permitted = false;
116 EXPECT_TRUE(checker->IsDependencyOf(&c_, &a_, &chain, &is_permitted));
117 ASSERT_EQ(3u, chain.size());
118 EXPECT_EQ(HeaderChecker::ChainLink(&c_, true), chain[0]);
119 EXPECT_EQ(HeaderChecker::ChainLink(&b_, true), chain[1]);
120 EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[2]);
121 EXPECT_TRUE(is_permitted);
122
123 // C does not depend on A.
124 chain.clear();
125 is_permitted = false;
126 EXPECT_FALSE(checker->IsDependencyOf(&a_, &c_, &chain, &is_permitted));
127 EXPECT_TRUE(chain.empty());
128 EXPECT_FALSE(is_permitted);
129
130 // Remove the B -> C public dependency, leaving P's private dep on C the only
131 // path from A to C. This should now be found.
132 chain.clear();
133 EXPECT_EQ(&c_, b_.public_deps()[0].ptr); // Validate it's the right one.
134 b_.public_deps().erase(b_.public_deps().begin());
135 EXPECT_TRUE(checker->IsDependencyOf(&c_, &a_, &chain, &is_permitted));
136 EXPECT_EQ(3u, chain.size());
137 EXPECT_EQ(HeaderChecker::ChainLink(&c_, false), chain[0]);
138 EXPECT_EQ(HeaderChecker::ChainLink(&p, true), chain[1]);
139 EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[2]);
140 EXPECT_FALSE(is_permitted);
141
142 // P privately depends on C. That dependency should be OK since it's only
143 // one hop.
144 chain.clear();
145 is_permitted = false;
146 EXPECT_TRUE(checker->IsDependencyOf(&c_, &p, &chain, &is_permitted));
147 ASSERT_EQ(2u, chain.size());
148 EXPECT_EQ(HeaderChecker::ChainLink(&c_, false), chain[0]);
149 EXPECT_EQ(HeaderChecker::ChainLink(&p, true), chain[1]);
150 EXPECT_TRUE(is_permitted);
151 }
152
TEST_F(HeaderCheckerTest,CheckInclude)153 TEST_F(HeaderCheckerTest, CheckInclude) {
154 InputFile input_file(SourceFile("//some_file.cc"));
155 input_file.SetContents(std::string());
156 LocationRange range; // Dummy value.
157
158 // Add a disconnected target d with a header to check that you have to have
159 // to depend on a target listing a header.
160 SourceFile d_header("//d_header.h");
161 d_.sources().push_back(SourceFile(d_header));
162
163 // Add a header on B and say everything in B is public.
164 SourceFile b_public("//b_public.h");
165 b_.sources().push_back(b_public);
166 c_.set_all_headers_public(true);
167
168 // Add a public and private header on C.
169 SourceFile c_public("//c_public.h");
170 SourceFile c_private("//c_private.h");
171 c_.sources().push_back(c_private);
172 c_.public_headers().push_back(c_public);
173 c_.set_all_headers_public(false);
174
175 // Create another toolchain.
176 Settings other_settings(setup_.build_settings(), "other/");
177 Toolchain other_toolchain(&other_settings,
178 Label(SourceDir("//toolchain/"), "other"));
179 TestWithScope::SetupToolchain(&other_toolchain);
180 other_settings.set_toolchain_label(other_toolchain.label());
181 other_settings.set_default_toolchain_label(setup_.toolchain()->label());
182
183 // Add a target in the other toolchain with a header in it that is not
184 // connected to any targets in the main toolchain.
185 Target otc(&other_settings,
186 Label(SourceDir("//p/"), "otc", other_toolchain.label().dir(),
187 other_toolchain.label().name()));
188 otc.set_output_type(Target::SOURCE_SET);
189 Err err;
190 EXPECT_TRUE(otc.SetToolchain(&other_toolchain, &err));
191 otc.visibility().SetPublic();
192 targets_.push_back(&otc);
193
194 SourceFile otc_header("//otc_header.h");
195 otc.sources().push_back(otc_header);
196 EXPECT_TRUE(otc.OnResolved(&err));
197
198 auto checker = CreateChecker();
199
200 std::set<std::pair<const Target*, const Target*>> no_dependency_cache;
201 // A file in target A can't include a header from D because A has no
202 // dependency on D.
203 std::vector<Err> errors;
204 checker->CheckInclude(&a_, input_file, d_header, range, &no_dependency_cache,
205 &errors);
206 EXPECT_GT(errors.size(), 0);
207
208 // A can include the public header in B.
209 errors.clear();
210 no_dependency_cache.clear();
211 checker->CheckInclude(&a_, input_file, b_public, range, &no_dependency_cache,
212 &errors);
213 EXPECT_EQ(errors.size(), 0);
214
215 // Check A depending on the public and private headers in C.
216 errors.clear();
217 no_dependency_cache.clear();
218 checker->CheckInclude(&a_, input_file, c_public, range, &no_dependency_cache,
219 &errors);
220 EXPECT_EQ(errors.size(), 0);
221 errors.clear();
222 no_dependency_cache.clear();
223 checker->CheckInclude(&a_, input_file, c_private, range, &no_dependency_cache,
224 &errors);
225 EXPECT_GT(errors.size(), 0);
226
227 // A can depend on a random file unknown to the build.
228 errors.clear();
229 no_dependency_cache.clear();
230 checker->CheckInclude(&a_, input_file, SourceFile("//random.h"), range,
231 &no_dependency_cache, &errors);
232 EXPECT_EQ(errors.size(), 0);
233
234 // A can depend on a file present only in another toolchain even with no
235 // dependency path.
236 errors.clear();
237 no_dependency_cache.clear();
238 checker->CheckInclude(&a_, input_file, otc_header, range,
239 &no_dependency_cache, &errors);
240 EXPECT_EQ(errors.size(), 0);
241 }
242
243 // A public chain of dependencies should always be identified first, even if
244 // it is longer than a private one.
TEST_F(HeaderCheckerTest,PublicFirst)245 TEST_F(HeaderCheckerTest, PublicFirst) {
246 // Now make a A -> Z -> D private dependency chain (one shorter than the
247 // public one to get to D).
248 Target z(setup_.settings(), Label(SourceDir("//a/"), "a"));
249 z.set_output_type(Target::SOURCE_SET);
250 Err err;
251 EXPECT_TRUE(z.SetToolchain(setup_.toolchain(), &err));
252 z.private_deps().push_back(LabelTargetPair(&d_));
253 EXPECT_TRUE(z.OnResolved(&err));
254 targets_.push_back(&z);
255
256 a_.private_deps().push_back(LabelTargetPair(&z));
257
258 // Check that D can be found from A, but since it's private, it will be
259 // marked as not permitted.
260 bool is_permitted = false;
261 HeaderChecker::Chain chain;
262 auto checker = CreateChecker();
263 EXPECT_TRUE(checker->IsDependencyOf(&d_, &a_, &chain, &is_permitted));
264
265 EXPECT_FALSE(is_permitted);
266 ASSERT_EQ(3u, chain.size());
267 EXPECT_EQ(HeaderChecker::ChainLink(&d_, false), chain[0]);
268 EXPECT_EQ(HeaderChecker::ChainLink(&z, false), chain[1]);
269 EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[2]);
270
271 // Hook up D to the existing public A -> B -> C chain to make a long one, and
272 // search for D again.
273 c_.public_deps().push_back(LabelTargetPair(&d_));
274 checker = CreateChecker();
275 chain.clear();
276 EXPECT_TRUE(checker->IsDependencyOf(&d_, &a_, &chain, &is_permitted));
277
278 // This should have found the long public one.
279 EXPECT_TRUE(is_permitted);
280 ASSERT_EQ(4u, chain.size());
281 EXPECT_EQ(HeaderChecker::ChainLink(&d_, true), chain[0]);
282 EXPECT_EQ(HeaderChecker::ChainLink(&c_, true), chain[1]);
283 EXPECT_EQ(HeaderChecker::ChainLink(&b_, true), chain[2]);
284 EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[3]);
285 }
286
287 // Checks that the allow_circular_includes_from list works.
TEST_F(HeaderCheckerTest,CheckIncludeAllowCircular)288 TEST_F(HeaderCheckerTest, CheckIncludeAllowCircular) {
289 InputFile input_file(SourceFile("//some_file.cc"));
290 input_file.SetContents(std::string());
291 LocationRange range; // Dummy value.
292
293 // Add an include file to A.
294 SourceFile a_public("//a_public.h");
295 a_.sources().push_back(a_public);
296
297 auto checker = CreateChecker();
298
299 // A depends on B. So B normally can't include headers from A.
300 std::vector<Err> errors;
301 std::set<std::pair<const Target*, const Target*>> no_dependency_cache;
302 checker->CheckInclude(&b_, input_file, a_public, range, &no_dependency_cache,
303 &errors);
304 EXPECT_GT(errors.size(), 0);
305
306 // Add an allow_circular_includes_from on A that lists B.
307 a_.allow_circular_includes_from().insert(b_.label());
308
309 // Now the include from B to A should be allowed.
310 errors.clear();
311 no_dependency_cache.clear();
312 checker->CheckInclude(&b_, input_file, a_public, range, &no_dependency_cache,
313 &errors);
314 EXPECT_EQ(errors.size(), 0);
315 }
316
317 // Check that CheckInclude() support swift targets.
TEST_F(HeaderCheckerTest,CheckIncludeSwiftModule)318 TEST_F(HeaderCheckerTest, CheckIncludeSwiftModule) {
319 // A target S that build a swift module.
320 Target s(setup_.settings(), Label(SourceDir("//s"), "s"));
321 s.set_output_type(Target::SOURCE_SET);
322
323 Err err;
324 s.SetToolchain(setup_.toolchain(), &err);
325 ASSERT_FALSE(err.has_error());
326
327 const SourceFile bridge_header("//bridge.h");
328 const SourceFile generated_header("//out/Debug/gen/s/s.h");
329
330 // S contains Swift sources and has bridge header set.
331 s.swift_values().module_name() = "s";
332 s.swift_values().bridge_header() = SourceFile("//bridge.h");
333 s.sources().push_back(SourceFile("//some_file.swift"));
334 s.source_types_used().Set(SourceFile::SOURCE_SWIFT);
335 s.visibility().SetPublic();
336
337 s.OnResolved(&err);
338 ASSERT_FALSE(err.has_error());
339 targets_.push_back(&s);
340
341 auto checker = CreateChecker();
342
343 InputFile input_file(SourceFile("//some_file.cc"));
344 input_file.SetContents(std::string());
345 LocationRange range; // Dummy value.
346
347 std::vector<Err> errors;
348 std::set<std::pair<const Target*, const Target*>> no_dependency_cache;
349
350 // Check that unrelated target D cannot include header generated by S.
351 errors.clear();
352 no_dependency_cache.clear();
353 checker->CheckInclude(&d_, input_file, generated_header, range,
354 &no_dependency_cache, &errors);
355 EXPECT_GT(errors.size(), 0);
356
357 // Check that unrelated target D cannot include S's bridge header.
358 errors.clear();
359 no_dependency_cache.clear();
360 checker->CheckInclude(&d_, input_file, bridge_header, range,
361 &no_dependency_cache, &errors);
362 EXPECT_GT(errors.size(), 0);
363 }
364
TEST_F(HeaderCheckerTest,SourceFileForInclude)365 TEST_F(HeaderCheckerTest, SourceFileForInclude) {
366 using base::FilePath;
367 const std::vector<SourceDir> kIncludeDirs = {
368 SourceDir("/c/custom_include/"), SourceDir("//"), SourceDir("//subdir")};
369 a_.sources().push_back(SourceFile("//lib/header1.h"));
370 b_.sources().push_back(SourceFile("/c/custom_include/header2.h"));
371 d_.sources().push_back(SourceFile("/d/subdir/header3.h"));
372
373 InputFile dummy_input_file(SourceFile("/d/subdir/some_file.cc"));
374 dummy_input_file.SetContents(std::string());
375
376 auto checker = CreateChecker();
377 {
378 Err err;
379 IncludeStringWithLocation include;
380 include.contents = "lib/header1.h";
381 SourceFile source_file = checker->SourceFileForInclude(
382 include, kIncludeDirs, dummy_input_file, &err);
383 EXPECT_FALSE(err.has_error());
384 EXPECT_EQ(SourceFile("//lib/header1.h"), source_file);
385 }
386
387 {
388 Err err;
389 IncludeStringWithLocation include;
390 include.contents = "header2.h";
391 SourceFile source_file = checker->SourceFileForInclude(
392 include, kIncludeDirs, dummy_input_file, &err);
393 EXPECT_FALSE(err.has_error());
394 EXPECT_EQ(SourceFile("/c/custom_include/header2.h"), source_file);
395 }
396
397 // A non system style include should find a header file in the same directory
398 // as the source file, regardless of include dirs.
399 {
400 Err err;
401 IncludeStringWithLocation include;
402 include.contents = "header3.h";
403 include.system_style_include = false;
404 SourceFile source_file = checker->SourceFileForInclude(
405 include, kIncludeDirs, dummy_input_file, &err);
406 EXPECT_FALSE(err.has_error());
407 EXPECT_EQ(SourceFile("/d/subdir/header3.h"), source_file);
408 }
409
410 // A system style include should *not* find a header file in the same
411 // directory as the source file if that directory is not in the include dirs.
412 {
413 Err err;
414 IncludeStringWithLocation include;
415 include.contents = "header3.h";
416 include.system_style_include = true;
417 SourceFile source_file = checker->SourceFileForInclude(
418 include, kIncludeDirs, dummy_input_file, &err);
419 EXPECT_TRUE(source_file.is_null());
420 EXPECT_FALSE(err.has_error());
421 }
422 }
423
TEST_F(HeaderCheckerTest,SourceFileForInclude_FileNotFound)424 TEST_F(HeaderCheckerTest, SourceFileForInclude_FileNotFound) {
425 using base::FilePath;
426 const char kFileContents[] = "Some dummy contents";
427 const std::vector<SourceDir> kIncludeDirs = {SourceDir("//")};
428 auto checker = CreateChecker();
429
430 Err err;
431 InputFile input_file(SourceFile("//input.cc"));
432 input_file.SetContents(std::string(kFileContents));
433
434 IncludeStringWithLocation include;
435 include.contents = "header.h";
436 SourceFile source_file =
437 checker->SourceFileForInclude(include, kIncludeDirs, input_file, &err);
438 EXPECT_TRUE(source_file.is_null());
439 EXPECT_FALSE(err.has_error());
440 }
441
TEST_F(HeaderCheckerTest,Friend)442 TEST_F(HeaderCheckerTest, Friend) {
443 // Note: we have a public dependency chain A -> B -> C set up already.
444 InputFile input_file(SourceFile("//some_file.cc"));
445 input_file.SetContents(std::string());
446 LocationRange range; // Dummy value.
447
448 // Add a private header on C.
449 SourceFile c_private("//c_private.h");
450 c_.sources().push_back(c_private);
451 c_.set_all_headers_public(false);
452
453 // List A as a friend of C.
454 Err err;
455 c_.friends().push_back(LabelPattern::GetPattern(
456 SourceDir("//"), std::string_view(), Value(nullptr, "//a:*"), &err));
457 ASSERT_FALSE(err.has_error());
458
459 // Must be after setting everything up for it to find the files.
460 auto checker = CreateChecker();
461
462 // B should not be allowed to include C's private header.
463 std::vector<Err> errors;
464 std::set<std::pair<const Target*, const Target*>> no_dependency_cache;
465 checker->CheckInclude(&b_, input_file, c_private, range, &no_dependency_cache,
466 &errors);
467 EXPECT_GT(errors.size(), 0);
468
469 // A should be able to because of the friend declaration.
470 errors.clear();
471 no_dependency_cache.clear();
472 checker->CheckInclude(&a_, input_file, c_private, range, &no_dependency_cache,
473 &errors);
474 EXPECT_EQ(errors.size(), 0);
475 }
476