[debugger] Hook up basic types to casting.

This hooks up the builtin types (int, char*, etc.) to the type parsing
code such that things like "print (int*)foo" will work.

Main symbol lookup is not yet hooked up. That will be done in a future
pass.

Adds "void" to the list of builtin types. This required some additions
because of the weird way DWARF encodes these types (with no type
reference at all) is not compatible with us building up a type
hierarchy. This uses the "none" base type to represent void. Unit tests
are added for the handling of this.

Fixes C-style cast operator precedence. Previously "(void*)a[0]" would
be parsed as converting a to a void* and then taking the 0th item.

Change-Id: If8c3e1492241ac69f378dceed033d55fd184f744
diff --git a/garnet/bin/zxdb/console/format_value.cc b/garnet/bin/zxdb/console/format_value.cc
index 078a784..29715af 100644
--- a/garnet/bin/zxdb/console/format_value.cc
+++ b/garnet/bin/zxdb/console/format_value.cc
@@ -279,6 +279,11 @@
         FormatUnsignedInt(value, options, &out);
         break;
       }
+      case BaseType::kBaseTypeNone: {
+        // Void. Just print the type name with no data.
+        out.Append(type->GetFullName());
+        break;
+      }
       default:
         if (value.data().empty()) {
           out.Append(ErrStringToOutput("no data"));
diff --git a/garnet/bin/zxdb/console/format_value_unittest.cc b/garnet/bin/zxdb/console/format_value_unittest.cc
index d7f8797..8787d31 100644
--- a/garnet/bin/zxdb/console/format_value_unittest.cc
+++ b/garnet/bin/zxdb/console/format_value_unittest.cc
@@ -82,6 +82,39 @@
 
 }  // namespace
 
