[value] Implement scope == operator
Bug: crbug.com/gn/11
Change-Id: Iea0cd4903b1f948c5c085fb9b15d47c416356a8a
Reviewed-on: https://gn-review.googlesource.com/c/2880
Reviewed-by: Brett Wilson <brettw@google.com>
diff --git a/tools/gn/parser.cc b/tools/gn/parser.cc
index df57c9e..f4f028f 100644
--- a/tools/gn/parser.cc
+++ b/tools/gn/parser.cc
@@ -214,6 +214,11 @@
myvalues.foo += 2
empty_scope.new_thing = [ 1, 2, 3 ]
+
+ Scope equality is defined as single-level scopes identical within the current
+ scope. That is, all values in the first scope must be present and identical
+ within the second, and vice versa. Note that this means inherited scopes are
+ always unequal by definition.
)*";
enum Precedence {
diff --git a/tools/gn/scope.cc b/tools/gn/scope.cc
index 2368356..dd1a576 100644
--- a/tools/gn/scope.cc
+++ b/tools/gn/scope.cc
@@ -274,6 +274,23 @@
(*output)[pair.first] = pair.second.value;
}
+bool Scope::CheckCurrentScopeValuesEqual(const Scope* other) const {
+ // If there are containing scopes, equality shouldn't work.
+ if (containing()) {
+ return false;
+ }
+ if (values_.size() != other->values_.size()) {
+ return false;
+ }
+ for (const auto& pair : values_) {
+ const Value* v = other->GetValue(pair.first);
+ if (!v || *v != pair.second.value) {
+ return false;
+ }
+ }
+ return true;
+}
+
bool Scope::NonRecursiveMergeTo(Scope* dest,
const MergeOptions& options,
const ParseNode* node_for_err,
diff --git a/tools/gn/scope.h b/tools/gn/scope.h
index 5e9745a..585f151 100644
--- a/tools/gn/scope.h
+++ b/tools/gn/scope.h
@@ -220,6 +220,11 @@
// scopes.
void GetCurrentScopeValues(KeyValueMap* output) const;
+ // Returns true if the values in the current scope are the same as all
+ // values in the given scope, without going to the parent scopes. Returns
+ // false if not.
+ bool CheckCurrentScopeValuesEqual(const Scope* other) const;
+
// Copies this scope's values into the destination. Values from the
// containing scope(s) (normally shadowed into the current one) will not be
// copied, neither will the reference to the containing scope (this is why
diff --git a/tools/gn/value.cc b/tools/gn/value.cc
index 04db7ee..aff4ab1 100644
--- a/tools/gn/value.cc
+++ b/tools/gn/value.cc
@@ -196,10 +196,7 @@
}
return true;
case Value::SCOPE:
- // Scopes are always considered not equal because there's currently
- // no use case for comparing them, and it requires a bunch of complex
- // iteration code.
- return false;
+ return scope_value()->CheckCurrentScopeValuesEqual(other.scope_value());
default:
return false;
}
diff --git a/tools/gn/value.h b/tools/gn/value.h
index 13beb6f..912717d 100644
--- a/tools/gn/value.h
+++ b/tools/gn/value.h
@@ -113,7 +113,9 @@
// false and sets the error.
bool VerifyTypeIs(Type t, Err* err) const;
- // Compares values. Only the "value" is compared, not the origin.
+ // Compares values. Only the "value" is compared, not the origin. Scope
+ // values check only the contents of the current scope, and do not go to
+ // parent scopes.
bool operator==(const Value& other) const;
bool operator!=(const Value& other) const;
diff --git a/tools/gn/value_unittest.cc b/tools/gn/value_unittest.cc
index 40ddf38..dd9be95 100644
--- a/tools/gn/value_unittest.cc
+++ b/tools/gn/value_unittest.cc
@@ -33,11 +33,28 @@
// Scopes.
TestWithScope setup;
- Scope* scope = new Scope(setup.scope());
+ Scope* scope = new Scope(setup.settings());
Value scopeval(nullptr, std::unique_ptr<Scope>(scope));
EXPECT_EQ("{ }", scopeval.ToString(false));
+ // Test that an empty scope equals an empty scope.
+ EXPECT_TRUE(scopeval == scopeval);
+
scope->SetValue("a", Value(nullptr, static_cast<int64_t>(42)), nullptr);
scope->SetValue("b", Value(nullptr, "hello, world"), nullptr);
EXPECT_EQ("{\n a = 42\n b = \"hello, world\"\n}", scopeval.ToString(false));
+ EXPECT_TRUE(scopeval == scopeval);
+
+ Scope* inner_scope = new Scope(setup.settings());
+ Value inner_scopeval(nullptr, std::unique_ptr<Scope>(inner_scope));
+ inner_scope->SetValue("d", Value(nullptr, static_cast<int64_t>(42)), nullptr);
+ scope->SetValue("c", inner_scopeval, nullptr);
+
+ // Test inner scope equality.
+ EXPECT_TRUE(scopeval == scopeval);
+
+ // Nested scopes should not be equal.
+ Scope* nested_scope = new Scope(scope);
+ Value nested_scopeval(nullptr, std::unique_ptr<Scope>(nested_scope));
+ EXPECT_FALSE(nested_scopeval == nested_scopeval);
}