diff --git a/gcc/rust/hir/tree/rust-hir-full-decls.h b/gcc/rust/hir/tree/rust-hir-full-decls.h
new file mode 100644
index 00000000000..2798ba9fd84
--- /dev/null
+++ b/gcc/rust/hir/tree/rust-hir-full-decls.h
@@ -0,0 +1,232 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_HIR_FULL_DECLS_H
+#define RUST_HIR_FULL_DECLS_H
+
+namespace Rust {
+namespace HIR {
+
+struct Literal;
+class Stmt;
+class Item;
+class Expr;
+class ExprWithoutBlock;
+class Pattern;
+class Type;
+class TypeNoBounds;
+class TypeParamBound;
+class Lifetime;
+class GenericParam;
+class LifetimeParam;
+
+class TraitItem;
+class ImplItem;
+struct Crate;
+class PathExpr;
+
+// rust-path.h
+class PathIdentSegment;
+struct GenericArgsBinding;
+struct GenericArgs;
+class PathExprSegment;
+class PathPattern;
+class PathInExpression;
+class TypePathSegment;
+class TypePathSegmentGeneric;
+struct TypePathFunction;
+class TypePathSegmentFunction;
+class TypePath;
+struct QualifiedPathType;
+class QualifiedPathInExpression;
+class QualifiedPathInType;
+
+// rust-expr.h
+class ExprWithBlock;
+class LiteralExpr;
+class AttrInputLiteral;
+class OperatorExpr;
+class BorrowExpr;
+class DereferenceExpr;
+class ErrorPropagationExpr;
+class NegationExpr;
+class ArithmeticOrLogicalExpr;
+class ComparisonExpr;
+class LazyBooleanExpr;
+class TypeCastExpr;
+class AssignmentExpr;
+class CompoundAssignmentExpr;
+class GroupedExpr;
+class ArrayElems;
+class ArrayElemsValues;
+class ArrayElemsCopied;
+class ArrayExpr;
+class ArrayIndexExpr;
+class TupleExpr;
+class TupleIndexExpr;
+class StructExpr;
+class StructExprStruct;
+struct StructBase;
+class StructExprField;
+class StructExprFieldIdentifier;
+class StructExprFieldWithVal;
+class StructExprFieldIdentifierValue;
+class StructExprFieldIndexValue;
+class StructExprStructFields;
+class StructExprStructBase;
+class CallExpr;
+class MethodCallExpr;
+class FieldAccessExpr;
+struct ClosureParam;
+class ClosureExpr;
+class ClosureExprInner;
+class BlockExpr;
+class ClosureExprInnerTyped;
+class ContinueExpr;
+class BreakExpr;
+class RangeExpr;
+class RangeFromToExpr;
+class RangeFromExpr;
+class RangeToExpr;
+class RangeFullExpr;
+class RangeFromToInclExpr;
+class RangeToInclExpr;
+class ReturnExpr;
+class UnsafeBlockExpr;
+class LoopLabel;
+class BaseLoopExpr;
+class LoopExpr;
+class WhileLoopExpr;
+class WhileLetLoopExpr;
+class ForLoopExpr;
+class IfExpr;
+class IfExprConseqElse;
+class IfExprConseqIf;
+class IfLetExpr;
+class IfExprConseqIfLet;
+class IfLetExprConseqElse;
+class IfLetExprConseqIf;
+class IfLetExprConseqIfLet;
+struct MatchArm;
+// class MatchCase;
+// class MatchCaseBlockExpr;
+// class MatchCaseExpr;
+struct MatchCase;
+class MatchExpr;
+class AwaitExpr;
+class AsyncBlockExpr;
+
+// rust-stmt.h
+class EmptyStmt;
+class LetStmt;
+class ExprStmt;
+class ExprStmtWithoutBlock;
+class ExprStmtWithBlock;
+
+// rust-item.h
+class TypeParam;
+class ConstGenericParam;
+class WhereClauseItem;
+class LifetimeWhereClauseItem;
+class TypeBoundWhereClauseItem;
+struct WhereClause;
+struct SelfParam;
+struct FunctionQualifiers;
+struct FunctionParam;
+struct Visibility;
+class VisItem;
+class Module;
+class ExternCrate;
+class UseTree;
+class UseTreeGlob;
+class UseTreeList;
+class UseTreeRebind;
+class UseDeclaration;
+class Function;
+class TypeAlias;
+class Struct;
+struct StructField;
+class StructStruct;
+struct TupleField;
+class TupleStruct;
+class EnumItem;
+class EnumItemTuple;
+class EnumItemStruct;
+class EnumItemDiscriminant;
+class Enum;
+class Union;
+class ConstantItem;
+class StaticItem;
+struct TraitFunctionDecl;
+class TraitItemFunc;
+class TraitItemConst;
+class TraitItemType;
+class Trait;
+class ImplBlock;
+class ExternalItem;
+class ExternalStaticItem;
+struct NamedFunctionParam;
+class ExternalFunctionItem;
+class ExternBlock;
+
+// rust-pattern.h
+class LiteralPattern;
+class IdentifierPattern;
+class WildcardPattern;
+class RangePatternBound;
+class RangePatternBoundLiteral;
+class RangePatternBoundPath;
+class RangePatternBoundQualPath;
+class RangePattern;
+class ReferencePattern;
+struct StructPatternEtc;
+class StructPatternField;
+class StructPatternFieldTuplePat;
+class StructPatternFieldIdentPat;
+class StructPatternFieldIdent;
+class StructPattern;
+class TupleStructItems;
+class TupleStructItemsNoRange;
+class TupleStructItemsRange;
+class TupleStructPattern;
+class TuplePatternItems;
+class TuplePatternItemsMultiple;
+class TuplePatternItemsRanged;
+class TuplePattern;
+class GroupedPattern;
+class SlicePattern;
+
+// rust-type.h
+class TraitBound;
+class ImplTraitType;
+class TraitObjectType;
+class ParenthesisedType;
+class ImplTraitTypeOneBound;
+class TupleType;
+class NeverType;
+class RawPointerType;
+class ReferenceType;
+class ArrayType;
+class SliceType;
+class InferredType;
+struct MaybeNamedParam;
+class BareFunctionType;
+} // namespace HIR
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/hir/tree/rust-hir-full-test.cc b/gcc/rust/hir/tree/rust-hir-full-test.cc
new file mode 100644
index 00000000000..4e255320e2d
--- /dev/null
+++ b/gcc/rust/hir/tree/rust-hir-full-test.cc
@@ -0,0 +1,5292 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-ast-full.h"
+#include "rust-hir-full.h"
+#include "rust-hir-visitor.h"
+#include "rust-diagnostics.h"
+
+/* Compilation unit used for various HIR-related functions that would make
+ * the headers too long if they were defined inline and don't receive any
+ * benefits from being defined inline because they are virtual. Also used
+ * for various other stuff. */
+
+namespace Rust {
+namespace HIR {
+
+enum indent_mode
+{
+  enter,
+  out,
+  stay
+};
+
+std::string
+indent_spaces (enum indent_mode mode)
+{
+  static int indent = 0;
+  std::string str = "";
+  if (out == mode)
+    indent--;
+  for (int i = 0; i < indent; i++)
+    str += " ";
+  if (enter == mode)
+    indent++;
+
+  return str;
+}
+
+// Gets a string in a certain delim type.
+std::string
+get_string_in_delims (std::string str_input, AST::DelimType delim_type)
+{
+  switch (delim_type)
+    {
+    case AST::DelimType::PARENS:
+      return "(" + str_input + ")";
+    case AST::DelimType::SQUARE:
+      return "[" + str_input + "]";
+    case AST::DelimType::CURLY:
+      return "{" + str_input + "}";
+    default:
+      return "ERROR-MARK-STRING (delims)";
+    }
+  gcc_unreachable ();
+}
+
+std::string
+Crate::as_string () const
+{
+  std::string str ("HIR::Crate: ");
+
+  // inner attributes
+  str += "\n inner attributes: ";
+  if (inner_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "inner attribute" syntax -
+       * just the body */
+      for (const auto &attr : inner_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  // items
+  str += "\n items: ";
+  if (items.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &item : items)
+	{
+	  // DEBUG: null pointer check
+	  if (item == nullptr)
+	    {
+	      rust_debug ("something really terrible has gone wrong - "
+			  "null pointer item in crate.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + item->as_string ();
+	}
+    }
+
+  return str + "\n::" + get_mappings ().as_string () + "\n";
+}
+
+std::string
+Visibility::as_string () const
+{
+  switch (vis_type)
+    {
+    case PRIVATE:
+      return std::string ("private");
+    case PUBLIC:
+      return std::string ("pub");
+    case RESTRICTED:
+      return std::string ("pub(in ") + path.get_mappings ().as_string ()
+	     + std::string (")");
+    default:
+      gcc_unreachable ();
+    }
+}
+
+// Creates a string that reflects the visibility stored.
+std::string
+VisItem::as_string () const
+{
+  // FIXME: can't do formatting on string to make identation occur.
+  std::string str = Item::as_string ();
+
+  if (has_visibility ())
+    {
+      str = visibility.as_string () + " ";
+    }
+
+  return str;
+}
+
+// Creates a string that reflects the outer attributes stored.
+std::string
+Item::as_string () const
+{
+  std::string str;
+
+  if (!outer_attrs.empty ())
+    {
+      for (const auto &attr : outer_attrs)
+	{
+	  str += attr.as_string () + "\n";
+	}
+    }
+
+  return str;
+}
+
+std::string
+Module::as_string () const
+{
+  // get module string for "[vis] mod [name]"
+  std::string str = VisItem::as_string () + "mod " + module_name;
+
+  // inner attributes
+  str += "\n inner attributes: ";
+  if (inner_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "inner attribute" syntax -
+       * just the body */
+      for (const auto &attr : inner_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  // items
+  str += "\n items: ";
+  if (items.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &item : items)
+	{
+	  // DEBUG: null pointer check
+	  if (item == nullptr)
+	    {
+	      rust_debug ("something really terrible has gone wrong - "
+			  "null pointer item in crate.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + item->as_string ();
+	}
+    }
+
+  return str + "\n";
+}
+
+std::string
+StaticItem::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += indent_spaces (stay) + "static";
+
+  if (is_mut ())
+    {
+      str += " mut";
+    }
+
+  str += name;
+
+  // DEBUG: null pointer check
+  if (type == nullptr)
+    {
+      rust_debug ("something really terrible has gone wrong - null "
+		  "pointer type in static item.");
+      return "nullptr_POINTER_MARK";
+    }
+  str += "\n" + indent_spaces (stay) + "Type: " + type->as_string ();
+
+  // DEBUG: null pointer check
+  if (expr == nullptr)
+    {
+      rust_debug ("something really terrible has gone wrong - null "
+		  "pointer expr in static item.");
+      return "nullptr_POINTER_MARK";
+    }
+  str += "\n" + indent_spaces (stay) + "Expression: " + expr->as_string ();
+
+  return str + "\n";
+}
+
+std::string
+ExternCrate::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += "extern crate " + referenced_crate;
+
+  if (has_as_clause ())
+    {
+      str += " as " + as_clause_name;
+    }
+
+  return str;
+}
+
+std::string
+TupleStruct::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += "struct " + struct_name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in enum.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  // tuple fields
+  str += "\n Tuple fields: ";
+  if (fields.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &field : fields)
+	{
+	  str += "\n  " + field.as_string ();
+	}
+    }
+
+  str += "\n Where clause: ";
+  if (has_where_clause ())
+    {
+      str += where_clause.as_string ();
+    }
+  else
+    {
+      str += "none";
+    }
+
+  return str;
+}
+
+std::string
+ConstantItem::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += "const " + identifier;
+
+  // DEBUG: null pointer check
+  if (type == nullptr)
+    {
+      rust_debug ("something really terrible has gone wrong - null "
+		  "pointer type in const item.");
+      return "nullptr_POINTER_MARK";
+    }
+  str += "\n  Type: " + type->as_string ();
+
+  // DEBUG: null pointer check
+  if (const_expr == nullptr)
+    {
+      rust_debug ("something really terrible has gone wrong - null "
+		  "pointer expr in const item.");
+      return "nullptr_POINTER_MARK";
+    }
+  str += "\n  Expression: " + const_expr->as_string ();
+
+  return str + "\n";
+}
+
+std::string
+ImplBlock::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += "impl ";
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in impl.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  str += "\n Type: " + impl_type->as_string ();
+
+  str += "\n Where clause: ";
+  if (has_where_clause ())
+    {
+      str += where_clause.as_string ();
+    }
+  else
+    {
+      str += "none";
+    }
+
+  // inner attributes
+  str += "\n inner attributes: ";
+  if (inner_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "inner attribute" syntax -
+       * just the body */
+      for (const auto &attr : inner_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  str += "\n impl items: ";
+  if (!has_impl_items ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &item : impl_items)
+	{
+	  str += "\n  " + item->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+StructStruct::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += "struct " + struct_name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in enum.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  str += "\n Where clause: ";
+  if (has_where_clause ())
+    {
+      str += where_clause.as_string ();
+    }
+  else
+    {
+      str += "none";
+    }
+
+  // struct fields
+  str += "\n Struct fields: ";
+  if (is_unit)
+    {
+      str += "none (unit)";
+    }
+  else if (fields.empty ())
+    {
+      str += "none (non-unit)";
+    }
+  else
+    {
+      for (const auto &field : fields)
+	{
+	  str += "\n  - " + field.as_string ();
+	}
+      str += "\n";
+    }
+
+  return str + "::" + get_mappings ().as_string () + "\n";
+}
+
+std::string
+UseDeclaration::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  // DEBUG: null pointer check
+  if (use_tree == nullptr)
+    {
+      rust_debug (
+	"something really terrible has gone wrong - null pointer use tree in "
+	"use declaration.");
+      return "nullptr_POINTER_MARK";
+    }
+
+  str += "use " + use_tree->as_string ();
+
+  return str;
+}
+
+std::string
+UseTreeGlob::as_string () const
+{
+  switch (glob_type)
+    {
+    case NO_PATH:
+      return "*";
+    case GLOBAL:
+      return "::*";
+      case PATH_PREFIXED: {
+	std::string path_str = path.as_string ();
+	return path_str + "::*";
+      }
+    default:
+      // some kind of error
+      return "ERROR-PATH";
+    }
+  gcc_unreachable ();
+}
+
+std::string
+UseTreeList::as_string () const
+{
+  std::string path_str;
+  switch (path_type)
+    {
+    case NO_PATH:
+      path_str = "{";
+      break;
+    case GLOBAL:
+      path_str = "::{";
+      break;
+      case PATH_PREFIXED: {
+	path_str = path.as_string () + "::{";
+	break;
+      }
+    default:
+      // some kind of error
+      return "ERROR-PATH-LIST";
+    }
+
+  if (has_trees ())
+    {
+      auto i = trees.begin ();
+      auto e = trees.end ();
+
+      // DEBUG: null pointer check
+      if (*i == nullptr)
+	{
+	  rust_debug ("something really terrible has gone wrong - null pointer "
+		      "tree in use tree list.");
+	  return "nullptr_POINTER_MARK";
+	}
+
+      for (; i != e; i++)
+	{
+	  path_str += (*i)->as_string ();
+	  if (e != i + 1)
+	    path_str += ", ";
+	}
+    }
+  else
+    {
+      path_str += "none";
+    }
+
+  return path_str + "}";
+}
+
+std::string
+UseTreeRebind::as_string () const
+{
+  std::string path_str = path.as_string ();
+
+  switch (bind_type)
+    {
+    case NONE:
+      // nothing to add, just path
+      break;
+    case IDENTIFIER:
+      path_str += " as " + identifier;
+      break;
+    case WILDCARD:
+      path_str += " as _";
+      break;
+    default:
+      // error
+      return "ERROR-PATH-REBIND";
+    }
+
+  return path_str;
+}
+
+std::string
+Enum::as_string () const
+{
+  std::string str = VisItem::as_string ();
+  str += enum_name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in enum.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  str += "\n Where clause: ";
+  if (has_where_clause ())
+    {
+      str += where_clause.as_string ();
+    }
+  else
+    {
+      str += "none";
+    }
+
+  // items
+  str += "\n Items: ";
+  if (items.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &item : items)
+	{
+	  // DEBUG: null pointer check
+	  if (item == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"enum item in enum.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + item->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+Trait::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  if (unsafety == Unsafety::Unsafe)
+    {
+      str += "unsafe ";
+    }
+
+  str += "trait " + name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in trait.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  str += "\n Type param bounds: ";
+  if (!has_type_param_bounds ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &bound : type_param_bounds)
+	{
+	  // DEBUG: null pointer check
+	  if (bound == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"type param bound in trait.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + bound->as_string ();
+	}
+    }
+
+  str += "\n Where clause: ";
+  if (!has_where_clause ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += where_clause.as_string ();
+    }
+
+  str += "\n Trait items: ";
+  if (!has_trait_items ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &item : trait_items)
+	{
+	  // DEBUG: null pointer check
+	  if (item == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"trait item in trait.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + item->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+Union::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += "union " + union_name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in union.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  str += "\n Where clause: ";
+  if (has_where_clause ())
+    {
+      str += where_clause.as_string ();
+    }
+  else
+    {
+      str += "none";
+    }
+
+  // struct fields
+  str += "\n Struct fields (variants): ";
+  if (variants.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &field : variants)
+	{
+	  str += "\n  " + field.as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+Function::as_string () const
+{
+  std::string str = VisItem::as_string () + "\n";
+  std::string qstr = qualifiers.as_string ();
+  if ("" != qstr)
+    str += qstr + " ";
+
+  if (has_function_return_type ())
+    {
+      // DEBUG: null pointer check
+      if (return_type == nullptr)
+	{
+	  rust_debug (
+	    "something really terrible has gone wrong - null pointer return "
+	    "type in function.");
+	  return "nullptr_POINTER_MARK";
+	}
+
+      str += return_type->as_string () + " ";
+    }
+  else
+    {
+      str += "void ";
+    }
+
+  str += function_name;
+
+  if (has_generics ())
+    {
+      str += "<";
+
+      auto i = generic_params.begin ();
+      auto e = generic_params.end ();
+
+      // DEBUG: null pointer check
+      if (i == e)
+	{
+	  rust_debug ("something really terrible has gone wrong - null pointer "
+		      "generic param in function item.");
+	  return "nullptr_POINTER_MARK";
+	}
+
+      for (; i != e; i++)
+	{
+	  str += (*i)->as_string ();
+	  if (e != i + 1)
+	    str += ", ";
+	}
+      str += ">";
+    }
+
+  if (has_function_params ())
+    {
+      auto i = function_params.begin ();
+      auto e = function_params.end ();
+      str += "(";
+      for (; i != e; i++)
+	{
+	  str += (*i).as_string ();
+	  if (e != i + 1)
+	    str += ", ";
+	}
+      str += ")";
+    }
+  else
+    {
+      str += "()";
+    }
+
+  if (has_where_clause ())
+    {
+      str += " where " + where_clause.as_string ();
+    }
+
+  str += "\n";
+
+  // DEBUG: null pointer check
+  if (function_body == nullptr)
+    {
+      rust_debug (
+	"something really terrible has gone wrong - null pointer function "
+	"body in function.");
+      return "nullptr_POINTER_MARK";
+    }
+  return str + function_body->as_string () + "::" + get_mappings ().as_string ()
+	 + "\n";
+}
+
+std::string
+WhereClause::as_string () const
+{
+  // just print where clause items, don't mention "where" or "where clause"
+  std::string str;
+
+  if (where_clause_items.empty ())
+    {
+      str = "none";
+    }
+  else
+    {
+      for (const auto &item : where_clause_items)
+	{
+	  str += "\n  " + item->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+BlockExpr::as_string () const
+{
+  std::string istr = indent_spaces (enter);
+  std::string str = istr + "BlockExpr:\n" + istr;
+  // get outer attributes
+  str += "{\n" + indent_spaces (stay) + Expr::as_string ();
+
+  // inner attributes
+  str += "\n" + indent_spaces (stay) + "inner attributes: ";
+  if (inner_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "inner attribute" syntax -
+       * just the body */
+      for (const auto &attr : inner_attrs)
+	{
+	  str += "\n" + indent_spaces (stay) + attr.as_string ();
+	}
+    }
+
+  // statements
+  str += "\n" + indent_spaces (stay) + "statements: ";
+  if (statements.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &stmt : statements)
+	{
+	  // DEBUG: null pointer check
+	  if (stmt == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"stmt in block expr.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n" + indent_spaces (stay) + stmt->as_string ();
+	}
+    }
+
+  // final expression
+  str += "\n" + indent_spaces (stay) + "final expression: ";
+  if (expr == nullptr)
+    {
+      str += "none";
+    }
+  else
+    {
+      str += "\n" + expr->as_string ();
+    }
+
+  str += "\n" + indent_spaces (out) + "}";
+  return str;
+}
+
+std::string
+TypeAlias::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += " " + new_type_name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (!has_generics ())
+    {
+      str += "none";
+    }
+  else
+    {
+      auto i = generic_params.begin ();
+      auto e = generic_params.end ();
+
+      for (; i != e; i++)
+	{
+	  str += (*i)->as_string ();
+	  if (e != i + 1)
+	    str += ", ";
+	}
+    }
+
+  str += "\n Where clause: ";
+  if (!has_where_clause ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += where_clause.as_string ();
+    }
+
+  str += "\n Type: " + existing_type->as_string ();
+
+  return str;
+}
+
+std::string
+ExternBlock::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += "extern ";
+  str += "\"" + get_string_from_abi (abi) + "\" ";
+
+  // inner attributes
+  str += "\n inner attributes: ";
+  if (inner_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "inner attribute" syntax -
+       * just the body */
+      for (const auto &attr : inner_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  str += "\n external items: ";
+  if (!has_extern_items ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &item : extern_items)
+	{
+	  str += "\n  " + item->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+PathInExpression::as_string () const
+{
+  std::string str;
+
+  if (has_opening_scope_resolution)
+    {
+      str = "::";
+    }
+
+  return str + PathPattern::as_string () + "::" + get_mappings ().as_string ();
+}
+
+std::string
+ExprStmtWithBlock::as_string () const
+{
+  std::string str = indent_spaces (enter) + "ExprStmtWithBlock: \n";
+
+  if (expr == nullptr)
+    {
+      str += "none (this should not happen and is an error)";
+    }
+  else
+    {
+      indent_spaces (enter);
+      str += expr->as_string ();
+      indent_spaces (out);
+    }
+
+  indent_spaces (out);
+  return str;
+}
+
+std::string
+ClosureParam::as_string () const
+{
+  std::string str (pattern->as_string ());
+
+  if (has_type_given ())
+    {
+      str += " : " + type->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+ClosureExpr::as_string () const
+{
+  std::string str ("ClosureExpr:\n Has move: ");
+  if (has_move)
+    {
+      str += "true";
+    }
+  else
+    {
+      str += "false";
+    }
+
+  str += "\n Params: ";
+  if (params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : params)
+	{
+	  str += "\n  " + param.as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+ClosureExprInnerTyped::as_string () const
+{
+  std::string str = ClosureExpr::as_string ();
+
+  str += "\n Return type: " + return_type->as_string ();
+
+  str += "\n Body: " + expr->as_string ();
+
+  return str;
+}
+
+std::string
+PathPattern::as_string () const
+{
+  std::string str;
+
+  for (const auto &segment : segments)
+    {
+      str += segment.as_string () + "::";
+    }
+
+  // basically a hack - remove last two characters of string (remove final ::)
+  str.erase (str.length () - 2);
+
+  return str;
+}
+
+std::string
+QualifiedPathType::as_string () const
+{
+  std::string str ("<");
+  str += type->as_string ();
+
+  if (has_as_clause ())
+    {
+      str += " as " + trait->as_string ();
+    }
+
+  return str + ">";
+}
+
+std::string
+QualifiedPathInExpression::as_string () const
+{
+  return path_type.as_string () + "::" + PathPattern::as_string ();
+}
+
+std::string
+BorrowExpr::as_string () const
+{
+  std::string str ("&");
+
+  if (double_borrow)
+    {
+      str += "&";
+    }
+
+  if (is_mut ())
+    {
+      str += "mut ";
+    }
+
+  str += main_or_left_expr->as_string ();
+
+  return str;
+}
+
+std::string
+ReturnExpr::as_string () const
+{
+  std::string str ("return ");
+
+  if (has_return_expr ())
+    {
+      str += return_expr->as_string ();
+    }
+
+  return str + "::" + get_mappings ().as_string ();
+}
+
+std::string
+GroupedExpr::as_string () const
+{
+  std::string str ("Grouped expr:");
+
+  // inner attributes
+  str += "\n inner attributes: ";
+  if (inner_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "inner attribute" syntax -
+       * just the body */
+      for (const auto &attr : inner_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  str += "\n Expr in parens: " + expr_in_parens->as_string ();
+
+  return str;
+}
+
+std::string
+RangeToExpr::as_string () const
+{
+  return ".." + to->as_string ();
+}
+
+std::string
+ContinueExpr::as_string () const
+{
+  std::string str ("continue ");
+
+  if (has_label ())
+    {
+      str += label.as_string ();
+    }
+
+  return str;
+}
+
+std::string
+NegationExpr::as_string () const
+{
+  std::string str;
+
+  switch (expr_type)
+    {
+    case NegationOperator::NEGATE:
+      str = "-";
+      break;
+    case NegationOperator::NOT:
+      str = "!";
+      break;
+    default:
+      return "ERROR_MARK_STRING - negation expr";
+    }
+
+  str += main_or_left_expr->as_string ();
+
+  return str;
+}
+
+std::string
+RangeFromExpr::as_string () const
+{
+  return from->as_string () + "..";
+}
+
+std::string
+RangeFullExpr::as_string () const
+{
+  return "..";
+}
+
+std::string
+ArrayIndexExpr::as_string () const
+{
+  return array_expr->as_string () + "[" + index_expr->as_string () + "]";
+}
+
+std::string
+AssignmentExpr::as_string () const
+{
+  return main_or_left_expr->as_string () + " = " + right_expr->as_string ()
+	 + "::" + get_mappings ().as_string ();
+}
+
+std::string
+CompoundAssignmentExpr::as_string () const
+{
+  std::string operator_str;
+  operator_str.reserve (1);
+
+  // get operator string
+  switch (expr_type)
+    {
+    case ArithmeticOrLogicalOperator::ADD:
+      operator_str = "+";
+      break;
+    case ArithmeticOrLogicalOperator::SUBTRACT:
+      operator_str = "-";
+      break;
+    case ArithmeticOrLogicalOperator::MULTIPLY:
+      operator_str = "*";
+      break;
+    case ArithmeticOrLogicalOperator::DIVIDE:
+      operator_str = "/";
+      break;
+    case ArithmeticOrLogicalOperator::MODULUS:
+      operator_str = "%";
+      break;
+    case ArithmeticOrLogicalOperator::BITWISE_AND:
+      operator_str = "&";
+      break;
+    case ArithmeticOrLogicalOperator::BITWISE_OR:
+      operator_str = "|";
+      break;
+    case ArithmeticOrLogicalOperator::BITWISE_XOR:
+      operator_str = "^";
+      break;
+    case ArithmeticOrLogicalOperator::LEFT_SHIFT:
+      operator_str = "<<";
+      break;
+    case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
+      operator_str = ">>";
+      break;
+    default:
+      gcc_unreachable ();
+      break;
+    }
+
+  operator_str += "=";
+
+  std::string str ("CompoundAssignmentExpr: ");
+  if (main_or_left_expr == nullptr || right_expr == nullptr)
+    {
+      str += "error. this is probably a parsing failure.";
+    }
+  else
+    {
+      str += "\n left: " + main_or_left_expr->as_string ();
+      str += "\n right: " + right_expr->as_string ();
+      str += "\n operator: " + operator_str;
+    }
+
+  return str;
+}
+
+std::string
+AsyncBlockExpr::as_string () const
+{
+  std::string str = "AsyncBlockExpr: ";
+
+  // get outer attributes
+  str += "\n " + Expr::as_string ();
+
+  str += "\n Has move: ";
+  str += has_move ? "true" : "false";
+
+  return str + "\n" + block_expr->as_string ();
+}
+
+std::string
+ComparisonExpr::as_string () const
+{
+  std::string str (main_or_left_expr->as_string ());
+
+  switch (expr_type)
+    {
+    case ComparisonOperator::EQUAL:
+      str += " == ";
+      break;
+    case ComparisonOperator::NOT_EQUAL:
+      str += " != ";
+      break;
+    case ComparisonOperator::GREATER_THAN:
+      str += " > ";
+      break;
+    case ComparisonOperator::LESS_THAN:
+      str += " < ";
+      break;
+    case ComparisonOperator::GREATER_OR_EQUAL:
+      str += " >= ";
+      break;
+    case ComparisonOperator::LESS_OR_EQUAL:
+      str += " <= ";
+      break;
+    default:
+      return "ERROR_MARK_STRING - comparison expr";
+    }
+
+  str += right_expr->as_string ();
+
+  return str;
+}
+
+std::string
+MethodCallExpr::as_string () const
+{
+  std::string str ("MethodCallExpr: \n Object (receiver) expr: ");
+
+  str += receiver->as_string ();
+
+  str += "\n Method path segment: \n";
+
+  str += method_name.as_string ();
+
+  str += "\n Call params:";
+  if (params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : params)
+	{
+	  if (param == nullptr)
+	    {
+	      return "ERROR_MARK_STRING - method call expr param is null";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+TupleIndexExpr::as_string () const
+{
+  return tuple_expr->as_string () + "." + std::to_string (tuple_index);
+}
+
+std::string
+DereferenceExpr::as_string () const
+{
+  return "*" + main_or_left_expr->as_string ();
+}
+
+std::string
+FieldAccessExpr::as_string () const
+{
+  return receiver->as_string () + "." + field;
+}
+
+std::string
+LazyBooleanExpr::as_string () const
+{
+  std::string str (main_or_left_expr->as_string ());
+
+  switch (expr_type)
+    {
+    case LazyBooleanOperator::LOGICAL_OR:
+      str += " || ";
+      break;
+    case LazyBooleanOperator::LOGICAL_AND:
+      str += " && ";
+      break;
+    default:
+      return "ERROR_MARK_STRING - lazy boolean expr out of bounds";
+    }
+
+  str += right_expr->as_string ();
+
+  return str;
+}
+
+std::string
+RangeFromToExpr::as_string () const
+{
+  return from->as_string () + ".." + to->as_string ();
+}
+
+std::string
+RangeToInclExpr::as_string () const
+{
+  return "..=" + to->as_string ();
+}
+
+std::string
+UnsafeBlockExpr::as_string () const
+{
+  std::string istr = indent_spaces (enter);
+  std::string str = istr + "UnsafeBlockExpr:";
+  str += istr + "{";
+
+  // get outer attributes
+  str += "\n" + indent_spaces (stay) + Expr::as_string ();
+
+  return str + "\n" + indent_spaces (out) + "}\n" + expr->as_string ();
+}
+
+std::string
+ClosureExprInner::as_string () const
+{
+  std::string str = ClosureExpr::as_string ();
+
+  str += "\n Expression: " + closure_inner->as_string ();
+
+  return str;
+}
+
+std::string
+IfExpr::as_string () const
+{
+  std::string str ("IfExpr: ");
+
+  str += "\n Condition expr: " + condition->as_string ();
+
+  str += "\n If block expr: " + if_block->as_string ();
+
+  return str;
+}
+
+std::string
+IfExprConseqElse::as_string () const
+{
+  std::string str = IfExpr::as_string ();
+
+  str += "\n Else block expr: " + else_block->as_string ();
+
+  return str;
+}
+
+std::string
+IfExprConseqIf::as_string () const
+{
+  std::string str = IfExpr::as_string ();
+
+  str += "\n Else if expr: \n  " + conseq_if_expr->as_string ();
+
+  return str;
+}
+
+std::string
+IfExprConseqIfLet::as_string () const
+{
+  std::string str = IfExpr::as_string ();
+
+  str += "\n Else if let expr: \n  " + if_let_expr->as_string ();
+
+  return str;
+}
+
+std::string
+IfLetExpr::as_string () const
+{
+  std::string str ("IfLetExpr: ");
+
+  str += "\n Condition match arm patterns: ";
+  if (match_arm_patterns.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &pattern : match_arm_patterns)
+	{
+	  str += "\n  " + pattern->as_string ();
+	}
+    }
+
+  str += "\n Scrutinee expr: " + value->as_string ();
+
+  str += "\n If let block expr: " + if_block->as_string ();
+
+  return str;
+}
+
+std::string
+IfLetExprConseqElse::as_string () const
+{
+  std::string str = IfLetExpr::as_string ();
+
+  str += "\n Else block expr: " + else_block->as_string ();
+
+  return str;
+}
+
+std::string
+IfLetExprConseqIf::as_string () const
+{
+  std::string str = IfLetExpr::as_string ();
+
+  str += "\n Else if expr: \n  " + if_expr->as_string ();
+
+  return str;
+}
+
+std::string
+IfLetExprConseqIfLet::as_string () const
+{
+  std::string str = IfLetExpr::as_string ();
+
+  str += "\n Else if let expr: \n  " + if_let_expr->as_string ();
+
+  return str;
+}
+
+std::string
+RangeFromToInclExpr::as_string () const
+{
+  return from->as_string () + "..=" + to->as_string ();
+}
+
+std::string
+ErrorPropagationExpr::as_string () const
+{
+  return main_or_left_expr->as_string () + "?";
+}
+
+std::string
+ArithmeticOrLogicalExpr::as_string () const
+{
+  std::string operator_str;
+  operator_str.reserve (1);
+
+  // get operator string
+  switch (expr_type)
+    {
+    case ArithmeticOrLogicalOperator::ADD:
+      operator_str = "+";
+      break;
+    case ArithmeticOrLogicalOperator::SUBTRACT:
+      operator_str = "-";
+      break;
+    case ArithmeticOrLogicalOperator::MULTIPLY:
+      operator_str = "*";
+      break;
+    case ArithmeticOrLogicalOperator::DIVIDE:
+      operator_str = "/";
+      break;
+    case ArithmeticOrLogicalOperator::MODULUS:
+      operator_str = "%";
+      break;
+    case ArithmeticOrLogicalOperator::BITWISE_AND:
+      operator_str = "&";
+      break;
+    case ArithmeticOrLogicalOperator::BITWISE_OR:
+      operator_str = "|";
+      break;
+    case ArithmeticOrLogicalOperator::BITWISE_XOR:
+      operator_str = "^";
+      break;
+    case ArithmeticOrLogicalOperator::LEFT_SHIFT:
+      operator_str = "<<";
+      break;
+    case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
+      operator_str = ">>";
+      break;
+    default:
+      gcc_unreachable ();
+      break;
+    }
+
+  std::string str = main_or_left_expr->as_string () + " ";
+  str += operator_str + " ";
+  str += right_expr->as_string ();
+
+  return "( " + str + " (" + get_mappings ().as_string () + "))";
+}
+
+std::string
+CallExpr::as_string () const
+{
+  std::string str = function->as_string () + "(";
+  if (!has_params ())
+    str += "none";
+  else
+    {
+      for (const auto &param : params)
+	{
+	  if (param == nullptr)
+	    {
+	      return "ERROR_MARK_STRING - call expr param is null";
+	    }
+
+	  str += param->as_string () + ",";
+	}
+    }
+  return str + ")" + "::" + get_mappings ().as_string ();
+}
+
+std::string
+WhileLoopExpr::as_string () const
+{
+  std::string str ("WhileLoopExpr: ");
+
+  str += "\n Label: ";
+  if (!has_loop_label ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += loop_label.as_string ();
+    }
+
+  str += "\n Conditional expr: " + condition->as_string ();
+
+  str += "\n Loop block: " + loop_block->as_string ();
+
+  return str;
+}
+
+std::string
+WhileLetLoopExpr::as_string () const
+{
+  std::string str ("WhileLetLoopExpr: ");
+
+  str += "\n Label: ";
+  if (!has_loop_label ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += loop_label.as_string ();
+    }
+
+  str += "\n Match arm patterns: ";
+  if (match_arm_patterns.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &pattern : match_arm_patterns)
+	{
+	  str += "\n  " + pattern->as_string ();
+	}
+    }
+
+  str += "\n Scrutinee expr: " + condition->as_string ();
+
+  str += "\n Loop block: " + loop_block->as_string ();
+
+  return str;
+}
+
+std::string
+LoopExpr::as_string () const
+{
+  std::string str ("LoopExpr: (infinite loop)");
+
+  str += "\n Label: ";
+  if (!has_loop_label ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += loop_label.as_string ();
+    }
+
+  str += "\n Loop block: " + loop_block->as_string ();
+
+  return str;
+}
+
+std::string
+ArrayExpr::as_string () const
+{
+  std::string str ("ArrayExpr:");
+
+  // inner attributes
+  str += "\n inner attributes: ";
+  if (inner_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "inner attribute" syntax -
+       * just the body */
+      for (const auto &attr : inner_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  str += "\n Array elems: ";
+  if (!has_array_elems ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += internal_elements->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+AwaitExpr::as_string () const
+{
+  return awaited_expr->as_string () + ".await";
+}
+
+std::string
+BreakExpr::as_string () const
+{
+  std::string str ("break ");
+
+  if (has_label ())
+    {
+      str += label.as_string () + " ";
+    }
+
+  if (has_break_expr ())
+    {
+      str += break_expr->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+LoopLabel::as_string () const
+{
+  return label.as_string () + ": (label) ";
+}
+
+std::string
+MatchArm::as_string () const
+{
+  // outer attributes
+  std::string str = "Outer attributes: ";
+  if (outer_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "outer attribute" syntax -
+       * just the body */
+      for (const auto &attr : outer_attrs)
+	{
+	  str += "\n " + attr.as_string ();
+	}
+    }
+
+  str += "\nPatterns: ";
+  if (match_arm_patterns.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &pattern : match_arm_patterns)
+	{
+	  str += "\n " + pattern->as_string ();
+	}
+    }
+
+  str += "\nGuard expr: ";
+  if (!has_match_arm_guard ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += guard_expr->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+MatchCase::as_string () const
+{
+  std::string str ("MatchCase: (match arm) ");
+
+  str += "\n Match arm matcher: \n" + arm.as_string ();
+  str += "\n Expr: " + expr->as_string ();
+
+  return str;
+}
+
+/*std::string
+MatchCaseBlockExpr::as_string () const
+{
+  std::string str = MatchCase::as_string ();
+
+  str += "\n Block expr: " + block_expr->as_string ();
+
+  return str;
+}
+
+std::string
+MatchCaseExpr::as_string () const
+{
+  std::string str = MatchCase::as_string ();
+
+  str += "\n Expr: " + expr->as_string ();
+
+  return str;
+}*/
+
+std::string
+MatchExpr::as_string () const
+{
+  std::string str ("MatchExpr:");
+
+  str += "\n Scrutinee expr: " + branch_value->as_string ();
+
+  // inner attributes
+  str += "\n inner attributes: ";
+  if (inner_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "inner attribute" syntax -
+       * just the body */
+      for (const auto &attr : inner_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  // match arms
+  str += "\n Match arms: ";
+  if (match_arms.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &arm : match_arms)
+	str += "\n  " + arm.as_string ();
+    }
+
+  return str;
+}
+
+std::string
+TupleExpr::as_string () const
+{
+  std::string str ("TupleExpr:");
+
+  // inner attributes
+  str += "\n inner attributes: ";
+  if (inner_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "inner attribute" syntax -
+       * just the body */
+      for (const auto &attr : inner_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  str += "\n Tuple elements: ";
+  if (tuple_elems.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &elem : tuple_elems)
+	{
+	  str += "\n  " + elem->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+ExprStmtWithoutBlock::as_string () const
+{
+  std::string str ("ExprStmtWithoutBlock:\n");
+  indent_spaces (enter);
+  str += indent_spaces (stay);
+
+  if (expr == nullptr)
+    {
+      str += "none (this shouldn't happen and is probably an error)";
+    }
+  else
+    {
+      str += expr->as_string ();
+    }
+  indent_spaces (out);
+
+  return str;
+}
+
+std::string
+FunctionParam::as_string () const
+{
+  return param_name->as_string () + " : " + type->as_string ();
+}
+
+std::string
+FunctionQualifiers::as_string () const
+{
+  std::string str;
+
+  switch (const_status)
+    {
+    case NONE:
+      // do nothing
+      break;
+    case CONST_FN:
+      str += "const ";
+      break;
+    case ASYNC_FN:
+      str += "async ";
+      break;
+    default:
+      return "ERROR_MARK_STRING: async-const status failure";
+    }
+
+  if (unsafety == Unsafety::Unsafe)
+    {
+      str += "unsafe ";
+    }
+
+  if (has_extern)
+    {
+      str += "extern";
+      str += " \"" + get_string_from_abi (abi) + "\"";
+    }
+
+  return str;
+}
+
+std::string
+TraitBound::as_string () const
+{
+  std::string str ("TraitBound:");
+
+  str += "\n Has opening question mark: ";
+  if (opening_question_mark)
+    {
+      str += "true";
+    }
+  else
+    {
+      str += "false";
+    }
+
+  str += "\n For lifetimes: ";
+  if (!has_for_lifetimes ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &lifetime : for_lifetimes)
+	{
+	  str += "\n  " + lifetime.as_string ();
+	}
+    }
+
+  str += "\n Type path: " + type_path.as_string ();
+
+  return str;
+}
+
+std::string
+LifetimeParam::as_string () const
+{
+  std::string str ("LifetimeParam: ");
+
+  str += "\n Outer attribute: ";
+  if (!has_outer_attribute ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += outer_attr.as_string ();
+    }
+
+  str += "\n Lifetime: " + lifetime.as_string ();
+
+  str += "\n Lifetime bounds: ";
+  if (!has_lifetime_bounds ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &bound : lifetime_bounds)
+	{
+	  str += "\n  " + bound.as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+QualifiedPathInType::as_string () const
+{
+  std::string str = path_type.as_string ();
+
+  for (const auto &segment : segments)
+    {
+      str += "::" + segment->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+Lifetime::as_string () const
+{
+  if (is_error ())
+    {
+      return "error lifetime";
+    }
+
+  switch (lifetime_type)
+    {
+    case AST::Lifetime::LifetimeType::NAMED:
+      return "'" + lifetime_name;
+    case AST::Lifetime::LifetimeType::STATIC:
+      return "'static";
+    case AST::Lifetime::LifetimeType::WILDCARD:
+      return "'_";
+    default:
+      return "ERROR-MARK-STRING: lifetime type failure";
+    }
+}
+
+std::string
+TypePath::as_string () const
+{
+  std::string str;
+
+  if (has_opening_scope_resolution)
+    {
+      str = "::";
+    }
+
+  for (const auto &segment : segments)
+    {
+      str += segment->as_string () + "::";
+    }
+
+  // kinda hack - remove last 2 '::' characters
+  str.erase (str.length () - 2);
+
+  return str;
+}
+
+std::string
+TypeParam::as_string () const
+{
+  std::string str ("TypeParam: ");
+
+  str += "\n Outer attribute: ";
+  if (!has_outer_attribute ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += outer_attr.as_string ();
+    }
+
+  str += "\n Identifier: " + type_representation;
+
+  str += "\n Type param bounds: ";
+  if (!has_type_param_bounds ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &bound : type_param_bounds)
+	{
+	  str += "\n  " + bound->as_string ();
+	}
+    }
+
+  str += "\n Type: ";
+  if (!has_type ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += type->as_string ();
+    }
+
+  return str;
+}
+
+AST::SimplePath
+PathPattern::convert_to_simple_path (bool with_opening_scope_resolution) const
+{
+  if (!has_segments ())
+    {
+      return AST::SimplePath::create_empty ();
+    }
+
+  // create vector of reserved size (to minimise reallocations)
+  std::vector<AST::SimplePathSegment> simple_segments;
+  simple_segments.reserve (segments.size ());
+
+  for (const auto &segment : segments)
+    {
+      // return empty path if doesn't meet simple path segment requirements
+      if (segment.has_generic_args () || segment.as_string () == "Self")
+	{
+	  return AST::SimplePath::create_empty ();
+	}
+
+      // create segment and add to vector
+      std::string segment_str = segment.as_string ();
+      simple_segments.push_back (
+	AST::SimplePathSegment (std::move (segment_str), segment.get_locus ()));
+    }
+
+  // kind of a HACK to get locus depending on opening scope resolution
+  Location locus = Linemap::unknown_location ();
+  if (with_opening_scope_resolution)
+    {
+      locus = simple_segments[0].get_locus () - 2; // minus 2 chars for ::
+    }
+  else
+    {
+      locus = simple_segments[0].get_locus ();
+    }
+
+  return AST::SimplePath (std::move (simple_segments),
+			  with_opening_scope_resolution, locus);
+}
+
+AST::SimplePath
+TypePath::as_simple_path () const
+{
+  if (segments.empty ())
+    {
+      return AST::SimplePath::create_empty ();
+    }
+
+  // create vector of reserved size (to minimise reallocations)
+  std::vector<AST::SimplePathSegment> simple_segments;
+  simple_segments.reserve (segments.size ());
+
+  for (const auto &segment : segments)
+    {
+      // return empty path if doesn't meet simple path segment requirements
+      if (segment == nullptr || segment->is_error ()
+	  || !segment->is_ident_only () || segment->as_string () == "Self")
+	{
+	  return AST::SimplePath::create_empty ();
+	}
+
+      // create segment and add to vector
+      std::string segment_str = segment->as_string ();
+      simple_segments.push_back (
+	AST::SimplePathSegment (std::move (segment_str),
+				segment->get_locus ()));
+    }
+
+  return AST::SimplePath (std::move (simple_segments),
+			  has_opening_scope_resolution, locus);
+}
+
+std::string
+PathExprSegment::as_string () const
+{
+  std::string ident_str = segment_name.as_string ();
+  if (has_generic_args ())
+    {
+      ident_str += "::<" + generic_args.as_string () + ">";
+    }
+
+  return ident_str;
+}
+
+std::string
+GenericArgs::as_string () const
+{
+  std::string args;
+
+  // lifetime args
+  if (!lifetime_args.empty ())
+    {
+      auto i = lifetime_args.begin ();
+      auto e = lifetime_args.end ();
+
+      for (; i != e; i++)
+	{
+	  args += (*i).as_string ();
+	  if (e != i + 1)
+	    args += ", ";
+	}
+    }
+
+  // type args
+  if (!type_args.empty ())
+    {
+      auto i = type_args.begin ();
+      auto e = type_args.end ();
+
+      for (; i != e; i++)
+	{
+	  args += (*i)->as_string ();
+	  if (e != i + 1)
+	    args += ", ";
+	}
+    }
+
+  // binding args
+  if (!binding_args.empty ())
+    {
+      auto i = binding_args.begin ();
+      auto e = binding_args.end ();
+
+      for (; i != e; i++)
+	{
+	  args += (*i).as_string ();
+	  if (e != i + 1)
+	    args += ", ";
+	}
+    }
+
+  return args;
+}
+
+std::string
+GenericArgsBinding::as_string () const
+{
+  return identifier + " = " + type->as_string ();
+}
+
+std::string
+ForLoopExpr::as_string () const
+{
+  std::string str ("ForLoopExpr: ");
+
+  str += "\n Label: ";
+  if (!has_loop_label ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += loop_label.as_string ();
+    }
+
+  str += "\n Pattern: " + pattern->as_string ();
+
+  str += "\n Iterator expr: " + iterator_expr->as_string ();
+
+  str += "\n Loop block: " + loop_block->as_string ();
+
+  return str;
+}
+
+std::string
+RangePattern::as_string () const
+{
+  if (has_ellipsis_syntax)
+    {
+      return lower->as_string () + "..." + upper->as_string ();
+    }
+  else
+    {
+      return lower->as_string () + "..=" + upper->as_string ();
+    }
+}
+
+std::string
+RangePatternBoundLiteral::as_string () const
+{
+  std::string str;
+
+  if (has_minus)
+    {
+      str += "-";
+    }
+
+  str += literal.as_string ();
+
+  return str;
+}
+
+std::string
+SlicePattern::as_string () const
+{
+  std::string str ("SlicePattern: ");
+
+  for (const auto &pattern : items)
+    {
+      str += "\n " + pattern->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+TuplePatternItemsMultiple::as_string () const
+{
+  std::string str;
+
+  for (const auto &pattern : patterns)
+    {
+      str += "\n " + pattern->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+TuplePatternItemsRanged::as_string () const
+{
+  std::string str;
+
+  str += "\n Lower patterns: ";
+  if (lower_patterns.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &lower : lower_patterns)
+	{
+	  str += "\n  " + lower->as_string ();
+	}
+    }
+
+  str += "\n Upper patterns: ";
+  if (upper_patterns.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &upper : upper_patterns)
+	{
+	  str += "\n  " + upper->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+TuplePattern::as_string () const
+{
+  return "TuplePattern: " + items->as_string ();
+}
+
+std::string
+StructPatternField::as_string () const
+{
+  // outer attributes
+  std::string str ("Outer attributes: ");
+  if (outer_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "outer attribute" syntax -
+       * just the body */
+      for (const auto &attr : outer_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+StructPatternFieldIdent::as_string () const
+{
+  std::string str = StructPatternField::as_string ();
+
+  str += "\n";
+
+  if (has_ref)
+    {
+      str += "ref ";
+    }
+
+  if (is_mut ())
+    {
+      str += "mut ";
+    }
+
+  str += ident;
+
+  return str;
+}
+
+std::string
+StructPatternFieldTuplePat::as_string () const
+{
+  std::string str = StructPatternField::as_string ();
+
+  str += "\n";
+
+  str += std::to_string (index) + " : " + tuple_pattern->as_string ();
+
+  return str;
+}
+
+std::string
+StructPatternFieldIdentPat::as_string () const
+{
+  std::string str = StructPatternField::as_string ();
+
+  str += "\n";
+
+  str += ident + " : " + ident_pattern->as_string ();
+
+  return str;
+}
+
+std::string
+StructPatternElements::as_string () const
+{
+  std::string str ("\n  Fields: ");
+
+  if (!has_struct_pattern_fields ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &field : fields)
+	{
+	  str += "\n   " + field->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+StructPattern::as_string () const
+{
+  std::string str ("StructPattern: \n Path: ");
+
+  str += path.as_string ();
+
+  str += "\n Struct pattern elems: ";
+  if (!has_struct_pattern_elems ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += elems.as_string ();
+    }
+
+  return str;
+}
+
+std::string
+LiteralPattern::as_string () const
+{
+  return lit.as_string ();
+}
+
+std::string
+ReferencePattern::as_string () const
+{
+  std::string str ("&");
+
+  if (has_two_amps)
+    {
+      str += "&";
+    }
+
+  if (is_mut ())
+    {
+      str += "mut ";
+    }
+
+  str += pattern->as_string ();
+
+  return str;
+}
+
+std::string
+IdentifierPattern::as_string () const
+{
+  std::string str;
+
+  if (is_ref)
+    {
+      str += "ref ";
+    }
+
+  if (is_mut ())
+    {
+      str += "mut ";
+    }
+
+  str += variable_ident;
+
+  if (has_pattern_to_bind ())
+    {
+      str += " @ " + to_bind->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+TupleStructItemsNoRange::as_string () const
+{
+  std::string str;
+
+  for (const auto &pattern : patterns)
+    {
+      str += "\n  " + pattern->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+TupleStructItemsRange::as_string () const
+{
+  std::string str ("\n  Lower patterns: ");
+
+  if (lower_patterns.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &lower : lower_patterns)
+	{
+	  str += "\n   " + lower->as_string ();
+	}
+    }
+
+  str += "\n  Upper patterns: ";
+  if (upper_patterns.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &upper : upper_patterns)
+	{
+	  str += "\n   " + upper->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+TupleStructPattern::as_string () const
+{
+  std::string str ("TupleStructPattern: \n Path: ");
+
+  str += path.as_string ();
+
+  str += "\n Tuple struct items: " + items->as_string ();
+
+  return str;
+}
+
+std::string
+LetStmt::as_string () const
+{
+  // outer attributes
+  std::string str = "Outer attributes: ";
+  if (outer_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "outer attribute" syntax -
+       * just the body */
+      indent_spaces (enter);
+      for (const auto &attr : outer_attrs)
+	{
+	  str += "\n" + indent_spaces (stay) + attr.as_string ();
+	}
+      indent_spaces (out);
+    }
+
+  str += "\n" + indent_spaces (stay) + "let " + variables_pattern->as_string ();
+
+  if (has_type ())
+    {
+      str += " : " + type->as_string ();
+    }
+
+  if (has_init_expr ())
+    {
+      str += " = " + init_expr->as_string ();
+    }
+
+  return str;
+}
+
+// Used to get outer attributes for expressions.
+std::string
+Expr::as_string () const
+{
+  // outer attributes
+  std::string str = "outer attributes: ";
+  if (outer_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "outer attribute" syntax -
+       * just the body */
+      for (const auto &attr : outer_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  return str;
+}
+
+// hopefully definition here will prevent circular dependency issue
+TraitBound *
+TypePath::to_trait_bound (bool in_parens) const
+{
+  // create clone FIXME is this required? or is copy constructor automatically
+  // called?
+  TypePath copy (*this);
+  return new TraitBound (mappings, std::move (copy), copy.get_locus (),
+			 in_parens);
+}
+
+std::string
+InferredType::as_string () const
+{
+  return "_ (inferred) " + get_mappings ().as_string ();
+}
+
+std::string
+TypeCastExpr::as_string () const
+{
+  return main_or_left_expr->as_string () + " as "
+	 + type_to_convert_to->as_string ();
+}
+
+std::string
+ImplTraitType::as_string () const
+{
+  std::string str ("ImplTraitType: \n TypeParamBounds: ");
+
+  if (type_param_bounds.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &bound : type_param_bounds)
+	{
+	  str += "\n  " + bound->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+ReferenceType::as_string () const
+{
+  std::string str ("&");
+
+  if (has_lifetime ())
+    {
+      str += lifetime.as_string () + " ";
+    }
+
+  if (is_mut ())
+    {
+      str += "mut ";
+    }
+
+  str += type->as_string ();
+
+  return str;
+}
+
+std::string
+RawPointerType::as_string () const
+{
+  return std::string ("*") + (is_mut () ? "mut " : "const ")
+	 + type->as_string ();
+}
+
+std::string
+TraitObjectType::as_string () const
+{
+  std::string str ("TraitObjectType: \n Has dyn dispatch: ");
+
+  if (has_dyn)
+    {
+      str += "true";
+    }
+  else
+    {
+      str += "false";
+    }
+
+  str += "\n TypeParamBounds: ";
+  if (type_param_bounds.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &bound : type_param_bounds)
+	{
+	  str += "\n  " + bound->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+BareFunctionType::as_string () const
+{
+  std::string str ("BareFunctionType: \n For lifetimes: ");
+
+  if (!has_for_lifetimes ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &for_lifetime : for_lifetimes)
+	{
+	  str += "\n  " + for_lifetime.as_string ();
+	}
+    }
+
+  str += "\n Qualifiers: " + function_qualifiers.as_string ();
+
+  str += "\n Params: ";
+  if (params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : params)
+	{
+	  str += "\n  " + param.as_string ();
+	}
+    }
+
+  str += "\n Is variadic: ";
+  if (is_variadic)
+    {
+      str += "true";
+    }
+  else
+    {
+      str += "false";
+    }
+
+  str += "\n Return type: ";
+  if (!has_return_type ())
+    {
+      str += "none (void)";
+    }
+  else
+    {
+      str += return_type->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+ImplTraitTypeOneBound::as_string () const
+{
+  std::string str ("ImplTraitTypeOneBound: \n TraitBound: ");
+
+  return str + trait_bound.as_string ();
+}
+
+std::string
+TypePathSegmentGeneric::as_string () const
+{
+  return TypePathSegment::as_string () + "<" + generic_args.as_string () + ">";
+}
+
+std::string
+TypePathFunction::as_string () const
+{
+  std::string str ("(");
+
+  if (has_inputs ())
+    {
+      auto i = inputs.begin ();
+      auto e = inputs.end ();
+
+      for (; i != e; i++)
+	{
+	  str += (*i)->as_string ();
+	  if (e != i + 1)
+	    str += ", ";
+	}
+    }
+
+  str += ")";
+
+  if (has_return_type ())
+    {
+      str += " -> " + return_type->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+TypePathSegmentFunction::as_string () const
+{
+  return TypePathSegment::as_string () + function_path.as_string ();
+}
+
+std::string
+ArrayType::as_string () const
+{
+  return "[" + elem_type->as_string () + "; " + size->as_string () + "]";
+}
+
+std::string
+SliceType::as_string () const
+{
+  return "[" + elem_type->as_string () + "]";
+}
+
+std::string
+TupleType::as_string () const
+{
+  std::string str ("(");
+
+  if (!is_unit_type ())
+    {
+      auto i = elems.begin ();
+      auto e = elems.end ();
+
+      for (; i != e; i++)
+	{
+	  str += (*i)->as_string ();
+	  if (e != i + 1)
+	    str += ", ";
+	}
+    }
+
+  str += ")";
+
+  return str;
+}
+
+std::string
+StructExpr::as_string () const
+{
+  std::string str = ExprWithoutBlock::as_string ();
+  indent_spaces (enter);
+  str += "\n" + indent_spaces (stay) + "StructExpr:";
+  indent_spaces (enter);
+  str += "\n" + indent_spaces (stay) + "PathInExpr:\n";
+  str += indent_spaces (stay) + struct_name.as_string ();
+  indent_spaces (out);
+  indent_spaces (out);
+  return str;
+}
+
+std::string
+StructExprStruct::as_string () const
+{
+  std::string str ("StructExprStruct (or subclass): ");
+
+  str += "\n Path: " + struct_name.as_string ();
+
+  // inner attributes
+  str += "\n inner attributes: ";
+  if (inner_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "inner attribute" syntax -
+       * just the body */
+      for (const auto &attr : inner_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+StructBase::as_string () const
+{
+  if (base_struct != nullptr)
+    {
+      return base_struct->as_string ();
+    }
+  else
+    {
+      return "ERROR_MARK_STRING - invalid struct base had as string applied";
+    }
+}
+
+std::string
+StructExprFieldWithVal::as_string () const
+{
+  // used to get value string
+  return value->as_string ();
+}
+
+std::string
+StructExprFieldIdentifierValue::as_string () const
+{
+  return field_name + " : " + StructExprFieldWithVal::as_string ();
+}
+
+std::string
+StructExprFieldIndexValue::as_string () const
+{
+  return std::to_string (index) + " : " + StructExprFieldWithVal::as_string ();
+}
+
+std::string
+StructExprStructFields::as_string () const
+{
+  std::string str = StructExprStruct::as_string ();
+
+  str += "\n Fields: ";
+  if (fields.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &field : fields)
+	{
+	  str += "\n  " + field->as_string ();
+	}
+    }
+
+  str += "\n Struct base: ";
+  if (!has_struct_base ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += struct_base->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+EnumItem::as_string () const
+{
+  std::string str = Item::as_string ();
+  str += variant_name;
+  str += " ";
+  switch (get_enum_item_kind ())
+    {
+    case Named:
+      str += "[Named variant]";
+      break;
+    case Tuple:
+      str += "[Tuple variant]";
+      break;
+    case Struct:
+      str += "[Struct variant]";
+      break;
+    case Discriminant:
+      str += "[Discriminant variant]";
+      break;
+    }
+
+  return str;
+}
+
+std::string
+EnumItemTuple::as_string () const
+{
+  std::string str = EnumItem::as_string ();
+
+  // add tuple opening parens
+  str += "(";
+
+  // tuple fields
+  if (has_tuple_fields ())
+    {
+      auto i = tuple_fields.begin ();
+      auto e = tuple_fields.end ();
+
+      for (; i != e; i++)
+	{
+	  str += (*i).as_string ();
+	  if (e != i + 1)
+	    str += ", ";
+	}
+    }
+
+  // add tuple closing parens
+  str += ")";
+
+  return str;
+}
+
+std::string
+TupleField::as_string () const
+{
+  // outer attributes
+  std::string str = "outer attributes: ";
+  if (outer_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "outer attribute" syntax -
+       * just the body */
+      for (const auto &attr : outer_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  if (has_visibility ())
+    {
+      str += "\n" + visibility.as_string ();
+    }
+
+  str += " " + field_type->as_string ();
+
+  return str;
+}
+
+std::string
+EnumItemStruct::as_string () const
+{
+  std::string str = EnumItem::as_string ();
+
+  // add struct opening parens
+  str += "{";
+
+  // tuple fields
+  if (has_struct_fields ())
+    {
+      auto i = struct_fields.begin ();
+      auto e = struct_fields.end ();
+
+      for (; i != e; i++)
+	{
+	  str += (*i).as_string ();
+	  if (e != i + 1)
+	    str += ", ";
+	}
+    }
+
+  // add struct closing parens
+  str += "}";
+
+  return str;
+}
+
+std::string
+StructField::as_string () const
+{
+  // outer attributes
+  std::string str = "outer attributes: ";
+  if (outer_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "outer attribute" syntax -
+       * just the body */
+      for (const auto &attr : outer_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  if (has_visibility ())
+    {
+      str += "\n" + visibility.as_string ();
+    }
+
+  str += " " + field_name + " : " + field_type->as_string ();
+
+  return str;
+}
+
+std::string
+EnumItemDiscriminant::as_string () const
+{
+  std::string str = EnumItem::as_string ();
+
+  // add equal and expression
+  str += " = " + expression->as_string ();
+
+  return str;
+}
+
+std::string
+ExternalItem::as_string () const
+{
+  // outer attributes
+  std::string str = "outer attributes: ";
+  if (outer_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "outer attribute" syntax -
+       * just the body */
+      for (const auto &attr : outer_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  // start visibility on new line and with a space
+  str += "\n" + visibility.as_string () + " ";
+
+  return str;
+}
+
+std::string
+ExternalStaticItem::as_string () const
+{
+  std::string str = ExternalItem::as_string ();
+
+  str += "static ";
+
+  if (is_mut ())
+    {
+      str += "mut ";
+    }
+
+  // add name
+  str += get_item_name ();
+
+  // add type on new line
+  str += "\n Type: " + item_type->as_string ();
+
+  return str;
+}
+
+std::string
+ExternalFunctionItem::as_string () const
+{
+  std::string str = ExternalItem::as_string ();
+
+  str += "fn ";
+
+  // add name
+  str += get_item_name ();
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in external function item.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  // function params
+  str += "\n Function params: ";
+  if (function_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : function_params)
+	{
+	  str += "\n  " + param.as_string ();
+	}
+      if (has_variadics)
+	{
+	  str += "\n  .. (variadic)";
+	}
+    }
+
+  // add type on new line)
+  str += "\n (return) Type: "
+	 + (has_return_type () ? return_type->as_string () : "()");
+
+  // where clause
+  str += "\n Where clause: ";
+  if (has_where_clause ())
+    {
+      str += where_clause.as_string ();
+    }
+  else
+    {
+      str += "none";
+    }
+
+  return str;
+}
+
+std::string
+NamedFunctionParam::as_string () const
+{
+  std::string str = name;
+
+  str += "\n Type: " + param_type->as_string ();
+
+  return str;
+}
+
+/*std::string TraitItem::as_string() const {
+    // outer attributes
+    std::string str = "outer attributes: ";
+    if (outer_attrs.empty()) {
+	str += "none";
+    } else {
+	// note that this does not print them with "outer attribute" syntax -
+just the body for (const auto& attr : outer_attrs) { str += "\n  " +
+attr.as_string();
+	}
+    }
+
+    return str;
+}*/
+
+std::string
+TraitItemFunc::as_string () const
+{
+  std::string str = "outer attributes: ";
+  if (outer_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "outer attribute" syntax -
+       * just the body */
+      for (const auto &attr : outer_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  str += "\n" + decl.as_string ();
+
+  str += "\n Definition (block expr): ";
+  if (has_definition ())
+    {
+      str += block_expr->as_string ();
+    }
+  else
+    {
+      str += "none";
+    }
+
+  return str;
+}
+
+std::string
+TraitFunctionDecl::as_string () const
+{
+  std::string str = qualifiers.as_string () + "fn " + function_name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in trait function decl.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  str += "\n Function params: ";
+  if (is_method ())
+    {
+      str += self.as_string () + (has_params () ? ", " : "");
+    }
+
+  if (has_params ())
+    {
+      for (const auto &param : function_params)
+	{
+	  str += "\n  " + param.as_string ();
+	}
+    }
+  else if (!is_method ())
+    {
+      str += "none";
+    }
+
+  str += "\n Return type: ";
+  if (has_return_type ())
+    {
+      str += return_type->as_string ();
+    }
+  else
+    {
+      str += "none (void)";
+    }
+
+  str += "\n Where clause: ";
+  if (has_where_clause ())
+    {
+      str += where_clause.as_string ();
+    }
+  else
+    {
+      str += "none";
+    }
+
+  return str;
+}
+
+std::string
+TraitItemConst::as_string () const
+{
+  std::string str = "outer attributes: ";
+  if (outer_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "outer attribute" syntax -
+       * just the body */
+      for (const auto &attr : outer_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  str += "\nconst " + name + " : " + type->as_string ();
+
+  if (has_expression ())
+    {
+      str += " = " + expr->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+TraitItemType::as_string () const
+{
+  std::string str = "outer attributes: ";
+  if (outer_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "outer attribute" syntax -
+       * just the body */
+      for (const auto &attr : outer_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  str += "\ntype " + name;
+
+  str += "\n Type param bounds: ";
+  if (!has_type_param_bounds ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &bound : type_param_bounds)
+	{
+	  // DEBUG: null pointer check
+	  if (bound == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"type param bound in trait item type.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + bound->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+SelfParam::as_string () const
+{
+  if (is_error ())
+    {
+      return "error";
+    }
+  else
+    {
+      if (has_type ())
+	{
+	  // type (i.e. not ref, no lifetime)
+	  std::string str;
+
+	  if (is_mut ())
+	    {
+	      str += "mut ";
+	    }
+
+	  str += "self : ";
+
+	  str += type->as_string ();
+
+	  return str;
+	}
+      else if (has_lifetime ())
+	{
+	  // ref and lifetime
+	  std::string str = "&" + lifetime.as_string () + " ";
+
+	  if (is_mut ())
+	    {
+	      str += "mut ";
+	    }
+
+	  str += "self";
+
+	  return str;
+	}
+      else if (is_ref ())
+	{
+	  // ref with no lifetime
+	  std::string str = "&";
+
+	  if (is_mut ())
+	    {
+	      str += " mut ";
+	    }
+
+	  str += "self";
+
+	  return str;
+	}
+      else
+	{
+	  // no ref, no type
+	  std::string str;
+
+	  if (is_mut ())
+	    {
+	      str += "mut ";
+	    }
+
+	  str += "self";
+
+	  return str;
+	}
+    }
+}
+
+std::string
+ArrayElemsCopied::as_string () const
+{
+  return elem_to_copy->as_string () + "; " + num_copies->as_string ();
+}
+
+std::string
+LifetimeWhereClauseItem::as_string () const
+{
+  std::string str ("Lifetime: ");
+
+  str += lifetime.as_string ();
+
+  str += "\nLifetime bounds: ";
+
+  for (const auto &bound : lifetime_bounds)
+    {
+      str += "\n " + bound.as_string ();
+    }
+
+  return str;
+}
+
+std::string
+TypeBoundWhereClauseItem::as_string () const
+{
+  std::string str ("For lifetimes: ");
+
+  if (!has_for_lifetimes ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &for_lifetime : for_lifetimes)
+	{
+	  str += "\n " + for_lifetime.as_string ();
+	}
+    }
+
+  str += "\nType: " + bound_type->as_string ();
+
+  str += "\nType param bounds bounds: ";
+
+  for (const auto &bound : type_param_bounds)
+    {
+      // debug null pointer check
+      if (bound == nullptr)
+	{
+	  return "nullptr_POINTER_MARK - type param bounds";
+	}
+
+      str += "\n " + bound->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+ArrayElemsValues::as_string () const
+{
+  std::string str;
+
+  for (const auto &expr : values)
+    {
+      // DEBUG: null pointer check
+      if (expr == nullptr)
+	{
+	  rust_debug ("something really terrible has gone wrong - null pointer "
+		      "expr in array elems values.");
+	  return "nullptr_POINTER_MARK";
+	}
+
+      str += "\n  " + expr->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+MaybeNamedParam::as_string () const
+{
+  std::string str;
+
+  switch (param_kind)
+    {
+    case UNNAMED:
+      break;
+    case IDENTIFIER:
+      str = name + " : ";
+      break;
+    case WILDCARD:
+      str = "_ : ";
+      break;
+    default:
+      return "ERROR_MARK_STRING - maybe named param unrecognised param kind";
+    }
+
+  str += param_type->as_string ();
+
+  return str;
+}
+
+/* Override that calls the function recursively on all items contained within
+ * the module. */
+void
+Module::add_crate_name (std::vector<std::string> &names) const
+{
+  /* TODO: test whether module has been 'cfg'-ed out to determine whether to
+   * exclude it from search */
+
+  for (const auto &item : items)
+    item->add_crate_name (names);
+}
+
+/* All accept_vis method below */
+
+void
+Lifetime::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LifetimeParam::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+PathInExpression::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+void
+PathInExpression::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypePathSegment::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypePathSegmentGeneric::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypePathSegmentFunction::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypePath::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+QualifiedPathInExpression::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+void
+QualifiedPathInExpression::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+QualifiedPathInType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LiteralExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LiteralExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+BorrowExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+BorrowExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+DereferenceExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+DereferenceExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ErrorPropagationExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ErrorPropagationExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+NegationExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+NegationExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArithmeticOrLogicalExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArithmeticOrLogicalExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ComparisonExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ComparisonExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LazyBooleanExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LazyBooleanExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypeCastExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypeCastExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+AssignmentExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+AssignmentExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+CompoundAssignmentExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+CompoundAssignmentExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+GroupedExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+GroupedExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArrayElemsValues::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArrayElemsCopied::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArrayExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArrayIndexExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleIndexExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprStruct::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprFieldIndexValue::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprStructFields::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprStructBase::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+CallExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+MethodCallExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+FieldAccessExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ClosureExprInner::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+BlockExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ClosureExprInnerTyped::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ContinueExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+BreakExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeFromToExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeFromExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeToExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeFullExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeFromToInclExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeToInclExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ReturnExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+UnsafeBlockExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LoopExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+WhileLoopExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+WhileLetLoopExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ForLoopExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfExprConseqElse::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfExprConseqIf::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfExprConseqIfLet::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfLetExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfLetExprConseqElse::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfLetExprConseqIf::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfLetExprConseqIfLet::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+MatchExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+AwaitExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+AsyncBlockExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypeParam::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LifetimeWhereClauseItem::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypeBoundWhereClauseItem::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Module::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Module::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Module::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternCrate::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+UseTreeGlob::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+UseTreeList::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+UseTreeRebind::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+UseDeclaration::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Function::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypeAlias::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructStruct::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleStruct::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EnumItem::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EnumItemTuple::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EnumItemStruct::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EnumItemDiscriminant::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Enum::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Union::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ConstantItem::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StaticItem::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitItemFunc::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitItemConst::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitItemType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Trait::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ImplBlock::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternalStaticItem::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternalFunctionItem::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternBlock::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LiteralPattern::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IdentifierPattern::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+WildcardPattern::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangePatternBoundLiteral::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangePatternBoundPath::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangePatternBoundQualPath::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangePattern::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ReferencePattern::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructPatternFieldTuplePat::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructPatternFieldIdentPat::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructPatternFieldIdent::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructPattern::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleStructItemsNoRange::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleStructItemsRange::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleStructPattern::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TuplePatternItemsMultiple::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TuplePatternItemsRanged::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TuplePattern::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+GroupedPattern::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+SlicePattern::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EmptyStmt::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LetStmt::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExprStmtWithoutBlock::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExprStmtWithBlock::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitBound::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ImplTraitType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitObjectType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ParenthesisedType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ImplTraitTypeOneBound::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+NeverType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RawPointerType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ReferenceType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArrayType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+SliceType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+InferredType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+BareFunctionType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+NeverType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ParenthesisedType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EmptyStmt::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+GroupedPattern::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+WildcardPattern::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitItemType::accept_vis (HIRTraitItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitItemConst::accept_vis (HIRTraitItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitItemFunc::accept_vis (HIRTraitItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternalFunctionItem::accept_vis (HIRExternalItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternalStaticItem::accept_vis (HIRExternalItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EnumItemDiscriminant::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EnumItemStruct::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EnumItemTuple::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EnumItem::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprStructFields::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprFieldIndexValue::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprFieldIdentifierValue::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprFieldIdentifierValue::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprFieldIdentifier::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprFieldIdentifier::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprStruct::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+SliceType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArrayType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ImplTraitTypeOneBound::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+BareFunctionType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitObjectType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RawPointerType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ReferenceType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ImplTraitType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+InferredType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LetStmt::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleStructPattern::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IdentifierPattern::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ReferencePattern::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LiteralPattern::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructPattern::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TuplePattern::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+SlicePattern::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangePattern::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ForLoopExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypePath::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+QualifiedPathInType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExprStmtWithoutBlock::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+MatchExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+BreakExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+AwaitExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArrayExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LoopExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+WhileLetLoopExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+WhileLoopExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+CallExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeFromToInclExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfLetExprConseqIfLet::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfLetExprConseqIf::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfLetExprConseqElse::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfLetExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfExprConseqIfLet::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfExprConseqIf::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfExprConseqElse::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ClosureExprInner::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+UnsafeBlockExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeToInclExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeFromToExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+FieldAccessExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleIndexExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+MethodCallExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+AsyncBlockExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArrayIndexExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeFullExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeFromExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ContinueExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeToExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ReturnExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+QualifiedPathInExpression::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ClosureExprInnerTyped::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExprStmtWithBlock::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+PathInExpression::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternBlock::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternBlock::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypeAlias::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypeAlias::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypeAlias::accept_vis (HIRImplVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+BlockExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Function::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Function::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Function::accept_vis (HIRImplVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Union::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Union::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Trait::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Trait::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Enum::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Enum::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+UseDeclaration::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+UseDeclaration::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructStruct::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructStruct::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ImplBlock::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ImplBlock::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ConstantItem::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ConstantItem::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ConstantItem::accept_vis (HIRImplVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleStruct::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleStruct::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternCrate::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternCrate::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StaticItem::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StaticItem::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+std::string
+ConstGenericParam::as_string () const
+{
+  auto result = "ConstGenericParam: " + name + " : " + type->as_string ();
+
+  if (default_expression)
+    result += " = " + default_expression->as_string ();
+
+  return result;
+}
+
+void
+ConstGenericParam::accept_vis (HIRFullVisitor &vis)
+{}
+
+} // namespace HIR
+} // namespace Rust
diff --git a/gcc/rust/hir/tree/rust-hir-full.h b/gcc/rust/hir/tree/rust-hir-full.h
new file mode 100644
index 00000000000..646b793654e
--- /dev/null
+++ b/gcc/rust/hir/tree/rust-hir-full.h
@@ -0,0 +1,30 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_HIR_FULL_H
+#define RUST_HIR_FULL_H
+
+#include "rust-hir.h"
+#include "rust-hir-expr.h"
+#include "rust-hir-item.h"
+#include "rust-hir-path.h"
+#include "rust-hir-pattern.h"
+#include "rust-hir-stmt.h"
+#include "rust-hir-type.h"
+
+#endif // RUST_HIR_FULL_H
diff --git a/gcc/rust/hir/tree/rust-hir-visitor.h b/gcc/rust/hir/tree/rust-hir-visitor.h
new file mode 100644
index 00000000000..b3c0b9359cc
--- /dev/null
+++ b/gcc/rust/hir/tree/rust-hir-visitor.h
@@ -0,0 +1,493 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_HIR_VISITOR_H
+#define RUST_HIR_VISITOR_H
+
+#include "rust-hir-full-decls.h"
+
+namespace Rust {
+namespace HIR {
+
+class HIRFullVisitor
+{
+public:
+  virtual void visit (Lifetime &lifetime) = 0;
+  virtual void visit (LifetimeParam &lifetime_param) = 0;
+  virtual void visit (PathInExpression &path) = 0;
+  virtual void visit (TypePathSegment &segment) = 0;
+  virtual void visit (TypePathSegmentGeneric &segment) = 0;
+  virtual void visit (TypePathSegmentFunction &segment) = 0;
+  virtual void visit (TypePath &path) = 0;
+  virtual void visit (QualifiedPathInExpression &path) = 0;
+  virtual void visit (QualifiedPathInType &path) = 0;
+  virtual void visit (LiteralExpr &expr) = 0;
+  virtual void visit (BorrowExpr &expr) = 0;
+  virtual void visit (DereferenceExpr &expr) = 0;
+  virtual void visit (ErrorPropagationExpr &expr) = 0;
+  virtual void visit (NegationExpr &expr) = 0;
+  virtual void visit (ArithmeticOrLogicalExpr &expr) = 0;
+  virtual void visit (ComparisonExpr &expr) = 0;
+  virtual void visit (LazyBooleanExpr &expr) = 0;
+  virtual void visit (TypeCastExpr &expr) = 0;
+  virtual void visit (AssignmentExpr &expr) = 0;
+  virtual void visit (CompoundAssignmentExpr &expr) = 0;
+  virtual void visit (GroupedExpr &expr) = 0;
+  virtual void visit (ArrayElemsValues &elems) = 0;
+  virtual void visit (ArrayElemsCopied &elems) = 0;
+  virtual void visit (ArrayExpr &expr) = 0;
+  virtual void visit (ArrayIndexExpr &expr) = 0;
+  virtual void visit (TupleExpr &expr) = 0;
+  virtual void visit (TupleIndexExpr &expr) = 0;
+  virtual void visit (StructExprStruct &expr) = 0;
+  virtual void visit (StructExprFieldIdentifier &field) = 0;
+  virtual void visit (StructExprFieldIdentifierValue &field) = 0;
+  virtual void visit (StructExprFieldIndexValue &field) = 0;
+  virtual void visit (StructExprStructFields &expr) = 0;
+  virtual void visit (StructExprStructBase &expr) = 0;
+  virtual void visit (CallExpr &expr) = 0;
+  virtual void visit (MethodCallExpr &expr) = 0;
+  virtual void visit (FieldAccessExpr &expr) = 0;
+  virtual void visit (ClosureExprInner &expr) = 0;
+  virtual void visit (BlockExpr &expr) = 0;
+  virtual void visit (ClosureExprInnerTyped &expr) = 0;
+  virtual void visit (ContinueExpr &expr) = 0;
+  virtual void visit (BreakExpr &expr) = 0;
+  virtual void visit (RangeFromToExpr &expr) = 0;
+  virtual void visit (RangeFromExpr &expr) = 0;
+  virtual void visit (RangeToExpr &expr) = 0;
+  virtual void visit (RangeFullExpr &expr) = 0;
+  virtual void visit (RangeFromToInclExpr &expr) = 0;
+  virtual void visit (RangeToInclExpr &expr) = 0;
+  virtual void visit (ReturnExpr &expr) = 0;
+  virtual void visit (UnsafeBlockExpr &expr) = 0;
+  virtual void visit (LoopExpr &expr) = 0;
+  virtual void visit (WhileLoopExpr &expr) = 0;
+  virtual void visit (WhileLetLoopExpr &expr) = 0;
+  virtual void visit (ForLoopExpr &expr) = 0;
+  virtual void visit (IfExpr &expr) = 0;
+  virtual void visit (IfExprConseqElse &expr) = 0;
+  virtual void visit (IfExprConseqIf &expr) = 0;
+  virtual void visit (IfExprConseqIfLet &expr) = 0;
+  virtual void visit (IfLetExpr &expr) = 0;
+  virtual void visit (IfLetExprConseqElse &expr) = 0;
+  virtual void visit (IfLetExprConseqIf &expr) = 0;
+  virtual void visit (IfLetExprConseqIfLet &expr) = 0;
+  virtual void visit (MatchExpr &expr) = 0;
+  virtual void visit (AwaitExpr &expr) = 0;
+  virtual void visit (AsyncBlockExpr &expr) = 0;
+  virtual void visit (TypeParam &param) = 0;
+  virtual void visit (ConstGenericParam &param) = 0;
+  virtual void visit (LifetimeWhereClauseItem &item) = 0;
+  virtual void visit (TypeBoundWhereClauseItem &item) = 0;
+  virtual void visit (Module &module) = 0;
+  virtual void visit (ExternCrate &crate) = 0;
+  virtual void visit (UseTreeGlob &use_tree) = 0;
+  virtual void visit (UseTreeList &use_tree) = 0;
+  virtual void visit (UseTreeRebind &use_tree) = 0;
+  virtual void visit (UseDeclaration &use_decl) = 0;
+  virtual void visit (Function &function) = 0;
+  virtual void visit (TypeAlias &type_alias) = 0;
+  virtual void visit (StructStruct &struct_item) = 0;
+  virtual void visit (TupleStruct &tuple_struct) = 0;
+  virtual void visit (EnumItem &item) = 0;
+  virtual void visit (EnumItemTuple &item) = 0;
+  virtual void visit (EnumItemStruct &item) = 0;
+  virtual void visit (EnumItemDiscriminant &item) = 0;
+  virtual void visit (Enum &enum_item) = 0;
+  virtual void visit (Union &union_item) = 0;
+  virtual void visit (ConstantItem &const_item) = 0;
+  virtual void visit (StaticItem &static_item) = 0;
+  virtual void visit (TraitItemFunc &item) = 0;
+  virtual void visit (TraitItemConst &item) = 0;
+  virtual void visit (TraitItemType &item) = 0;
+  virtual void visit (Trait &trait) = 0;
+  virtual void visit (ImplBlock &impl) = 0;
+  virtual void visit (ExternalStaticItem &item) = 0;
+  virtual void visit (ExternalFunctionItem &item) = 0;
+  virtual void visit (ExternBlock &block) = 0;
+  virtual void visit (LiteralPattern &pattern) = 0;
+  virtual void visit (IdentifierPattern &pattern) = 0;
+  virtual void visit (WildcardPattern &pattern) = 0;
+  virtual void visit (RangePatternBoundLiteral &bound) = 0;
+  virtual void visit (RangePatternBoundPath &bound) = 0;
+  virtual void visit (RangePatternBoundQualPath &bound) = 0;
+  virtual void visit (RangePattern &pattern) = 0;
+  virtual void visit (ReferencePattern &pattern) = 0;
+  virtual void visit (StructPatternFieldTuplePat &field) = 0;
+  virtual void visit (StructPatternFieldIdentPat &field) = 0;
+  virtual void visit (StructPatternFieldIdent &field) = 0;
+  virtual void visit (StructPattern &pattern) = 0;
+  virtual void visit (TupleStructItemsNoRange &tuple_items) = 0;
+  virtual void visit (TupleStructItemsRange &tuple_items) = 0;
+  virtual void visit (TupleStructPattern &pattern) = 0;
+  virtual void visit (TuplePatternItemsMultiple &tuple_items) = 0;
+  virtual void visit (TuplePatternItemsRanged &tuple_items) = 0;
+  virtual void visit (TuplePattern &pattern) = 0;
+  virtual void visit (GroupedPattern &pattern) = 0;
+  virtual void visit (SlicePattern &pattern) = 0;
+  virtual void visit (EmptyStmt &stmt) = 0;
+  virtual void visit (LetStmt &stmt) = 0;
+  virtual void visit (ExprStmtWithoutBlock &stmt) = 0;
+  virtual void visit (ExprStmtWithBlock &stmt) = 0;
+  virtual void visit (TraitBound &bound) = 0;
+  virtual void visit (ImplTraitType &type) = 0;
+  virtual void visit (TraitObjectType &type) = 0;
+  virtual void visit (ParenthesisedType &type) = 0;
+  virtual void visit (ImplTraitTypeOneBound &type) = 0;
+  virtual void visit (TupleType &type) = 0;
+  virtual void visit (NeverType &type) = 0;
+  virtual void visit (RawPointerType &type) = 0;
+  virtual void visit (ReferenceType &type) = 0;
+  virtual void visit (ArrayType &type) = 0;
+  virtual void visit (SliceType &type) = 0;
+  virtual void visit (InferredType &type) = 0;
+  virtual void visit (BareFunctionType &type) = 0;
+};
+
+class HIRFullVisitorBase : public HIRFullVisitor
+{
+public:
+  virtual ~HIRFullVisitorBase () {}
+
+  virtual void visit (Lifetime &) override {}
+  virtual void visit (LifetimeParam &) override {}
+  virtual void visit (PathInExpression &) override {}
+  virtual void visit (TypePathSegment &) override {}
+  virtual void visit (TypePathSegmentGeneric &) override {}
+  virtual void visit (TypePathSegmentFunction &) override {}
+  virtual void visit (TypePath &) override {}
+  virtual void visit (QualifiedPathInExpression &) override {}
+  virtual void visit (QualifiedPathInType &) override {}
+
+  virtual void visit (LiteralExpr &) override {}
+  virtual void visit (BorrowExpr &) override {}
+  virtual void visit (DereferenceExpr &) override {}
+  virtual void visit (ErrorPropagationExpr &) override {}
+  virtual void visit (NegationExpr &) override {}
+  virtual void visit (ArithmeticOrLogicalExpr &) override {}
+  virtual void visit (ComparisonExpr &) override {}
+  virtual void visit (LazyBooleanExpr &) override {}
+  virtual void visit (TypeCastExpr &) override {}
+  virtual void visit (AssignmentExpr &) override {}
+  virtual void visit (CompoundAssignmentExpr &) override {}
+  virtual void visit (GroupedExpr &) override {}
+
+  virtual void visit (ArrayElemsValues &) override {}
+  virtual void visit (ArrayElemsCopied &) override {}
+  virtual void visit (ArrayExpr &) override {}
+  virtual void visit (ArrayIndexExpr &) override {}
+  virtual void visit (TupleExpr &) override {}
+  virtual void visit (TupleIndexExpr &) override {}
+  virtual void visit (StructExprStruct &) override {}
+
+  virtual void visit (StructExprFieldIdentifier &) override {}
+  virtual void visit (StructExprFieldIdentifierValue &) override {}
+
+  virtual void visit (StructExprFieldIndexValue &) override {}
+  virtual void visit (StructExprStructFields &) override {}
+  virtual void visit (StructExprStructBase &) override {}
+
+  virtual void visit (CallExpr &) override {}
+  virtual void visit (MethodCallExpr &) override {}
+  virtual void visit (FieldAccessExpr &) override {}
+  virtual void visit (ClosureExprInner &) override {}
+  virtual void visit (BlockExpr &) override {}
+  virtual void visit (ClosureExprInnerTyped &) override {}
+  virtual void visit (ContinueExpr &) override {}
+  virtual void visit (BreakExpr &) override {}
+  virtual void visit (RangeFromToExpr &) override {}
+  virtual void visit (RangeFromExpr &) override {}
+  virtual void visit (RangeToExpr &) override {}
+  virtual void visit (RangeFullExpr &) override {}
+  virtual void visit (RangeFromToInclExpr &) override {}
+  virtual void visit (RangeToInclExpr &) override {}
+  virtual void visit (ReturnExpr &) override {}
+  virtual void visit (UnsafeBlockExpr &) override {}
+  virtual void visit (LoopExpr &) override {}
+  virtual void visit (WhileLoopExpr &) override {}
+  virtual void visit (WhileLetLoopExpr &) override {}
+  virtual void visit (ForLoopExpr &) override {}
+  virtual void visit (IfExpr &) override {}
+  virtual void visit (IfExprConseqElse &) override {}
+  virtual void visit (IfExprConseqIf &) override {}
+  virtual void visit (IfExprConseqIfLet &) override {}
+  virtual void visit (IfLetExpr &) override {}
+  virtual void visit (IfLetExprConseqElse &) override {}
+  virtual void visit (IfLetExprConseqIf &) override {}
+  virtual void visit (IfLetExprConseqIfLet &) override {}
+
+  virtual void visit (MatchExpr &) override {}
+  virtual void visit (AwaitExpr &) override {}
+  virtual void visit (AsyncBlockExpr &) override {}
+
+  virtual void visit (TypeParam &) override {}
+  virtual void visit (ConstGenericParam &) override {}
+
+  virtual void visit (LifetimeWhereClauseItem &) override {}
+  virtual void visit (TypeBoundWhereClauseItem &) override {}
+  virtual void visit (Module &) override {}
+  virtual void visit (ExternCrate &) override {}
+
+  virtual void visit (UseTreeGlob &) override {}
+  virtual void visit (UseTreeList &) override {}
+  virtual void visit (UseTreeRebind &) override {}
+  virtual void visit (UseDeclaration &) override {}
+  virtual void visit (Function &) override {}
+  virtual void visit (TypeAlias &) override {}
+  virtual void visit (StructStruct &) override {}
+  virtual void visit (TupleStruct &) override {}
+  virtual void visit (EnumItem &) override {}
+  virtual void visit (EnumItemTuple &) override {}
+  virtual void visit (EnumItemStruct &) override {}
+  virtual void visit (EnumItemDiscriminant &) override {}
+  virtual void visit (Enum &) override {}
+  virtual void visit (Union &) override {}
+  virtual void visit (ConstantItem &) override {}
+  virtual void visit (StaticItem &) override {}
+  virtual void visit (TraitItemFunc &) override {}
+  virtual void visit (TraitItemConst &) override {}
+  virtual void visit (TraitItemType &) override {}
+  virtual void visit (Trait &) override {}
+  virtual void visit (ImplBlock &) override {}
+
+  virtual void visit (ExternalStaticItem &) override {}
+  virtual void visit (ExternalFunctionItem &) override {}
+  virtual void visit (ExternBlock &) override {}
+
+  virtual void visit (LiteralPattern &) override {}
+  virtual void visit (IdentifierPattern &) override {}
+  virtual void visit (WildcardPattern &) override {}
+
+  virtual void visit (RangePatternBoundLiteral &) override {}
+  virtual void visit (RangePatternBoundPath &) override {}
+  virtual void visit (RangePatternBoundQualPath &) override {}
+  virtual void visit (RangePattern &) override {}
+  virtual void visit (ReferencePattern &) override {}
+
+  virtual void visit (StructPatternFieldTuplePat &) override {}
+  virtual void visit (StructPatternFieldIdentPat &) override {}
+  virtual void visit (StructPatternFieldIdent &) override {}
+  virtual void visit (StructPattern &) override {}
+
+  virtual void visit (TupleStructItemsNoRange &) override {}
+  virtual void visit (TupleStructItemsRange &) override {}
+  virtual void visit (TupleStructPattern &) override {}
+
+  virtual void visit (TuplePatternItemsMultiple &) override {}
+  virtual void visit (TuplePatternItemsRanged &) override {}
+  virtual void visit (TuplePattern &) override {}
+  virtual void visit (GroupedPattern &) override {}
+  virtual void visit (SlicePattern &) override {}
+
+  virtual void visit (EmptyStmt &) override {}
+  virtual void visit (LetStmt &) override {}
+  virtual void visit (ExprStmtWithoutBlock &) override {}
+  virtual void visit (ExprStmtWithBlock &) override {}
+
+  virtual void visit (TraitBound &) override {}
+  virtual void visit (ImplTraitType &) override {}
+  virtual void visit (TraitObjectType &) override {}
+  virtual void visit (ParenthesisedType &) override {}
+  virtual void visit (ImplTraitTypeOneBound &) override {}
+  virtual void visit (TupleType &) override {}
+  virtual void visit (NeverType &) override {}
+  virtual void visit (RawPointerType &) override {}
+  virtual void visit (ReferenceType &) override {}
+  virtual void visit (ArrayType &) override {}
+  virtual void visit (SliceType &) override {}
+  virtual void visit (InferredType &) override {}
+  virtual void visit (BareFunctionType &) override {}
+};
+
+class HIRExternalItemVisitor
+{
+public:
+  virtual void visit (ExternalStaticItem &item) = 0;
+  virtual void visit (ExternalFunctionItem &item) = 0;
+};
+
+class HIRTraitItemVisitor
+{
+public:
+  virtual void visit (TraitItemFunc &item) = 0;
+  virtual void visit (TraitItemConst &item) = 0;
+  virtual void visit (TraitItemType &item) = 0;
+};
+
+class HIRVisItemVisitor
+{
+public:
+  virtual void visit (Module &module) = 0;
+  virtual void visit (ExternCrate &crate) = 0;
+  virtual void visit (UseDeclaration &use_decl) = 0;
+  virtual void visit (Function &function) = 0;
+  virtual void visit (TypeAlias &type_alias) = 0;
+  virtual void visit (StructStruct &struct_item) = 0;
+  virtual void visit (TupleStruct &tuple_struct) = 0;
+  virtual void visit (Enum &enum_item) = 0;
+  virtual void visit (Union &union_item) = 0;
+  virtual void visit (ConstantItem &const_item) = 0;
+  virtual void visit (StaticItem &static_item) = 0;
+  virtual void visit (Trait &trait) = 0;
+  virtual void visit (ImplBlock &impl) = 0;
+  virtual void visit (ExternBlock &block) = 0;
+};
+
+class HIRImplVisitor
+{
+public:
+  virtual void visit (Function &function) = 0;
+  virtual void visit (ConstantItem &const_item) = 0;
+  virtual void visit (TypeAlias &type_alias) = 0;
+};
+
+class HIRTypeVisitor
+{
+public:
+  virtual void visit (TypePathSegmentFunction &segment) = 0;
+  virtual void visit (TypePath &path) = 0;
+  virtual void visit (QualifiedPathInType &path) = 0;
+  virtual void visit (TraitBound &bound) = 0;
+  virtual void visit (ImplTraitType &type) = 0;
+  virtual void visit (TraitObjectType &type) = 0;
+  virtual void visit (ParenthesisedType &type) = 0;
+  virtual void visit (ImplTraitTypeOneBound &type) = 0;
+  virtual void visit (TupleType &type) = 0;
+  virtual void visit (NeverType &type) = 0;
+  virtual void visit (RawPointerType &type) = 0;
+  virtual void visit (ReferenceType &type) = 0;
+  virtual void visit (ArrayType &type) = 0;
+  virtual void visit (SliceType &type) = 0;
+  virtual void visit (InferredType &type) = 0;
+  virtual void visit (BareFunctionType &type) = 0;
+};
+
+class HIRStmtVisitor
+{
+public:
+  virtual void visit (EnumItemTuple &) = 0;
+  virtual void visit (EnumItemStruct &) = 0;
+  virtual void visit (EnumItem &item) = 0;
+  virtual void visit (TupleStruct &tuple_struct) = 0;
+  virtual void visit (EnumItemDiscriminant &) = 0;
+  virtual void visit (TypePathSegmentFunction &segment) = 0;
+  virtual void visit (TypePath &path) = 0;
+  virtual void visit (QualifiedPathInType &path) = 0;
+  virtual void visit (Module &module) = 0;
+  virtual void visit (ExternCrate &crate) = 0;
+  virtual void visit (UseDeclaration &use_decl) = 0;
+  virtual void visit (Function &function) = 0;
+  virtual void visit (TypeAlias &type_alias) = 0;
+  virtual void visit (StructStruct &struct_item) = 0;
+  virtual void visit (Enum &enum_item) = 0;
+  virtual void visit (Union &union_item) = 0;
+  virtual void visit (ConstantItem &const_item) = 0;
+  virtual void visit (StaticItem &static_item) = 0;
+  virtual void visit (Trait &trait) = 0;
+  virtual void visit (ImplBlock &impl) = 0;
+  virtual void visit (ExternBlock &block) = 0;
+  virtual void visit (EmptyStmt &stmt) = 0;
+  virtual void visit (LetStmt &stmt) = 0;
+  virtual void visit (ExprStmtWithoutBlock &stmt) = 0;
+  virtual void visit (ExprStmtWithBlock &stmt) = 0;
+};
+
+class HIRExpressionVisitor
+{
+public:
+  // These are StructExprField
+  // Added because of CompileStructExprField
+  virtual void visit (StructExprFieldIdentifier &field) = 0;
+  virtual void visit (StructExprFieldIdentifierValue &field) = 0;
+  virtual void visit (StructExprFieldIndexValue &field) = 0;
+
+  virtual void visit (HIR::QualifiedPathInExpression &expr) = 0;
+  virtual void visit (HIR::PathInExpression &expr) = 0;
+  virtual void visit (ClosureExprInnerTyped &) = 0;
+  virtual void visit (ClosureExprInner &expr) = 0;
+  virtual void visit (StructExprStructFields &) = 0;
+  virtual void visit (StructExprStruct &) = 0;
+  virtual void visit (LiteralExpr &expr) = 0;
+  virtual void visit (BorrowExpr &expr) = 0;
+  virtual void visit (DereferenceExpr &expr) = 0;
+  virtual void visit (ErrorPropagationExpr &expr) = 0;
+  virtual void visit (NegationExpr &expr) = 0;
+  virtual void visit (ArithmeticOrLogicalExpr &expr) = 0;
+  virtual void visit (ComparisonExpr &expr) = 0;
+  virtual void visit (LazyBooleanExpr &expr) = 0;
+  virtual void visit (TypeCastExpr &expr) = 0;
+  virtual void visit (AssignmentExpr &expr) = 0;
+  virtual void visit (CompoundAssignmentExpr &expr) = 0;
+  virtual void visit (GroupedExpr &expr) = 0;
+  virtual void visit (ArrayExpr &expr) = 0;
+  virtual void visit (ArrayIndexExpr &expr) = 0;
+  virtual void visit (TupleExpr &expr) = 0;
+  virtual void visit (TupleIndexExpr &expr) = 0;
+  virtual void visit (CallExpr &expr) = 0;
+  virtual void visit (MethodCallExpr &expr) = 0;
+  virtual void visit (FieldAccessExpr &expr) = 0;
+  virtual void visit (BlockExpr &expr) = 0;
+  virtual void visit (ContinueExpr &expr) = 0;
+  virtual void visit (BreakExpr &expr) = 0;
+  virtual void visit (RangeFromToExpr &expr) = 0;
+  virtual void visit (RangeFromExpr &expr) = 0;
+  virtual void visit (RangeToExpr &expr) = 0;
+  virtual void visit (RangeFullExpr &expr) = 0;
+  virtual void visit (RangeFromToInclExpr &expr) = 0;
+  virtual void visit (RangeToInclExpr &expr) = 0;
+  virtual void visit (ReturnExpr &expr) = 0;
+  virtual void visit (UnsafeBlockExpr &expr) = 0;
+  virtual void visit (LoopExpr &expr) = 0;
+  virtual void visit (WhileLoopExpr &expr) = 0;
+  virtual void visit (WhileLetLoopExpr &expr) = 0;
+  virtual void visit (ForLoopExpr &expr) = 0;
+  virtual void visit (IfExpr &expr) = 0;
+  virtual void visit (IfExprConseqElse &expr) = 0;
+  virtual void visit (IfExprConseqIf &expr) = 0;
+  virtual void visit (IfExprConseqIfLet &expr) = 0;
+  virtual void visit (IfLetExpr &expr) = 0;
+  virtual void visit (IfLetExprConseqElse &expr) = 0;
+  virtual void visit (IfLetExprConseqIf &expr) = 0;
+  virtual void visit (IfLetExprConseqIfLet &expr) = 0;
+  virtual void visit (MatchExpr &expr) = 0;
+  virtual void visit (AwaitExpr &expr) = 0;
+  virtual void visit (AsyncBlockExpr &expr) = 0;
+};
+
+class HIRPatternVisitor
+{
+public:
+  virtual void visit (GroupedPattern &) = 0;
+  virtual void visit (IdentifierPattern &) = 0;
+  virtual void visit (LiteralPattern &) = 0;
+  virtual void visit (PathInExpression &) = 0;
+  virtual void visit (QualifiedPathInExpression &) = 0;
+  virtual void visit (RangePattern &) = 0;
+  virtual void visit (ReferencePattern &) = 0;
+  virtual void visit (SlicePattern &) = 0;
+  virtual void visit (StructPattern &) = 0;
+  virtual void visit (TuplePattern &) = 0;
+  virtual void visit (TupleStructPattern &) = 0;
+  virtual void visit (WildcardPattern &) = 0;
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/hir/tree/rust-hir.h b/gcc/rust/hir/tree/rust-hir.h
new file mode 100644
index 00000000000..927ac06fc4a
--- /dev/null
+++ b/gcc/rust/hir/tree/rust-hir.h
@@ -0,0 +1,921 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_HIR_BASE_H
+#define RUST_HIR_BASE_H
+
+#include "rust-ast.h"
+#include "rust-system.h"
+#include "rust-token.h"
+#include "rust-location.h"
+#include "rust-hir-map.h"
+#include "rust-diagnostics.h"
+
+namespace Rust {
+typedef std::string Identifier;
+typedef int TupleIndex;
+
+namespace HIR {
+// foward decl: ast visitor
+class HIRFullVisitor;
+class HIRStmtVisitor;
+class HIRTraitItemVisitor;
+class HIRExternalItemVisitor;
+class HIRVisItemVisitor;
+class HIRExpressionVisitor;
+class HIRPatternVisitor;
+class HIRImplVisitor;
+class HIRTypeVisitor;
+
+// forward decl for use in token tree method
+class Token;
+
+class Node
+{
+public:
+  // Kind for downcasting various HIR nodes to other base classes when visiting
+  // them
+  enum BaseKind
+  {
+    /* class ExternalItem */
+    EXTERNAL,
+    /* class TraitItem */
+    TRAIT_ITEM,
+    /* class VisItem */
+    VIS_ITEM,
+    /* class Item */
+    ITEM,
+    /* class ImplItem */
+    IMPL,
+    /* class Type */
+    TYPE,
+    /* class Stmt */
+    STMT,
+    /* class Expr */
+    EXPR,
+    /* class Pattern */
+    PATTERN,
+  };
+
+  /**
+   * Get the kind of HIR node we are dealing with. This is useful for
+   * downcasting to more precise types when necessary, i.e going from an `Item*`
+   * to a `VisItem*`
+   */
+  virtual BaseKind get_hir_kind () = 0;
+};
+
+// A literal - value with a type. Used in LiteralExpr and LiteralPattern.
+struct Literal
+{
+public:
+  enum LitType
+  {
+    CHAR,
+    STRING,
+    BYTE,
+    BYTE_STRING,
+    INT,
+    FLOAT,
+    BOOL
+  };
+
+private:
+  std::string value_as_string;
+  LitType type;
+  PrimitiveCoreType type_hint;
+
+public:
+  std::string as_string () const { return value_as_string; }
+
+  LitType get_lit_type () const { return type; }
+
+  PrimitiveCoreType get_type_hint () const { return type_hint; }
+
+  Literal (std::string value_as_string, LitType type,
+	   PrimitiveCoreType type_hint)
+    : value_as_string (std::move (value_as_string)), type (type),
+      type_hint (type_hint)
+  {}
+
+  static Literal create_error ()
+  {
+    return Literal ("", CHAR, PrimitiveCoreType::CORETYPE_UNKNOWN);
+  }
+
+  void set_lit_type (LitType lt) { type = lt; }
+
+  // Returns whether literal is in an invalid state.
+  bool is_error () const { return value_as_string == ""; }
+
+  bool is_equal (Literal &other)
+  {
+    return value_as_string == other.value_as_string && type == other.type
+	   && type_hint == other.type_hint;
+  }
+};
+
+/* Base statement abstract class. Note that most "statements" are not allowed in
+ * top-level module scope - only a subclass of statements called "items" are. */
+class Stmt : public Node
+{
+public:
+  // Unique pointer custom clone function
+  std::unique_ptr<Stmt> clone_stmt () const
+  {
+    return std::unique_ptr<Stmt> (clone_stmt_impl ());
+  }
+
+  BaseKind get_hir_kind () override { return STMT; }
+
+  virtual ~Stmt () {}
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+  virtual void accept_vis (HIRStmtVisitor &vis) = 0;
+
+  virtual Location get_locus () const = 0;
+
+  virtual bool is_unit_check_needed () const { return false; }
+
+  const Analysis::NodeMapping &get_mappings () const { return mappings; }
+
+  virtual bool is_item () const = 0;
+
+protected:
+  Stmt (Analysis::NodeMapping mappings) : mappings (std::move (mappings)) {}
+
+  // Clone function implementation as pure virtual method
+  virtual Stmt *clone_stmt_impl () const = 0;
+
+  Analysis::NodeMapping mappings;
+};
+
+// Rust "item" HIR node (declaration of top-level/module-level allowed stuff)
+class Item : public Stmt
+{
+  AST::AttrVec outer_attrs;
+
+  // TODO: should outer attrs be defined here or in each derived class?
+
+public:
+  enum class ItemKind
+  {
+    Static,
+    Constant,
+    TypeAlias,
+    Function,
+    UseDeclaration,
+    ExternBlock,
+    ExternCrate,
+    Struct,
+    Union,
+    Enum,
+    EnumItem, // FIXME: ARTHUR: Do we need that?
+    Trait,
+    Impl,
+    Module,
+  };
+
+  virtual ItemKind get_item_kind () const = 0;
+
+  // Unique pointer custom clone function
+  std::unique_ptr<Item> clone_item () const
+  {
+    return std::unique_ptr<Item> (clone_item_impl ());
+  }
+
+  BaseKind get_hir_kind () override { return ITEM; }
+
+  std::string as_string () const override;
+
+  /* Adds crate names to the vector passed by reference, if it can
+   * (polymorphism). */
+  virtual void
+  add_crate_name (std::vector<std::string> &names ATTRIBUTE_UNUSED) const
+  {}
+
+  AST::AttrVec &get_outer_attrs () { return outer_attrs; }
+  const AST::AttrVec &get_outer_attrs () const { return outer_attrs; }
+
+  bool is_item () const override final { return true; }
+
+protected:
+  // Constructor
+  Item (Analysis::NodeMapping mappings,
+	AST::AttrVec outer_attribs = AST::AttrVec ())
+    : Stmt (std::move (mappings)), outer_attrs (std::move (outer_attribs))
+  {}
+
+  // Clone function implementation as pure virtual method
+  virtual Item *clone_item_impl () const = 0;
+
+  /* Save having to specify two clone methods in derived classes by making
+   * statement clone return item clone. Hopefully won't affect performance too
+   * much. */
+  Item *clone_stmt_impl () const override { return clone_item_impl (); }
+};
+
+// forward decl of ExprWithoutBlock
+class ExprWithoutBlock;
+
+// Base expression HIR node - abstract
+class Expr : public Node
+{
+  AST::AttrVec outer_attrs;
+  Analysis::NodeMapping mappings;
+
+public:
+  enum BlockType
+  {
+    WITH_BLOCK,
+    WITHOUT_BLOCK,
+  };
+
+  enum ExprType
+  {
+    Lit,
+    Operator,
+    Grouped,
+    Array,
+    ArrayIndex,
+    Tuple,
+    TupleIdx,
+    Struct,
+    Call,
+    MethodCall,
+    FieldAccess,
+    Closure,
+    Block,
+    Continue,
+    Break,
+    Range,
+    Return,
+    UnsafeBlock,
+    BaseLoop,
+    If,
+    IfLet,
+    Match,
+    Await,
+    AsyncBlock,
+    Path,
+  };
+
+  BaseKind get_hir_kind () override final { return EXPR; }
+
+  const AST::AttrVec &get_outer_attrs () const { return outer_attrs; }
+
+  // Unique pointer custom clone function
+  std::unique_ptr<Expr> clone_expr () const
+  {
+    return std::unique_ptr<Expr> (clone_expr_impl ());
+  }
+
+  /* HACK: downcasting without dynamic_cast (if possible) via polymorphism -
+   * overrided in subclasses of ExprWithoutBlock */
+  virtual ExprWithoutBlock *as_expr_without_block () const { return nullptr; }
+
+  // TODO: make pure virtual if move out outer attributes to derived classes
+  virtual std::string as_string () const;
+
+  virtual ~Expr () {}
+
+  virtual Location get_locus () const = 0;
+
+  const Analysis::NodeMapping &get_mappings () const { return mappings; }
+
+  // Clone function implementation as pure virtual method
+  virtual Expr *clone_expr_impl () const = 0;
+
+  virtual BlockType get_block_expr_type () const = 0;
+
+  virtual ExprType get_expression_type () const = 0;
+
+  virtual void accept_vis (HIRExpressionVisitor &vis) = 0;
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+
+protected:
+  // Constructor
+  Expr (Analysis::NodeMapping mappings,
+	AST::AttrVec outer_attribs = AST::AttrVec ())
+    : outer_attrs (std::move (outer_attribs)), mappings (std::move (mappings))
+  {}
+
+  // TODO: think of less hacky way to implement this kind of thing
+  // Sets outer attributes.
+  void set_outer_attrs (AST::AttrVec outer_attrs_to_set)
+  {
+    outer_attrs = std::move (outer_attrs_to_set);
+  }
+};
+
+// HIR node for an expression without an accompanying block - abstract
+class ExprWithoutBlock : public Expr
+{
+protected:
+  // Constructor
+  ExprWithoutBlock (Analysis::NodeMapping mappings,
+		    AST::AttrVec outer_attribs = AST::AttrVec ())
+    : Expr (std::move (mappings), std::move (outer_attribs))
+  {}
+
+  // pure virtual clone implementation
+  virtual ExprWithoutBlock *clone_expr_without_block_impl () const = 0;
+
+  /* Save having to specify two clone methods in derived classes by making expr
+   * clone return exprwithoutblock clone. Hopefully won't affect performance too
+   * much. */
+  ExprWithoutBlock *clone_expr_impl () const override
+  {
+    return clone_expr_without_block_impl ();
+  }
+
+public:
+  // Unique pointer custom clone function
+  std::unique_ptr<ExprWithoutBlock> clone_expr_without_block () const
+  {
+    return std::unique_ptr<ExprWithoutBlock> (clone_expr_without_block_impl ());
+  }
+
+  /* downcasting hack from expr to use pratt parsing with
+   * parse_expr_without_block */
+  ExprWithoutBlock *as_expr_without_block () const override
+  {
+    return clone_expr_without_block_impl ();
+  }
+
+  BlockType get_block_expr_type () const final override
+  {
+    return BlockType::WITHOUT_BLOCK;
+  };
+};
+
+// Pattern base HIR node
+class Pattern : public Node
+{
+public:
+  enum PatternType
+  {
+    PATH,
+    LITERAL,
+    IDENTIFIER,
+    WILDCARD,
+    RANGE,
+    REFERENCE,
+    STRUCT,
+    TUPLE_STRUCT,
+    TUPLE,
+    GROUPED,
+    SLICE,
+  };
+
+  BaseKind get_hir_kind () override final { return PATTERN; }
+
+  // Unique pointer custom clone function
+  std::unique_ptr<Pattern> clone_pattern () const
+  {
+    return std::unique_ptr<Pattern> (clone_pattern_impl ());
+  }
+
+  // possible virtual methods: is_refutable()
+
+  virtual ~Pattern () {}
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+  virtual void accept_vis (HIRPatternVisitor &vis) = 0;
+
+  virtual Analysis::NodeMapping get_pattern_mappings () const = 0;
+
+  virtual Location get_locus () const = 0;
+
+  virtual PatternType get_pattern_type () const = 0;
+
+protected:
+  // Clone pattern implementation as pure virtual method
+  virtual Pattern *clone_pattern_impl () const = 0;
+};
+
+// forward decl for Type
+class TraitBound;
+
+// Base class for types as represented in HIR - abstract
+class Type : public Node
+{
+public:
+  // Unique pointer custom clone function
+  std::unique_ptr<Type> clone_type () const
+  {
+    return std::unique_ptr<Type> (clone_type_impl ());
+  }
+
+  // virtual destructor
+  virtual ~Type () {}
+
+  BaseKind get_hir_kind () override final { return TYPE; }
+
+  virtual std::string as_string () const = 0;
+
+  /* HACK: convert to trait bound. Virtual method overriden by classes that
+   * enable this. */
+  virtual TraitBound *to_trait_bound (bool in_parens ATTRIBUTE_UNUSED) const
+  {
+    return nullptr;
+  }
+  /* as pointer, shouldn't require definition beforehand, only forward
+   * declaration. */
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+  virtual void accept_vis (HIRTypeVisitor &vis) = 0;
+
+  virtual Analysis::NodeMapping get_mappings () const { return mappings; }
+  virtual Location get_locus () const { return locus; }
+
+protected:
+  Type (Analysis::NodeMapping mappings, Location locus)
+    : mappings (mappings), locus (locus)
+  {}
+
+  // Clone function implementation as pure virtual method
+  virtual Type *clone_type_impl () const = 0;
+
+  Analysis::NodeMapping mappings;
+  Location locus;
+};
+
+// A type without parentheses? - abstract
+class TypeNoBounds : public Type
+{
+public:
+  // Unique pointer custom clone function
+  std::unique_ptr<TypeNoBounds> clone_type_no_bounds () const
+  {
+    return std::unique_ptr<TypeNoBounds> (clone_type_no_bounds_impl ());
+  }
+
+protected:
+  TypeNoBounds (Analysis::NodeMapping mappings, Location locus)
+    : Type (mappings, locus)
+  {}
+
+  // Clone function implementation as pure virtual method
+  virtual TypeNoBounds *clone_type_no_bounds_impl () const = 0;
+
+  /* Save having to specify two clone methods in derived classes by making type
+   * clone return typenobounds clone. Hopefully won't affect performance too
+   * much. */
+  TypeNoBounds *clone_type_impl () const override
+  {
+    return clone_type_no_bounds_impl ();
+  }
+};
+
+/* Abstract base class representing a type param bound - Lifetime and TraitBound
+ * extends it */
+class TypeParamBound
+{
+public:
+  enum BoundType
+  {
+    LIFETIME,
+    TRAITBOUND
+  };
+
+  virtual ~TypeParamBound () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<TypeParamBound> clone_type_param_bound () const
+  {
+    return std::unique_ptr<TypeParamBound> (clone_type_param_bound_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+
+  virtual Analysis::NodeMapping get_mappings () const = 0;
+
+  virtual Location get_locus () const = 0;
+
+  virtual BoundType get_bound_type () const = 0;
+
+protected:
+  // Clone function implementation as pure virtual method
+  virtual TypeParamBound *clone_type_param_bound_impl () const = 0;
+};
+
+// Represents a lifetime (and is also a kind of type param bound)
+class Lifetime : public TypeParamBound
+{
+private:
+  AST::Lifetime::LifetimeType lifetime_type;
+  std::string lifetime_name;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  // Constructor
+  Lifetime (Analysis::NodeMapping mapping, AST::Lifetime::LifetimeType type,
+	    std::string name, Location locus)
+    : lifetime_type (type), lifetime_name (std::move (name)), locus (locus),
+      mappings (mapping)
+  {}
+
+  // Returns true if the lifetime is in an error state.
+  bool is_error () const
+  {
+    return lifetime_type == AST::Lifetime::LifetimeType::NAMED
+	   && lifetime_name.empty ();
+  }
+
+  static Lifetime error ()
+  {
+    return Lifetime (Analysis::NodeMapping::get_error (),
+		     AST::Lifetime::LifetimeType::NAMED, "", Location ());
+  }
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  std::string get_name () const { return lifetime_name; }
+
+  AST::Lifetime::LifetimeType get_lifetime_type () const
+  {
+    return lifetime_type;
+  }
+
+  Location get_locus () const override final { return locus; }
+
+  Analysis::NodeMapping get_mappings () const override final
+  {
+    return mappings;
+  }
+
+  BoundType get_bound_type () const final override { return LIFETIME; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  Lifetime *clone_type_param_bound_impl () const override
+  {
+    return new Lifetime (*this);
+  }
+};
+
+/* Base generic parameter in HIR. Abstract - can be represented by a Lifetime or
+ * Type param */
+class GenericParam
+{
+public:
+  virtual ~GenericParam () {}
+
+  enum class GenericKind
+  {
+    TYPE,
+    LIFETIME,
+    CONST,
+  };
+
+  // Unique pointer custom clone function
+  std::unique_ptr<GenericParam> clone_generic_param () const
+  {
+    return std::unique_ptr<GenericParam> (clone_generic_param_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+
+  virtual Location get_locus () const = 0;
+
+  Analysis::NodeMapping get_mappings () const { return mappings; }
+
+  enum GenericKind get_kind () const { return kind; }
+
+protected:
+  // Clone function implementation as pure virtual method
+  virtual GenericParam *clone_generic_param_impl () const = 0;
+
+  Analysis::NodeMapping mappings;
+
+  enum GenericKind kind;
+
+  GenericParam (Analysis::NodeMapping mapping,
+		enum GenericKind kind = GenericKind::TYPE)
+    : mappings (mapping), kind (kind)
+  {}
+};
+
+// A lifetime generic parameter (as opposed to a type generic parameter)
+class LifetimeParam : public GenericParam
+{
+  Lifetime lifetime;
+
+  // bool has_lifetime_bounds;
+  // LifetimeBounds lifetime_bounds;
+  std::vector<Lifetime> lifetime_bounds; // inlined LifetimeBounds
+
+  // bool has_outer_attribute;
+  // std::unique_ptr<Attribute> outer_attr;
+  AST::Attribute outer_attr;
+
+  Location locus;
+
+public:
+  Lifetime get_lifetime () { return lifetime; }
+
+  // Returns whether the lifetime param has any lifetime bounds.
+  bool has_lifetime_bounds () const { return !lifetime_bounds.empty (); }
+
+  // Returns whether the lifetime param has an outer attribute.
+  bool has_outer_attribute () const { return !outer_attr.is_empty (); }
+
+  // Returns whether the lifetime param is in an error state.
+  bool is_error () const { return lifetime.is_error (); }
+
+  // Constructor
+  LifetimeParam (Analysis::NodeMapping mappings, Lifetime lifetime,
+		 Location locus = Location (),
+		 std::vector<Lifetime> lifetime_bounds
+		 = std::vector<Lifetime> (),
+		 AST::Attribute outer_attr = AST::Attribute::create_empty ())
+    : GenericParam (mappings, GenericKind::LIFETIME),
+      lifetime (std::move (lifetime)),
+      lifetime_bounds (std::move (lifetime_bounds)),
+      outer_attr (std::move (outer_attr)), locus (locus)
+  {}
+
+  // TODO: remove copy and assignment operator definitions - not required
+
+  // Copy constructor with clone
+  LifetimeParam (LifetimeParam const &other)
+    : GenericParam (other.mappings, GenericKind::LIFETIME),
+      lifetime (other.lifetime), lifetime_bounds (other.lifetime_bounds),
+      outer_attr (other.outer_attr), locus (other.locus)
+  {}
+
+  // Overloaded assignment operator to clone attribute
+  LifetimeParam &operator= (LifetimeParam const &other)
+  {
+    lifetime = other.lifetime;
+    lifetime_bounds = other.lifetime_bounds;
+    outer_attr = other.outer_attr;
+    locus = other.locus;
+    mappings = other.mappings;
+
+    return *this;
+  }
+
+  // move constructors
+  LifetimeParam (LifetimeParam &&other) = default;
+  LifetimeParam &operator= (LifetimeParam &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  Location get_locus () const override final { return locus; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  LifetimeParam *clone_generic_param_impl () const override
+  {
+    return new LifetimeParam (*this);
+  }
+};
+
+class ConstGenericParam : public GenericParam
+{
+public:
+  ConstGenericParam (std::string name, std::unique_ptr<Type> type,
+		     std::unique_ptr<Expr> default_expression,
+		     Analysis::NodeMapping mapping, Location locus)
+    : GenericParam (mapping, GenericKind::CONST), name (std::move (name)),
+      type (std::move (type)),
+      default_expression (std::move (default_expression)), locus (locus)
+  {}
+
+  ConstGenericParam (const ConstGenericParam &other) : GenericParam (other)
+  {
+    name = other.name;
+    locus = other.locus;
+
+    if (other.type)
+      type = other.type->clone_type ();
+    if (other.default_expression)
+      default_expression = other.default_expression->clone_expr ();
+  }
+
+  std::string as_string () const override final;
+
+  void accept_vis (HIRFullVisitor &vis) override final;
+
+  Location get_locus () const override final { return locus; };
+
+  bool has_default_expression () { return default_expression != nullptr; }
+
+  std::unique_ptr<Type> &get_type () { return type; }
+  std::unique_ptr<Expr> &get_default_expression ()
+  {
+    rust_assert (has_default_expression ());
+
+    return default_expression;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ConstGenericParam *clone_generic_param_impl () const override
+  {
+    return new ConstGenericParam (*this);
+  }
+
+private:
+  std::string name;
+  std::unique_ptr<Type> type;
+
+  /* Optional - can be a null pointer if there is no default expression */
+  std::unique_ptr<Expr> default_expression;
+
+  Location locus;
+};
+
+// Item used in trait declarations - abstract base class
+class TraitItem : public Node
+{
+public:
+  enum TraitItemKind
+  {
+    FUNC,
+    CONST,
+    TYPE
+  };
+
+  BaseKind get_hir_kind () override final { return TRAIT_ITEM; }
+
+protected:
+  // Constructor
+  TraitItem (Analysis::NodeMapping mappings) : mappings (mappings) {}
+
+  // Clone function implementation as pure virtual method
+  virtual TraitItem *clone_trait_item_impl () const = 0;
+
+  Analysis::NodeMapping mappings;
+
+public:
+  virtual ~TraitItem () {}
+
+  std::unique_ptr<TraitItem> clone_trait_item () const
+  {
+    return std::unique_ptr<TraitItem> (clone_trait_item_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRTraitItemVisitor &vis) = 0;
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+
+  virtual const std::string trait_identifier () const = 0;
+
+  const Analysis::NodeMapping get_mappings () const { return mappings; }
+
+  virtual TraitItemKind get_item_kind () const = 0;
+
+  virtual AST::AttrVec &get_outer_attrs () = 0;
+  virtual const AST::AttrVec &get_outer_attrs () const = 0;
+};
+
+class ImplItem : public Node
+{
+public:
+  enum ImplItemType
+  {
+    FUNCTION,
+    TYPE_ALIAS,
+    CONSTANT
+  };
+
+  virtual ~ImplItem () {}
+
+  BaseKind get_hir_kind () override final { return IMPL; }
+
+  // Unique pointer custom clone function
+  std::unique_ptr<ImplItem> clone_inherent_impl_item () const
+  {
+    return std::unique_ptr<ImplItem> (clone_inherent_impl_item_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRImplVisitor &vis) = 0;
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+  virtual void accept_vis (HIRStmtVisitor &vis) = 0;
+
+  virtual Analysis::NodeMapping get_impl_mappings () const = 0;
+
+  virtual Location get_locus () const = 0;
+
+  virtual ImplItemType get_impl_item_type () const = 0;
+
+protected:
+  // Clone function implementation as pure virtual method
+  virtual ImplItem *clone_inherent_impl_item_impl () const = 0;
+};
+
+// A crate HIR object - holds all the data for a single compilation unit
+struct Crate
+{
+  AST::AttrVec inner_attrs;
+  // dodgy spacing required here
+  /* TODO: is it better to have a vector of items here or a module (implicit
+   * top-level one)? */
+  std::vector<std::unique_ptr<Item> > items;
+
+  Analysis::NodeMapping mappings;
+
+public:
+  // Constructor
+  Crate (std::vector<std::unique_ptr<Item> > items, AST::AttrVec inner_attrs,
+	 Analysis::NodeMapping mappings)
+    : inner_attrs (std::move (inner_attrs)), items (std::move (items)),
+      mappings (mappings)
+  {}
+
+  // Copy constructor with vector clone
+  Crate (Crate const &other)
+    : inner_attrs (other.inner_attrs), mappings (other.mappings)
+  {
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_item ());
+  }
+
+  ~Crate () = default;
+
+  // Overloaded assignment operator with vector clone
+  Crate &operator= (Crate const &other)
+  {
+    inner_attrs = other.inner_attrs;
+    mappings = other.mappings;
+
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_item ());
+
+    return *this;
+  }
+
+  // Move constructors
+  Crate (Crate &&other) = default;
+  Crate &operator= (Crate &&other) = default;
+
+  // Get crate representation as string (e.g. for debugging).
+  std::string as_string () const;
+
+  const Analysis::NodeMapping &get_mappings () const { return mappings; }
+};
+
+// Base path expression HIR node - abstract
+class PathExpr : public ExprWithoutBlock
+{
+protected:
+  PathExpr (Analysis::NodeMapping mappings, AST::AttrVec outer_attribs)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs))
+  {}
+
+public:
+  /* Replaces the outer attributes of this path expression with the given outer
+   * attributes. */
+  void replace_outer_attrs (AST::AttrVec outer_attrs)
+  {
+    set_outer_attrs (std::move (outer_attrs));
+  }
+
+  ExprType get_expression_type () const final override
+  {
+    return ExprType::Path;
+  }
+};
+} // namespace HIR
+} // namespace Rust
+
+#endif