+TEST_F(FormatValueTest, Void) {
+  FormatExprValueOptions opts;
+
+  // Bare void type (not valid in C++ but we can generate).
+  auto void_type = fxl::MakeRefCounted<BaseType>();
+  ExprValue val_void(void_type, {});
+  EXPECT_EQ("void", SyncFormatValue(val_void, opts));
+
+  // Void type with overridden name.
+  auto named_void_type =
+      fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeNone, 0, "VOID");
+  ExprValue val_named_void(named_void_type, {});
+  EXPECT_EQ("VOID", SyncFormatValue(val_named_void, opts));
+
+  // Void pointer encoded as a pointer to a "none" BaseType.
+  auto void_ptr_type = fxl::MakeRefCounted<ModifiedType>(DwarfTag::kPointerType,
+                                                         LazySymbol(void_type));
+  ExprValue val_void_ptr(void_ptr_type, {8, 7, 6, 5, 4, 3, 2, 1});
+  EXPECT_EQ("(void*) 0x102030405060708", SyncFormatValue(val_void_ptr, opts));
+
+  // Void pointer encoded as a pointer to nothing.
+  auto void_ptr_type2 =
+      fxl::MakeRefCounted<ModifiedType>(DwarfTag::kPointerType, LazySymbol());
+  ExprValue val_void_ptr2(void_ptr_type2, {8, 7, 6, 5, 4, 3, 2, 1});
+  EXPECT_EQ("(void*) 0x102030405060708", SyncFormatValue(val_void_ptr2, opts));
+
+  // Minimal verbosity with above values.
+  opts.verbosity = FormatExprValueOptions::Verbosity::kMinimal;
+  EXPECT_EQ("void", SyncFormatValue(val_void, opts));
+  EXPECT_EQ("(*) 0x102030405060708", SyncFormatValue(val_void_ptr, opts));
+  EXPECT_EQ("(*) 0x102030405060708", SyncFormatValue(val_void_ptr2, opts));
+}
+
 TEST_F(FormatValueTest, Signed) {
   FormatExprValueOptions opts;
 
diff --git a/garnet/bin/zxdb/expr/builtin_types.cc b/garnet/bin/zxdb/expr/builtin_types.cc
index 697483a..a4e8505 100644
--- a/garnet/bin/zxdb/expr/builtin_types.cc
+++ b/garnet/bin/zxdb/expr/builtin_types.cc
@@ -23,6 +23,7 @@
 const BuiltinTypeInfo kBuiltinInfo[] = {
     // clang-format off
 
+    { "void",     BaseType::kBaseTypeNone,         0 },
     { "bool",     BaseType::kBaseTypeBoolean,      1 },
 
     // Integer types.
diff --git a/garnet/bin/zxdb/expr/expr.cc b/garnet/bin/zxdb/expr/expr.cc
index 4fc023e..1701a5d 100644
--- a/garnet/bin/zxdb/expr/expr.cc
+++ b/garnet/bin/zxdb/expr/expr.cc
@@ -20,8 +20,8 @@
     return;
   }
 
-  // TODO(brettw) hook up the symbol lookup callback here.
-  ExprParser parser(tokenizer.TakeTokens());
+  ExprParser parser(tokenizer.TakeTokens(),
+                    context->GetSymbolNameLookupCallback());
   auto node = parser.Parse();
   if (parser.err().has_error()) {
     // Add context information since we have the original input string (the
diff --git a/garnet/bin/zxdb/expr/expr_eval_context.h b/garnet/bin/zxdb/expr/expr_eval_context.h
index 4484aa1..dd8c809 100644
--- a/garnet/bin/zxdb/expr/expr_eval_context.h
+++ b/garnet/bin/zxdb/expr/expr_eval_context.h
@@ -6,6 +6,7 @@
 
 #include <functional>
 
+#include "garnet/bin/zxdb/expr/name_lookup.h"
 #include "lib/fxl/memory/ref_counted.h"
 
 namespace zxdb {
@@ -47,6 +48,12 @@
   virtual SymbolVariableResolver& GetVariableResolver() = 0;
 
   virtual fxl::RefPtr<SymbolDataProvider> GetDataProvider() = 0;
+
+  // Returns a callback the parser can use to lookup type names.
+  //
+  // It is assumed this callback is used for parsing and discarded rather than
+  // stored since it may have references back the eval context.
+  virtual NameLookupCallback GetSymbolNameLookupCallback() = 0;
 };
 
 }  // namespace zxdb
diff --git a/garnet/bin/zxdb/expr/expr_parser.cc b/garnet/bin/zxdb/expr/expr_parser.cc
index 86609fa..648ead2 100644
--- a/garnet/bin/zxdb/expr/expr_parser.cc
+++ b/garnet/bin/zxdb/expr/expr_parser.cc
@@ -601,8 +601,11 @@
     return nullptr;
 
   if (const TypeExprNode* type_expr = expr->AsType()) {
-    // Convert "(TypeName)..." into a cast.
-    auto cast_expr = ParseExpression(kPrecedenceCallAccess);
+    // Convert "(TypeName)..." into a cast. Note the "-1" here which converts
+    // to right-associative. With variable names, () is left-associative in
+    // that "(foo)(bar)[baz]" means to execute left-to-right. But when "(foo)"
+    // is a C-style cast, this means "(bar)[baz]" is a unit.
+    auto cast_expr = ParseExpression(kPrecedenceCallAccess - 1);
     if (has_error())
       return nullptr;
 
diff --git a/garnet/bin/zxdb/expr/expr_parser_unittest.cc b/garnet/bin/zxdb/expr/expr_parser_unittest.cc
index 9f1f654..eadc947 100644
--- a/garnet/bin/zxdb/expr/expr_parser_unittest.cc
+++ b/garnet/bin/zxdb/expr/expr_parser_unittest.cc
@@ -573,6 +573,16 @@
       " IDENTIFIER(\"b\")\n",
       GetParseString("(Type*)a && b", &TestLookupName));
 
+  EXPECT_EQ(
+      "CAST(C)\n"
+      " TYPE(Type)\n"
+      " ACCESSOR(->)\n"
+      "  ARRAY_ACCESS\n"
+      "   IDENTIFIER(\"a\")\n"
+      "   LITERAL(0)\n"
+      "  b\n",
+      GetParseString("(Type)a[0]->b", &TestLookupName));
+
   // Looks like a cast but it's not a type.
   auto result = Parse("(NotType)a", &TestLookupName);
   EXPECT_FALSE(result);
diff --git a/garnet/bin/zxdb/expr/mock_expr_eval_context.cc b/garnet/bin/zxdb/expr/mock_expr_eval_context.cc
index e6271a3..b17dee6 100644
--- a/garnet/bin/zxdb/expr/mock_expr_eval_context.cc
+++ b/garnet/bin/zxdb/expr/mock_expr_eval_context.cc
@@ -4,6 +4,7 @@
 
 #include "garnet/bin/zxdb/expr/mock_expr_eval_context.h"
 
+#include "garnet/bin/zxdb/expr/builtin_types.h"
 #include "garnet/bin/zxdb/expr/expr_value.h"
 #include "garnet/bin/zxdb/expr/identifier.h"
 
@@ -40,4 +41,13 @@
   return data_provider_;
 }
 
+NameLookupCallback MockExprEvalContext::GetSymbolNameLookupCallback() {
+  // This mock version just integrates with builtin types.
+  return [](const Identifier& ident) -> NameLookupResult {
+    if (auto type = GetBuiltinType(ident.GetFullName()))
+       return NameLookupResult(NameLookupResult::kType, std::move(type));
+    return NameLookupResult();
+  };
+}
+
 }  // namespace zxdv
