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 <memory>
8
9 #include "base/logging.h"
10 #include "gn/parse_tree.h"
11 #include "gn/source_file.h"
12 #include "gn/template.h"
13
14 namespace {
15
16 // FLags set in the mode_flags_ of a scope. If a bit is set, it applies
17 // recursively to all dependent scopes.
18 const unsigned kProcessingBuildConfigFlag = 1;
19 const unsigned kProcessingImportFlag = 2;
20
21 // Returns true if this variable name should be considered private. Private
22 // values start with an underscore, and are not imported from "gni" files
23 // when processing an import.
IsPrivateVar(std::string_view name)24 bool IsPrivateVar(std::string_view name) {
25 return name.empty() || name[0] == '_';
26 }
27
28 } // namespace
29
30 // Defaults to all false, which are the things least likely to cause errors.
MergeOptions()31 Scope::MergeOptions::MergeOptions()
32 : clobber_existing(false),
33 skip_private_vars(false),
34 mark_dest_used(false) {}
35
36 Scope::MergeOptions::~MergeOptions() = default;
37
~ProgrammaticProvider()38 Scope::ProgrammaticProvider::~ProgrammaticProvider() {
39 scope_->RemoveProvider(this);
40 }
41
Describe() const42 std::string Scope::TemplateInvocationEntry::Describe() const {
43 std::string ret = template_name;
44 ret += "(\"" + target_name + "\") ";
45 ret += location.Describe(false);
46 return ret;
47 }
48
Scope(const Settings * settings)49 Scope::Scope(const Settings* settings)
50 : const_containing_(nullptr),
51 mutable_containing_(nullptr),
52 settings_(settings),
53 mode_flags_(0),
54 item_collector_(nullptr) {}
55
Scope(Scope * parent)56 Scope::Scope(Scope* parent)
57 : const_containing_(nullptr),
58 mutable_containing_(parent),
59 settings_(parent->settings()),
60 mode_flags_(0),
61 item_collector_(nullptr),
62 build_dependency_files_(parent->build_dependency_files_) {}
63
Scope(const Scope * parent)64 Scope::Scope(const Scope* parent)
65 : const_containing_(parent),
66 mutable_containing_(nullptr),
67 settings_(parent->settings()),
68 mode_flags_(0),
69 item_collector_(nullptr),
70 build_dependency_files_(parent->build_dependency_files_) {}
71
72 Scope::~Scope() = default;
73
DetachFromContaining()74 void Scope::DetachFromContaining() {
75 const_containing_ = nullptr;
76 mutable_containing_ = nullptr;
77 }
78
HasValues(SearchNested search_nested) const79 bool Scope::HasValues(SearchNested search_nested) const {
80 DCHECK(search_nested == SEARCH_CURRENT);
81 return !values_.empty();
82 }
83
GetValue(std::string_view ident,bool counts_as_used)84 const Value* Scope::GetValue(std::string_view ident, bool counts_as_used) {
85 const Scope* found_in_scope = nullptr;
86 return GetValueWithScope(ident, counts_as_used, &found_in_scope);
87 }
88
GetValueWithScope(std::string_view ident,bool counts_as_used,const Scope ** found_in_scope)89 const Value* Scope::GetValueWithScope(std::string_view ident,
90 bool counts_as_used,
91 const Scope** found_in_scope) {
92 // First check for programmatically-provided values.
93 for (auto* provider : programmatic_providers_) {
94 const Value* v = provider->GetProgrammaticValue(ident);
95 if (v) {
96 *found_in_scope = nullptr;
97 return v;
98 }
99 }
100
101 RecordMap::iterator found = values_.find(ident);
102 if (found != values_.end()) {
103 if (counts_as_used)
104 found->second.used = true;
105 *found_in_scope = this;
106 return &found->second.value;
107 }
108
109 // Search in the parent scope.
110 if (const_containing_)
111 return const_containing_->GetValueWithScope(ident, found_in_scope);
112 if (mutable_containing_) {
113 return mutable_containing_->GetValueWithScope(ident, counts_as_used,
114 found_in_scope);
115 }
116 return nullptr;
117 }
118
GetMutableValue(std::string_view ident,SearchNested search_mode,bool counts_as_used)119 Value* Scope::GetMutableValue(std::string_view ident,
120 SearchNested search_mode,
121 bool counts_as_used) {
122 // Don't do programmatic values, which are not mutable.
123 RecordMap::iterator found = values_.find(ident);
124 if (found != values_.end()) {
125 if (counts_as_used)
126 found->second.used = true;
127 return &found->second.value;
128 }
129
130 // Search in the parent mutable scope if requested, but not const one.
131 if (search_mode == SEARCH_NESTED && mutable_containing_) {
132 return mutable_containing_->GetMutableValue(ident, Scope::SEARCH_NESTED,
133 counts_as_used);
134 }
135 return nullptr;
136 }
137
GetStorageKey(std::string_view ident) const138 std::string_view Scope::GetStorageKey(std::string_view ident) const {
139 RecordMap::const_iterator found = values_.find(ident);
140 if (found != values_.end())
141 return found->first;
142
143 // Search in parent scope.
144 if (containing())
145 return containing()->GetStorageKey(ident);
146 return std::string_view();
147 }
148
GetValue(std::string_view ident) const149 const Value* Scope::GetValue(std::string_view ident) const {
150 const Scope* found_in_scope = nullptr;
151 return GetValueWithScope(ident, &found_in_scope);
152 }
153
GetValueWithScope(std::string_view ident,const Scope ** found_in_scope) const154 const Value* Scope::GetValueWithScope(std::string_view ident,
155 const Scope** found_in_scope) const {
156 RecordMap::const_iterator found = values_.find(ident);
157 if (found != values_.end()) {
158 *found_in_scope = this;
159 return &found->second.value;
160 }
161 if (containing())
162 return containing()->GetValueWithScope(ident, found_in_scope);
163 return nullptr;
164 }
165
SetValue(std::string_view ident,Value v,const ParseNode * set_node)166 Value* Scope::SetValue(std::string_view ident,
167 Value v,
168 const ParseNode* set_node) {
169 Record& r = values_[ident]; // Clears any existing value.
170 r.value = std::move(v);
171 r.value.set_origin(set_node);
172 return &r.value;
173 }
174
RemoveIdentifier(std::string_view ident)175 void Scope::RemoveIdentifier(std::string_view ident) {
176 RecordMap::iterator found = values_.find(ident);
177 if (found != values_.end())
178 values_.erase(found);
179 }
180
RemovePrivateIdentifiers()181 void Scope::RemovePrivateIdentifiers() {
182 // Do it in two phases to avoid mutating while iterating. Our hash map is
183 // currently backed by several different vendor-specific implementations and
184 // I'm not sure if all of them support mutating while iterating. Since this
185 // is not perf-critical, do the safe thing.
186 std::vector<std::string_view> to_remove;
187 for (const auto& cur : values_) {
188 if (IsPrivateVar(cur.first))
189 to_remove.push_back(cur.first);
190 }
191
192 for (const auto& cur : to_remove)
193 values_.erase(cur);
194 }
195
AddTemplate(const std::string & name,const Template * templ)196 bool Scope::AddTemplate(const std::string& name, const Template* templ) {
197 if (GetTemplate(name))
198 return false;
199 templates_[name] = templ;
200 return true;
201 }
202
GetTemplate(const std::string & name) const203 const Template* Scope::GetTemplate(const std::string& name) const {
204 TemplateMap::const_iterator found = templates_.find(name);
205 if (found != templates_.end())
206 return found->second.get();
207 if (containing())
208 return containing()->GetTemplate(name);
209 return nullptr;
210 }
211
MarkUsed(std::string_view ident)212 void Scope::MarkUsed(std::string_view ident) {
213 RecordMap::iterator found = values_.find(ident);
214 if (found == values_.end()) {
215 NOTREACHED();
216 return;
217 }
218 found->second.used = true;
219 }
220
MarkAllUsed()221 void Scope::MarkAllUsed() {
222 for (auto& cur : values_)
223 cur.second.used = true;
224 }
225
MarkAllUsed(const std::set<std::string> & excluded_values)226 void Scope::MarkAllUsed(const std::set<std::string>& excluded_values) {
227 for (auto& cur : values_) {
228 if (!excluded_values.empty() &&
229 excluded_values.find(std::string(cur.first)) != excluded_values.end()) {
230 continue; // Skip this excluded value.
231 }
232 cur.second.used = true;
233 }
234 }
235
MarkUnused(std::string_view ident)236 void Scope::MarkUnused(std::string_view ident) {
237 RecordMap::iterator found = values_.find(ident);
238 if (found == values_.end()) {
239 NOTREACHED();
240 return;
241 }
242 found->second.used = false;
243 }
244
IsSetButUnused(std::string_view ident) const245 bool Scope::IsSetButUnused(std::string_view ident) const {
246 RecordMap::const_iterator found = values_.find(ident);
247 if (found != values_.end()) {
248 if (!found->second.used) {
249 return true;
250 }
251 }
252 return false;
253 }
254
CheckForUnusedVars(Err * err) const255 bool Scope::CheckForUnusedVars(Err* err) const {
256 for (const auto& pair : values_) {
257 if (!pair.second.used) {
258 std::string help =
259 "You set the variable \"" + std::string(pair.first) +
260 "\" here and it was unused before it went\nout of scope.";
261
262 // Gather the template invocations that led up to this scope.
263 auto entries = GetTemplateInvocationEntries();
264 if (entries.size() != 0) {
265 help.append("\n\nVia these template invocations:\n");
266 for (const auto& entry : entries) {
267 help.append(" " + entry.Describe() + "\n");
268 }
269 }
270
271 const BinaryOpNode* binary = pair.second.value.origin()->AsBinaryOp();
272 if (binary && binary->op().type() == Token::EQUAL) {
273 // Make a nicer error message for normal var sets.
274 *err =
275 Err(binary->left()->GetRange(), "Assignment had no effect.", help);
276 } else {
277 // This will happen for internally-generated variables.
278 *err =
279 Err(pair.second.value.origin(), "Assignment had no effect.", help);
280 }
281 return false;
282 }
283 }
284 return true;
285 }
286
GetCurrentScopeValues(KeyValueMap * output) const287 void Scope::GetCurrentScopeValues(KeyValueMap* output) const {
288 for (const auto& pair : values_)
289 (*output)[pair.first] = pair.second.value;
290 }
291
CheckCurrentScopeValuesEqual(const Scope * other) const292 bool Scope::CheckCurrentScopeValuesEqual(const Scope* other) const {
293 // If there are containing scopes, equality shouldn't work.
294 if (containing()) {
295 return false;
296 }
297 if (values_.size() != other->values_.size()) {
298 return false;
299 }
300 for (const auto& pair : values_) {
301 const Value* v = other->GetValue(pair.first);
302 if (!v || *v != pair.second.value) {
303 return false;
304 }
305 }
306 return true;
307 }
308
NonRecursiveMergeTo(Scope * dest,const MergeOptions & options,const ParseNode * node_for_err,const char * desc_for_err,Err * err) const309 bool Scope::NonRecursiveMergeTo(Scope* dest,
310 const MergeOptions& options,
311 const ParseNode* node_for_err,
312 const char* desc_for_err,
313 Err* err) const {
314 // Values.
315 for (const auto& pair : values_) {
316 const std::string_view current_name = pair.first;
317 if (options.skip_private_vars && IsPrivateVar(current_name))
318 continue; // Skip this private var.
319 if (!options.excluded_values.empty() &&
320 options.excluded_values.find(std::string(current_name)) !=
321 options.excluded_values.end()) {
322 continue; // Skip this excluded value.
323 }
324
325 const Value& new_value = pair.second.value;
326 if (!options.clobber_existing) {
327 const Value* existing_value = dest->GetValue(current_name);
328 if (existing_value && new_value != *existing_value) {
329 // Value present in both the source and the dest.
330 std::string desc_string(desc_for_err);
331 *err = Err(node_for_err, "Value collision.",
332 "This " + desc_string + " contains \"" +
333 std::string(current_name) + "\"");
334 err->AppendSubErr(
335 Err(pair.second.value, "defined here.",
336 "Which would clobber the one in your current scope"));
337 err->AppendSubErr(
338 Err(*existing_value, "defined here.",
339 "Executing " + desc_string +
340 " should not conflict with anything "
341 "in the current\nscope unless the values are identical."));
342 return false;
343 }
344 }
345 dest->values_[current_name] = pair.second;
346
347 if (options.mark_dest_used)
348 dest->MarkUsed(current_name);
349 }
350
351 // Target defaults are owning pointers.
352 for (const auto& pair : target_defaults_) {
353 const std::string& current_name = pair.first;
354 if (!options.excluded_values.empty() &&
355 options.excluded_values.find(current_name) !=
356 options.excluded_values.end()) {
357 continue; // Skip the excluded value.
358 }
359
360 if (!options.clobber_existing) {
361 const Scope* dest_defaults = dest->GetTargetDefaults(current_name);
362 if (dest_defaults) {
363 if (RecordMapValuesEqual(pair.second->values_,
364 dest_defaults->values_)) {
365 // Values of the two defaults are equivalent, just ignore the
366 // collision.
367 continue;
368 } else {
369 // TODO(brettw) it would be nice to know the origin of a
370 // set_target_defaults so we can give locations for the colliding
371 // target defaults.
372 std::string desc_string(desc_for_err);
373 *err = Err(node_for_err, "Target defaults collision.",
374 "This " + desc_string +
375 " contains target defaults for\n"
376 "\"" +
377 current_name +
378 "\" which would clobber one for the\n"
379 "same target type in your current scope. It's "
380 "unfortunate that "
381 "I'm too stupid\nto tell you the location of where "
382 "the target "
383 "defaults were set. Usually\nthis happens in the "
384 "BUILDCONFIG.gn "
385 "file or in a related .gni file.\n");
386 return false;
387 }
388 }
389 }
390
391 std::unique_ptr<Scope>& dest_scope = dest->target_defaults_[current_name];
392 dest_scope = std::make_unique<Scope>(settings_);
393 pair.second->NonRecursiveMergeTo(dest_scope.get(), options, node_for_err,
394 "<SHOULDN'T HAPPEN>", err);
395 }
396
397 // Templates.
398 for (const auto& pair : templates_) {
399 const std::string& current_name = pair.first;
400 if (options.skip_private_vars && IsPrivateVar(current_name))
401 continue; // Skip this private template.
402 if (!options.excluded_values.empty() &&
403 options.excluded_values.find(current_name) !=
404 options.excluded_values.end()) {
405 continue; // Skip the excluded value.
406 }
407
408 if (!options.clobber_existing) {
409 const Template* existing_template = dest->GetTemplate(current_name);
410 // Since templates are refcounted, we can check if it's the same one by
411 // comparing pointers.
412 if (existing_template && pair.second.get() != existing_template) {
413 // Rule present in both the source and the dest, and they're not the
414 // same one.
415 std::string desc_string(desc_for_err);
416 *err = Err(node_for_err, "Template collision.",
417 "This " + desc_string + " contains a template \"" +
418 current_name + "\"");
419 err->AppendSubErr(
420 Err(pair.second->GetDefinitionRange(), "defined here.",
421 "Which would clobber the one in your current scope"));
422 err->AppendSubErr(Err(existing_template->GetDefinitionRange(),
423 "defined here.",
424 "Executing " + desc_string +
425 " should not conflict with anything "
426 "in the current\nscope."));
427 return false;
428 }
429 }
430
431 // Be careful to delete any pointer we're about to clobber.
432 dest->templates_[current_name] = pair.second;
433 }
434
435 // Propagate build dependency files,
436 dest->AddBuildDependencyFiles(build_dependency_files_);
437
438 return true;
439 }
440
MakeClosure() const441 std::unique_ptr<Scope> Scope::MakeClosure() const {
442 std::unique_ptr<Scope> result;
443 if (const_containing_) {
444 // We reached the top of the mutable scope stack. The result scope just
445 // references the const scope (which will never change).
446 result = std::make_unique<Scope>(const_containing_);
447 } else if (mutable_containing_) {
448 // There are more nested mutable scopes. Recursively go up the stack to
449 // get the closure.
450 result = mutable_containing_->MakeClosure();
451 } else {
452 // This is a standalone scope, just copy it.
453 result = std::make_unique<Scope>(settings_);
454 }
455
456 // Want to clobber since we've flattened some nested scopes, and our parent
457 // scope may have a duplicate value set.
458 MergeOptions options;
459 options.clobber_existing = true;
460
461 // Add in our variables and we're done.
462 Err err;
463 NonRecursiveMergeTo(result.get(), options, nullptr, "<SHOULDN'T HAPPEN>",
464 &err);
465 DCHECK(!err.has_error());
466 return result;
467 }
468
MakeTargetDefaults(const std::string & target_type)469 Scope* Scope::MakeTargetDefaults(const std::string& target_type) {
470 std::unique_ptr<Scope>& dest = target_defaults_[target_type];
471 dest = std::make_unique<Scope>(settings_);
472 return dest.get();
473 }
474
GetTargetDefaults(const std::string & target_type) const475 const Scope* Scope::GetTargetDefaults(const std::string& target_type) const {
476 NamedScopeMap::const_iterator found = target_defaults_.find(target_type);
477 if (found != target_defaults_.end())
478 return found->second.get();
479 if (containing())
480 return containing()->GetTargetDefaults(target_type);
481 return nullptr;
482 }
483
SetProcessingBuildConfig()484 void Scope::SetProcessingBuildConfig() {
485 DCHECK((mode_flags_ & kProcessingBuildConfigFlag) == 0);
486 mode_flags_ |= kProcessingBuildConfigFlag;
487 }
488
ClearProcessingBuildConfig()489 void Scope::ClearProcessingBuildConfig() {
490 DCHECK(mode_flags_ & kProcessingBuildConfigFlag);
491 mode_flags_ &= ~(kProcessingBuildConfigFlag);
492 }
493
IsProcessingBuildConfig() const494 bool Scope::IsProcessingBuildConfig() const {
495 if (mode_flags_ & kProcessingBuildConfigFlag)
496 return true;
497 if (containing())
498 return containing()->IsProcessingBuildConfig();
499 return false;
500 }
501
SetProcessingImport()502 void Scope::SetProcessingImport() {
503 DCHECK((mode_flags_ & kProcessingImportFlag) == 0);
504 mode_flags_ |= kProcessingImportFlag;
505 }
506
ClearProcessingImport()507 void Scope::ClearProcessingImport() {
508 DCHECK(mode_flags_ & kProcessingImportFlag);
509 mode_flags_ &= ~(kProcessingImportFlag);
510 }
511
IsProcessingImport() const512 bool Scope::IsProcessingImport() const {
513 if (mode_flags_ & kProcessingImportFlag)
514 return true;
515 if (containing())
516 return containing()->IsProcessingImport();
517 return false;
518 }
519
GetSourceDir() const520 const SourceDir& Scope::GetSourceDir() const {
521 if (!source_dir_.is_null())
522 return source_dir_;
523 if (containing())
524 return containing()->GetSourceDir();
525 return source_dir_;
526 }
527
AddBuildDependencyFile(const SourceFile & build_dependency_file)528 void Scope::AddBuildDependencyFile(const SourceFile& build_dependency_file) {
529 build_dependency_files_.insert(build_dependency_file);
530 }
531
AddBuildDependencyFiles(const SourceFileSet & build_dependency_files)532 void Scope::AddBuildDependencyFiles(
533 const SourceFileSet& build_dependency_files) {
534 build_dependency_files_.insert(build_dependency_files.begin(),
535 build_dependency_files.end());
536 }
537
GetItemCollector()538 Scope::ItemVector* Scope::GetItemCollector() {
539 if (item_collector_)
540 return item_collector_;
541 if (mutable_containing())
542 return mutable_containing()->GetItemCollector();
543 return nullptr;
544 }
545
SetProperty(const void * key,void * value)546 void Scope::SetProperty(const void* key, void* value) {
547 if (!value) {
548 DCHECK(properties_.find(key) != properties_.end());
549 properties_.erase(key);
550 } else {
551 properties_[key] = value;
552 }
553 }
554
GetProperty(const void * key,const Scope ** found_on_scope) const555 void* Scope::GetProperty(const void* key, const Scope** found_on_scope) const {
556 PropertyMap::const_iterator found = properties_.find(key);
557 if (found != properties_.end()) {
558 if (found_on_scope)
559 *found_on_scope = this;
560 return found->second;
561 }
562 if (containing())
563 return containing()->GetProperty(key, found_on_scope);
564 return nullptr;
565 }
566
AddProvider(ProgrammaticProvider * p)567 void Scope::AddProvider(ProgrammaticProvider* p) {
568 programmatic_providers_.insert(p);
569 }
570
RemoveProvider(ProgrammaticProvider * p)571 void Scope::RemoveProvider(ProgrammaticProvider* p) {
572 DCHECK(programmatic_providers_.find(p) != programmatic_providers_.end());
573 programmatic_providers_.erase(p);
574 }
575
SetTemplateInvocationEntry(std::string template_name,std::string target_name,Location location)576 void Scope::SetTemplateInvocationEntry(std::string template_name,
577 std::string target_name,
578 Location location) {
579 template_invocation_entry_ = std::make_unique<TemplateInvocationEntry>(
580 TemplateInvocationEntry{std::move(template_name), std::move(target_name),
581 std::move(location)});
582 }
583
FindTemplateInvocationEntry() const584 const Scope::TemplateInvocationEntry* Scope::FindTemplateInvocationEntry()
585 const {
586 if (template_invocation_entry_)
587 return template_invocation_entry_.get();
588 if (const Scope* scope = containing())
589 return scope->FindTemplateInvocationEntry();
590 return nullptr;
591 }
592
AppendTemplateInvocationEntries(std::vector<TemplateInvocationEntry> * out) const593 void Scope::AppendTemplateInvocationEntries(
594 std::vector<TemplateInvocationEntry>* out) const {
595 const Value* invoker = GetValue("invoker");
596 if (invoker && invoker->type() == Value::SCOPE)
597 invoker->scope_value()->AppendTemplateInvocationEntries(out);
598
599 const TemplateInvocationEntry* entry = FindTemplateInvocationEntry();
600 if (entry)
601 out->push_back(*entry);
602 }
603
604 std::vector<Scope::TemplateInvocationEntry>
GetTemplateInvocationEntries() const605 Scope::GetTemplateInvocationEntries() const {
606 std::vector<Scope::TemplateInvocationEntry> result;
607 AppendTemplateInvocationEntries(&result);
608 return result;
609 }
610
611 // static
RecordMapValuesEqual(const RecordMap & a,const RecordMap & b)612 bool Scope::RecordMapValuesEqual(const RecordMap& a, const RecordMap& b) {
613 if (a.size() != b.size())
614 return false;
615 for (const auto& pair : a) {
616 const auto& found_b = b.find(pair.first);
617 if (found_b == b.end())
618 return false; // Item in 'a' but not 'b'.
619 if (pair.second.value != found_b->second.value)
620 return false; // Values for variable in 'a' and 'b' are different.
621 }
622 return true;
623 }
624