Add DecoratedVariable tests

This CL adds tests for decorated variable and updates the API to be
slightly nicer.

Bug: tint:11
Change-Id: I0bd5b8b6e6f682dc9d405e02e51d5dbcaeccc5f3
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/16465
Commit-Queue: Sarah Mashayekhi <sarahmashay@google.com>
Reviewed-by: Sarah Mashayekhi <sarahmashay@google.com>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6846c12..e5f97e9 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -205,6 +205,7 @@
   ast/cast_expression_test.cc
   ast/const_initializer_expression_test.cc
   ast/continue_statement_test.cc
+  ast/decorated_variable_test.cc
   ast/entry_point_test.cc
   ast/import_test.cc
   ast/int_literal_test.cc
diff --git a/src/ast/binding_decoration.cc b/src/ast/binding_decoration.cc
index 4278dd5..d9532fe 100644
--- a/src/ast/binding_decoration.cc
+++ b/src/ast/binding_decoration.cc
@@ -22,7 +22,7 @@
 BindingDecoration::~BindingDecoration() = default;
 
 void BindingDecoration::to_str(std::ostream& out) const {
-  out << "binding " << value_;
+  out << "BindingDecoration{" << value_ << "}" << std::endl;
 }
 
 }  // namespace ast
diff --git a/src/ast/builtin_decoration.cc b/src/ast/builtin_decoration.cc
index c0264af..89d116e 100644
--- a/src/ast/builtin_decoration.cc
+++ b/src/ast/builtin_decoration.cc
@@ -22,7 +22,7 @@
 BuiltinDecoration::~BuiltinDecoration() = default;
 
 void BuiltinDecoration::to_str(std::ostream& out) const {
-  out << "builtin " << builtin_;
+  out << "BuiltinDecoration{" << builtin_ << "}" << std::endl;
 }
 
 }  // namespace ast
diff --git a/src/ast/decorated_variable.cc b/src/ast/decorated_variable.cc
index 2a6b42e..038aaed 100644
--- a/src/ast/decorated_variable.cc
+++ b/src/ast/decorated_variable.cc
@@ -19,6 +19,9 @@
 
 DecoratedVariable::DecoratedVariable() = default;
 
+DecoratedVariable::DecoratedVariable(std::unique_ptr<Variable> var)
+    : Variable(var->source(), var->name(), var->storage_class(), var->type()) {}
+
 DecoratedVariable::DecoratedVariable(DecoratedVariable&&) = default;
 
 DecoratedVariable::~DecoratedVariable() = default;
@@ -32,17 +35,17 @@
   out << "DecoratedVariable{" << std::endl;
 
   make_indent(out, indent + 2);
-  out << "decorations{" << std::endl;
+  out << "Decorations{" << std::endl;
   for (const auto& deco : decorations_) {
     make_indent(out, indent + 4);
     deco->to_str(out);
-    out << std::endl;
   }
 
   make_indent(out, indent + 2);
   out << "}" << std::endl;
 
   info_to_str(out, indent + 2);
+  initializer_to_str(out, indent + 2);
   make_indent(out, indent);
   out << "}" << std::endl;
 }
diff --git a/src/ast/decorated_variable.h b/src/ast/decorated_variable.h
index d81de81..4008a48 100644
--- a/src/ast/decorated_variable.h
+++ b/src/ast/decorated_variable.h
@@ -30,6 +30,9 @@
  public:
   /// Create a new empty decorated variable statement
   DecoratedVariable();
+  /// Create a decorated variable from an existing variable
+  /// @param var the variable to initialize from
+  explicit DecoratedVariable(std::unique_ptr<Variable> var);
   /// Move constructor
   DecoratedVariable(DecoratedVariable&&);
 