diff --git a/garnet/bin/zxdb/expr/mock_expr_eval_context.h b/garnet/bin/zxdb/expr/mock_expr_eval_context.h
index 6537652..005e6b9 100644
--- a/garnet/bin/zxdb/expr/mock_expr_eval_context.h
+++ b/garnet/bin/zxdb/expr/mock_expr_eval_context.h
@@ -30,6 +30,7 @@
       override;
   SymbolVariableResolver& GetVariableResolver() override;
   fxl::RefPtr<SymbolDataProvider> GetDataProvider() override;
+  NameLookupCallback GetSymbolNameLookupCallback() override;
 
  private:
   fxl::RefPtr<MockSymbolDataProvider> data_provider_;
diff --git a/garnet/bin/zxdb/expr/symbol_eval_context.cc b/garnet/bin/zxdb/expr/symbol_eval_context.cc
index 5875efb..59c59fb 100644
--- a/garnet/bin/zxdb/expr/symbol_eval_context.cc
+++ b/garnet/bin/zxdb/expr/symbol_eval_context.cc
@@ -5,6 +5,7 @@
 #include "garnet/bin/zxdb/expr/symbol_eval_context.h"
 
 #include "garnet/bin/zxdb/common/err.h"
+#include "garnet/bin/zxdb/expr/builtin_types.h"
 #include "garnet/bin/zxdb/expr/expr_value.h"
 #include "garnet/bin/zxdb/expr/find_variable.h"
 #include "garnet/bin/zxdb/expr/identifier.h"
@@ -103,6 +104,22 @@
   return data_provider_;
 }
 
+NameLookupCallback SymbolEvalContext::GetSymbolNameLookupCallback() {
+  // The contract for this function is that the callback must not be stored
+  // so the callback can reference |this|.
+  return [this](const Identifier& ident) -> NameLookupResult {
+    // Look up the symbols in the symbol table if possible.
+    NameLookupResult result = DoTargetSymbolsNameLookup(ident);
+
+    // Fall back on builtin types.
+    if (result.kind == NameLookupResult::kOther) {
+      if (auto type = GetBuiltinType(ident.GetFullName()))
+        return NameLookupResult(NameLookupResult::kType, std::move(type));
+    }
+    return result;
+  };
+}
+
 void SymbolEvalContext::DoResolve(FoundVariable found, ValueCallback cb) const {
   if (!found.is_object_member()) {
     // Simple variable resolution.
@@ -144,4 +161,10 @@
       });
 }
 
+NameLookupResult SymbolEvalContext::DoTargetSymbolsNameLookup(
+    const Identifier& ident) {
+  // TODO(brettw) hook up actual symbol lookup here.
+  return NameLookupResult();
+}
+
 }  // namespace zxdb
diff --git a/garnet/bin/zxdb/expr/symbol_eval_context.h b/garnet/bin/zxdb/expr/symbol_eval_context.h
index 9b73890..baf8f00 100644
--- a/garnet/bin/zxdb/expr/symbol_eval_context.h
+++ b/garnet/bin/zxdb/expr/symbol_eval_context.h
@@ -51,6 +51,7 @@
   void GetNamedValue(const Identifier& name, ValueCallback cb) override;
   SymbolVariableResolver& GetVariableResolver() override;
   fxl::RefPtr<SymbolDataProvider> GetDataProvider() override;
+  NameLookupCallback GetSymbolNameLookupCallback() override;
 
  private:
   struct SearchResult {
@@ -81,6 +82,9 @@
   // asynchronously, possibly not).
   void DoResolve(FoundVariable found, ValueCallback cb) const;
 
+  // Implements type name lookup on the target's symbol index.
+  NameLookupResult DoTargetSymbolsNameLookup(const Identifier& ident);
+
   fxl::WeakPtr<const ProcessSymbols> process_symbols_;  // Possibly null.
   SymbolContext symbol_context_;
   fxl::RefPtr<SymbolDataProvider> data_provider_;  // Possibly null.
diff --git a/garnet/bin/zxdb/symbols/base_type.cc b/garnet/bin/zxdb/symbols/base_type.cc
index edc6471..7150030 100644
--- a/garnet/bin/zxdb/symbols/base_type.cc
+++ b/garnet/bin/zxdb/symbols/base_type.cc
@@ -29,4 +29,18 @@
 
 const BaseType* BaseType::AsBaseType() const { return this; }
 
+const std::string& BaseType::GetAssignedName() const {
+  const std::string& assigned_name = Type::GetAssignedName();
+  if (!assigned_name.empty())
+    return assigned_name;
+
+  // Special-case void types with no assigned names.
+  if (base_type_ == kBaseTypeNone) {
+    static std::string void_str("void");
+    return void_str;
+  }
+
+  return assigned_name;
+}
+
 }  // namespace zxdb
