• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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