/* General AST-related method implementations for Rust frontend.
Copyright (C) 2009-2024 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
. */
#include "rust-ast.h"
#include "optional.h"
#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 "rust-operators.h"
#include "rust-dir-owner.h"
#include "rust-attribute-values.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 {
SingleASTNode::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 ASSOC_ITEM:
assoc_item = other.assoc_item->clone_associated_item ();
break;
case TYPE:
type = other.type->clone_type ();
break;
}
}
SingleASTNode
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 ASSOC_ITEM:
assoc_item = other.assoc_item->clone_associated_item ();
break;
case TYPE:
type = other.type->clone_type ();
break;
}
return *this;
}
void
SingleASTNode::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 ASSOC_ITEM:
assoc_item->accept_vis (vis);
break;
case TYPE:
type->accept_vis (vis);
break;
}
}
bool
SingleASTNode::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 ASSOC_ITEM:
return assoc_item == nullptr;
case TYPE:
return type == nullptr;
}
rust_unreachable ();
return true;
}
std::string
SingleASTNode::as_string () const
{
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 ASSOC_ITEM:
return "Associated Item: " + assoc_item->as_string ();
case TYPE:
return "Type: " + type->as_string ();
}
rust_unreachable ();
return "";
}
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 ();
}
bool
Attribute::is_derive () const
{
return has_attr_input () && get_path () == "derive";
}
/**
* Returns a list of traits to derive from within a given attribute.
*
* @param attrs The attributes on the item to derive
*/
std::vector>
Attribute::get_traits_to_derive ()
{
rust_assert (this->is_derive ());
this->parse_attr_to_meta_item ();
std::vector> result;
auto &input = get_attr_input ();
switch (input.get_attr_input_type ())
{
case AST::AttrInput::META_ITEM: {
auto &meta = static_cast (input);
for (auto ¤t : meta.get_items ())
{
// HACK: Find a better way to achieve the downcast.
switch (current->get_kind ())
{
case AST::MetaItemInner::Kind::MetaItem: {
// Let raw pointer go out of scope without freeing, it doesn't
// own the data anyway
auto meta_item
= static_cast (current.get ());
switch (meta_item->get_item_kind ())
{
case AST::MetaItem::ItemKind::Path: {
auto path
= static_cast (meta_item);
result.push_back (path->get_path ());
}
break;
case AST::MetaItem::ItemKind::Word: {
auto word = static_cast (meta_item);
// Convert current word to path
current
= make_unique (AST::MetaItemPath (
AST::SimplePath (word->get_ident ())));
auto path
= static_cast (current.get ());
result.push_back (path->get_path ());
}
break;
case AST::MetaItem::ItemKind::ListPaths:
case AST::MetaItem::ItemKind::NameValueStr:
case AST::MetaItem::ItemKind::PathLit:
case AST::MetaItem::ItemKind::Seq:
case AST::MetaItem::ItemKind::ListNameValueStr:
default:
gcc_unreachable ();
break;
}
}
break;
case AST::MetaItemInner::Kind::LitExpr:
default:
gcc_unreachable ();
break;
}
}
}
break;
case AST::AttrInput::TOKEN_TREE:
case AST::AttrInput::LITERAL:
case AST::AttrInput::MACRO:
rust_unreachable ();
break;
}
return result;
}
// 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;
}
const std::string
SimplePath::as_string () const
{
std::string path;
if (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:
rust_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.as_string ();
// 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.as_string ();
// 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.as_string ();
// 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
StructStruct::as_string () const
{
std::string str = VisItem::as_string ();
str += "struct " + struct_name.as_string ();
// 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";
}
rust_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.as_string ();
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.as_string ();
// 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.as_string ();
// 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.as_string ();
// 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;
}
Function::Function (Function const &other)
: VisItem (other), qualifiers (other.qualifiers),
function_name (other.function_name), where_clause (other.where_clause),
locus (other.locus), is_default (other.is_default)
{
// guard to prevent null dereference (always required)
if (other.return_type != nullptr)
return_type = other.return_type->clone_type ();
// guard to prevent null dereference (only required if error state)
if (other.has_body ())
function_body = other.function_body.value ()->clone_block_expr ();
else
function_body = tl::nullopt;
generic_params.reserve (other.generic_params.size ());
for (const auto &e : other.generic_params)
generic_params.push_back (e->clone_generic_param ());
function_params.reserve (other.function_params.size ());
for (const auto &e : other.function_params)
function_params.push_back (e->clone_param ());
}
Function &
Function::operator= (Function const &other)
{
VisItem::operator= (other);
function_name = other.function_name;
qualifiers = other.qualifiers;
where_clause = other.where_clause;
// visibility = other.visibility->clone_visibility();
// outer_attrs = other.outer_attrs;
locus = other.locus;
is_default = other.is_default;
// guard to prevent null dereference (always required)
if (other.return_type != nullptr)
return_type = other.return_type->clone_type ();
else
return_type = nullptr;
// guard to prevent null dereference (only required if error state)
if (other.has_body ())
function_body = other.function_body.value ()->clone_block_expr ();
else
function_body = tl::nullopt;
generic_params.reserve (other.generic_params.size ());
for (const auto &e : other.generic_params)
generic_params.push_back (e->clone_generic_param ());
function_params.reserve (other.function_params.size ());
for (const auto &e : other.function_params)
function_params.push_back (e->clone_param ());
return *this;
}
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.as_string ();
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";
if (has_body ())
str += function_body.value ()->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.as_string ();
// 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);
// TODO: deal with macro_2_0
str += "macro_rules!";
str += rule_name.as_string ();
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: ";
auto is_builtin = kind == InvocKind::Builtin;
if (is_builtin)
str += "[builtin] ";
else
str += "[regular] ";
str += append_attributes (outer_attrs, OUTER);
str += "\n " + invoc_data.as_string ();
str += "\n has semicolon: ";
str += has_semicolon () ? "true" : "false";
if (is_builtin)
{
str += "[PENDING EAGER INVOCATIONS]: ";
for (auto &pending : pending_eager_invocs)
{
str += pending->as_string ();
str += "\n";
}
}
return str;
}
std::string
MacroInvocData::as_string () const
{
return path.as_string () + "!" + token_tree.as_string ();
}
std::string
ExprStmt::as_string () const
{
std::string str = indent_spaces (enter) + "ExprStmt: \n";
if (expr == nullptr)
{
str += "none (this should not happen and is an error)";
}
else
{
indent_spaces (enter);
str += expr->as_string ();
if (semicolon_followed)
str += ";";
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
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
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
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.as_string ();
}
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 expr: " + else_block->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 expr: " + else_block->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
FunctionParam::as_string () const
{
// TODO: rewrite dump to allow non-literal types
return param_name->as_string () + " : " + type->as_string ();
}
void
FunctionParam::accept_vis (ASTVisitor &vis)
{
vis.visit (*this);
}
void
SelfParam::accept_vis (ASTVisitor &vis)
{
vis.visit (*this);
}
void
VariadicParam::accept_vis (ASTVisitor &vis)
{
vis.visit (*this);
}
std::string
VariadicParam::as_string () const
{
if (has_pattern ())
return get_pattern ()->as_string () + " : ...";
else
return "...";
}
std::string
FunctionQualifiers::as_string () const
{
std::string str;
if (is_async ())
str += "async ";
if (is_const ())
str += "const ";
if (is_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
MacroMatchFragment::as_string () const
{
return "$" + ident.as_string () + ": " + frag_spec.as_string ();
}
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
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.as_string ();
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;
}
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
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;
}
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
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
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.as_string () + " : " + 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.as_string ();
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.as_string () + " : " + 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
ExternalTypeItem::as_string () const
{
auto str = append_attributes (outer_attrs, OUTER);
str += "type " + item_name.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.as_string ();
// 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.as_string ();
// 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 ())
{
str += "none";
}
else
{
for (const auto ¶m : function_params)
str += "\n " + param.as_string ();
}
// 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);
if (has_name ())
str += "\n" + name;
if (is_variadic ())
str += "...";
else
str += "\n Type: " + param_type->as_string ();
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.as_string () + " : " + 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.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 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.as_string () + " : ";
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
MetaItemInner::to_meta_name_value_str () const
{
if (is_key_value_pair ())
{
auto converted_item = static_cast (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.as_string () + "(";
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.as_string () + "(";
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 + ")";
}
std::string
AttrInputMacro::as_string () const
{
return " = " + macro->as_string ();
}
/* Override that calls the function recursively on all items contained within
* the module. */
void
Module::add_crate_name (std::vector &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 &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 ;' directive
std::string including_fpath (outer_filename);
std::string expected_file_path = module_name.as_string () + ".rs";
std::string expected_dir_path = "mod.rs";
auto dir_slash_pos = including_fpath.rfind (file_separator);
std::string current_directory_name;
std::string including_fname;
// If we haven't found a file_separator, then we may have to look for files in
// the current directory ('.')
if (dir_slash_pos == std::string::npos)
{
including_fname = std::move (including_fpath);
including_fpath = std::string (".") + file_separator + including_fname;
dir_slash_pos = 1;
}
else
{
including_fname = including_fpath.substr (dir_slash_pos + 1);
}
current_directory_name
= including_fpath.substr (0, dir_slash_pos) + file_separator;
auto path_string = filename_from_path_attribute (get_outer_attrs ());
std::string including_subdir;
if (path_string.empty () && module_scope.empty ()
&& get_file_subdir (including_fname, including_subdir))
current_directory_name += including_subdir + 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);
}
if (!path_string.empty ())
{
module_file = current_directory_name + path_string;
return;
}
// FIXME: We also have to search for
// //.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 /.rs
std::string file_mod_path = current_directory_name + expected_file_path;
bool file_mod_found = file_exists (file_mod_path);
// Then, search for //mod.rs
std::string dir_mod_path = current_directory_name + module_name.as_string ()
+ 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.as_string ().c_str (),
module_name.as_string ().c_str (),
module_name.as_string ().c_str (), file_separator);
if (no_candidates_found)
rust_error_at (locus, "no candidate found for module %s",
module_name.as_string ().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 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 ();
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 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> token_stream = to_token_stream ();
AttributeParser parser (std::move (token_stream));
std::vector> meta_items (
parser.parse_meta_item_seq ());
return new AttrInputMetaItemContainer (std::move (meta_items));
}
std::unique_ptr
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 (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 (
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 meta_name_value_str_items;
for (const auto &item : meta_items)
{
std::unique_ptr 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 (
new MetaListNameValueStr (ident, ident_locus,
std::move (meta_name_value_str_items)));
}
// // pass for meta list idents
// std::vector ident_items;
// for (const auto &item : meta_items)
// {
// std::unique_ptr 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 (
// 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 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 (
new MetaListPaths (ident, ident_locus, std::move (path_items)));
}
rust_error_at (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
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> meta_items
= parse_meta_item_seq ();
return std::unique_ptr (
new MetaItemSeq (std::move (path), std::move (meta_items)));
}
case EQUAL: {
skip_token ();
location_t 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 (
new MetaItemPathLit (std::move (path), std::move (expr)));
}
case COMMA:
// just simple path
return std::unique_ptr (
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>
AttributeParser::parse_meta_item_seq ()
{
int vec_length = token_stream.size ();
std::vector> 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 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>
DelimTokenTree::to_token_stream () const
{
std::vector> tokens;
for (const auto &tree : token_trees)
{
std::vector> 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 &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 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 &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
AttributeParser::parse_meta_item_lit ()
{
location_t locus = peek_token ()->get_locus ();
LiteralExpr lit_expr (parse_literal (), {}, locus);
return std::unique_ptr (
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.as_string () == "all")
{
for (const auto &str : strs)
{
if (!str.check_cfg_predicate (session))
return false;
}
return true;
}
else if (ident.as_string () == "any")
{
for (const auto &str : strs)
{
if (str.check_cfg_predicate (session))
return true;
}
return false;
}
else if (ident.as_string () == "not")
{
if (strs.size () != 1)
{
/* HACK: convert vector platform-dependent size_type to string to
* use in printf */
rust_error_at (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 (UNKNOWN_LOCATION,
"cfg predicate could not be checked for "
"MetaListNameValueStr with ident of "
"'%s' - ident must be 'all' or 'any'",
ident.as_string ().c_str ());
return false;
}
}
bool
MetaListPaths::check_cfg_predicate (const Session &session) const
{
if (ident.as_string () == "all")
{
for (const auto &path : paths)
{
if (!check_path_exists_in_cfg (session, path))
return false;
}
return true;
}
else if (ident.as_string () == "any")
{
for (const auto &path : paths)
{
if (check_path_exists_in_cfg (session, path))
return true;
}
return false;
}
else if (ident.as_string () == "not")
{
if (paths.size () != 1)
{
// HACK: convert vector platform-dependent size_type to string to
// use in printf
rust_error_at (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 (UNKNOWN_LOCATION,
"cfg predicate could not be checked for "
"MetaListNameValueStr with ident of "
"'%s' - ident must be 'all' or 'any'",
ident.as_string ().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 (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 (
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.as_string ());
}
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.as_string ().c_str (), str.c_str (),
session.options.target_data.has_key_value_pair (ident.as_string (), str)
? ""
: " not");
return session.options.target_data.has_key_value_pair (ident.as_string (),
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>
Token::to_token_stream () const
{
/* initialisation list doesn't work as it needs copy constructor, so have to
* do this */
std::vector> dummy_vector;
dummy_vector.reserve (1);
dummy_vector.push_back (std::unique_ptr (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.as_string (), ident_locus),
std::unique_ptr (
new AttrInputLiteral (std::move (lit_expr))));
}
Attribute
MetaItemPath::to_attribute () const
{
return Attribute (path, nullptr);
}
Attribute
MetaItemSeq::to_attribute () const
{
std::vector> new_seq;
new_seq.reserve (seq.size ());
for (const auto &e : seq)
new_seq.push_back (e->clone_meta_item_inner ());
std::unique_ptr 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.as_string (), 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> new_seq;
new_seq.reserve (paths.size ());
for (const auto &e : paths)
new_seq.push_back (std::unique_ptr (new MetaItemPath (e)));
std::unique_ptr new_seq_container (
new AttrInputMetaItemContainer (std::move (new_seq)));
return Attribute (SimplePath::from_str (ident.as_string (), ident_locus),
std::move (new_seq_container));
}
Attribute
MetaListNameValueStr::to_attribute () const
{
std::vector> new_seq;
new_seq.reserve (strs.size ());
for (const auto &e : strs)
new_seq.push_back (
std::unique_ptr (new MetaNameValueStr (e)));
std::unique_ptr new_seq_container (
new AttrInputMetaItemContainer (std::move (new_seq)));
return Attribute (SimplePath::from_str (ident.as_string (), ident_locus),
std::move (new_seq_container));
}
Attribute
MetaItemPathLit::to_attribute () const
{
return Attribute (path, std::unique_ptr (
new AttrInputLiteral (lit)));
}
std::vector
AttrInputMetaItemContainer::separate_cfg_attrs () const
{
rust_assert (!items.empty ());
if (items.size () == 1)
return {};
std::vector 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 () != Values::Attributes::CFG
&& path.as_string () != Values::Attributes::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;
auto &meta_item = static_cast (*attr_input);
return meta_item.get_items ().front ()->check_cfg_predicate (session);
}
std::vector
Attribute::separate_cfg_attrs () const
{
if (!has_attr_input () || path.as_string () != Values::Attributes::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 ();
}
void
BlockExpr::normalize_tail_expr ()
{
if (!expr)
{
// HACK: try to turn the last statement into a tail expression
if (!statements.empty () && statements.back ()->is_expr ())
{
// Watch out: This reference become invalid when the vector is
// modified.
auto &stmt = static_cast (*statements.back ());
if (!stmt.is_semicolon_followed ())
{
expr = std::move (stmt.get_expr ());
statements.pop_back ();
}
}
}
}
// needed here because "rust-expr.h" doesn't include "rust-macro.h"
AttrInputMacro::AttrInputMacro (const AttrInputMacro &oth)
: macro (oth.macro->clone_macro_invocation_impl ())
{}
void
AttrInputMacro::operator= (const AttrInputMacro &oth)
{
macro = std::unique_ptr (
oth.macro->clone_macro_invocation_impl ());
}
/* 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
LiteralExpr::accept_vis (ASTVisitor &vis)
{
vis.visit (*this);
}
void
AttrInputLiteral::accept_vis (ASTVisitor &vis)
{
vis.visit (*this);
}
void
AttrInputMacro::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
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
IfLetExpr::accept_vis (ASTVisitor &vis)
{
vis.visit (*this);
}
void
IfLetExprConseqElse::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
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
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
ExternalTypeItem::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
EmptyStmt::accept_vis (ASTVisitor &vis)
{
vis.visit (*this);
}
void
LetStmt::accept_vis (ASTVisitor &vis)
{
vis.visit (*this);
}
void
ExprStmt::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);
}
} // namespace AST
std::ostream &
operator<< (std::ostream &os, Identifier const &i)
{
return os << i.as_string ();
}
} // namespace Rust