diff --git a/garnet/bin/zxdb/symbols/base_type.h b/garnet/bin/zxdb/symbols/base_type.h
index 25d977d..00fddc6 100644
--- a/garnet/bin/zxdb/symbols/base_type.h
+++ b/garnet/bin/zxdb/symbols/base_type.h
@@ -24,7 +24,12 @@
   // handle every one of these values. To make it clear which values we handle,
   // the irrelevant values are removed. But code should not assume that only
   // these values might ever appear.
-  static constexpr int kBaseTypeNone = 0x00;  // Means uninitialized.
+  //
+  // Note on void types: DWARF encodes void* as a pointer to nothing and
+  // normally when there's nothing returned, it just lists no return type.
+  // But special-casing this in the expression evaluation code is awkward so
+  // we also treat "kBaseTypeNone" as refering to a void type.
+  static constexpr int kBaseTypeNone = 0x00;  // Means uninitialized or void.
   static constexpr int kBaseTypeAddress = 0x01;
   static constexpr int kBaseTypeBoolean = 0x02;
   static constexpr int kBaseTypeFloat = 0x04;
@@ -36,6 +41,7 @@
 
   // Symbol overrides.
   const BaseType* AsBaseType() const final;
+  const std::string& GetAssignedName() const final;
 
   // Returns one of kBaseType* or possibly something else if the language is
   // new or unusual. Don't handle, but also don't crash on unexpected values.
diff --git a/garnet/bin/zxdb/symbols/modified_type_unittest.cc b/garnet/bin/zxdb/symbols/modified_type_unittest.cc
index d17cc04..999e081 100644
--- a/garnet/bin/zxdb/symbols/modified_type_unittest.cc
+++ b/garnet/bin/zxdb/symbols/modified_type_unittest.cc
@@ -90,20 +90,30 @@
   typedef_void->set_assigned_name("VoidType");
   EXPECT_EQ("VoidType", typedef_void->GetFullName());
 
-  // void*
+  // void* (There are two ways to encode: pointer to nothing, and pointer to
+  // "none" base type).
   auto void_ptr =
       fxl::MakeRefCounted<ModifiedType>(DwarfTag::kPointerType, LazySymbol());
   EXPECT_EQ("void*", void_ptr->GetFullName());
+  auto void_ptr2 = fxl::MakeRefCounted<ModifiedType>(
+      DwarfTag::kPointerType, LazySymbol(fxl::MakeRefCounted<BaseType>()));
+  EXPECT_EQ("void*", void_ptr2->GetFullName());
 
-  // const void
+  // const void (same two ways to encode as void*).
   auto const_void =
       fxl::MakeRefCounted<ModifiedType>(DwarfTag::kConstType, LazySymbol());
   EXPECT_EQ("const void", const_void->GetFullName());
+  auto const_void2 = fxl::MakeRefCounted<ModifiedType>(
+      DwarfTag::kConstType, LazySymbol(fxl::MakeRefCounted<BaseType>()));
+  EXPECT_EQ("const void", const_void2->GetFullName());
 
-  // const void*
+  // const void* (same two ways to encode as void*).
   auto const_void_ptr = fxl::MakeRefCounted<ModifiedType>(
       DwarfTag::kPointerType, LazySymbol(const_void));
   EXPECT_EQ("const void*", const_void_ptr->GetFullName());
+  auto const_void_ptr2 = fxl::MakeRefCounted<ModifiedType>(
+      DwarfTag::kPointerType, LazySymbol(const_void2));
+  EXPECT_EQ("const void*", const_void_ptr2->GetFullName());
 }
 
 }  // namespace zxdb
diff --git a/garnet/bin/zxdb/symbols/type.h b/garnet/bin/zxdb/symbols/type.h
index 72a80af..1113207 100644
--- a/garnet/bin/zxdb/symbols/type.h
+++ b/garnet/bin/zxdb/symbols/type.h
@@ -15,7 +15,7 @@
  public:
   // Symbol overrides.
   const Type* AsType() const final;
-  const std::string& GetAssignedName() const final { return assigned_name_; }
+  const std::string& GetAssignedName() const { return assigned_name_; }
 
   // Returns the type with no "const", "volatile", or similar modifiers that
   // don't affect the stored data, and expand typedef and using statements.