1 // Copyright (c) 2013 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 "gn/scope.h"
6
7 #include "gn/input_file.h"
8 #include "gn/parse_tree.h"
9 #include "gn/source_file.h"
10 #include "gn/template.h"
11 #include "gn/test_with_scope.h"
12 #include "util/test/test.h"
13
14 namespace {
15
HasStringValueEqualTo(const Scope * scope,const char * name,const char * expected_value)16 bool HasStringValueEqualTo(const Scope* scope,
17 const char* name,
18 const char* expected_value) {
19 const Value* value = scope->GetValue(name);
20 if (!value)
21 return false;
22 if (value->type() != Value::STRING)
23 return false;
24 return value->string_value() == expected_value;
25 }
26
ContainsBuildDependencyFile(const Scope * scope,const SourceFile & source_file)27 bool ContainsBuildDependencyFile(const Scope* scope,
28 const SourceFile& source_file) {
29 const auto& build_dependency_files = scope->build_dependency_files();
30 return build_dependency_files.end() !=
31 build_dependency_files.find(source_file);
32 }
33
34 } // namespace
35
TEST(Scope,InheritBuildDependencyFilesFromParent)36 TEST(Scope, InheritBuildDependencyFilesFromParent) {
37 TestWithScope setup;
38 SourceFile source_file = SourceFile("//a/BUILD.gn");
39 setup.scope()->AddBuildDependencyFile(source_file);
40
41 Scope new_scope(setup.scope());
42 EXPECT_EQ(1U, new_scope.build_dependency_files().size());
43 EXPECT_TRUE(ContainsBuildDependencyFile(&new_scope, source_file));
44 }
45
TEST(Scope,NonRecursiveMergeTo)46 TEST(Scope, NonRecursiveMergeTo) {
47 TestWithScope setup;
48
49 // Make a pretend parse node with proper tracking that we can blame for the
50 // given value.
51 InputFile input_file(SourceFile("//foo"));
52 Token assignment_token(Location(&input_file, 1, 1), Token::STRING,
53 "\"hello\"");
54 LiteralNode assignment;
55 assignment.set_value(assignment_token);
56
57 // Add some values to the scope.
58 Value old_value(&assignment, "hello");
59 setup.scope()->SetValue("v", old_value, &assignment);
60 std::string_view private_var_name("_private");
61 setup.scope()->SetValue(private_var_name, old_value, &assignment);
62
63 // Add some templates to the scope.
64 FunctionCallNode templ_definition;
65 scoped_refptr<Template> templ(new Template(setup.scope(), &templ_definition));
66 setup.scope()->AddTemplate("templ", templ.get());
67 scoped_refptr<Template> private_templ(
68 new Template(setup.scope(), &templ_definition));
69 setup.scope()->AddTemplate("_templ", private_templ.get());
70
71 // Detect collisions of values' values.
72 {
73 Scope new_scope(setup.settings());
74 Value new_value(&assignment, "goodbye");
75 new_scope.SetValue("v", new_value, &assignment);
76
77 Err err;
78 EXPECT_FALSE(setup.scope()->NonRecursiveMergeTo(
79 &new_scope, Scope::MergeOptions(), &assignment, "error", &err));
80 EXPECT_TRUE(err.has_error());
81 }
82
83 // Template name collisions.
84 {
85 Scope new_scope(setup.settings());
86
87 scoped_refptr<Template> new_templ(
88 new Template(&new_scope, &templ_definition));
89 new_scope.AddTemplate("templ", new_templ.get());
90
91 Err err;
92 EXPECT_FALSE(setup.scope()->NonRecursiveMergeTo(
93 &new_scope, Scope::MergeOptions(), &assignment, "error", &err));
94 EXPECT_TRUE(err.has_error());
95 }
96
97 // The clobber flag should just overwrite colliding values.
98 {
99 Scope new_scope(setup.settings());
100 Value new_value(&assignment, "goodbye");
101 new_scope.SetValue("v", new_value, &assignment);
102
103 Err err;
104 Scope::MergeOptions options;
105 options.clobber_existing = true;
106 EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(&new_scope, options,
107 &assignment, "error", &err));
108 EXPECT_FALSE(err.has_error());
109
110 const Value* found_value = new_scope.GetValue("v");
111 ASSERT_TRUE(found_value);
112 EXPECT_TRUE(old_value == *found_value);
113 }
114
115 // Clobber flag for templates.
116 {
117 Scope new_scope(setup.settings());
118
119 scoped_refptr<Template> new_templ(
120 new Template(&new_scope, &templ_definition));
121 new_scope.AddTemplate("templ", new_templ.get());
122 Scope::MergeOptions options;
123 options.clobber_existing = true;
124
125 Err err;
126 EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(&new_scope, options,
127 &assignment, "error", &err));
128 EXPECT_FALSE(err.has_error());
129
130 const Template* found_value = new_scope.GetTemplate("templ");
131 ASSERT_TRUE(found_value);
132 EXPECT_TRUE(templ.get() == found_value);
133 }
134
135 // Don't flag values that technically collide but have the same value.
136 {
137 Scope new_scope(setup.settings());
138 Value new_value(&assignment, "hello");
139 new_scope.SetValue("v", new_value, &assignment);
140
141 Err err;
142 EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(
143 &new_scope, Scope::MergeOptions(), &assignment, "error", &err));
144 EXPECT_FALSE(err.has_error());
145 }
146
147 // Templates that technically collide but are the same.
148 {
149 Scope new_scope(setup.settings());
150
151 scoped_refptr<Template> new_templ(
152 new Template(&new_scope, &templ_definition));
153 new_scope.AddTemplate("templ", templ.get());
154
155 Err err;
156 EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(
157 &new_scope, Scope::MergeOptions(), &assignment, "error", &err));
158 EXPECT_FALSE(err.has_error());
159 }
160
161 // Copy private values and templates.
162 {
163 Scope new_scope(setup.settings());
164
165 Err err;
166 EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(
167 &new_scope, Scope::MergeOptions(), &assignment, "error", &err));
168 EXPECT_FALSE(err.has_error());
169 EXPECT_TRUE(new_scope.GetValue(private_var_name));
170 EXPECT_TRUE(new_scope.GetTemplate("_templ"));
171 }
172
173 // Skip private values and templates.
174 {
175 Scope new_scope(setup.settings());
176
177 Err err;
178 Scope::MergeOptions options;
179 options.skip_private_vars = true;
180 EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(&new_scope, options,
181 &assignment, "error", &err));
182 EXPECT_FALSE(err.has_error());
183 EXPECT_FALSE(new_scope.GetValue(private_var_name));
184 EXPECT_FALSE(new_scope.GetTemplate("_templ"));
185 }
186
187 // Don't mark used.
188 {
189 Scope new_scope(setup.settings());
190
191 Err err;
192 Scope::MergeOptions options;
193 EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(&new_scope, options,
194 &assignment, "error", &err));
195 EXPECT_FALSE(err.has_error());
196 EXPECT_FALSE(new_scope.CheckForUnusedVars(&err));
197 EXPECT_TRUE(err.has_error());
198 }
199
200 // Mark dest used.
201 {
202 Scope new_scope(setup.settings());
203
204 Err err;
205 Scope::MergeOptions options;
206 options.mark_dest_used = true;
207 EXPECT_TRUE(setup.scope()->NonRecursiveMergeTo(&new_scope, options,
208 &assignment, "error", &err));
209 EXPECT_FALSE(err.has_error());
210 EXPECT_TRUE(new_scope.CheckForUnusedVars(&err));
211 EXPECT_FALSE(err.has_error());
212 }
213
214 // Build dependency files are merged.
215 {
216 Scope from_scope(setup.settings());
217 SourceFile source_file = SourceFile("//a/BUILD.gn");
218 from_scope.AddBuildDependencyFile(source_file);
219
220 Scope to_scope(setup.settings());
221 EXPECT_FALSE(ContainsBuildDependencyFile(&to_scope, source_file));
222
223 Scope::MergeOptions options;
224 Err err;
225 EXPECT_TRUE(from_scope.NonRecursiveMergeTo(&to_scope, options, &assignment,
226 "error", &err));
227 EXPECT_FALSE(err.has_error());
228 EXPECT_EQ(1U, to_scope.build_dependency_files().size());
229 EXPECT_TRUE(ContainsBuildDependencyFile(&to_scope, source_file));
230 }
231 }
232
TEST(Scope,MakeClosure)233 TEST(Scope, MakeClosure) {
234 // Create 3 nested scopes [const root from setup] <- nested1 <- nested2.
235 TestWithScope setup;
236
237 // Make a pretend parse node with proper tracking that we can blame for the
238 // given value.
239 InputFile input_file(SourceFile("//foo"));
240 Token assignment_token(Location(&input_file, 1, 1), Token::STRING,
241 "\"hello\"");
242 LiteralNode assignment;
243 assignment.set_value(assignment_token);
244 setup.scope()->SetValue("on_root", Value(&assignment, "on_root"),
245 &assignment);
246
247 // Root scope should be const from the nested caller's perspective.
248 Scope nested1(static_cast<const Scope*>(setup.scope()));
249 nested1.SetValue("on_one", Value(&assignment, "on_one"), &assignment);
250
251 Scope nested2(&nested1);
252 nested2.SetValue("on_one", Value(&assignment, "on_two"), &assignment);
253 nested2.SetValue("on_two", Value(&assignment, "on_two2"), &assignment);
254
255 // Making a closure from the root scope.
256 std::unique_ptr<Scope> result = setup.scope()->MakeClosure();
257 EXPECT_FALSE(result->containing()); // Should have no containing scope.
258 EXPECT_TRUE(result->GetValue("on_root")); // Value should be copied.
259
260 // Making a closure from the second nested scope.
261 result = nested2.MakeClosure();
262 EXPECT_EQ(setup.scope(),
263 result->containing()); // Containing scope should be the root.
264 EXPECT_TRUE(HasStringValueEqualTo(result.get(), "on_root", "on_root"));
265 EXPECT_TRUE(HasStringValueEqualTo(result.get(), "on_one", "on_two"));
266 EXPECT_TRUE(HasStringValueEqualTo(result.get(), "on_two", "on_two2"));
267 }
268
TEST(Scope,GetMutableValue)269 TEST(Scope, GetMutableValue) {
270 TestWithScope setup;
271
272 // Make a pretend parse node with proper tracking that we can blame for the
273 // given value.
274 InputFile input_file(SourceFile("//foo"));
275 Token assignment_token(Location(&input_file, 1, 1), Token::STRING,
276 "\"hello\"");
277 LiteralNode assignment;
278 assignment.set_value(assignment_token);
279
280 const char kOnConst[] = "on_const";
281 const char kOnMutable1[] = "on_mutable1";
282 const char kOnMutable2[] = "on_mutable2";
283
284 Value value(&assignment, "hello");
285
286 // Create a root scope with one value.
287 Scope root_scope(setup.settings());
288 root_scope.SetValue(kOnConst, value, &assignment);
289
290 // Create a first nested scope with a different value.
291 const Scope* const_root_scope = &root_scope;
292 Scope mutable_scope1(const_root_scope);
293 mutable_scope1.SetValue(kOnMutable1, value, &assignment);
294
295 // Create a second nested scope with a different value.
296 Scope mutable_scope2(&mutable_scope1);
297 mutable_scope2.SetValue(kOnMutable2, value, &assignment);
298
299 // Check getting root scope values.
300 EXPECT_TRUE(mutable_scope2.GetValue(kOnConst, true));
301 EXPECT_FALSE(
302 mutable_scope2.GetMutableValue(kOnConst, Scope::SEARCH_NESTED, true));
303
304 // Test reading a value from scope 1.
305 Value* mutable1_result =
306 mutable_scope2.GetMutableValue(kOnMutable1, Scope::SEARCH_NESTED, false);
307 ASSERT_TRUE(mutable1_result);
308 EXPECT_TRUE(*mutable1_result == value);
309
310 // Make sure CheckForUnusedVars works on scope1 (we didn't mark the value as
311 // used in the previous step).
312 Err err;
313 EXPECT_FALSE(mutable_scope1.CheckForUnusedVars(&err));
314 mutable1_result =
315 mutable_scope2.GetMutableValue(kOnMutable1, Scope::SEARCH_NESTED, true);
316 EXPECT_TRUE(mutable1_result);
317 err = Err();
318 EXPECT_TRUE(mutable_scope1.CheckForUnusedVars(&err));
319
320 // Test reading a value from scope 2.
321 Value* mutable2_result =
322 mutable_scope2.GetMutableValue(kOnMutable2, Scope::SEARCH_NESTED, true);
323 ASSERT_TRUE(mutable2_result);
324 EXPECT_TRUE(*mutable2_result == value);
325 }
326
TEST(Scope,RemovePrivateIdentifiers)327 TEST(Scope, RemovePrivateIdentifiers) {
328 TestWithScope setup;
329 setup.scope()->SetValue("a", Value(nullptr, true), nullptr);
330 setup.scope()->SetValue("_b", Value(nullptr, true), nullptr);
331
332 setup.scope()->RemovePrivateIdentifiers();
333 EXPECT_TRUE(setup.scope()->GetValue("a"));
334 EXPECT_FALSE(setup.scope()->GetValue("_b"));
335 }
336