diff --git a/src/ast/decorated_variable_test.cc b/src/ast/decorated_variable_test.cc
new file mode 100644
index 0000000..e2ed800
--- /dev/null
+++ b/src/ast/decorated_variable_test.cc
@@ -0,0 +1,97 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/ast/decorated_variable.h"
+
+#include "gtest/gtest.h"
+#include "src/ast/binding_decoration.h"
+#include "src/ast/identifier_expression.h"
+#include "src/ast/set_decoration.h"
+#include "src/ast/type/f32_type.h"
+#include "src/ast/type/i32_type.h"
+#include "src/ast/variable.h"
+#include "src/ast/variable_decoration.h"
+
+namespace tint {
+namespace ast {
+
+using DecoratedVariableTest = testing::Test;
+
+TEST_F(DecoratedVariableTest, Creation) {
+  type::I32Type t;
+  auto var = std::make_unique<Variable>("my_var", StorageClass::kFunction, &t);
+  DecoratedVariable dv(std::move(var));
+
+  EXPECT_EQ(dv.name(), "my_var");
+  EXPECT_EQ(dv.storage_class(), StorageClass::kFunction);
+  EXPECT_EQ(dv.type(), &t);
+  EXPECT_EQ(dv.line(), 0);
+  EXPECT_EQ(dv.column(), 0);
+}
+
+TEST_F(DecoratedVariableTest, CreationWithSource) {
+  Source s{27, 4};
+  type::F32Type t;
+  auto var = std::make_unique<Variable>(s, "i", StorageClass::kPrivate, &t);
+  DecoratedVariable dv(std::move(var));
+
+  EXPECT_EQ(dv.name(), "i");
+  EXPECT_EQ(dv.storage_class(), StorageClass::kPrivate);
+  EXPECT_EQ(dv.type(), &t);
+  EXPECT_EQ(dv.line(), 27);
+  EXPECT_EQ(dv.column(), 4);
+}
+
+TEST_F(DecoratedVariableTest, IsValid) {
+  type::I32Type t;
+  auto var = std::make_unique<Variable>("my_var", StorageClass::kNone, &t);
+  DecoratedVariable dv(std::move(var));
+  EXPECT_TRUE(dv.IsValid());
+}
+
+TEST_F(DecoratedVariableTest, IsDecorated) {
+  DecoratedVariable dv;
+  EXPECT_TRUE(dv.IsDecorated());
+}
+
+TEST_F(DecoratedVariableTest, to_str) {
+  type::F32Type t;
+  auto var = std::make_unique<Variable>("my_var", StorageClass::kFunction, &t);
+  DecoratedVariable dv(std::move(var));
+  dv.set_initializer(std::make_unique<IdentifierExpression>("expr"));
+
+  std::vector<std::unique_ptr<VariableDecoration>> decos;
+  decos.push_back(std::make_unique<BindingDecoration>(2));
+  decos.push_back(std::make_unique<SetDecoration>(1));
+
+  dv.set_decorations(std::move(decos));
+  std::ostringstream out;
+  dv.to_str(out, 2);
+  EXPECT_EQ(out.str(), R"(  DecoratedVariable{
+    Decorations{
+      BindingDecoration{2}
+      SetDecoration{1}
+    }
+    my_var
+    function
+    __f32
+    {
+      Identifier{expr}
+    }
+  }
+)");
+}
+
+}  // namespace ast
+}  // namespace tint
diff --git a/src/ast/location_decoration.cc b/src/ast/location_decoration.cc
index 45bec56..9fb8d5f 100644
--- a/src/ast/location_decoration.cc
+++ b/src/ast/location_decoration.cc
@@ -22,7 +22,7 @@
 LocationDecoration::~LocationDecoration() = default;
 
 void LocationDecoration::to_str(std::ostream& out) const {
-  out << "location " << value_;
+  out << "LocationDecoration{" << value_ << "}" << std::endl;
 }
 
 }  // namespace ast
diff --git a/src/ast/set_decoration.cc b/src/ast/set_decoration.cc
index c27ee26..7d54d98 100644
--- a/src/ast/set_decoration.cc
+++ b/src/ast/set_decoration.cc
@@ -22,7 +22,7 @@
 SetDecoration::~SetDecoration() = default;
 
 void SetDecoration::to_str(std::ostream& out) const {
-  out << "set " << value_;
+  out << "SetDecoration{" << value_ << "}" << std::endl;
 }
 
 }  // namespace ast
diff --git a/src/ast/variable.cc b/src/ast/variable.cc
index 1caa64b..d9990b8 100644
--- a/src/ast/variable.cc
+++ b/src/ast/variable.cc
@@ -56,18 +56,26 @@
   out << type_->type_name() << std::endl;
 }
 
+void Variable::initializer_to_str(std::ostream& out, size_t indent) const {
+  if (initializer_ == nullptr)
+    return;
+
+  make_indent(out, indent);
+  out << "{" << std::endl;
+
+  initializer_->to_str(out, indent + 2);
+
+  make_indent(out, indent);
+  out << "}" << std::endl;
+}
+
 void Variable::to_str(std::ostream& out, size_t indent) const {
-  info_to_str(out, indent);
-
-  if (initializer_ != nullptr) {
-    make_indent(out, indent);
-    out << "{" << std::endl;
-
-    initializer_->to_str(out, indent + 2);
-
-    make_indent(out, indent);
-    out << "}" << std::endl;
-  }
+  make_indent(out, indent);
+  out << "Variable{" << std::endl;
+  info_to_str(out, indent + 2);
+  initializer_to_str(out, indent + 2);
+  make_indent(out, indent);
+  out << "}" << std::endl;
 }
 
 }  // namespace ast
diff --git a/src/ast/variable.h b/src/ast/variable.h
index db27ed2..3fec0d6 100644
--- a/src/ast/variable.h
+++ b/src/ast/variable.h
@@ -105,6 +105,10 @@
   /// @param out the stream to write to
   /// @param indent number of spaces to indent the node when writing
   void info_to_str(std::ostream& out, size_t indent) const;
+  /// Output initializer for this variable.
+  /// @param out the stream to write to
+  /// @param indent number of spaces to indent the node when writing
+  void initializer_to_str(std::ostream& out, size_t indent) const;
 
  private:
   Variable(const Variable&) = delete;
diff --git a/src/ast/variable_test.cc b/src/ast/variable_test.cc
index 0f4f59b..8156d23 100644
--- a/src/ast/variable_test.cc
+++ b/src/ast/variable_test.cc
@@ -89,10 +89,12 @@
   type::F32Type t;
   Variable v{"my_var", StorageClass::kFunction, &t};
   std::ostringstream out;
-  v.to_str(out, 0);
-  EXPECT_EQ(out.str(), R"(my_var
-function
-__f32
+  v.to_str(out, 2);
+  EXPECT_EQ(out.str(), R"(  Variable{
+    my_var
+    function
+    __f32
+  }
 )");
 }
 
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index 58d4bf2..59d461b 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -329,11 +329,7 @@
   }
 
   if (decos.size() > 0) {
-    auto dv = std::make_unique<ast::DecoratedVariable>();
-    dv->set_source(var->source());
-    dv->set_name(var->name());
-    dv->set_type(var->type());
-    dv->set_storage_class(var->storage_class());
+    auto dv = std::make_unique<ast::DecoratedVariable>(std::move(var));
     dv->set_decorations(std::move(decos));
 
     var = std::move(dv);