Add ICU message format support
[chromium-blink-merge.git] / tools / gn / scope.cc
blob37ddbddb11a35fb08d32edb8a036c8f4cfe87dd3
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.
5 #include "tools/gn/scope.h"
7 #include "base/logging.h"
8 #include "base/stl_util.h"
9 #include "tools/gn/parse_tree.h"
10 #include "tools/gn/template.h"
12 namespace {
14 // FLags set in the mode_flags_ of a scope. If a bit is set, it applies
15 // recursively to all dependent scopes.
16 const unsigned kProcessingBuildConfigFlag = 1;
17 const unsigned kProcessingImportFlag = 2;
19 // Returns true if this variable name should be considered private. Private
20 // values start with an underscore, and are not imported from "gni" files
21 // when processing an import.
22 bool IsPrivateVar(const base::StringPiece& name) {
23 return name.empty() || name[0] == '_';
26 } // namespace
29 Scope::ProgrammaticProvider::~ProgrammaticProvider() {
30 scope_->RemoveProvider(this);
33 Scope::Scope(const Settings* settings)
34 : const_containing_(nullptr),
35 mutable_containing_(nullptr),
36 settings_(settings),
37 mode_flags_(0),
38 item_collector_(nullptr) {
41 Scope::Scope(Scope* parent)
42 : const_containing_(nullptr),
43 mutable_containing_(parent),
44 settings_(parent->settings()),
45 mode_flags_(0),
46 item_collector_(nullptr) {
49 Scope::Scope(const Scope* parent)
50 : const_containing_(parent),
51 mutable_containing_(nullptr),
52 settings_(parent->settings()),
53 mode_flags_(0),
54 item_collector_(nullptr) {
57 Scope::~Scope() {
58 STLDeleteContainerPairSecondPointers(target_defaults_.begin(),
59 target_defaults_.end());
62 const Value* Scope::GetValue(const base::StringPiece& ident,
63 bool counts_as_used) {
64 // First check for programmatically-provided values.
65 for (const auto& provider : programmatic_providers_) {
66 const Value* v = provider->GetProgrammaticValue(ident);
67 if (v)
68 return v;
71 RecordMap::iterator found = values_.find(ident);
72 if (found != values_.end()) {
73 if (counts_as_used)
74 found->second.used = true;
75 return &found->second.value;
78 // Search in the parent scope.
79 if (const_containing_)
80 return const_containing_->GetValue(ident);
81 if (mutable_containing_)
82 return mutable_containing_->GetValue(ident, counts_as_used);
83 return nullptr;
86 Value* Scope::GetMutableValue(const base::StringPiece& ident,
87 bool counts_as_used) {
88 // Don't do programmatic values, which are not mutable.
89 RecordMap::iterator found = values_.find(ident);
90 if (found != values_.end()) {
91 if (counts_as_used)
92 found->second.used = true;
93 return &found->second.value;
96 // Search in the parent mutable scope, but not const one.
97 if (mutable_containing_)
98 return mutable_containing_->GetMutableValue(ident, counts_as_used);
99 return nullptr;
102 Value* Scope::GetValueForcedToCurrentScope(const base::StringPiece& ident,
103 const ParseNode* set_node) {
104 RecordMap::iterator found = values_.find(ident);
105 if (found != values_.end())
106 return &found->second.value; // Already have in the current scope.
108 // Search in the parent scope.
109 if (containing()) {
110 const Value* in_containing = containing()->GetValue(ident);
111 if (in_containing) {
112 // Promote to current scope.
113 return SetValue(ident, *in_containing, set_node);
116 return nullptr;
119 base::StringPiece Scope::GetStorageKey(const base::StringPiece& ident) const {
120 RecordMap::const_iterator found = values_.find(ident);
121 if (found != values_.end())
122 return found->first;
124 // Search in parent scope.
125 if (containing())
126 return containing()->GetStorageKey(ident);
127 return base::StringPiece();
130 const Value* Scope::GetValue(const base::StringPiece& ident) const {
131 RecordMap::const_iterator found = values_.find(ident);
132 if (found != values_.end())
133 return &found->second.value;
134 if (containing())
135 return containing()->GetValue(ident);
136 return nullptr;
139 Value* Scope::SetValue(const base::StringPiece& ident,
140 const Value& v,
141 const ParseNode* set_node) {
142 Record& r = values_[ident]; // Clears any existing value.
143 r.value = v;
144 r.value.set_origin(set_node);
145 return &r.value;
148 void Scope::RemoveIdentifier(const base::StringPiece& ident) {
149 RecordMap::iterator found = values_.find(ident);
150 if (found != values_.end())
151 values_.erase(found);
154 void Scope::RemovePrivateIdentifiers() {
155 // Do it in two phases to avoid mutating while iterating. Our hash map is
156 // currently backed by several different vendor-specific implementations and
157 // I'm not sure if all of them support mutating while iterating. Since this
158 // is not perf-critical, do the safe thing.
159 std::vector<base::StringPiece> to_remove;
160 for (const auto& cur : values_) {
161 if (IsPrivateVar(cur.first))
162 to_remove.push_back(cur.first);
165 for (const auto& cur : to_remove)
166 values_.erase(cur);
169 bool Scope::AddTemplate(const std::string& name, const Template* templ) {
170 if (GetTemplate(name))
171 return false;
172 templates_[name] = templ;
173 return true;
176 const Template* Scope::GetTemplate(const std::string& name) const {
177 TemplateMap::const_iterator found = templates_.find(name);
178 if (found != templates_.end())
179 return found->second.get();
180 if (containing())
181 return containing()->GetTemplate(name);
182 return nullptr;
185 void Scope::MarkUsed(const base::StringPiece& ident) {
186 RecordMap::iterator found = values_.find(ident);
187 if (found == values_.end()) {
188 NOTREACHED();
189 return;
191 found->second.used = true;
194 void Scope::MarkAllUsed() {
195 for (auto& cur : values_)
196 cur.second.used = true;
199 void Scope::MarkUnused(const base::StringPiece& ident) {
200 RecordMap::iterator found = values_.find(ident);
201 if (found == values_.end()) {
202 NOTREACHED();
203 return;
205 found->second.used = false;
208 bool Scope::IsSetButUnused(const base::StringPiece& ident) const {
209 RecordMap::const_iterator found = values_.find(ident);
210 if (found != values_.end()) {
211 if (!found->second.used) {
212 return true;
215 return false;
218 bool Scope::CheckForUnusedVars(Err* err) const {
219 for (const auto& pair : values_) {
220 if (!pair.second.used) {
221 std::string help = "You set the variable \"" + pair.first.as_string() +
222 "\" here and it was unused before it went\nout of scope.";
224 const BinaryOpNode* binary = pair.second.value.origin()->AsBinaryOp();
225 if (binary && binary->op().type() == Token::EQUAL) {
226 // Make a nicer error message for normal var sets.
227 *err = Err(binary->left()->GetRange(), "Assignment had no effect.",
228 help);
229 } else {
230 // This will happen for internally-generated variables.
231 *err = Err(pair.second.value.origin(), "Assignment had no effect.",
232 help);
234 return false;
237 return true;
240 void Scope::GetCurrentScopeValues(KeyValueMap* output) const {
241 for (const auto& pair : values_)
242 (*output)[pair.first] = pair.second.value;
245 bool Scope::NonRecursiveMergeTo(Scope* dest,
246 const MergeOptions& options,
247 const ParseNode* node_for_err,
248 const char* desc_for_err,
249 Err* err) const {
250 // Values.
251 for (const auto& pair : values_) {
252 if (options.skip_private_vars && IsPrivateVar(pair.first))
253 continue; // Skip this private var.
255 const Value& new_value = pair.second.value;
256 if (!options.clobber_existing) {
257 const Value* existing_value = dest->GetValue(pair.first);
258 if (existing_value && new_value != *existing_value) {
259 // Value present in both the source and the dest.
260 std::string desc_string(desc_for_err);
261 *err = Err(node_for_err, "Value collision.",
262 "This " + desc_string + " contains \"" + pair.first.as_string() +
263 "\"");
264 err->AppendSubErr(Err(pair.second.value, "defined here.",
265 "Which would clobber the one in your current scope"));
266 err->AppendSubErr(Err(*existing_value, "defined here.",
267 "Executing " + desc_string + " should not conflict with anything "
268 "in the current\nscope unless the values are identical."));
269 return false;
272 dest->values_[pair.first] = pair.second;
274 if (options.mark_dest_used)
275 dest->MarkUsed(pair.first);
278 // Target defaults are owning pointers.
279 for (const auto& pair : target_defaults_) {
280 if (!options.clobber_existing) {
281 if (dest->GetTargetDefaults(pair.first)) {
282 // TODO(brettw) it would be nice to know the origin of a
283 // set_target_defaults so we can give locations for the colliding target
284 // defaults.
285 std::string desc_string(desc_for_err);
286 *err = Err(node_for_err, "Target defaults collision.",
287 "This " + desc_string + " contains target defaults for\n"
288 "\"" + pair.first + "\" which would clobber one for the\n"
289 "same target type in your current scope. It's unfortunate that I'm "
290 "too stupid\nto tell you the location of where the target defaults "
291 "were set. Usually\nthis happens in the BUILDCONFIG.gn file.");
292 return false;
296 // Be careful to delete any pointer we're about to clobber.
297 Scope** dest_scope = &dest->target_defaults_[pair.first];
298 if (*dest_scope)
299 delete *dest_scope;
300 *dest_scope = new Scope(settings_);
301 pair.second->NonRecursiveMergeTo(*dest_scope, options, node_for_err,
302 "<SHOULDN'T HAPPEN>", err);
305 // Sources assignment filter.
306 if (sources_assignment_filter_) {
307 if (!options.clobber_existing) {
308 if (dest->GetSourcesAssignmentFilter()) {
309 // Sources assignment filter present in both the source and the dest.
310 std::string desc_string(desc_for_err);
311 *err = Err(node_for_err, "Assignment filter collision.",
312 "The " + desc_string + " contains a sources_assignment_filter "
313 "which\nwould clobber the one in your current scope.");
314 return false;
317 dest->sources_assignment_filter_.reset(
318 new PatternList(*sources_assignment_filter_));
321 // Templates.
322 for (const auto& pair : templates_) {
323 if (options.skip_private_vars && IsPrivateVar(pair.first))
324 continue; // Skip this private template.
326 if (!options.clobber_existing) {
327 const Template* existing_template = dest->GetTemplate(pair.first);
328 // Since templates are refcounted, we can check if it's the same one by
329 // comparing pointers.
330 if (existing_template && pair.second.get() != existing_template) {
331 // Rule present in both the source and the dest, and they're not the
332 // same one.
333 std::string desc_string(desc_for_err);
334 *err = Err(node_for_err, "Template collision.",
335 "This " + desc_string + " contains a template \"" +
336 pair.first + "\"");
337 err->AppendSubErr(Err(pair.second->GetDefinitionRange(),
338 "defined here.",
339 "Which would clobber the one in your current scope"));
340 err->AppendSubErr(Err(existing_template->GetDefinitionRange(),
341 "defined here.",
342 "Executing " + desc_string + " should not conflict with anything "
343 "in the current\nscope."));
344 return false;
348 // Be careful to delete any pointer we're about to clobber.
349 dest->templates_[pair.first] = pair.second;
352 return true;
355 scoped_ptr<Scope> Scope::MakeClosure() const {
356 scoped_ptr<Scope> result;
357 if (const_containing_) {
358 // We reached the top of the mutable scope stack. The result scope just
359 // references the const scope (which will never change).
360 result.reset(new Scope(const_containing_));
361 } else if (mutable_containing_) {
362 // There are more nested mutable scopes. Recursively go up the stack to
363 // get the closure.
364 result = mutable_containing_->MakeClosure();
365 } else {
366 // This is a standalone scope, just copy it.
367 result.reset(new Scope(settings_));
370 // Want to clobber since we've flattened some nested scopes, and our parent
371 // scope may have a duplicate value set.
372 MergeOptions options;
373 options.clobber_existing = true;
375 // Add in our variables and we're done.
376 Err err;
377 NonRecursiveMergeTo(result.get(), options, nullptr, "<SHOULDN'T HAPPEN>",
378 &err);
379 DCHECK(!err.has_error());
380 return result.Pass();
383 Scope* Scope::MakeTargetDefaults(const std::string& target_type) {
384 if (GetTargetDefaults(target_type))
385 return nullptr;
387 Scope** dest = &target_defaults_[target_type];
388 if (*dest) {
389 NOTREACHED(); // Already set.
390 return *dest;
392 *dest = new Scope(settings_);
393 return *dest;
396 const Scope* Scope::GetTargetDefaults(const std::string& target_type) const {
397 NamedScopeMap::const_iterator found = target_defaults_.find(target_type);
398 if (found != target_defaults_.end())
399 return found->second;
400 if (containing())
401 return containing()->GetTargetDefaults(target_type);
402 return nullptr;
405 const PatternList* Scope::GetSourcesAssignmentFilter() const {
406 if (sources_assignment_filter_)
407 return sources_assignment_filter_.get();
408 if (containing())
409 return containing()->GetSourcesAssignmentFilter();
410 return nullptr;
413 void Scope::SetProcessingBuildConfig() {
414 DCHECK((mode_flags_ & kProcessingBuildConfigFlag) == 0);
415 mode_flags_ |= kProcessingBuildConfigFlag;
418 void Scope::ClearProcessingBuildConfig() {
419 DCHECK(mode_flags_ & kProcessingBuildConfigFlag);
420 mode_flags_ &= ~(kProcessingBuildConfigFlag);
423 bool Scope::IsProcessingBuildConfig() const {
424 if (mode_flags_ & kProcessingBuildConfigFlag)
425 return true;
426 if (containing())
427 return containing()->IsProcessingBuildConfig();
428 return false;
431 void Scope::SetProcessingImport() {
432 DCHECK((mode_flags_ & kProcessingImportFlag) == 0);
433 mode_flags_ |= kProcessingImportFlag;
436 void Scope::ClearProcessingImport() {
437 DCHECK(mode_flags_ & kProcessingImportFlag);
438 mode_flags_ &= ~(kProcessingImportFlag);
441 bool Scope::IsProcessingImport() const {
442 if (mode_flags_ & kProcessingImportFlag)
443 return true;
444 if (containing())
445 return containing()->IsProcessingImport();
446 return false;
449 const SourceDir& Scope::GetSourceDir() const {
450 if (!source_dir_.is_null())
451 return source_dir_;
452 if (containing())
453 return containing()->GetSourceDir();
454 return source_dir_;
457 Scope::ItemVector* Scope::GetItemCollector() {
458 if (item_collector_)
459 return item_collector_;
460 if (mutable_containing())
461 return mutable_containing()->GetItemCollector();
462 return nullptr;
465 void Scope::SetProperty(const void* key, void* value) {
466 if (!value) {
467 DCHECK(properties_.find(key) != properties_.end());
468 properties_.erase(key);
469 } else {
470 properties_[key] = value;
474 void* Scope::GetProperty(const void* key, const Scope** found_on_scope) const {
475 PropertyMap::const_iterator found = properties_.find(key);
476 if (found != properties_.end()) {
477 if (found_on_scope)
478 *found_on_scope = this;
479 return found->second;
481 if (containing())
482 return containing()->GetProperty(key, found_on_scope);
483 return nullptr;
486 void Scope::AddProvider(ProgrammaticProvider* p) {
487 programmatic_providers_.insert(p);
490 void Scope::RemoveProvider(ProgrammaticProvider* p) {
491 DCHECK(programmatic_providers_.find(p) != programmatic_providers_.end());
492 programmatic_providers_.erase(p);