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