[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); }