--- /dev/null
+/* General AST-related method implementations for Rust frontend.
+ Copyright (C) 2009-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-system.h"
+#include "rust-ast-full.h"
+#include "rust-diagnostics.h"
+#include "rust-ast-visitor.h"
+#include "rust-macro.h"
+#include "rust-session-manager.h"
+#include "rust-lex.h"
+#include "rust-parse.h"
+#include "operator.h"
+
+/* Compilation unit used for various AST-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 AST {
+
+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, DelimType delim_type)
+{
+ switch (delim_type)
+ {
+ case PARENS:
+ return "(" + str_input + ")";
+ case SQUARE:
+ return "[" + str_input + "]";
+ case CURLY:
+ return "{" + str_input + "}";
+ default:
+ return "ERROR-MARK-STRING (delims)";
+ }
+ gcc_unreachable ();
+}
+
+enum AttrMode
+{
+ OUTER,
+ INNER
+};
+
+std::string
+get_mode_dump_desc (AttrMode mode)
+{
+ switch (mode)
+ {
+ case OUTER:
+ return "outer attributes";
+ case INNER:
+ return "inner attributes";
+ default:
+ gcc_unreachable ();
+ return "";
+ }
+}
+
+// Adds lines below adding attributes
+std::string
+append_attributes (std::vector<Attribute> attrs, AttrMode mode)
+{
+ indent_spaces (enter);
+
+ std::string str
+ = "\n" + indent_spaces (stay) + get_mode_dump_desc (mode) + ": ";
+ // str += "\n" + indent_spaces (stay) + "inner attributes: ";
+ if (attrs.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ /* note that this does not print them with outer or "inner attribute"
+ * syntax - just prints the body */
+ for (const auto &attr : attrs)
+ str += "\n" + indent_spaces (stay) + attr.as_string ();
+ }
+
+ indent_spaces (out);
+
+ return str;
+}
+
+// Removes the beginning and end quotes of a quoted string.
+std::string
+unquote_string (std::string input)
+{
+ rust_assert (input.front () == '"');
+ rust_assert (input.back () == '"');
+ return input.substr (1, input.length () - 2);
+}
+
+std::string
+Crate::as_string () const
+{
+ rust_debug ("beginning crate recursive as-string");
+
+ std::string str ("Crate: ");
+
+ // inner attributes
+ str += append_attributes (inner_attrs, INNER);
+
+ // 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 "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + item->as_string ();
+ }
+ }
+
+ return str + "\n";
+}
+
+std::string
+Attribute::as_string () const
+{
+ std::string path_str = path.as_string ();
+ if (attr_input == nullptr)
+ return path_str;
+ else
+ return path_str + attr_input->as_string ();
+}
+
+// Copy constructor must deep copy attr_input as unique pointer
+Attribute::Attribute (Attribute const &other)
+ : path (other.path), locus (other.locus)
+{
+ // guard to protect from null pointer dereference
+ if (other.attr_input != nullptr)
+ attr_input = other.attr_input->clone_attr_input ();
+}
+
+// overload assignment operator to use custom clone method
+Attribute &
+Attribute::operator= (Attribute const &other)
+{
+ path = other.path;
+ locus = other.locus;
+ // guard to protect from null pointer dereference
+ if (other.attr_input != nullptr)
+ attr_input = other.attr_input->clone_attr_input ();
+ else
+ attr_input = nullptr;
+
+ return *this;
+}
+
+std::string
+DelimTokenTree::as_string () const
+{
+ std::string start_delim;
+ std::string end_delim;
+ switch (delim_type)
+ {
+ case PARENS:
+ start_delim = "(";
+ end_delim = ")";
+ break;
+ case SQUARE:
+ start_delim = "[";
+ end_delim = "]";
+ break;
+ case CURLY:
+ start_delim = "{";
+ end_delim = "}";
+ break;
+ default:
+ rust_debug ("Invalid delimiter type, "
+ "Should be PARENS, SQUARE, or CURLY.");
+ return "Invalid delimiter type";
+ }
+ std::string str = start_delim;
+ if (!token_trees.empty ())
+ {
+ for (const auto &tree : token_trees)
+ {
+ // DEBUG: null pointer check
+ if (tree == nullptr)
+ {
+ rust_debug (
+ "something really terrible has gone wrong - null pointer "
+ "token tree in delim token tree.");
+ return "NULL_POINTER_MARK";
+ }
+
+ str += tree->as_string ();
+ }
+ }
+ str += end_delim;
+
+ return str;
+}
+
+std::string
+Token::as_string () const
+{
+ if (tok_ref->has_str ())
+ {
+ std::string str = tok_ref->get_str ();
+
+ std::string quote = is_string_lit () ? "\"" : "";
+ return quote + str + quote;
+ }
+ else
+ {
+ return tok_ref->get_token_description ();
+ }
+}
+
+std::string
+SimplePathSegment::as_string () const
+{
+ return segment_name;
+}
+
+std::string
+SimplePath::as_string () const
+{
+ std::string path;
+ if (has_opening_scope_resolution)
+ path = "::";
+
+ // crappy hack because doing proper for loop would be more code
+ bool first_time = true;
+ for (const auto &segment : segments)
+ {
+ if (first_time)
+ {
+ path += segment.as_string ();
+ first_time = false;
+ }
+ else
+ {
+ path += "::" + segment.as_string ();
+ }
+
+ // DEBUG: remove later. Checks for path error.
+ if (segment.is_error ())
+ {
+ rust_debug ("segment in path is error - this should've been filtered "
+ "out. first segment "
+ "was '%s'",
+ segments.at (0).as_string ().c_str ());
+ }
+ }
+
+ return path;
+}
+
+std::string
+Visibility::as_string () const
+{
+ switch (vis_type)
+ {
+ case PRIV:
+ return std::string ("");
+ case PUB:
+ return std::string ("pub");
+ case PUB_CRATE:
+ return std::string ("pub(crate)");
+ case PUB_SELF:
+ return std::string ("pub(self)");
+ case PUB_SUPER:
+ return std::string ("pub(super)");
+ case PUB_IN_PATH:
+ return std::string ("pub(in ") + in_path.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;
+
+ if (!outer_attrs.empty ())
+ {
+ for (const auto &attr : outer_attrs)
+ str += attr.as_string () + "\n";
+ }
+
+ if (has_visibility ())
+ str += visibility.as_string () + " ";
+
+ return str;
+}
+
+std::string
+Module::as_string () const
+{
+ std::string str = VisItem::as_string () + "mod " + module_name;
+
+ // Return early if we're dealing with an unloaded module as their body resides
+ // in a different file
+ if (kind == ModuleKind::UNLOADED)
+ return str + "\n no body (reference to external file)\n";
+
+ // inner attributes
+ str += append_attributes (inner_attrs, INNER);
+
+ // items
+ str += "\n items: ";
+
+ // This can still happen if the module is loaded but empty, i.e. `mod foo {}`
+ 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 "NULL_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 (has_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 "NULL_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 "NULL_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 ¶m : generic_params)
+ {
+ // DEBUG: null pointer check
+ if (param == nullptr)
+ {
+ rust_debug (
+ "something really terrible has gone wrong - null pointer "
+ "generic param in enum.");
+ return "NULL_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 "NULL_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 "NULL_POINTER_MARK";
+ }
+ str += "\n Expression: " + const_expr->as_string ();
+
+ return str + "\n";
+}
+
+std::string
+InherentImpl::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 ¶m : generic_params)
+ {
+ // DEBUG: null pointer check
+ if (param == nullptr)
+ {
+ rust_debug (
+ "something really terrible has gone wrong - null pointer "
+ "generic param in inherent impl.");
+ return "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + param->as_string ();
+ }
+ }
+
+ str += "\n Type: " + trait_type->as_string ();
+
+ str += "\n Where clause: ";
+ if (has_where_clause ())
+ str += where_clause.as_string ();
+ else
+ str += "none";
+
+ // inner attributes
+ str += append_attributes (inner_attrs, INNER);
+
+ // inherent impl items
+ str += "\n Inherent impl items: ";
+ if (!has_impl_items ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &item : impl_items)
+ str += "\n " + item->as_string ();
+ }
+
+ return str;
+}
+
+std::string
+Method::as_string () const
+{
+ std::string str ("Method: \n ");
+
+ str += vis.as_string () + " " + qualifiers.as_string ();
+
+ str += " fn " + method_name;
+
+ // generic params
+ str += "\n Generic params: ";
+ if (generic_params.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto ¶m : generic_params)
+ {
+ // DEBUG: null pointer check
+ if (param == nullptr)
+ {
+ rust_debug (
+ "something really terrible has gone wrong - null pointer "
+ "generic param in method.");
+ return "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + param->as_string ();
+ }
+ }
+
+ str += "\n Self param: " + self_param.as_string ();
+
+ str += "\n Function params: ";
+ if (function_params.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto ¶m : function_params)
+ str += "\n " + param.as_string ();
+ }
+
+ 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";
+
+ str += "\n Block expr (body): \n ";
+ str += function_body->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 ¶m : generic_params)
+ {
+ // DEBUG: null pointer check
+ if (param == nullptr)
+ {
+ rust_debug (
+ "something really terrible has gone wrong - null pointer "
+ "generic param in enum.");
+ return "NULL_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 ();
+ }
+
+ return str;
+}
+
+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 "NULL_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 "NULL_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 ¶m : generic_params)
+ {
+ // DEBUG: null pointer check
+ if (param == nullptr)
+ {
+ rust_debug (
+ "something really terrible has gone wrong - null pointer "
+ "generic param in enum.");
+ return "NULL_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 "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + item->as_string ();
+ }
+ }
+
+ return str;
+}
+
+std::string
+Trait::as_string () const
+{
+ std::string str = VisItem::as_string ();
+
+ if (has_unsafe)
+ str += "unsafe ";
+
+ str += "trait " + name;
+
+ // generic params
+ str += "\n Generic params: ";
+ if (generic_params.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto ¶m : generic_params)
+ {
+ // DEBUG: null pointer check
+ if (param == nullptr)
+ {
+ rust_debug (
+ "something really terrible has gone wrong - null pointer "
+ "generic param in trait.");
+ return "NULL_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 "NULL_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 "NULL_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 ¶m : generic_params)
+ {
+ // DEBUG: null pointer check
+ if (param == nullptr)
+ {
+ rust_debug (
+ "something really terrible has gone wrong - null pointer "
+ "generic param in union.");
+ return "NULL_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_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 "NULL_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 "NULL_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 "NULL_POINTER_MARK";
+ }
+ str += function_body->as_string () + "\n";
+
+ return str;
+}
+
+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 += append_attributes (outer_attrs, OUTER);
+
+ // inner attributes
+ str += append_attributes (inner_attrs, INNER);
+
+ // 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 "NULL_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
+TraitImpl::as_string () const
+{
+ std::string str = VisItem::as_string ();
+
+ if (has_unsafe)
+ str += "unsafe ";
+
+ str += "impl ";
+
+ // generic params
+ str += "\n Generic params: ";
+ if (!has_generics ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto ¶m : generic_params)
+ str += "\n " + param->as_string ();
+ }
+
+ str += "\n Has exclam: ";
+ if (has_exclam)
+ str += "true";
+ else
+ str += "false";
+
+ str += "\n TypePath (to trait): " + trait_path.as_string ();
+
+ str += "\n Type (struct to impl on): " + trait_type->as_string ();
+
+ str += "\n Where clause: ";
+ if (!has_where_clause ())
+ str += "none";
+ else
+ str += where_clause.as_string ();
+
+ // inner attributes
+ str += append_attributes (inner_attrs, INNER);
+
+ str += "\n trait impl items: ";
+ if (!has_impl_items ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &item : impl_items)
+ str += "\n " + item->as_string ();
+ }
+
+ 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 ";
+ if (has_abi ())
+ str += "\"" + abi + "\" ";
+
+ str += append_attributes (inner_attrs, INNER);
+
+ 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
+MacroRule::as_string () const
+{
+ std::string str ("Macro rule: ");
+
+ str += "\n Matcher: \n ";
+ str += matcher.as_string ();
+
+ str += "\n Transcriber: \n ";
+ str += transcriber.as_string ();
+
+ return str;
+}
+
+std::string
+MacroRulesDefinition::as_string () const
+{
+ std::string str;
+
+ // get outer attrs
+ str += append_attributes (outer_attrs, OUTER);
+
+ str += "macro_rules!";
+
+ str += rule_name;
+
+ str += "\n Macro rules: ";
+ if (rules.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &rule : rules)
+ str += "\n " + rule.as_string ();
+ }
+
+ str += "\n Delim type: ";
+ switch (delim_type)
+ {
+ case PARENS:
+ str += "parentheses";
+ break;
+ case SQUARE:
+ str += "square";
+ break;
+ case CURLY:
+ str += "curly";
+ break;
+ default:
+ return "ERROR_MARK_STRING - delim type in macro invocation";
+ }
+
+ return str;
+}
+
+std::string
+MacroInvocation::as_string () const
+{
+ std::string str = "MacroInvocation: ";
+
+ str += append_attributes (outer_attrs, OUTER);
+
+ str += "\n " + invoc_data.as_string ();
+
+ str += "\n has semicolon: ";
+ str += has_semicolon () ? "true" : "false";
+
+ return str;
+}
+
+std::string
+MacroInvocData::as_string () const
+{
+ return path.as_string () + "!" + token_tree.as_string ();
+}
+
+std::string
+PathInExpression::as_string () const
+{
+ std::string str;
+
+ if (has_opening_scope_resolution)
+ str = "::";
+
+ return str + PathPattern::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:";
+
+ str += append_attributes (outer_attrs, OUTER);
+
+ str += "\n Has move: ";
+ if (has_move)
+ str += "true";
+ else
+ str += "false";
+
+ str += "\n Params: ";
+ if (params.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto ¶m : 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_to_invoke_on->as_string ();
+
+ if (has_as_clause ())
+ str += " as " + trait_path.as_string ();
+
+ return str + ">";
+}
+
+std::string
+QualifiedPathInExpression::as_string () const
+{
+ return path_type.as_string () + "::" + PathPattern::as_string ();
+}
+
+std::string
+BorrowExpr::as_string () const
+{
+ /* TODO: find way to incorporate outer attrs - may have to represent in
+ * different style (i.e. something more like BorrowExpr: \n outer attrs) */
+
+ 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
+{
+ /* TODO: find way to incorporate outer attrs - may have to represent in
+ * different style (i.e. something more like BorrowExpr: \n outer attrs) */
+
+ std::string str ("return ");
+
+ if (has_returned_expr ())
+ str += return_expr->as_string ();
+
+ return str;
+}
+
+std::string
+GroupedExpr::as_string () const
+{
+ std::string str ("Grouped expr:");
+
+ // outer attrs
+ str += append_attributes (outer_attrs, OUTER);
+
+ // inner attributes
+ str += append_attributes (inner_attrs, INNER);
+
+ 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
+{
+ // TODO: rewrite format to allow outer attributes
+ std::string str ("continue ");
+
+ if (has_label ())
+ str += label.as_string ();
+
+ return str;
+}
+
+std::string
+NegationExpr::as_string () const
+{
+ // TODO: rewrite formula to allow outer attributes
+ 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
+{
+ // TODO: rewrite formula to allow outer attributes
+ return array_expr->as_string () + "[" + index_expr->as_string () + "]";
+}
+
+std::string
+AssignmentExpr::as_string () const
+{
+ std::string str ("AssignmentExpr: ");
+
+ if (main_or_left_expr == nullptr || right_expr == nullptr)
+ {
+ str += "error (either or both expressions are null)";
+ }
+ else
+ {
+ // left expr
+ str += "\n left: " + main_or_left_expr->as_string ();
+
+ // right expr
+ str += "\n right: " + right_expr->as_string ();
+ }
+
+ return str;
+}
+
+std::string
+AsyncBlockExpr::as_string () const
+{
+ std::string str = "AsyncBlockExpr: ";
+
+ // get outer attributes
+ // str += "\n " + Expr::as_string ();
+ str += append_attributes (outer_attrs, OUTER);
+
+ str += "\n Has move: ";
+ str += has_move ? "true" : "false";
+
+ return str + "\n" + block_expr->as_string ();
+}
+
+std::string
+ComparisonExpr::as_string () const
+{
+ // TODO: rewrite to better reflect non-literal expressions
+ 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: ";
+
+ str += append_attributes (outer_attrs, OUTER);
+
+ str += "\n Object (receiver) expr: \n";
+ 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 ¶m : 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
+{
+ // TODO: rewrite dump to better reflect non-literal exprs
+ return tuple_expr->as_string () + "." + std::to_string (tuple_index);
+}
+
+std::string
+DereferenceExpr::as_string () const
+{
+ // TODO: rewrite dump to better reflect non-literal exprs
+ return "*" + main_or_left_expr->as_string ();
+}
+
+std::string
+FieldAccessExpr::as_string () const
+{
+ // TODO: rewrite dump to better reflect non-literal exprs
+ return receiver->as_string () + "." + field;
+}
+
+std::string
+LazyBooleanExpr::as_string () const
+{
+ // TODO: rewrite dump to better reflect non-literal exprs
+ 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
+{
+ // TODO: rewrite dump to better reflect non-literal exprs
+ return from->as_string () + ".." + to->as_string ();
+}
+
+std::string
+RangeToInclExpr::as_string () const
+{
+ // TODO: rewrite dump to better reflect non-literal exprs
+ return "..=" + to->as_string ();
+}
+
+std::string
+UnsafeBlockExpr::as_string () const
+{
+ std::string str = "UnsafeBlockExpr:" + indent_spaces (enter);
+
+ // get outer attributes
+ str += append_attributes (outer_attrs, OUTER);
+
+ str += indent_spaces (stay) + expr->as_string () + "\n" + indent_spaces (out);
+
+ return str;
+}
+
+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 += append_attributes (outer_attrs, OUTER);
+
+ 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 += append_attributes (outer_attrs, OUTER);
+
+ 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
+{
+ // TODO: rewrite to allow dumps with non-literal exprs
+ return from->as_string () + "..=" + to->as_string ();
+}
+
+std::string
+ErrorPropagationExpr::as_string () const
+{
+ // TODO: rewrite to allow dumps with non-literal exprs
+ return main_or_left_expr->as_string () + "?";
+}
+
+std::string
+CompoundAssignmentExpr::as_string () const
+{
+ std::string operator_str;
+ operator_str.reserve (1);
+
+ // get operator string
+ switch (expr_type)
+ {
+ case CompoundAssignmentOperator::ADD:
+ operator_str = "+";
+ break;
+ case CompoundAssignmentOperator::SUBTRACT:
+ operator_str = "-";
+ break;
+ case CompoundAssignmentOperator::MULTIPLY:
+ operator_str = "*";
+ break;
+ case CompoundAssignmentOperator::DIVIDE:
+ operator_str = "/";
+ break;
+ case CompoundAssignmentOperator::MODULUS:
+ operator_str = "%";
+ break;
+ case CompoundAssignmentOperator::BITWISE_AND:
+ operator_str = "&";
+ break;
+ case CompoundAssignmentOperator::BITWISE_OR:
+ operator_str = "|";
+ break;
+ case CompoundAssignmentOperator::BITWISE_XOR:
+ operator_str = "^";
+ break;
+ case CompoundAssignmentOperator::LEFT_SHIFT:
+ operator_str = "<<";
+ break;
+ case CompoundAssignmentOperator::RIGHT_SHIFT:
+ operator_str = ">>";
+ break;
+ default:
+ operator_str = "invalid operator. wtf";
+ 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
+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:
+ operator_str = "invalid operator. wtf";
+ break;
+ }
+
+ std::string str ("ArithmeticOrLogicalExpr: ");
+ if (main_or_left_expr == nullptr || right_expr == nullptr)
+ {
+ str += "error. this is probably a parsing failure.";
+ }
+ else
+ {
+ str += main_or_left_expr->as_string () + " ";
+ str += operator_str + " ";
+ str += right_expr->as_string ();
+ }
+
+ return str;
+}
+
+std::string
+CallExpr::as_string () const
+{
+ std::string str = "CallExpr: ";
+
+ str += append_attributes (outer_attrs, OUTER);
+
+ str += "\n Function expr: ";
+ str += function->as_string ();
+
+ str += "\n Call params:";
+ if (!has_params ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto ¶m : params)
+ {
+ if (param == nullptr)
+ return "ERROR_MARK_STRING - call expr param is null";
+
+ str += "\n " + param->as_string ();
+ }
+ }
+
+ return str;
+}
+
+std::string
+WhileLoopExpr::as_string () const
+{
+ std::string str = "WhileLoopExpr: ";
+
+ str += append_attributes (outer_attrs, OUTER);
+
+ 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 += append_attributes (outer_attrs, OUTER);
+
+ 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: " + scrutinee->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 += append_attributes (outer_attrs, OUTER);
+
+ 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:";
+
+ str += append_attributes (outer_attrs, OUTER);
+
+ // inner attributes
+ str += append_attributes (inner_attrs, INNER);
+
+ str += "\n Array elems: ";
+ str += internal_elements->as_string ();
+
+ return str;
+}
+
+std::string
+AwaitExpr::as_string () const
+{
+ // TODO: rewrite dump to allow non-literal exprs
+ return awaited_expr->as_string () + ".await";
+}
+
+std::string
+BreakExpr::as_string () const
+{
+ // TODO: rewrite dump to allow outer attrs, non-literal exprs
+ 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 = append_attributes (outer_attrs, OUTER);
+
+ 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
+MatchExpr::as_string () const
+{
+ std::string str ("MatchExpr:");
+
+ str += append_attributes (outer_attrs, OUTER);
+
+ str += "\n Scrutinee expr: " + branch_value->as_string ();
+
+ // inner attributes
+ str += append_attributes (inner_attrs, INNER);
+
+ // 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:");
+
+ str += append_attributes (outer_attrs, OUTER);
+
+ // inner attributes
+ str += append_attributes (inner_attrs, INNER);
+
+ 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
+{
+ // TODO: rewrite dump to allow non-literal types
+ 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 (has_unsafe)
+ str += "unsafe ";
+
+ if (has_extern)
+ {
+ str += "extern";
+ if (extern_abi != "")
+ str += " \"" + extern_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
+MacroMatcher::as_string () const
+{
+ std::string str ("Macro matcher: ");
+
+ str += "\n Delim type: ";
+
+ switch (delim_type)
+ {
+ case PARENS:
+ str += "parentheses";
+ break;
+ case SQUARE:
+ str += "square";
+ break;
+ case CURLY:
+ str += "curly";
+ break;
+ default:
+ return "ERROR_MARK_STRING - macro matcher delim";
+ }
+
+ str += "\n Matches: ";
+
+ if (matches.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &match : matches)
+ str += "\n " + match->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
+ConstGenericParam::as_string () const
+{
+ std::string str ("ConstGenericParam: ");
+ str += "const " + name + ": " + type->as_string ();
+
+ if (has_default_value ())
+ str += " = " + get_default_value ().as_string ();
+
+ return str;
+}
+
+std::string
+MacroMatchFragment::as_string () const
+{
+ return "$" + ident + ": " + frag_spec.as_string ();
+}
+
+std::string
+QualifiedPathInType::as_string () const
+{
+ /* TODO: this may need adjusting if segments (e.g. with functions) can't be
+ * literalised */
+ std::string str = path_type.as_string ();
+
+ for (const auto &segment : segments)
+ str += "::" + segment->as_string ();
+
+ return str;
+}
+
+std::string
+MacroMatchRepetition::as_string () const
+{
+ std::string str ("Macro match repetition: ");
+
+ str += "\n Matches: ";
+ if (matches.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto &match : matches)
+ str += "\n " + match->as_string ();
+ }
+
+ str += "\n Sep: ";
+ if (!has_sep ())
+ str += "none";
+ else
+ str += sep->as_string ();
+
+ str += "\n Op: ";
+ switch (op)
+ {
+ case ANY:
+ str += "*";
+ break;
+ case ONE_OR_MORE:
+ str += "+";
+ break;
+ case ZERO_OR_ONE:
+ str += "?";
+ break;
+ case NONE:
+ str += "no op? shouldn't be allowed";
+ break;
+ default:
+ return "ERROR_MARK_STRING - unknown op in macro match repetition";
+ }
+
+ return str;
+}
+
+std::string
+Lifetime::as_string () const
+{
+ if (is_error ())
+ return "error lifetime";
+
+ switch (lifetime_type)
+ {
+ case NAMED:
+ return "'" + lifetime_name;
+ case STATIC:
+ return "'static";
+ case WILDCARD:
+ return "'_";
+ default:
+ return "ERROR-MARK-STRING: lifetime type failure";
+ }
+}
+
+std::string
+TypePath::as_string () const
+{
+ /* TODO: this may need to be rewritten if a segment (e.g. function) can't be
+ * literalised */
+ 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;
+}
+
+SimplePath
+PathPattern::convert_to_simple_path (bool with_opening_scope_resolution) const
+{
+ if (!has_segments ())
+ return SimplePath::create_empty ();
+
+ // create vector of reserved size (to minimise reallocations)
+ std::vector<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.is_error () || segment.has_generic_args ()
+ || segment.as_string () == "Self")
+ return SimplePath::create_empty ();
+
+ // create segment and add to vector
+ std::string segment_str = segment.as_string ();
+ simple_segments.push_back (
+ 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 ();
+ // FIXME: this hack probably doesn't actually work
+
+ return SimplePath (std::move (simple_segments), with_opening_scope_resolution,
+ locus);
+}
+
+SimplePath
+TypePath::as_simple_path () const
+{
+ if (segments.empty ())
+ return SimplePath::create_empty ();
+
+ // create vector of reserved size (to minimise reallocations)
+ std::vector<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 SimplePath::create_empty ();
+
+ // create segment and add to vector
+ std::string segment_str = segment->as_string ();
+ simple_segments.push_back (
+ SimplePathSegment (std::move (segment_str), segment->get_locus ()));
+ }
+
+ return SimplePath (std::move (simple_segments), has_opening_scope_resolution,
+ locus);
+}
+
+std::string
+PathExprSegment::as_string () const
+{
+ // TODO: rewrite dump to work with non-literalisable types
+ 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 (!generic_args.empty ())
+ {
+ auto i = generic_args.begin ();
+ auto e = generic_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
+{
+ // TODO: rewrite to work with non-literalisable types
+ return identifier + " = " + type->as_string ();
+}
+
+std::string
+ForLoopExpr::as_string () const
+{
+ std::string str = "ForLoopExpr: ";
+
+ str += append_attributes (outer_attrs, OUTER);
+
+ 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
+{
+ // TODO: maybe rewrite to work with non-linearisable bounds
+ 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 = append_attributes (outer_attrs, OUTER);
+
+ return str;
+}
+
+std::string
+StructPatternFieldIdent::as_string () const
+{
+ std::string str = StructPatternField::as_string ();
+
+ str += "\n";
+
+ if (has_ref)
+ str += "ref ";
+
+ if (has_mut)
+ str += "mut ";
+
+ str += ident;
+
+ return str;
+}
+
+std::string
+StructPatternFieldTuplePat::as_string () const
+{
+ // TODO: maybe rewrite to work with non-linearisable patterns
+ 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
+{
+ // TODO: maybe rewrite to work with non-linearisable patterns
+ 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 ();
+ }
+
+ str += "\n Etc: ";
+ if (has_struct_pattern_etc)
+ str += "true";
+ else
+ str += "false";
+
+ 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
+{
+ // TODO: maybe rewrite to work with non-linearisable patterns
+ 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
+{
+ // TODO: maybe rewrite to work with non-linearisable patterns
+ 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
+{
+ // TODO: rewrite to work with non-linearisable types and exprs
+ std::string str = append_attributes (outer_attrs, OUTER);
+
+ 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;
+}
+
+// hopefully definition here will prevent circular dependency issue
+TraitBound *
+TypePath::to_trait_bound (bool in_parens) const
+{
+ return new TraitBound (TypePath (*this), get_locus (), in_parens);
+}
+
+std::string
+InferredType::as_string () const
+{
+ return "_ (inferred)";
+}
+
+std::string
+TypeCastExpr::as_string () const
+{
+ // TODO: rewrite to work with non-linearisable exprs and types
+ 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
+{
+ // TODO: rewrite to work with non-linearisable types
+ std::string str ("&");
+
+ if (has_lifetime ())
+ str += lifetime.as_string () + " ";
+
+ if (has_mut)
+ str += "mut ";
+
+ str += type->as_string ();
+
+ return str;
+}
+
+std::string
+RawPointerType::as_string () const
+{
+ // TODO: rewrite to work with non-linearisable types
+ std::string str ("*");
+
+ switch (pointer_type)
+ {
+ case MUT:
+ str += "mut ";
+ break;
+ case CONST:
+ str += "const ";
+ break;
+ default:
+ return "ERROR_MARK_STRING - unknown pointer type in raw pointer type";
+ }
+
+ str += type->as_string ();
+
+ return str;
+}
+
+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 ¶m : 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
+{
+ // TODO: rewrite to work with non-linearisable types
+ return TypePathSegment::as_string () + "<" + generic_args.as_string () + ">";
+}
+
+std::string
+TraitObjectTypeOneBound::as_string () const
+{
+ std::string str ("TraitObjectTypeOneBound: \n Has dyn dispatch: ");
+
+ if (has_dyn)
+ str += "true";
+ else
+ str += "false";
+
+ str += "\n TraitBound: " + trait_bound.as_string ();
+
+ return str;
+}
+
+std::string
+TypePathFunction::as_string () const
+{
+ // TODO: rewrite to work with non-linearisable types
+ 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
+{
+ // TODO: rewrite to work with non-linearisable types
+ return TypePathSegment::as_string () + function_path.as_string ();
+}
+
+std::string
+ArrayType::as_string () const
+{
+ // TODO: rewrite to work with non-linearisable types and exprs
+ return "[" + elem_type->as_string () + "; " + size->as_string () + "]";
+}
+
+std::string
+SliceType::as_string () const
+{
+ // TODO: rewrite to work with non-linearisable types
+ return "[" + elem_type->as_string () + "]";
+}
+
+std::string
+TupleType::as_string () const
+{
+ // TODO: rewrite to work with non-linearisable types
+ 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 = append_attributes (outer_attrs, OUTER);
+ 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
+{
+ // TODO: doesn't this require data from StructExpr?
+ std::string str ("StructExprStruct (or subclass): ");
+
+ str += "\n Path: " + get_struct_name ().as_string ();
+
+ // inner attributes
+ str += append_attributes (inner_attrs, INNER);
+
+ 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
+{
+ // TODO: rewrite to work with non-linearisable exprs
+ return field_name + " : " + StructExprFieldWithVal::as_string ();
+}
+
+std::string
+StructExprFieldIndexValue::as_string () const
+{
+ // TODO: rewrite to work with non-linearisable exprs
+ 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 = VisItem::as_string ();
+ str += variant_name;
+
+ 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
+{
+ // TODO: rewrite to work with non-linearisable exprs
+
+ // outer attributes
+ std::string str = append_attributes (outer_attrs, OUTER);
+
+ 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
+{
+ // TODO: rewrite to work with non-linearisable exprs
+ // outer attributes
+ std::string str = append_attributes (outer_attrs, OUTER);
+
+ if (has_visibility ())
+ str += "\n" + visibility.as_string ();
+
+ str += " " + field_name + " : " + field_type->as_string ();
+
+ return str;
+}
+
+std::string
+EnumItemDiscriminant::as_string () const
+{
+ // TODO: rewrite to work with non-linearisable exprs
+ std::string str = EnumItem::as_string ();
+
+ // add equal and expression
+ str += " = " + expression->as_string ();
+
+ return str;
+}
+
+std::string
+ExternalStaticItem::as_string () const
+{
+ // outer attributes
+ std::string str = append_attributes (outer_attrs, OUTER);
+
+ // start visibility on new line and with a space
+ str += "\n" + visibility.as_string () + " ";
+
+ str += "static ";
+
+ if (has_mut)
+ str += "mut ";
+
+ // add name
+ str += item_name;
+
+ // add type on new line
+ str += "\n Type: " + item_type->as_string ();
+
+ return str;
+}
+
+std::string
+ExternalFunctionItem::as_string () const
+{
+ // outer attributes
+ std::string str = append_attributes (outer_attrs, OUTER);
+
+ // start visibility on new line and with a space
+ str += "\n" + visibility.as_string () + " ";
+
+ str += "fn ";
+
+ // add name
+ str += item_name;
+
+ // generic params
+ str += "\n Generic params: ";
+ if (generic_params.empty ())
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto ¶m : 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 "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + param->as_string ();
+ }
+ }
+
+ // function params
+ str += "\n Function params: ";
+ if (function_params.empty () && !has_variadics)
+ {
+ str += "none";
+ }
+ else
+ {
+ for (const auto ¶m : function_params)
+ str += "\n " + param.as_string ();
+
+ if (has_variadics)
+ {
+ str += "\n variadic outer attrs: ";
+ if (has_variadic_outer_attrs ())
+ {
+ for (const auto &attr : variadic_outer_attrs)
+ str += "\n " + attr.as_string ();
+ }
+ else
+ {
+ str += "none";
+ }
+ 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 = append_attributes (outer_attrs, OUTER);
+
+ str += "\n" + name;
+
+ str += "\n Type: " + param_type->as_string ();
+
+ return str;
+}
+
+std::string
+TraitItemFunc::as_string () const
+{
+ std::string str = append_attributes (outer_attrs, OUTER);
+
+ 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 ¶m : 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 "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + param->as_string ();
+ }
+ }
+
+ str += "\n Function params: ";
+ if (has_params ())
+ {
+ for (const auto ¶m : function_params)
+ str += "\n " + param.as_string ();
+ }
+ else
+ {
+ 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
+TraitItemMethod::as_string () const
+{
+ std::string str = append_attributes (outer_attrs, OUTER);
+
+ 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
+TraitMethodDecl::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 ¶m : 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 "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + param->as_string ();
+ }
+ }
+
+ str += "\n Self param: " + self_param.as_string ();
+
+ str += "\n Function params: ";
+ if (has_params ())
+ {
+ for (const auto ¶m : function_params)
+ str += "\n " + param.as_string ();
+ }
+ else
+ {
+ 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
+{
+ // TODO: rewrite to work with non-linearisable exprs
+ std::string str = append_attributes (outer_attrs, OUTER);
+
+ str += "\nconst " + name + " : " + type->as_string ();
+
+ if (has_expression ())
+ str += " = " + expr->as_string ();
+
+ return str;
+}
+
+std::string
+TraitItemType::as_string () const
+{
+ std::string str = append_attributes (outer_attrs, OUTER);
+
+ 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 "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + bound->as_string ();
+ }
+ }
+
+ return str;
+}
+
+std::string
+SelfParam::as_string () const
+{
+ // TODO: rewrite to allow non-linearisable types
+ 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 (has_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
+{
+ // TODO: rewrite to allow non-linearisable exprs
+ 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 "NULL_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 "NULL_POINTER_MARK";
+ }
+
+ str += "\n " + expr->as_string ();
+ }
+
+ return str;
+}
+
+std::string
+MaybeNamedParam::as_string () const
+{
+ // TODO: rewrite to allow using non-linearisable types in dump
+ 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;
+}
+
+MetaItemInner::~MetaItemInner () = default;
+
+std::unique_ptr<MetaNameValueStr>
+MetaItemInner::to_meta_name_value_str () const
+{
+ if (is_key_value_pair ())
+ {
+ auto converted_item = static_cast<const MetaNameValueStr *> (this);
+ return converted_item->to_meta_name_value_str ();
+ }
+ // TODO actually parse foo = bar
+ return nullptr;
+}
+
+std::string
+MetaItemSeq::as_string () const
+{
+ std::string path_str = path.as_string () + "(";
+
+ auto i = seq.begin ();
+ auto e = seq.end ();
+
+ for (; i != e; i++)
+ {
+ path_str += (*i)->as_string ();
+ if (e != i + 1)
+ path_str += ", ";
+ }
+
+ return path_str + ")";
+}
+
+std::string
+MetaListPaths::as_string () const
+{
+ std::string str = ident + "(";
+
+ auto i = paths.begin ();
+ auto e = paths.end ();
+
+ for (; i != e; i++)
+ {
+ str += (*i).as_string ();
+ if (e != i + 1)
+ str += ", ";
+ }
+
+ return str + ")";
+}
+
+std::string
+MetaListNameValueStr::as_string () const
+{
+ std::string str = ident + "(";
+
+ auto i = strs.begin ();
+ auto e = strs.end ();
+
+ for (; i != e; i++)
+ {
+ str += (*i).as_string ();
+ if (e != i + 1)
+ str += ", ";
+ }
+
+ return str + ")";
+}
+
+std::string
+AttrInputMetaItemContainer::as_string () const
+{
+ std::string str = "(";
+
+ auto i = items.begin ();
+ auto e = items.end ();
+
+ for (; i != e; i++)
+ {
+ str += (*i)->as_string ();
+ if (e != i + 1)
+ str += ", ";
+ }
+
+ 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);
+}
+
+static bool
+file_exists (const std::string path)
+{
+ // Simply check if the file exists
+ // FIXME: This does not work on Windows
+ return access (path.c_str (), F_OK) != -1;
+}
+
+static std::string
+filename_from_path_attribute (std::vector<Attribute> &outer_attrs)
+{
+ // An out-of-line module cannot have inner attributes. Additionally, the
+ // default name is specified as `""` so that the caller can detect the case
+ // of "no path given" and use the default path logic (`name.rs` or
+ // `name/mod.rs`).
+ return extract_module_path ({}, outer_attrs, "");
+}
+
+void
+Module::process_file_path ()
+{
+ rust_assert (kind == Module::ModuleKind::UNLOADED);
+ rust_assert (module_file.empty ());
+
+ // This corresponds to the path of the file 'including' the module. So the
+ // file that contains the 'mod <file>;' directive
+ std::string including_fname (outer_filename);
+
+ std::string expected_file_path = module_name + ".rs";
+ std::string expected_dir_path = "mod.rs";
+
+ auto dir_slash_pos = including_fname.rfind (file_separator);
+ std::string current_directory_name;
+
+ // If we haven't found a file_separator, then we have to look for files in the
+ // current directory ('.')
+ if (dir_slash_pos == std::string::npos)
+ current_directory_name = std::string (".") + file_separator;
+ else
+ current_directory_name
+ = including_fname.substr (0, dir_slash_pos) + file_separator;
+
+ // Handle inline module declarations adding path components.
+ for (auto const &name : module_scope)
+ {
+ current_directory_name.append (name);
+ current_directory_name.append (file_separator);
+ }
+
+ auto path_string = filename_from_path_attribute (get_outer_attrs ());
+ if (!path_string.empty ())
+ {
+ module_file = current_directory_name + path_string;
+ return;
+ }
+
+ // FIXME: We also have to search for
+ // <directory>/<including_fname>/<module_name>.rs In rustc, this is done via
+ // the concept of `DirOwnernship`, which is based on whether or not the
+ // current file is titled `mod.rs`.
+
+ // First, we search for <directory>/<module_name>.rs
+ std::string file_mod_path = current_directory_name + expected_file_path;
+ bool file_mod_found = file_exists (file_mod_path);
+
+ // Then, search for <directory>/<module_name>/mod.rs
+ std::string dir_mod_path
+ = current_directory_name + module_name + file_separator + expected_dir_path;
+ bool dir_mod_found = file_exists (dir_mod_path);
+
+ bool multiple_candidates_found = file_mod_found && dir_mod_found;
+ bool no_candidates_found = !file_mod_found && !dir_mod_found;
+
+ if (multiple_candidates_found)
+ rust_error_at (locus,
+ "two candidates found for module %s: %s.rs and %s%smod.rs",
+ module_name.c_str (), module_name.c_str (),
+ module_name.c_str (), file_separator);
+
+ if (no_candidates_found)
+ rust_error_at (locus, "no candidate found for module %s",
+ module_name.c_str ());
+
+ if (no_candidates_found || multiple_candidates_found)
+ return;
+
+ module_file = std::move (file_mod_found ? file_mod_path : dir_mod_path);
+}
+
+void
+Module::load_items ()
+{
+ process_file_path ();
+
+ // We will already have errored out appropriately in the process_file_path ()
+ // method
+ if (module_file.empty ())
+ return;
+
+ RAIIFile file_wrap (module_file.c_str ());
+ Linemap *linemap = Session::get_instance ().linemap;
+ if (!file_wrap.ok ())
+ {
+ rust_error_at (get_locus (), "cannot open module file %s: %m",
+ module_file.c_str ());
+ return;
+ }
+
+ rust_debug ("Attempting to parse file %s", module_file.c_str ());
+
+ Lexer lex (module_file.c_str (), std::move (file_wrap), linemap);
+ Parser<Lexer> parser (lex);
+
+ // we need to parse any possible inner attributes for this module
+ inner_attrs = parser.parse_inner_attributes ();
+ auto parsed_items = parser.parse_items ();
+ for (const auto &error : parser.get_errors ())
+ error.emit_error ();
+
+ items = std::move (parsed_items);
+ kind = ModuleKind::LOADED;
+}
+
+void
+Attribute::parse_attr_to_meta_item ()
+{
+ // only parse if has attribute input and not already parsed
+ if (!has_attr_input () || is_parsed_to_meta_item ())
+ return;
+
+ auto res = attr_input->parse_to_meta_item ();
+ std::unique_ptr<AttrInput> converted_input (res);
+
+ if (converted_input != nullptr)
+ attr_input = std::move (converted_input);
+}
+
+AttrInputMetaItemContainer *
+DelimTokenTree::parse_to_meta_item () const
+{
+ // must have token trees
+ if (token_trees.empty ())
+ return nullptr;
+
+ /* assume top-level delim token tree in attribute - convert all nested ones
+ * to token stream */
+ std::vector<std::unique_ptr<Token>> token_stream = to_token_stream ();
+
+ AttributeParser parser (std::move (token_stream));
+ std::vector<std::unique_ptr<MetaItemInner>> meta_items (
+ parser.parse_meta_item_seq ());
+
+ return new AttrInputMetaItemContainer (std::move (meta_items));
+}
+
+std::unique_ptr<MetaItemInner>
+AttributeParser::parse_meta_item_inner ()
+{
+ // if first tok not identifier, not a "special" case one
+ if (peek_token ()->get_id () != IDENTIFIER)
+ {
+ switch (peek_token ()->get_id ())
+ {
+ case CHAR_LITERAL:
+ case STRING_LITERAL:
+ case BYTE_CHAR_LITERAL:
+ case BYTE_STRING_LITERAL:
+ case INT_LITERAL:
+ case FLOAT_LITERAL:
+ case TRUE_LITERAL:
+ case FALSE_LITERAL:
+ return parse_meta_item_lit ();
+
+ case SUPER:
+ case SELF:
+ case CRATE:
+ case DOLLAR_SIGN:
+ case SCOPE_RESOLUTION:
+ return parse_path_meta_item ();
+
+ default:
+ rust_error_at (peek_token ()->get_locus (),
+ "unrecognised token '%s' in meta item",
+ get_token_description (peek_token ()->get_id ()));
+ return nullptr;
+ }
+ }
+
+ // else, check for path
+ if (peek_token (1)->get_id () == SCOPE_RESOLUTION)
+ {
+ // path
+ return parse_path_meta_item ();
+ }
+
+ auto ident = peek_token ()->as_string ();
+ auto ident_locus = peek_token ()->get_locus ();
+
+ if (is_end_meta_item_tok (peek_token (1)->get_id ()))
+ {
+ // meta word syntax
+ skip_token ();
+ return std::unique_ptr<MetaWord> (new MetaWord (ident, ident_locus));
+ }
+
+ if (peek_token (1)->get_id () == EQUAL)
+ {
+ // maybe meta name value str syntax - check next 2 tokens
+ if (peek_token (2)->get_id () == STRING_LITERAL
+ && is_end_meta_item_tok (peek_token (3)->get_id ()))
+ {
+ // meta name value str syntax
+ auto &value_tok = peek_token (2);
+ auto value = value_tok->as_string ();
+ auto locus = value_tok->get_locus ();
+
+ skip_token (2);
+
+ // remove the quotes from the string value
+ std::string raw_value = unquote_string (std::move (value));
+
+ return std::unique_ptr<MetaNameValueStr> (
+ new MetaNameValueStr (ident, ident_locus, std::move (raw_value),
+ locus));
+ }
+ else
+ {
+ // just interpret as path-based meta item
+ return parse_path_meta_item ();
+ }
+ }
+
+ if (peek_token (1)->get_id () != LEFT_PAREN)
+ {
+ rust_error_at (peek_token (1)->get_locus (),
+ "unexpected token '%s' after identifier in attribute",
+ get_token_description (peek_token (1)->get_id ()));
+ return nullptr;
+ }
+
+ // is it one of those special cases like not?
+ if (peek_token ()->get_id () == IDENTIFIER)
+ {
+ return parse_path_meta_item ();
+ }
+
+ auto meta_items = parse_meta_item_seq ();
+
+ // pass for meta name value str
+ std::vector<MetaNameValueStr> meta_name_value_str_items;
+ for (const auto &item : meta_items)
+ {
+ std::unique_ptr<MetaNameValueStr> converted_item
+ = item->to_meta_name_value_str ();
+ if (converted_item == nullptr)
+ {
+ meta_name_value_str_items.clear ();
+ break;
+ }
+ meta_name_value_str_items.push_back (std::move (*converted_item));
+ }
+ // if valid, return this
+ if (!meta_name_value_str_items.empty ())
+ {
+ return std::unique_ptr<MetaListNameValueStr> (
+ new MetaListNameValueStr (ident, ident_locus,
+ std::move (meta_name_value_str_items)));
+ }
+
+ // // pass for meta list idents
+ // std::vector<Identifier> ident_items;
+ // for (const auto &item : meta_items)
+ // {
+ // std::unique_ptr<Identifier> converted_ident (item->to_ident_item ());
+ // if (converted_ident == nullptr)
+ // {
+ // ident_items.clear ();
+ // break;
+ // }
+ // ident_items.push_back (std::move (*converted_ident));
+ // }
+ // // if valid return this
+ // if (!ident_items.empty ())
+ // {
+ // return std::unique_ptr<MetaListIdents> (
+ // new MetaListIdents (std::move (ident), std::move (ident_items)));
+ // }
+ // // as currently no meta list ident, currently no path. may change in future
+
+ // pass for meta list paths
+ std::vector<SimplePath> path_items;
+ for (const auto &item : meta_items)
+ {
+ SimplePath converted_path (item->to_path_item ());
+ if (converted_path.is_empty ())
+ {
+ path_items.clear ();
+ break;
+ }
+ path_items.push_back (std::move (converted_path));
+ }
+ if (!path_items.empty ())
+ {
+ return std::unique_ptr<MetaListPaths> (
+ new MetaListPaths (ident, ident_locus, std::move (path_items)));
+ }
+
+ rust_error_at (Linemap::unknown_location (),
+ "failed to parse any meta item inner");
+ return nullptr;
+}
+
+bool
+AttributeParser::is_end_meta_item_tok (TokenId id) const
+{
+ return id == COMMA || id == RIGHT_PAREN;
+}
+
+std::unique_ptr<MetaItem>
+AttributeParser::parse_path_meta_item ()
+{
+ SimplePath path = parse_simple_path ();
+ if (path.is_empty ())
+ {
+ rust_error_at (peek_token ()->get_locus (),
+ "failed to parse simple path in attribute");
+ return nullptr;
+ }
+
+ switch (peek_token ()->get_id ())
+ {
+ case LEFT_PAREN: {
+ std::vector<std::unique_ptr<MetaItemInner>> meta_items
+ = parse_meta_item_seq ();
+
+ return std::unique_ptr<MetaItemSeq> (
+ new MetaItemSeq (std::move (path), std::move (meta_items)));
+ }
+ case EQUAL: {
+ skip_token ();
+
+ Location locus = peek_token ()->get_locus ();
+ Literal lit = parse_literal ();
+ if (lit.is_error ())
+ {
+ rust_error_at (peek_token ()->get_locus (),
+ "failed to parse literal in attribute");
+ return nullptr;
+ }
+ LiteralExpr expr (std::move (lit), {}, locus);
+ // stream_pos++;
+ /* shouldn't be required anymore due to parsing literal actually
+ * skipping the token */
+ return std::unique_ptr<MetaItemPathLit> (
+ new MetaItemPathLit (std::move (path), std::move (expr)));
+ }
+ case COMMA:
+ // just simple path
+ return std::unique_ptr<MetaItemPath> (
+ new MetaItemPath (std::move (path)));
+ default:
+ rust_error_at (peek_token ()->get_locus (),
+ "unrecognised token '%s' in meta item",
+ get_token_description (peek_token ()->get_id ()));
+ return nullptr;
+ }
+}
+
+/* Parses a parenthesised sequence of meta item inners. Parentheses are
+ * required here. */
+std::vector<std::unique_ptr<MetaItemInner>>
+AttributeParser::parse_meta_item_seq ()
+{
+ int vec_length = token_stream.size ();
+ std::vector<std::unique_ptr<MetaItemInner>> meta_items;
+
+ if (peek_token ()->get_id () != LEFT_PAREN)
+ {
+ rust_error_at (peek_token ()->get_locus (),
+ "missing left paren in delim token tree");
+ return {};
+ }
+ skip_token ();
+
+ while (stream_pos < vec_length && peek_token ()->get_id () != RIGHT_PAREN)
+ {
+ std::unique_ptr<MetaItemInner> inner = parse_meta_item_inner ();
+ if (inner == nullptr)
+ {
+ rust_error_at (peek_token ()->get_locus (),
+ "failed to parse inner meta item in attribute");
+ return {};
+ }
+ meta_items.push_back (std::move (inner));
+
+ if (peek_token ()->get_id () != COMMA)
+ break;
+
+ skip_token ();
+ }
+
+ if (peek_token ()->get_id () != RIGHT_PAREN)
+ {
+ rust_error_at (peek_token ()->get_locus (),
+ "missing right paren in delim token tree");
+ return {};
+ }
+ skip_token ();
+
+ return meta_items;
+}
+
+/* Collects any nested token trees into a flat token stream, suitable for
+ * parsing. */
+std::vector<std::unique_ptr<Token>>
+DelimTokenTree::to_token_stream () const
+{
+ std::vector<std::unique_ptr<Token>> tokens;
+ for (const auto &tree : token_trees)
+ {
+ std::vector<std::unique_ptr<Token>> stream = tree->to_token_stream ();
+
+ tokens.insert (tokens.end (), std::make_move_iterator (stream.begin ()),
+ std::make_move_iterator (stream.end ()));
+ }
+
+ tokens.shrink_to_fit ();
+ return tokens;
+}
+
+Literal
+AttributeParser::parse_literal ()
+{
+ const std::unique_ptr<Token> &tok = peek_token ();
+ switch (tok->get_id ())
+ {
+ case CHAR_LITERAL:
+ skip_token ();
+ return Literal (tok->as_string (), Literal::CHAR, tok->get_type_hint ());
+ case STRING_LITERAL:
+ skip_token ();
+ return Literal (tok->as_string (), Literal::STRING,
+ tok->get_type_hint ());
+ case BYTE_CHAR_LITERAL:
+ skip_token ();
+ return Literal (tok->as_string (), Literal::BYTE, tok->get_type_hint ());
+ case BYTE_STRING_LITERAL:
+ skip_token ();
+ return Literal (tok->as_string (), Literal::BYTE_STRING,
+ tok->get_type_hint ());
+ case INT_LITERAL:
+ skip_token ();
+ return Literal (tok->as_string (), Literal::INT, tok->get_type_hint ());
+ case FLOAT_LITERAL:
+ skip_token ();
+ return Literal (tok->as_string (), Literal::FLOAT, tok->get_type_hint ());
+ case TRUE_LITERAL:
+ skip_token ();
+ return Literal ("true", Literal::BOOL, tok->get_type_hint ());
+ case FALSE_LITERAL:
+ skip_token ();
+ return Literal ("false", Literal::BOOL, tok->get_type_hint ());
+ default:
+ rust_error_at (tok->get_locus (), "expected literal - found '%s'",
+ get_token_description (tok->get_id ()));
+ return Literal::create_error ();
+ }
+}
+
+SimplePath
+AttributeParser::parse_simple_path ()
+{
+ bool has_opening_scope_res = false;
+ if (peek_token ()->get_id () == SCOPE_RESOLUTION)
+ {
+ has_opening_scope_res = true;
+ skip_token ();
+ }
+
+ std::vector<SimplePathSegment> segments;
+
+ SimplePathSegment segment = parse_simple_path_segment ();
+ if (segment.is_error ())
+ {
+ rust_error_at (
+ peek_token ()->get_locus (),
+ "failed to parse simple path segment in attribute simple path");
+ return SimplePath::create_empty ();
+ }
+ segments.push_back (std::move (segment));
+
+ while (peek_token ()->get_id () == SCOPE_RESOLUTION)
+ {
+ skip_token ();
+
+ SimplePathSegment segment = parse_simple_path_segment ();
+ if (segment.is_error ())
+ {
+ rust_error_at (
+ peek_token ()->get_locus (),
+ "failed to parse simple path segment in attribute simple path");
+ return SimplePath::create_empty ();
+ }
+ segments.push_back (std::move (segment));
+ }
+ segments.shrink_to_fit ();
+
+ return SimplePath (std::move (segments), has_opening_scope_res);
+}
+
+SimplePathSegment
+AttributeParser::parse_simple_path_segment ()
+{
+ const std::unique_ptr<Token> &tok = peek_token ();
+ switch (tok->get_id ())
+ {
+ case IDENTIFIER:
+ skip_token ();
+ return SimplePathSegment (tok->as_string (), tok->get_locus ());
+ case SUPER:
+ skip_token ();
+ return SimplePathSegment ("super", tok->get_locus ());
+ case SELF:
+ skip_token ();
+ return SimplePathSegment ("self", tok->get_locus ());
+ case CRATE:
+ skip_token ();
+ return SimplePathSegment ("crate", tok->get_locus ());
+ case DOLLAR_SIGN:
+ if (peek_token (1)->get_id () == CRATE)
+ {
+ skip_token (1);
+ return SimplePathSegment ("$crate", tok->get_locus ());
+ }
+ gcc_fallthrough ();
+ default:
+ rust_error_at (tok->get_locus (),
+ "unexpected token '%s' in simple path segment",
+ get_token_description (tok->get_id ()));
+ return SimplePathSegment::create_error ();
+ }
+}
+
+std::unique_ptr<MetaItemLitExpr>
+AttributeParser::parse_meta_item_lit ()
+{
+ Location locus = peek_token ()->get_locus ();
+ LiteralExpr lit_expr (parse_literal (), {}, locus);
+ return std::unique_ptr<MetaItemLitExpr> (
+ new MetaItemLitExpr (std::move (lit_expr)));
+}
+
+bool
+AttrInputMetaItemContainer::check_cfg_predicate (const Session &session) const
+{
+ if (items.empty ())
+ return false;
+
+ for (const auto &inner_item : items)
+ {
+ if (!inner_item->check_cfg_predicate (session))
+ return false;
+ }
+
+ return true;
+}
+
+bool
+MetaItemLitExpr::check_cfg_predicate (const Session &) const
+{
+ /* as far as I can tell, a literal expr can never be a valid cfg body, so
+ * false */
+ return false;
+}
+
+bool
+MetaListNameValueStr::check_cfg_predicate (const Session &session) const
+{
+ if (ident == "all")
+ {
+ for (const auto &str : strs)
+ {
+ if (!str.check_cfg_predicate (session))
+ return false;
+ }
+ return true;
+ }
+ else if (ident == "any")
+ {
+ for (const auto &str : strs)
+ {
+ if (str.check_cfg_predicate (session))
+ return true;
+ }
+ return false;
+ }
+ else if (ident == "not")
+ {
+ if (strs.size () != 1)
+ {
+ /* HACK: convert vector platform-dependent size_type to string to
+ * use in printf */
+ rust_error_at (Linemap::unknown_location (),
+ "cfg predicate could not be checked for "
+ "MetaListNameValueStr with ident of "
+ "'not' because there are '%s' elements, not '1'",
+ std::to_string (strs.size ()).c_str ());
+ return false;
+ }
+
+ return !strs[0].check_cfg_predicate (session);
+ }
+ else
+ {
+ rust_error_at (Linemap::unknown_location (),
+ "cfg predicate could not be checked for "
+ "MetaListNameValueStr with ident of "
+ "'%s' - ident must be 'all' or 'any'",
+ ident.c_str ());
+ return false;
+ }
+}
+
+bool
+MetaListPaths::check_cfg_predicate (const Session &session) const
+{
+ if (ident == "all")
+ {
+ for (const auto &path : paths)
+ {
+ if (!check_path_exists_in_cfg (session, path))
+ return false;
+ }
+ return true;
+ }
+ else if (ident == "any")
+ {
+ for (const auto &path : paths)
+ {
+ if (check_path_exists_in_cfg (session, path))
+ return true;
+ }
+ return false;
+ }
+ else if (ident == "not")
+ {
+ if (paths.size () != 1)
+ {
+ // HACK: convert vector platform-dependent size_type to string to
+ // use in printf
+ rust_error_at (Linemap::unknown_location (),
+ "cfg predicate could not be checked for MetaListPaths "
+ "with ident of 'not' "
+ "because there are '%s' elements, not '1'",
+ std::to_string (paths.size ()).c_str ());
+ return false;
+ }
+
+ return !check_path_exists_in_cfg (session, paths[0]);
+ }
+ else
+ {
+ rust_error_at (Linemap::unknown_location (),
+ "cfg predicate could not be checked for "
+ "MetaListNameValueStr with ident of "
+ "'%s' - ident must be 'all' or 'any'",
+ ident.c_str ());
+ return false;
+ }
+}
+
+bool
+MetaListPaths::check_path_exists_in_cfg (const Session &session,
+ const SimplePath &path) const
+{
+ return session.options.target_data.has_key (path.as_string ());
+}
+
+bool
+MetaItemSeq::check_cfg_predicate (const Session &session) const
+{
+ if (path.as_string () == "all")
+ {
+ for (const auto &item : seq)
+ {
+ if (!item->check_cfg_predicate (session))
+ return false;
+ }
+ return true;
+ }
+ else if (path.as_string () == "any")
+ {
+ for (const auto &item : seq)
+ {
+ if (item->check_cfg_predicate (session))
+ return true;
+ }
+ return false;
+ }
+ else if (path.as_string () == "not")
+ {
+ if (seq.size () != 1)
+ {
+ /* HACK: convert vector platform-dependent size_type to string to
+ * use in printf */
+ rust_error_at (Linemap::unknown_location (),
+ "cfg predicate could not be checked for MetaItemSeq "
+ "with ident of 'not' "
+ "because there are '%s' elements, not '1'",
+ std::to_string (seq.size ()).c_str ());
+ return false;
+ }
+
+ return !seq[0]->check_cfg_predicate (session);
+ }
+ else
+ {
+ rust_error_at (
+ Linemap::unknown_location (),
+ "cfg predicate could not be checked for MetaItemSeq with path of "
+ "'%s' - path must be 'all' or 'any'",
+ path.as_string ().c_str ());
+ return false;
+ }
+}
+
+bool
+MetaWord::check_cfg_predicate (const Session &session) const
+{
+ return session.options.target_data.has_key (ident);
+}
+
+bool
+MetaItemPath::check_cfg_predicate (const Session &session) const
+{
+ /* Strictly speaking, this should always be false, but maybe do check
+ * relating to SimplePath being identifier. Currently, it would return true
+ * if path as identifier existed, and if the path in string form existed
+ * (though this shouldn't occur). */
+ return session.options.target_data.has_key (path.as_string ());
+}
+
+bool
+MetaNameValueStr::check_cfg_predicate (const Session &session) const
+{
+ // DEBUG
+ rust_debug (
+ "checked key-value pair for cfg: '%s', '%s' - is%s in target data",
+ ident.c_str (), str.c_str (),
+ session.options.target_data.has_key_value_pair (ident, str) ? "" : " not");
+
+ return session.options.target_data.has_key_value_pair (ident, str);
+}
+
+bool
+MetaItemPathLit::check_cfg_predicate (const Session &session) const
+{
+ return session.options.target_data.has_key_value_pair (path.as_string (),
+ lit.as_string ());
+}
+
+std::vector<std::unique_ptr<Token>>
+Token::to_token_stream () const
+{
+ /* initialisation list doesn't work as it needs copy constructor, so have to
+ * do this */
+ std::vector<std::unique_ptr<Token>> dummy_vector;
+ dummy_vector.reserve (1);
+ dummy_vector.push_back (std::unique_ptr<Token> (clone_token_impl ()));
+ return dummy_vector;
+}
+
+Attribute
+MetaNameValueStr::to_attribute () const
+{
+ LiteralExpr lit_expr (str, Literal::LitType::STRING,
+ PrimitiveCoreType::CORETYPE_UNKNOWN, {}, str_locus);
+ // FIXME: What location do we put here? Is the literal above supposed to have
+ // an empty location as well?
+ // Should MetaNameValueStr keep a location?
+ return Attribute (SimplePath::from_str (ident, ident_locus),
+ std::unique_ptr<AttrInputLiteral> (
+ new AttrInputLiteral (std::move (lit_expr))));
+}
+
+Attribute
+MetaItemPath::to_attribute () const
+{
+ return Attribute (path, nullptr);
+}
+
+Attribute
+MetaItemSeq::to_attribute () const
+{
+ std::vector<std::unique_ptr<MetaItemInner>> new_seq;
+ new_seq.reserve (seq.size ());
+ for (const auto &e : seq)
+ new_seq.push_back (e->clone_meta_item_inner ());
+
+ std::unique_ptr<AttrInputMetaItemContainer> new_seq_container (
+ new AttrInputMetaItemContainer (std::move (new_seq)));
+ return Attribute (path, std::move (new_seq_container));
+}
+
+Attribute
+MetaWord::to_attribute () const
+{
+ return Attribute (SimplePath::from_str (ident, ident_locus), nullptr);
+}
+
+Attribute
+MetaListPaths::to_attribute () const
+{
+ /* probably one of the most annoying conversions - have to lose specificity by
+ * turning it into just AttrInputMetaItemContainer (i.e. paths-only nature is
+ * no longer known). If conversions back are required, might have to do a
+ * "check all are paths" pass or something. */
+
+ std::vector<std::unique_ptr<MetaItemInner>> new_seq;
+ new_seq.reserve (paths.size ());
+ for (const auto &e : paths)
+ new_seq.push_back (std::unique_ptr<MetaItemPath> (new MetaItemPath (e)));
+
+ std::unique_ptr<AttrInputMetaItemContainer> new_seq_container (
+ new AttrInputMetaItemContainer (std::move (new_seq)));
+ return Attribute (SimplePath::from_str (ident, ident_locus),
+ std::move (new_seq_container));
+}
+
+Attribute
+MetaListNameValueStr::to_attribute () const
+{
+ std::vector<std::unique_ptr<MetaItemInner>> new_seq;
+ new_seq.reserve (strs.size ());
+ for (const auto &e : strs)
+ new_seq.push_back (
+ std::unique_ptr<MetaNameValueStr> (new MetaNameValueStr (e)));
+
+ std::unique_ptr<AttrInputMetaItemContainer> new_seq_container (
+ new AttrInputMetaItemContainer (std::move (new_seq)));
+ return Attribute (SimplePath::from_str (ident, ident_locus),
+ std::move (new_seq_container));
+}
+
+Attribute
+MetaItemPathLit::to_attribute () const
+{
+ return Attribute (path, std::unique_ptr<AttrInputLiteral> (
+ new AttrInputLiteral (lit)));
+}
+
+std::vector<Attribute>
+AttrInputMetaItemContainer::separate_cfg_attrs () const
+{
+ rust_assert (!items.empty ());
+
+ if (items.size () == 1)
+ return {};
+
+ std::vector<Attribute> attrs;
+ attrs.reserve (items.size () - 1);
+
+ for (auto it = items.begin () + 1; it != items.end (); ++it)
+ {
+ Attribute attr = (*it)->to_attribute ();
+ if (attr.is_empty ())
+ {
+ /* TODO should this be an error that causes us to chuck out
+ * everything? */
+ continue;
+ }
+ attrs.push_back (std::move (attr));
+ }
+
+ attrs.shrink_to_fit ();
+ return attrs;
+}
+
+bool
+Attribute::check_cfg_predicate (const Session &session) const
+{
+ /* assume that cfg predicate actually can exist, i.e. attribute has cfg or
+ * cfg_attr path */
+ if (!has_attr_input ()
+ || (path.as_string () != "cfg" && path.as_string () != "cfg_attr"))
+ {
+ // DEBUG message
+ rust_debug (
+ "tried to check cfg predicate on attr that either has no input "
+ "or invalid path. attr: '%s'",
+ as_string ().c_str ());
+
+ return false;
+ }
+
+ // assume that it has already been parsed
+ if (!is_parsed_to_meta_item ())
+ return false;
+
+ return attr_input->check_cfg_predicate (session);
+}
+
+std::vector<Attribute>
+Attribute::separate_cfg_attrs () const
+{
+ if (!has_attr_input () || path.as_string () != "cfg_attr")
+ return {};
+
+ // assume that it has already been parsed
+ if (!is_parsed_to_meta_item ())
+ return {};
+
+ return attr_input->separate_cfg_attrs ();
+}
+
+bool
+Attribute::is_parsed_to_meta_item () const
+{
+ return has_attr_input () && attr_input->is_meta_item ();
+}
+
+/* Visitor implementations - these are short but inlining can't happen anyway
+ * due to virtual functions and I didn't want to make the ast header includes
+ * any longer than they already are. */
+
+void
+Token::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+DelimTokenTree::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+IdentifierExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+Lifetime::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+LifetimeParam::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ConstGenericParam::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+PathInExpression::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TypePathSegment::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TypePathSegmentGeneric::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TypePathSegmentFunction::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TypePath::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+QualifiedPathInExpression::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+QualifiedPathInType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+LiteralExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+AttrInputLiteral::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MetaItemLitExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MetaItemPathLit::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+BorrowExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+DereferenceExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ErrorPropagationExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+NegationExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ArithmeticOrLogicalExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ComparisonExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+LazyBooleanExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TypeCastExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+AssignmentExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+CompoundAssignmentExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+GroupedExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ArrayElemsValues::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ArrayElemsCopied::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ArrayExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ArrayIndexExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TupleExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TupleIndexExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructExprStruct::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructExprFieldIdentifier::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructExprFieldIdentifierValue::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructExprFieldIndexValue::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructExprStructFields::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructExprStructBase::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+CallExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MethodCallExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+FieldAccessExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ClosureExprInner::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+BlockExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ClosureExprInnerTyped::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ContinueExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+BreakExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+RangeFromToExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+RangeFromExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+RangeToExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+RangeFullExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+RangeFromToInclExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+RangeToInclExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ReturnExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+UnsafeBlockExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+LoopExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+WhileLoopExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+WhileLetLoopExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ForLoopExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+IfExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+IfExprConseqElse::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+IfExprConseqIf::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+IfExprConseqIfLet::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+IfLetExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+IfLetExprConseqElse::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+IfLetExprConseqIf::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+IfLetExprConseqIfLet::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MatchExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+AwaitExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+AsyncBlockExpr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TypeParam::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+LifetimeWhereClauseItem::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TypeBoundWhereClauseItem::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+Method::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+Module::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ExternCrate::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+UseTreeGlob::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+UseTreeList::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+UseTreeRebind::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+UseDeclaration::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+Function::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TypeAlias::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructStruct::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TupleStruct::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+EnumItem::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+EnumItemTuple::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+EnumItemStruct::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+EnumItemDiscriminant::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+Enum::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+Union::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ConstantItem::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StaticItem::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TraitItemFunc::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TraitItemMethod::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TraitItemConst::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TraitItemType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+Trait::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+InherentImpl::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TraitImpl::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ExternalStaticItem::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ExternalFunctionItem::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ExternBlock::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MacroMatchFragment::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MacroMatchRepetition::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MacroMatcher::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MacroRulesDefinition::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MacroInvocation::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+LiteralPattern::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+IdentifierPattern::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+WildcardPattern::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+RangePatternBoundLiteral::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+RangePatternBoundPath::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+RangePatternBoundQualPath::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+RangePattern::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ReferencePattern::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructPatternFieldTuplePat::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructPatternFieldIdentPat::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructPatternFieldIdent::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+StructPattern::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TupleStructItemsNoRange::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TupleStructItemsRange::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TupleStructPattern::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TuplePatternItemsMultiple::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TuplePatternItemsRanged::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TuplePattern::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+GroupedPattern::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+SlicePattern::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+EmptyStmt::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+LetStmt::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ExprStmtWithoutBlock::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ExprStmtWithBlock::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TraitBound::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ImplTraitType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TraitObjectType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ParenthesisedType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ImplTraitTypeOneBound::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TraitObjectTypeOneBound::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+TupleType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+NeverType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+RawPointerType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ReferenceType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+ArrayType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+SliceType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+InferredType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+BareFunctionType::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MetaItemSeq::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MetaItemPath::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MetaListPaths::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MetaNameValueStr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MetaListNameValueStr::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+AttrInputMetaItemContainer::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+void
+MetaWord::accept_vis (ASTVisitor &vis)
+{
+ vis.visit (*this);
+}
+
+GenericArg
+GenericArg::disambiguate_to_const () const
+{
+ rust_assert (get_kind () == Kind::Either);
+
+ // FIXME: is it fine to have no outer attributes?
+ return GenericArg::create_const (
+ std::unique_ptr<Expr> (new IdentifierExpr (path, {}, locus)));
+}
+
+GenericArg
+GenericArg::disambiguate_to_type () const
+{
+ rust_assert (get_kind () == Kind::Either);
+
+ auto segment = std::unique_ptr<TypePathSegment> (
+ new TypePathSegment (path, false, locus));
+ auto segments = std::vector<std::unique_ptr<TypePathSegment>> ();
+ segments.emplace_back (std::move (segment));
+
+ return GenericArg::create_type (
+ std::unique_ptr<Type> (new TypePath (std::move (segments), locus)));
+}
+
+} // namespace AST
+} // namespace Rust
--- /dev/null
+// 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_AST_BASE_H
+#define RUST_AST_BASE_H
+// Base for AST used in gccrs, basically required by all specific ast things
+
+#include "rust-system.h"
+#include "rust-hir-map.h"
+#include "rust-token.h"
+#include "rust-location.h"
+
+namespace Rust {
+// TODO: remove typedefs and make actual types for these
+typedef std::string Identifier;
+typedef int TupleIndex;
+struct Session;
+
+namespace AST {
+// foward decl: ast visitor
+class ASTVisitor;
+using AttrVec = std::vector<Attribute>;
+
+// The available kinds of AST Nodes
+enum Kind
+{
+ UNKNOWN,
+ MACRO_RULES_DEFINITION,
+ MACRO_INVOCATION,
+};
+
+// Abstract base class for all AST elements
+class Node
+{
+public:
+ /**
+ * Get the kind of Node this is. This is used to differentiate various AST
+ * elements with very little overhead when extracting the derived type through
+ * static casting is not necessary.
+ */
+ // FIXME: Mark this as `= 0` in the future to make sure every node implements
+ // it
+ virtual Kind get_ast_kind () const { return Kind::UNKNOWN; }
+};
+
+// Delimiter types - used in macros and whatever.
+enum DelimType
+{
+ PARENS,
+ SQUARE,
+ CURLY
+};
+
+// forward decl for use in token tree method
+class Token;
+
+// A tree of tokens (or a single token) - abstract base class
+class TokenTree
+{
+public:
+ virtual ~TokenTree () {}
+
+ // Unique pointer custom clone function
+ std::unique_ptr<TokenTree> clone_token_tree () const
+ {
+ return std::unique_ptr<TokenTree> (clone_token_tree_impl ());
+ }
+
+ virtual std::string as_string () const = 0;
+
+ virtual void accept_vis (ASTVisitor &vis) = 0;
+
+ /* Converts token tree to a flat token stream. Tokens must be pointer to avoid
+ * mutual dependency with Token. */
+ virtual std::vector<std::unique_ptr<Token> > to_token_stream () const = 0;
+
+protected:
+ // pure virtual clone implementation
+ virtual TokenTree *clone_token_tree_impl () const = 0;
+};
+
+// Abstract base class for a macro match
+class MacroMatch
+{
+public:
+ enum MacroMatchType
+ {
+ Fragment,
+ Repetition,
+ Matcher,
+ Tok
+ };
+
+ virtual ~MacroMatch () {}
+
+ virtual std::string as_string () const = 0;
+ virtual Location get_match_locus () const = 0;
+
+ // Unique pointer custom clone function
+ std::unique_ptr<MacroMatch> clone_macro_match () const
+ {
+ return std::unique_ptr<MacroMatch> (clone_macro_match_impl ());
+ }
+
+ virtual void accept_vis (ASTVisitor &vis) = 0;
+
+ virtual MacroMatchType get_macro_match_type () const = 0;
+
+protected:
+ // pure virtual clone implementation
+ virtual MacroMatch *clone_macro_match_impl () const = 0;
+};
+
+// A token is a kind of token tree (except delimiter tokens)
+class Token : public TokenTree, public MacroMatch
+{
+ // A token is a kind of token tree (except delimiter tokens)
+ // A token is a kind of MacroMatch (except $ and delimiter tokens)
+#if 0
+ // TODO: improve member variables - current ones are the same as lexer token
+ // Token kind.
+ TokenId token_id;
+ // Token location.
+ Location locus;
+ // Associated text (if any) of token.
+ std::string str;
+ // Token type hint (if any).
+ PrimitiveCoreType type_hint;
+#endif
+
+ const_TokenPtr tok_ref;
+
+ /* new idea: wrapper around const_TokenPtr used for heterogeneuous storage in
+ * token trees. rather than convert back and forth when parsing macros, just
+ * wrap it. */
+
+public:
+ // Unique pointer custom clone function
+ std::unique_ptr<Token> clone_token () const
+ {
+ return std::unique_ptr<Token> (clone_token_impl ());
+ }
+
+#if 0
+ /* constructor from general text - avoid using if lexer const_TokenPtr is
+ * available */
+ Token (TokenId token_id, Location locus, std::string str,
+ PrimitiveCoreType type_hint)
+ : token_id (token_id), locus (locus), str (std::move (str)),
+ type_hint (type_hint)
+ {}
+#endif
+ // not doable with new implementation - will have to make a const_TokenPtr
+
+ // Constructor from lexer const_TokenPtr
+#if 0
+ /* TODO: find workaround for std::string being nullptr - probably have to
+ * introduce new method in lexer Token, or maybe make conversion method
+ * there */
+ Token (const_TokenPtr lexer_token_ptr)
+ : token_id (lexer_token_ptr->get_id ()),
+ locus (lexer_token_ptr->get_locus ()), str (""),
+ type_hint (lexer_token_ptr->get_type_hint ())
+ {
+ // FIXME: change to "should have str" later?
+ if (lexer_token_ptr->has_str ())
+ {
+ str = lexer_token_ptr->get_str ();
+
+ // DEBUG
+ rust_debug ("ast token created with str '%s'", str.c_str ());
+ }
+ else
+ {
+ // FIXME: is this returning correct thing?
+ str = lexer_token_ptr->get_token_description ();
+
+ // DEBUG
+ rust_debug ("ast token created with string '%s'", str.c_str ());
+ }
+
+ // DEBUG
+ if (lexer_token_ptr->should_have_str () && !lexer_token_ptr->has_str ())
+ {
+ rust_debug (
+ "BAD: for token '%s', should have string but does not!",
+ lexer_token_ptr->get_token_description ());
+ }
+ }
+#endif
+ Token (const_TokenPtr lexer_tok_ptr) : tok_ref (std::move (lexer_tok_ptr)) {}
+
+ bool is_string_lit () const
+ {
+ switch (get_id ())
+ {
+ case STRING_LITERAL:
+ case BYTE_STRING_LITERAL:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ std::string as_string () const override;
+ Location get_match_locus () const override { return tok_ref->get_locus (); };
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Return copy of itself but in token stream form.
+ std::vector<std::unique_ptr<Token> > to_token_stream () const override;
+
+ TokenId get_id () const { return tok_ref->get_id (); }
+ const std::string &get_str () const { return tok_ref->get_str (); }
+
+ Location get_locus () const { return tok_ref->get_locus (); }
+
+ PrimitiveCoreType get_type_hint () const { return tok_ref->get_type_hint (); }
+
+ // Get a new token pointer copy.
+ const_TokenPtr get_tok_ptr () const { return tok_ref; }
+
+ MacroMatchType get_macro_match_type () const override
+ {
+ return MacroMatchType::Tok;
+ }
+
+protected:
+ // No virtual for now as not polymorphic but can be in future
+ /*virtual*/ Token *clone_token_impl () const { return new Token (*this); }
+
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ Token *clone_token_tree_impl () const final override
+ {
+ return clone_token_impl ();
+ }
+
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ Token *clone_macro_match_impl () const final override
+ {
+ return clone_token_impl ();
+ }
+};
+
+// A literal - value with a type. Used in LiteralExpr and LiteralPattern.
+struct Literal
+{
+public:
+ enum LitType
+ {
+ CHAR,
+ STRING,
+ BYTE,
+ BYTE_STRING,
+ INT,
+ FLOAT,
+ BOOL,
+ ERROR
+ };
+
+private:
+ /* TODO: maybe make subclasses of each type of literal with their typed values
+ * (or generics) */
+ 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 ("", ERROR, PrimitiveCoreType::CORETYPE_UNKNOWN);
+ }
+
+ // Returns whether literal is in an invalid state.
+ bool is_error () const { return type == ERROR; }
+};
+
+/* Forward decl - definition moved to rust-expr.h as it requires LiteralExpr to
+ * be defined */
+class AttrInputLiteral;
+
+/* TODO: move applicable stuff into here or just don't include it because
+ * nothing uses it A segment of a path (maybe) */
+class PathSegment
+{
+public:
+ virtual ~PathSegment () {}
+
+ virtual std::string as_string () const = 0;
+
+ // TODO: add visitor here?
+};
+
+// A segment of a simple path without generic or type arguments
+class SimplePathSegment : public PathSegment
+{
+ std::string segment_name;
+ Location locus;
+ NodeId node_id;
+
+ // only allow identifiers, "super", "self", "crate", or "$crate"
+public:
+ // TODO: put checks in constructor to enforce this rule?
+ SimplePathSegment (std::string segment_name, Location locus)
+ : segment_name (std::move (segment_name)), locus (locus),
+ node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ /* Returns whether simple path segment is in an invalid state (currently, if
+ * empty). */
+ bool is_error () const { return segment_name.empty (); }
+
+ // Creates an error SimplePathSegment
+ static SimplePathSegment create_error ()
+ {
+ return SimplePathSegment (std::string (""), Location ());
+ }
+
+ std::string as_string () const override;
+
+ Location get_locus () const { return locus; }
+ NodeId get_node_id () const { return node_id; }
+ const std::string &get_segment_name () const { return segment_name; }
+ bool is_super_path_seg () const
+ {
+ return as_string ().compare ("super") == 0;
+ }
+ bool is_crate_path_seg () const
+ {
+ return as_string ().compare ("crate") == 0;
+ }
+ bool is_lower_self () const { return as_string ().compare ("self") == 0; }
+ bool is_big_self () const { return as_string ().compare ("Self") == 0; }
+};
+
+// A simple path without generic or type arguments
+class SimplePath
+{
+ bool has_opening_scope_resolution;
+ std::vector<SimplePathSegment> segments;
+ Location locus;
+ NodeId node_id;
+
+public:
+ // Constructor
+ SimplePath (std::vector<SimplePathSegment> path_segments,
+ bool has_opening_scope_resolution = false,
+ Location locus = Location ())
+ : has_opening_scope_resolution (has_opening_scope_resolution),
+ segments (std::move (path_segments)), locus (locus),
+ node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ // Creates an empty SimplePath.
+ static SimplePath create_empty ()
+ {
+ return SimplePath (std::vector<SimplePathSegment> ());
+ }
+
+ // Returns whether the SimplePath is empty, i.e. has path segments.
+ bool is_empty () const { return segments.empty (); }
+
+ std::string as_string () const;
+
+ Location get_locus () const { return locus; }
+ NodeId get_node_id () const { return node_id; }
+
+ // does this need visitor if not polymorphic? probably not
+
+ // path-to-string comparison operator
+ bool operator== (const std::string &rhs) const
+ {
+ return !has_opening_scope_resolution && segments.size () == 1
+ && segments[0].as_string () == rhs;
+ }
+
+ /* Creates a single-segment SimplePath from a string. This will not check to
+ * ensure that this is a valid identifier in path, so be careful. Also, this
+ * will have no location data.
+ * TODO have checks? */
+ static SimplePath from_str (std::string str, Location locus)
+ {
+ std::vector<AST::SimplePathSegment> single_segments
+ = {AST::SimplePathSegment (std::move (str), locus)};
+ return SimplePath (std::move (single_segments));
+ }
+
+ const std::vector<SimplePathSegment> &get_segments () const
+ {
+ return segments;
+ }
+
+ std::vector<SimplePathSegment> &get_segments () { return segments; }
+};
+
+// path-to-string inverse comparison operator
+inline bool
+operator!= (const SimplePath &lhs, const std::string &rhs)
+{
+ return !(lhs == rhs);
+}
+
+// forward decl for Attribute
+class AttrInput;
+
+// aka Attr
+// Attribute AST representation
+struct Attribute
+{
+private:
+ SimplePath path;
+
+ // bool has_attr_input;
+ std::unique_ptr<AttrInput> attr_input;
+
+ Location locus;
+
+ // TODO: maybe a variable storing whether attr input is parsed or not
+
+public:
+ // Returns whether Attribute has AttrInput
+ bool has_attr_input () const { return attr_input != nullptr; }
+
+ // Constructor has pointer AttrInput for polymorphism reasons
+ Attribute (SimplePath path, std::unique_ptr<AttrInput> input,
+ Location locus = Location ())
+ : path (std::move (path)), attr_input (std::move (input)), locus (locus)
+ {}
+
+ // default destructor
+ ~Attribute () = default;
+
+ // no point in being defined inline as requires virtual call anyway
+ Attribute (const Attribute &other);
+
+ // no point in being defined inline as requires virtual call anyway
+ Attribute &operator= (const Attribute &other);
+
+ // default move semantics
+ Attribute (Attribute &&other) = default;
+ Attribute &operator= (Attribute &&other) = default;
+
+ // Unique pointer custom clone function
+ std::unique_ptr<Attribute> clone_attribute () const
+ {
+ return std::unique_ptr<Attribute> (clone_attribute_impl ());
+ }
+
+ // Creates an empty attribute (which is invalid)
+ static Attribute create_empty ()
+ {
+ return Attribute (SimplePath::create_empty (), nullptr);
+ }
+
+ // Returns whether the attribute is considered an "empty" attribute.
+ bool is_empty () const { return attr_input == nullptr && path.is_empty (); }
+
+ Location get_locus () const { return locus; }
+
+ AttrInput &get_attr_input () const { return *attr_input; }
+
+ /* e.g.:
+ #![crate_type = "lib"]
+ #[test]
+ #[cfg(target_os = "linux")]
+ #[allow(non_camel_case_types)]
+ #![allow(unused_variables)]
+ */
+
+ // Full built-in attribute list:
+ /* cfg
+ * cfg_attr
+ * test
+ * ignore
+ * should_panic
+ * derive
+ * macro_export
+ * macro_use
+ * proc_macro
+ * proc_macro_derive
+ * proc_macro_attribute
+ * allow
+ * warn
+ * deny
+ * forbid
+ * deprecated
+ * must_use
+ * link
+ * link_name
+ * no_link
+ * repr
+ * crate_type
+ * no_main
+ * export_name
+ * link_section
+ * no_mangle
+ * used
+ * crate_name
+ * inline
+ * cold
+ * no_builtins
+ * target_feature
+ * doc
+ * no_std
+ * no_implicit_prelude
+ * path
+ * recursion_limit
+ * type_length_limit
+ * panic_handler
+ * global_allocator
+ * windows_subsystem
+ * feature */
+
+ std::string as_string () const;
+
+ // no visitor pattern as not currently polymorphic
+
+ const SimplePath &get_path () const { return path; }
+ SimplePath &get_path () { return path; }
+
+ // Call to parse attribute body to meta item syntax.
+ void parse_attr_to_meta_item ();
+
+ /* Determines whether cfg predicate is true and item with attribute should not
+ * be stripped. Attribute body must already be parsed to meta item. */
+ bool check_cfg_predicate (const Session &session) const;
+
+ // Returns whether body has been parsed to meta item form or not.
+ bool is_parsed_to_meta_item () const;
+
+ /* Returns any attributes generated from cfg_attr attributes. Attribute body
+ * must already be parsed to meta item. */
+ std::vector<Attribute> separate_cfg_attrs () const;
+
+protected:
+ // not virtual as currently no subclasses of Attribute, but could be in future
+ /*virtual*/ Attribute *clone_attribute_impl () const
+ {
+ return new Attribute (*this);
+ }
+};
+
+// Attribute body - abstract base class
+class AttrInput
+{
+public:
+ enum AttrInputType
+ {
+ LITERAL,
+ META_ITEM,
+ TOKEN_TREE,
+ };
+
+ virtual ~AttrInput () {}
+
+ // Unique pointer custom clone function
+ std::unique_ptr<AttrInput> clone_attr_input () const
+ {
+ return std::unique_ptr<AttrInput> (clone_attr_input_impl ());
+ }
+
+ virtual std::string as_string () const = 0;
+
+ virtual void accept_vis (ASTVisitor &vis) = 0;
+
+ virtual bool check_cfg_predicate (const Session &session) const = 0;
+
+ // Parse attribute input to meta item, if possible
+ virtual AttrInput *parse_to_meta_item () const { return nullptr; }
+
+ virtual std::vector<Attribute> separate_cfg_attrs () const { return {}; }
+
+ // Returns whether attr input has been parsed to meta item syntax.
+ virtual bool is_meta_item () const = 0;
+
+ virtual AttrInputType get_attr_input_type () const = 0;
+
+protected:
+ // pure virtual clone implementation
+ virtual AttrInput *clone_attr_input_impl () const = 0;
+};
+
+// Forward decl - defined in rust-macro.h
+class MetaNameValueStr;
+
+// abstract base meta item inner class
+class MetaItemInner
+{
+protected:
+ // pure virtual as MetaItemInner
+ virtual MetaItemInner *clone_meta_item_inner_impl () const = 0;
+
+public:
+ // Unique pointer custom clone function
+ std::unique_ptr<MetaItemInner> clone_meta_item_inner () const
+ {
+ return std::unique_ptr<MetaItemInner> (clone_meta_item_inner_impl ());
+ }
+
+ virtual ~MetaItemInner ();
+
+ virtual std::string as_string () const = 0;
+
+ virtual void accept_vis (ASTVisitor &vis) = 0;
+
+ /* HACK: used to simplify parsing - creates a copy of that type, or returns
+ * null */
+ virtual std::unique_ptr<MetaNameValueStr> to_meta_name_value_str () const;
+
+ // HACK: used to simplify parsing - same thing
+ virtual SimplePath to_path_item () const
+ {
+ return SimplePath::create_empty ();
+ }
+
+ virtual Attribute to_attribute () const { return Attribute::create_empty (); }
+
+ virtual bool check_cfg_predicate (const Session &session) const = 0;
+
+ virtual bool is_key_value_pair () const { return false; }
+};
+
+// Container used to store MetaItems as AttrInput (bridge-ish kinda thing)
+class AttrInputMetaItemContainer : public AttrInput
+{
+ std::vector<std::unique_ptr<MetaItemInner> > items;
+
+public:
+ AttrInputMetaItemContainer (
+ std::vector<std::unique_ptr<MetaItemInner> > items)
+ : items (std::move (items))
+ {}
+
+ // copy constructor with vector clone
+ AttrInputMetaItemContainer (const AttrInputMetaItemContainer &other)
+ {
+ items.reserve (other.items.size ());
+ for (const auto &e : other.items)
+ items.push_back (e->clone_meta_item_inner ());
+ }
+
+ // copy assignment operator with vector clone
+ AttrInputMetaItemContainer &
+ operator= (const AttrInputMetaItemContainer &other)
+ {
+ AttrInput::operator= (other);
+
+ items.reserve (other.items.size ());
+ for (const auto &e : other.items)
+ items.push_back (e->clone_meta_item_inner ());
+
+ return *this;
+ }
+
+ // default move constructors
+ AttrInputMetaItemContainer (AttrInputMetaItemContainer &&other) = default;
+ AttrInputMetaItemContainer &operator= (AttrInputMetaItemContainer &&other)
+ = default;
+
+ std::string as_string () const override;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ bool check_cfg_predicate (const Session &session) const override;
+
+ AttrInputType get_attr_input_type () const final override
+ {
+ return AttrInput::AttrInputType::META_ITEM;
+ }
+
+ // Clones this object.
+ std::unique_ptr<AttrInputMetaItemContainer>
+ clone_attr_input_meta_item_container () const
+ {
+ return std::unique_ptr<AttrInputMetaItemContainer> (
+ clone_attr_input_meta_item_container_impl ());
+ }
+
+ std::vector<Attribute> separate_cfg_attrs () const override;
+
+ bool is_meta_item () const override { return true; }
+
+ // TODO: this mutable getter seems dodgy
+ std::vector<std::unique_ptr<MetaItemInner> > &get_items () { return items; }
+ const std::vector<std::unique_ptr<MetaItemInner> > &get_items () const
+ {
+ return items;
+ }
+
+protected:
+ // Use covariance to implement clone function as returning this type
+ AttrInputMetaItemContainer *clone_attr_input_impl () const final override
+ {
+ return clone_attr_input_meta_item_container_impl ();
+ }
+
+ AttrInputMetaItemContainer *clone_attr_input_meta_item_container_impl () const
+ {
+ return new AttrInputMetaItemContainer (*this);
+ }
+};
+
+// A token tree with delimiters
+class DelimTokenTree : public TokenTree, public AttrInput
+{
+ DelimType delim_type;
+ std::vector<std::unique_ptr<TokenTree> > token_trees;
+ Location locus;
+
+protected:
+ DelimTokenTree *clone_delim_tok_tree_impl () const
+ {
+ return new DelimTokenTree (*this);
+ }
+
+ /* Use covariance to implement clone function as returning a DelimTokenTree
+ * object */
+ DelimTokenTree *clone_attr_input_impl () const final override
+ {
+ return clone_delim_tok_tree_impl ();
+ }
+
+ /* Use covariance to implement clone function as returning a DelimTokenTree
+ * object */
+ DelimTokenTree *clone_token_tree_impl () const final override
+ {
+ return clone_delim_tok_tree_impl ();
+ }
+
+public:
+ DelimTokenTree (DelimType delim_type,
+ std::vector<std::unique_ptr<TokenTree> > token_trees
+ = std::vector<std::unique_ptr<TokenTree> > (),
+ Location locus = Location ())
+ : delim_type (delim_type), token_trees (std::move (token_trees)),
+ locus (locus)
+ {}
+
+ // Copy constructor with vector clone
+ DelimTokenTree (DelimTokenTree const &other)
+ : delim_type (other.delim_type), locus (other.locus)
+ {
+ token_trees.reserve (other.token_trees.size ());
+ for (const auto &e : other.token_trees)
+ token_trees.push_back (e->clone_token_tree ());
+ }
+
+ // overloaded assignment operator with vector clone
+ DelimTokenTree &operator= (DelimTokenTree const &other)
+ {
+ delim_type = other.delim_type;
+ locus = other.locus;
+
+ token_trees.reserve (other.token_trees.size ());
+ for (const auto &e : other.token_trees)
+ token_trees.push_back (e->clone_token_tree ());
+
+ return *this;
+ }
+
+ // move constructors
+ DelimTokenTree (DelimTokenTree &&other) = default;
+ DelimTokenTree &operator= (DelimTokenTree &&other) = default;
+
+ static DelimTokenTree create_empty () { return DelimTokenTree (PARENS); }
+
+ std::string as_string () const override;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ bool check_cfg_predicate (const Session &) const override
+ {
+ // this should never be called - should be converted first
+ rust_assert (false);
+ return false;
+ }
+
+ AttrInputMetaItemContainer *parse_to_meta_item () const override;
+
+ std::vector<std::unique_ptr<Token> > to_token_stream () const override;
+
+ std::unique_ptr<DelimTokenTree> clone_delim_token_tree () const
+ {
+ return std::unique_ptr<DelimTokenTree> (clone_delim_tok_tree_impl ());
+ }
+
+ bool is_meta_item () const override { return false; }
+
+ AttrInputType get_attr_input_type () const final override
+ {
+ return AttrInput::AttrInputType::TOKEN_TREE;
+ }
+
+ std::vector<std::unique_ptr<TokenTree> > &get_token_trees ()
+ {
+ return token_trees;
+ }
+
+ DelimType get_delim_type () const { return delim_type; }
+};
+
+/* Forward decl - definition moved to rust-expr.h as it requires LiteralExpr to
+ * be defined */
+class AttrInputLiteral;
+
+// abstract base meta item class
+class MetaItem : public MetaItemInner
+{
+};
+
+// Forward decl - defined in rust-expr.h
+class MetaItemLitExpr;
+
+// Forward decl - defined in rust-expr.h
+class MetaItemPathLit;
+
+// Forward decl - defined in rust-macro.h
+class MetaItemPath;
+
+// Forward decl - defined in rust-macro.h
+class MetaItemSeq;
+
+// Forward decl - defined in rust-macro.h
+class MetaWord;
+
+// Forward decl - defined in rust-macro.h
+class MetaListPaths;
+
+// Forward decl - defined in rust-macro.h
+class MetaListNameValueStr;
+
+/* 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 ());
+ }
+
+ virtual ~Stmt () {}
+
+ virtual std::string as_string () const = 0;
+
+ virtual void accept_vis (ASTVisitor &vis) = 0;
+
+ virtual Location get_locus () const = 0;
+
+ virtual void mark_for_strip () = 0;
+ virtual bool is_marked_for_strip () const = 0;
+ NodeId get_node_id () const { return node_id; }
+
+ virtual bool is_item () const = 0;
+
+protected:
+ Stmt () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {}
+
+ // Clone function implementation as pure virtual method
+ virtual Stmt *clone_stmt_impl () const = 0;
+
+ NodeId node_id;
+};
+
+// Rust "item" AST node (declaration of top-level/module-level allowed stuff)
+class Item : public Stmt
+{
+public:
+ // Unique pointer custom clone function
+ std::unique_ptr<Item> clone_item () const
+ {
+ return std::unique_ptr<Item> (clone_item_impl ());
+ }
+
+ /* Adds crate names to the vector passed by reference, if it can
+ * (polymorphism). TODO: remove, unused. */
+ virtual void
+ add_crate_name (std::vector<std::string> &names ATTRIBUTE_UNUSED) const
+ {}
+
+ // FIXME: ARTHUR: Is it okay to have removed that final? Is it *required*
+ // behavior that we have items that can also be expressions?
+ bool is_item () const override { return true; }
+
+protected:
+ // 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 final override { return clone_item_impl (); }
+};
+
+// forward decl of ExprWithoutBlock
+class ExprWithoutBlock;
+
+// Base expression AST node - abstract
+class Expr : public Node
+{
+public:
+ // Unique pointer custom clone function
+ std::unique_ptr<Expr> clone_expr () const
+ {
+ return std::unique_ptr<Expr> (clone_expr_impl ());
+ }
+
+ /* TODO: public methods that could be useful:
+ * - get_type() - returns type of expression. set_type() may also be useful
+ * for some?
+ * - evaluate() - evaluates expression if constant? can_evaluate()? */
+
+ /* HACK: downcasting without dynamic_cast (if possible) via polymorphism -
+ * overrided in subclasses of ExprWithoutBlock */
+ virtual ExprWithoutBlock *as_expr_without_block () const { return nullptr; }
+
+ virtual std::string as_string () const = 0;
+
+ virtual ~Expr () {}
+
+ virtual Location get_locus () const = 0;
+
+ // HACK: strictly not needed, but faster than full downcast clone
+ virtual bool is_expr_without_block () const = 0;
+
+ virtual void accept_vis (ASTVisitor &vis) = 0;
+
+ virtual void mark_for_strip () = 0;
+ virtual bool is_marked_for_strip () const = 0;
+
+ virtual NodeId get_node_id () const { return node_id; }
+
+ virtual void set_node_id (NodeId id) { node_id = id; }
+
+protected:
+ // Constructor
+ Expr () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {}
+
+ // Clone function implementation as pure virtual method
+ virtual Expr *clone_expr_impl () const = 0;
+
+ // TODO: think of less hacky way to implement this kind of thing
+ // Sets outer attributes.
+ virtual void set_outer_attrs (std::vector<Attribute>) = 0;
+
+ NodeId node_id;
+};
+
+// AST node for an expression without an accompanying block - abstract
+class ExprWithoutBlock : public Expr
+{
+protected:
+ // 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 final override
+ {
+ return clone_expr_without_block_impl ();
+ }
+
+ bool is_expr_without_block () const final override { return true; };
+
+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 final override
+ {
+ return clone_expr_without_block_impl ();
+ }
+
+ virtual ExprWithoutBlock *to_stmt () const { return clone_expr_impl (); }
+};
+
+/* HACK: IdentifierExpr, delete when figure out identifier vs expr problem in
+ * Pratt parser */
+/* Alternatively, identifiers could just be represented as single-segment paths
+ */
+class IdentifierExpr : public ExprWithoutBlock
+{
+ std::vector<Attribute> outer_attrs;
+ Identifier ident;
+ Location locus;
+
+public:
+ IdentifierExpr (Identifier ident, std::vector<Attribute> outer_attrs,
+ Location locus)
+ : outer_attrs (std::move (outer_attrs)), ident (std::move (ident)),
+ locus (locus)
+ {}
+
+ std::string as_string () const override { return ident; }
+
+ Location get_locus () const override final { return locus; }
+
+ Identifier get_ident () const { return ident; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Clones this object.
+ std::unique_ptr<IdentifierExpr> clone_identifier_expr () const
+ {
+ return std::unique_ptr<IdentifierExpr> (clone_identifier_expr_impl ());
+ }
+
+ // "Error state" if ident is empty, so base stripping on this.
+ void mark_for_strip () override { ident = {}; }
+ bool is_marked_for_strip () const override { return ident.empty (); }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+
+protected:
+ // Clone method implementation
+ IdentifierExpr *clone_expr_without_block_impl () const final override
+ {
+ return clone_identifier_expr_impl ();
+ }
+
+ IdentifierExpr *clone_identifier_expr_impl () const
+ {
+ return new IdentifierExpr (*this);
+ }
+};
+
+// Pattern base AST node
+class Pattern
+{
+public:
+ // 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 (ASTVisitor &vis) = 0;
+
+ // as only one kind of pattern can be stripped, have default of nothing
+ virtual void mark_for_strip () {}
+ virtual bool is_marked_for_strip () const { return false; }
+
+ virtual Location get_locus () const = 0;
+ virtual NodeId get_pattern_node_id () 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 AST - 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 () {}
+
+ 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) const { return nullptr; }
+ /* as pointer, shouldn't require definition beforehand, only forward
+ * declaration. */
+
+ virtual void accept_vis (ASTVisitor &vis) = 0;
+
+ // as only two kinds of types can be stripped, have default of nothing
+ virtual void mark_for_strip () {}
+ virtual bool is_marked_for_strip () const { return false; }
+
+ virtual Location get_locus () const = 0;
+
+ NodeId get_node_id () const { return node_id; }
+
+protected:
+ Type () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {}
+
+ // Clone function implementation as pure virtual method
+ virtual Type *clone_type_impl () const = 0;
+
+ NodeId node_id;
+};
+
+// 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:
+ // 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 final override
+ {
+ return clone_type_no_bounds_impl ();
+ }
+
+ TypeNoBounds () : Type () {}
+};
+
+/* Abstract base class representing a type param bound - Lifetime and TraitBound
+ * extends it */
+class TypeParamBound
+{
+public:
+ 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 (ASTVisitor &vis) = 0;
+
+ NodeId get_node_id () const { return node_id; }
+
+ virtual Location get_locus () const = 0;
+
+protected:
+ // Clone function implementation as pure virtual method
+ virtual TypeParamBound *clone_type_param_bound_impl () const = 0;
+
+ TypeParamBound (NodeId node_id) : node_id (node_id) {}
+
+ NodeId node_id;
+};
+
+// Represents a lifetime (and is also a kind of type param bound)
+class Lifetime : public TypeParamBound
+{
+public:
+ enum LifetimeType
+ {
+ NAMED, // corresponds to LIFETIME_OR_LABEL
+ STATIC, // corresponds to 'static
+ WILDCARD // corresponds to '_
+ };
+
+private:
+ LifetimeType lifetime_type;
+ std::string lifetime_name;
+ Location locus;
+ NodeId node_id;
+
+public:
+ // Constructor
+ Lifetime (LifetimeType type, std::string name = std::string (),
+ Location locus = Location ())
+ : TypeParamBound (Analysis::Mappings::get ()->get_next_node_id ()),
+ lifetime_type (type), lifetime_name (std::move (name)), locus (locus)
+ {}
+
+ Lifetime (NodeId id, LifetimeType type, std::string name = std::string (),
+ Location locus = Location ())
+ : TypeParamBound (id), lifetime_type (type),
+ lifetime_name (std::move (name)), locus (locus)
+ {}
+
+ // Creates an "error" lifetime.
+ static Lifetime error () { return Lifetime (NAMED, ""); }
+
+ // Returns true if the lifetime is in an error state.
+ bool is_error () const
+ {
+ return lifetime_type == NAMED && lifetime_name.empty ();
+ }
+
+ std::string as_string () const override;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ LifetimeType get_lifetime_type () { return lifetime_type; }
+
+ Location get_locus () const override final { return locus; }
+
+ std::string get_lifetime_name () const { return lifetime_name; }
+
+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 (node_id, lifetime_type, lifetime_name, locus);
+ }
+};
+
+/* Base generic parameter in AST. Abstract - can be represented by a Lifetime or
+ * Type param */
+class GenericParam
+{
+public:
+ enum class Kind
+ {
+ Lifetime,
+ Type,
+ Const,
+ };
+
+ virtual ~GenericParam () {}
+
+ // 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 (ASTVisitor &vis) = 0;
+
+ virtual Location get_locus () const = 0;
+
+ virtual Kind get_kind () const = 0;
+
+ NodeId get_node_id () { return node_id; }
+
+protected:
+ GenericParam () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {}
+ GenericParam (NodeId node_id) : node_id (node_id) {}
+
+ // Clone function implementation as pure virtual method
+ virtual GenericParam *clone_generic_param_impl () const = 0;
+
+ NodeId node_id;
+};
+
+// A lifetime generic parameter (as opposed to a type generic parameter)
+class LifetimeParam : public GenericParam
+{
+ Lifetime lifetime;
+ std::vector<Lifetime> lifetime_bounds;
+ Attribute outer_attr;
+ Location locus;
+
+public:
+ Lifetime get_lifetime () const { 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 (); }
+
+ // Creates an error state lifetime param.
+ static LifetimeParam create_error ()
+ {
+ return LifetimeParam (Lifetime::error (), {}, Attribute::create_empty (),
+ Location ());
+ }
+
+ // Returns whether the lifetime param is in an error state.
+ bool is_error () const { return lifetime.is_error (); }
+
+ // Constructor
+ LifetimeParam (Lifetime lifetime, std::vector<Lifetime> lifetime_bounds,
+ Attribute outer_attr, Location locus)
+ : lifetime (std::move (lifetime)),
+ lifetime_bounds (std::move (lifetime_bounds)),
+ outer_attr (std::move (outer_attr)), locus (locus)
+ {}
+
+ std::string as_string () const override;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ Location get_locus () const override final { return locus; }
+
+ Kind get_kind () const override final { return Kind::Lifetime; }
+
+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);
+ }
+};
+
+// A macro item AST node - abstract base class
+class MacroItem : public Item
+{
+};
+
+// Item used in trait declarations - abstract base class
+class TraitItem
+{
+protected:
+ TraitItem () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {}
+
+ // Clone function implementation as pure virtual method
+ virtual TraitItem *clone_trait_item_impl () const = 0;
+
+ NodeId node_id;
+
+public:
+ virtual ~TraitItem () {}
+
+ // Unique pointer custom clone function
+ 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 (ASTVisitor &vis) = 0;
+
+ virtual void mark_for_strip () = 0;
+ virtual bool is_marked_for_strip () const = 0;
+
+ NodeId get_node_id () const { return node_id; }
+};
+
+/* Abstract base class for items used within an inherent impl block (the impl
+ * name {} one) */
+class InherentImplItem
+{
+protected:
+ // Clone function implementation as pure virtual method
+ virtual InherentImplItem *clone_inherent_impl_item_impl () const = 0;
+
+public:
+ virtual ~InherentImplItem () {}
+
+ // Unique pointer custom clone function
+ std::unique_ptr<InherentImplItem> clone_inherent_impl_item () const
+ {
+ return std::unique_ptr<InherentImplItem> (clone_inherent_impl_item_impl ());
+ }
+
+ virtual std::string as_string () const = 0;
+
+ virtual void accept_vis (ASTVisitor &vis) = 0;
+
+ virtual void mark_for_strip () = 0;
+ virtual bool is_marked_for_strip () const = 0;
+
+ virtual Location get_locus () const = 0;
+};
+
+// Abstract base class for items used in a trait impl
+class TraitImplItem
+{
+protected:
+ virtual TraitImplItem *clone_trait_impl_item_impl () const = 0;
+
+public:
+ virtual ~TraitImplItem (){};
+
+ // Unique pointer custom clone function
+ std::unique_ptr<TraitImplItem> clone_trait_impl_item () const
+ {
+ return std::unique_ptr<TraitImplItem> (clone_trait_impl_item_impl ());
+ }
+
+ virtual std::string as_string () const = 0;
+
+ virtual void accept_vis (ASTVisitor &vis) = 0;
+
+ virtual void mark_for_strip () = 0;
+ virtual bool is_marked_for_strip () const = 0;
+};
+
+// Abstract base class for an item used inside an extern block
+class ExternalItem
+{
+public:
+ ExternalItem () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {}
+
+ virtual ~ExternalItem () {}
+
+ // Unique pointer custom clone function
+ std::unique_ptr<ExternalItem> clone_external_item () const
+ {
+ return std::unique_ptr<ExternalItem> (clone_external_item_impl ());
+ }
+
+ virtual std::string as_string () const = 0;
+
+ virtual void accept_vis (ASTVisitor &vis) = 0;
+
+ virtual void mark_for_strip () = 0;
+ virtual bool is_marked_for_strip () const = 0;
+
+ NodeId get_node_id () const { return node_id; }
+
+protected:
+ // Clone function implementation as pure virtual method
+ virtual ExternalItem *clone_external_item_impl () const = 0;
+
+ NodeId node_id;
+};
+
+/* Data structure to store the data used in macro invocations and macro
+ * invocations with semicolons. */
+struct MacroInvocData
+{
+private:
+ SimplePath path;
+ DelimTokenTree token_tree;
+
+ // One way of parsing the macro. Probably not applicable for all macros.
+ std::vector<std::unique_ptr<MetaItemInner> > parsed_items;
+ bool parsed_to_meta_item = false;
+
+public:
+ std::string as_string () const;
+
+ MacroInvocData (SimplePath path, DelimTokenTree token_tree)
+ : path (std::move (path)), token_tree (std::move (token_tree))
+ {}
+
+ // Copy constructor with vector clone
+ MacroInvocData (const MacroInvocData &other)
+ : path (other.path), token_tree (other.token_tree),
+ parsed_to_meta_item (other.parsed_to_meta_item)
+ {
+ parsed_items.reserve (other.parsed_items.size ());
+ for (const auto &e : other.parsed_items)
+ parsed_items.push_back (e->clone_meta_item_inner ());
+ }
+
+ // Copy assignment operator with vector clone
+ MacroInvocData &operator= (const MacroInvocData &other)
+ {
+ path = other.path;
+ token_tree = other.token_tree;
+ parsed_to_meta_item = other.parsed_to_meta_item;
+
+ parsed_items.reserve (other.parsed_items.size ());
+ for (const auto &e : other.parsed_items)
+ parsed_items.push_back (e->clone_meta_item_inner ());
+
+ return *this;
+ }
+
+ // Move constructors
+ MacroInvocData (MacroInvocData &&other) = default;
+ MacroInvocData &operator= (MacroInvocData &&other) = default;
+
+ // Invalid if path is empty, so base stripping on that.
+ void mark_for_strip () { path = SimplePath::create_empty (); }
+ bool is_marked_for_strip () const { return path.is_empty (); }
+
+ // Returns whether the macro has been parsed already.
+ bool is_parsed () const { return parsed_to_meta_item; }
+ // TODO: update on other ways of parsing it
+
+ // TODO: this mutable getter seems kinda dodgy
+ DelimTokenTree &get_delim_tok_tree () { return token_tree; }
+ const DelimTokenTree &get_delim_tok_tree () const { return token_tree; }
+
+ // TODO: this mutable getter seems kinda dodgy
+ SimplePath &get_path () { return path; }
+ const SimplePath &get_path () const { return path; }
+
+ void
+ set_meta_item_output (std::vector<std::unique_ptr<MetaItemInner> > new_items)
+ {
+ parsed_items = std::move (new_items);
+ }
+ // TODO: mutable getter seems kinda dodgy
+ std::vector<std::unique_ptr<MetaItemInner> > &get_meta_items ()
+ {
+ return parsed_items;
+ }
+ const std::vector<std::unique_ptr<MetaItemInner> > &get_meta_items () const
+ {
+ return parsed_items;
+ }
+};
+
+class SingleASTNode
+{
+public:
+ enum NodeType
+ {
+ EXPRESSION,
+ ITEM,
+ STMT,
+ EXTERN,
+ TRAIT,
+ IMPL,
+ TRAIT_IMPL,
+ TYPE,
+ };
+
+private:
+ NodeType kind;
+
+ // FIXME make this a union
+ std::unique_ptr<Expr> expr;
+ std::unique_ptr<Item> item;
+ std::unique_ptr<Stmt> stmt;
+ std::unique_ptr<ExternalItem> external_item;
+ std::unique_ptr<TraitItem> trait_item;
+ std::unique_ptr<InherentImplItem> impl_item;
+ std::unique_ptr<TraitImplItem> trait_impl_item;
+ std::unique_ptr<Type> type;
+
+public:
+ SingleASTNode (std::unique_ptr<Expr> expr)
+ : kind (EXPRESSION), expr (std::move (expr))
+ {}
+
+ SingleASTNode (std::unique_ptr<Item> item)
+ : kind (ITEM), item (std::move (item))
+ {}
+
+ SingleASTNode (std::unique_ptr<Stmt> stmt)
+ : kind (STMT), stmt (std::move (stmt))
+ {}
+
+ SingleASTNode (std::unique_ptr<ExternalItem> item)
+ : kind (EXTERN), external_item (std::move (item))
+ {}
+
+ SingleASTNode (std::unique_ptr<TraitItem> item)
+ : kind (TRAIT), trait_item (std::move (item))
+ {}
+
+ SingleASTNode (std::unique_ptr<InherentImplItem> item)
+ : kind (IMPL), impl_item (std::move (item))
+ {}
+
+ SingleASTNode (std::unique_ptr<TraitImplItem> trait_impl_item)
+ : kind (TRAIT_IMPL), trait_impl_item (std::move (trait_impl_item))
+ {}
+
+ SingleASTNode (std::unique_ptr<Type> type)
+ : kind (TYPE), type (std::move (type))
+ {}
+
+ SingleASTNode (SingleASTNode const &other)
+ {
+ kind = other.kind;
+ switch (kind)
+ {
+ case EXPRESSION:
+ expr = other.expr->clone_expr ();
+ break;
+
+ case ITEM:
+ item = other.item->clone_item ();
+ break;
+
+ case STMT:
+ stmt = other.stmt->clone_stmt ();
+ break;
+
+ case EXTERN:
+ external_item = other.external_item->clone_external_item ();
+ break;
+
+ case TRAIT:
+ trait_item = other.trait_item->clone_trait_item ();
+ break;
+
+ case IMPL:
+ impl_item = other.impl_item->clone_inherent_impl_item ();
+ break;
+
+ case TRAIT_IMPL:
+ trait_impl_item = other.trait_impl_item->clone_trait_impl_item ();
+ break;
+
+ case TYPE:
+ type = other.type->clone_type ();
+ break;
+ }
+ }
+
+ SingleASTNode operator= (SingleASTNode const &other)
+ {
+ kind = other.kind;
+ switch (kind)
+ {
+ case EXPRESSION:
+ expr = other.expr->clone_expr ();
+ break;
+
+ case ITEM:
+ item = other.item->clone_item ();
+ break;
+
+ case STMT:
+ stmt = other.stmt->clone_stmt ();
+ break;
+
+ case EXTERN:
+ external_item = other.external_item->clone_external_item ();
+ break;
+
+ case TRAIT:
+ trait_item = other.trait_item->clone_trait_item ();
+ break;
+
+ case IMPL:
+ impl_item = other.impl_item->clone_inherent_impl_item ();
+ break;
+
+ case TRAIT_IMPL:
+ trait_impl_item = other.trait_impl_item->clone_trait_impl_item ();
+ break;
+
+ case TYPE:
+ type = other.type->clone_type ();
+ break;
+ }
+ return *this;
+ }
+
+ SingleASTNode (SingleASTNode &&other) = default;
+ SingleASTNode &operator= (SingleASTNode &&other) = default;
+
+ NodeType get_kind () const { return kind; }
+
+ std::unique_ptr<Expr> &get_expr ()
+ {
+ rust_assert (kind == EXPRESSION);
+ return expr;
+ }
+
+ std::unique_ptr<Item> &get_item ()
+ {
+ rust_assert (kind == ITEM);
+ return item;
+ }
+
+ std::unique_ptr<Stmt> &get_stmt ()
+ {
+ rust_assert (kind == STMT);
+ return stmt;
+ }
+
+ /**
+ * Access the inner nodes and take ownership of them.
+ * You can only call these functions once per node
+ */
+
+ std::unique_ptr<Stmt> take_stmt ()
+ {
+ rust_assert (!is_error ());
+ return std::move (stmt);
+ }
+
+ std::unique_ptr<Expr> take_expr ()
+ {
+ rust_assert (!is_error ());
+ return std::move (expr);
+ }
+
+ std::unique_ptr<Item> take_item ()
+ {
+ rust_assert (!is_error ());
+ return std::move (item);
+ }
+
+ std::unique_ptr<TraitItem> take_trait_item ()
+ {
+ rust_assert (!is_error ());
+ return std::move (trait_item);
+ }
+
+ std::unique_ptr<ExternalItem> take_external_item ()
+ {
+ rust_assert (!is_error ());
+ return std::move (external_item);
+ }
+
+ std::unique_ptr<InherentImplItem> take_impl_item ()
+ {
+ rust_assert (!is_error ());
+ return std::move (impl_item);
+ }
+
+ std::unique_ptr<TraitImplItem> take_trait_impl_item ()
+ {
+ rust_assert (!is_error ());
+ return std::move (trait_impl_item);
+ }
+
+ std::unique_ptr<Type> take_type ()
+ {
+ rust_assert (!is_error ());
+ return std::move (type);
+ }
+
+ void accept_vis (ASTVisitor &vis)
+ {
+ switch (kind)
+ {
+ case EXPRESSION:
+ expr->accept_vis (vis);
+ break;
+
+ case ITEM:
+ item->accept_vis (vis);
+ break;
+
+ case STMT:
+ stmt->accept_vis (vis);
+ break;
+
+ case EXTERN:
+ external_item->accept_vis (vis);
+ break;
+
+ case TRAIT:
+ trait_item->accept_vis (vis);
+ break;
+
+ case IMPL:
+ impl_item->accept_vis (vis);
+ break;
+
+ case TRAIT_IMPL:
+ trait_impl_item->accept_vis (vis);
+ break;
+
+ case TYPE:
+ type->accept_vis (vis);
+ break;
+ }
+ }
+
+ bool is_error ()
+ {
+ switch (kind)
+ {
+ case EXPRESSION:
+ return expr == nullptr;
+ case ITEM:
+ return item == nullptr;
+ case STMT:
+ return stmt == nullptr;
+ case EXTERN:
+ return external_item == nullptr;
+ case TRAIT:
+ return trait_item == nullptr;
+ case IMPL:
+ return impl_item == nullptr;
+ case TRAIT_IMPL:
+ return trait_impl_item == nullptr;
+ case TYPE:
+ return type == nullptr;
+ }
+
+ gcc_unreachable ();
+ return true;
+ }
+
+ std::string as_string ()
+ {
+ switch (kind)
+ {
+ case EXPRESSION:
+ return "Expr: " + expr->as_string ();
+ case ITEM:
+ return "Item: " + item->as_string ();
+ case STMT:
+ return "Stmt: " + stmt->as_string ();
+ case EXTERN:
+ return "External Item: " + external_item->as_string ();
+ case TRAIT:
+ return "Trait Item: " + trait_item->as_string ();
+ case IMPL:
+ return "Impl Item: " + impl_item->as_string ();
+ case TRAIT_IMPL:
+ return "Trait Impl Item: " + trait_impl_item->as_string ();
+ case TYPE:
+ return "Type: " + type->as_string ();
+ }
+
+ gcc_unreachable ();
+ return "";
+ }
+};
+
+/* Basically, a "fragment" that can be incorporated into the AST, created as
+ * a result of macro expansion. Really annoying to work with due to the fact
+ * that macros can really expand to anything. As such, horrible representation
+ * at the moment. */
+class ASTFragment
+{
+private:
+ /* basic idea: essentially, a vector of tagged unions of different AST node
+ * types. Now, this could actually be stored without a tagged union if the
+ * different AST node types had a unified parent, but that would create
+ * issues with the diamond problem or significant performance penalties. So
+ * a tagged union had to be used instead. A vector is used to represent the
+ * ability for a macro to expand to two statements, for instance. */
+
+ std::vector<SingleASTNode> nodes;
+ bool fragment_is_error;
+
+ /**
+ * We need to make a special case for Expression and Type fragments as only
+ * one Node will be extracted from the `nodes` vector
+ */
+
+ bool is_single_fragment () const { return nodes.size () == 1; }
+
+ bool is_single_fragment_kind (SingleASTNode::NodeType kind) const
+ {
+ return is_single_fragment () && nodes[0].get_kind () == kind;
+ }
+
+public:
+ ASTFragment (std::vector<SingleASTNode> nodes, bool fragment_is_error = false)
+ : nodes (std::move (nodes)), fragment_is_error (fragment_is_error)
+ {
+ if (fragment_is_error)
+ rust_assert (nodes.empty ());
+ }
+
+ ASTFragment (ASTFragment const &other)
+ : fragment_is_error (other.fragment_is_error)
+ {
+ nodes.clear ();
+ nodes.reserve (other.nodes.size ());
+ for (auto &n : other.nodes)
+ {
+ nodes.push_back (n);
+ }
+ }
+
+ ASTFragment &operator= (ASTFragment const &other)
+ {
+ fragment_is_error = other.fragment_is_error;
+ nodes.clear ();
+ nodes.reserve (other.nodes.size ());
+ for (auto &n : other.nodes)
+ {
+ nodes.push_back (n);
+ }
+
+ return *this;
+ }
+
+ static ASTFragment create_error () { return ASTFragment ({}, true); }
+
+ std::vector<SingleASTNode> &get_nodes () { return nodes; }
+ bool is_error () const { return fragment_is_error; }
+
+ bool should_expand () const { return !is_error (); }
+
+ std::unique_ptr<Expr> take_expression_fragment ()
+ {
+ rust_assert (is_single_fragment_kind (SingleASTNode::NodeType::EXPRESSION));
+ return nodes[0].take_expr ();
+ }
+
+ std::unique_ptr<Type> take_type_fragment ()
+ {
+ rust_assert (is_single_fragment_kind (SingleASTNode::NodeType::TYPE));
+ return nodes[0].take_type ();
+ }
+
+ void accept_vis (ASTVisitor &vis)
+ {
+ for (auto &node : nodes)
+ node.accept_vis (vis);
+ }
+};
+
+// A crate AST object - holds all the data for a single compilation unit
+struct Crate
+{
+ std::vector<Attribute> 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;
+
+ NodeId node_id;
+
+public:
+ // Constructor
+ Crate (std::vector<std::unique_ptr<Item> > items,
+ std::vector<Attribute> inner_attrs)
+ : inner_attrs (std::move (inner_attrs)), items (std::move (items)),
+ node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ // Copy constructor with vector clone
+ Crate (Crate const &other)
+ : inner_attrs (other.inner_attrs), node_id (other.node_id)
+ {
+ 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;
+ node_id = other.node_id;
+
+ 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;
+
+ // Delete all crate information, e.g. if fails cfg.
+ void strip_crate ()
+ {
+ inner_attrs.clear ();
+ inner_attrs.shrink_to_fit ();
+
+ items.clear ();
+ items.shrink_to_fit ();
+ // TODO: is this the best way to do this?
+ }
+
+ NodeId get_node_id () const { return node_id; }
+ const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
+};
+
+// Base path expression AST node - abstract
+class PathExpr : public ExprWithoutBlock
+{
+};
+} // namespace AST
+} // namespace Rust
+
+#endif