From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wm1-x329.google.com (mail-wm1-x329.google.com [IPv6:2a00:1450:4864:20::329]) by sourceware.org (Postfix) with ESMTPS id 5CEE9395C050 for ; Tue, 6 Dec 2022 10:11:59 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 5CEE9395C050 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=embecosm.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=embecosm.com Received: by mail-wm1-x329.google.com with SMTP id v124-20020a1cac82000000b003cf7a4ea2caso13892891wme.5 for ; Tue, 06 Dec 2022 02:11:59 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=embecosm.com; s=google; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to; bh=IIcygeub+zzDqPOZq1mAHY00CwN7bO32XdOU4fVOk50=; b=OW5kXkMu4BaAesGvmlTe4U3pZOMHkLeXfiiPUFopfeObq4FFJWeQ/RUjjPhSFRnE24 6e7r7p7u7vgCPzs6oVkaoVbvgiEdVjwaAJ3KIaTUtyEyhJ0mmNcN/15glPsvk+fFnJlU LSs3Cad5Y5uOkS3B3vXxyXsVHGzxGyP2hRCDynY0SFHQKDs9GfsTuBYrxTSqSMiOchw/ apoWVAK5CHpnc5U9cVwb5xNT4o1832PL2aZ1Ls1D+ENKubduEKVqLLsJwIJIKHZBQjz8 ayR0xYeDkCdz3cXgPMS6VJ3zRyfrYDzwRAv7jcy8MkZdKucIAzZpqV0XkvlfvCF5JR/+ VK3w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=IIcygeub+zzDqPOZq1mAHY00CwN7bO32XdOU4fVOk50=; b=zZamaJC6uyrftw1s2R9DrPW1q8flJxfqUiXD/+c2lexZRnypGKtXIy/onZK9B3t7xY WwPfu8Sz+xrarSbhpUnY8KnltFichXlKGZxTTT0m5nwgYtkQPYJHJ0GZTQfDhcOkqesu lLq7T0zXox9ga21yWQ934HKksbjp+Arf8o6riN0NfiItJ12o+av/vJ4wNRaib+3UI7j5 DAsytGHgD4ft4BuHOJ7RBTZMAZRUVc360BnFs0OUhjaduceobkJM62Iku38zgBMopk6H rPR/MZOgbt5g3EcuWZ58PPz3RQS2sviWgh+8fQ3Lo5C86JAAfn85a3PMAXnwNIAhPXA+ 8MaA== X-Gm-Message-State: ANoB5pnp8SZLsEpYf5JqEut0N73wFkZGSHIvvnzhFTb1nWTJPOnujcSn ch2hZCQgAiofnHjLAn73wbCj X-Google-Smtp-Source: AA0mqf60eug2Anx5aR8v3nhdioDByLd4+oC+kPujuwI9rtsIdpM0upbCFxcE6ZKglJCD0+Jf7ERBag== X-Received: by 2002:a05:600c:3586:b0:3cf:a3b0:df6a with SMTP id p6-20020a05600c358600b003cfa3b0df6amr65318923wmq.126.1670321516904; Tue, 06 Dec 2022 02:11:56 -0800 (PST) Received: from platypus.lan ([2001:861:5e4c:3bb0:6424:328a:1734:3249]) by smtp.googlemail.com with ESMTPSA id r10-20020a05600c458a00b003cfd4a50d5asm27052699wmo.34.2022.12.06.02.11.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Dec 2022 02:11:55 -0800 (PST) From: arthur.cohen@embecosm.com To: gcc-patches@gcc.gnu.org Cc: gcc-rust@gcc.gnu.org, The Other , Philip Herron Subject: [PATCH Rust front-end v4 08/46] gccrs: Add Rust front-end base AST data structures Date: Tue, 6 Dec 2022 11:13:40 +0100 Message-Id: <20221206101417.778807-9-arthur.cohen@embecosm.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221206101417.778807-1-arthur.cohen@embecosm.com> References: <20221206101417.778807-1-arthur.cohen@embecosm.com> Reply-To: arthur.cohen@embecosm.com MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-29.6 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,KAM_SHORT,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: From: The Other This is a full C++11 class hierarchy representing the Rust AST. We do not allow dynamic_cast and so the main mechanism to work with the AST is by using the visitor interface. Slowly we are adding TREE_CODE style node types to the AST which will allow for more ways to work with the AST but for now this is it. See: https://doc.rust-lang.org/reference/items.html Co-authored-by: Philip Herron Co-authored-by: Arthur Cohen . + +#ifndef RUST_AST_FULL_DECLS_H +#define RUST_AST_FULL_DECLS_H + +// Forward declarations for all AST classes. Useful for not having to include +// all definitions. + +namespace Rust { +namespace AST { +// rust-ast.h +class AttrInput; +class TokenTree; +class MacroMatch; +class Token; +struct Literal; +class DelimTokenTree; +class PathSegment; +class SimplePathSegment; +class SimplePath; +struct Attribute; +class MetaItemInner; +class AttrInputMetaItemContainer; +class MetaItem; +class Stmt; +class Item; +class Expr; +class ExprWithoutBlock; +class IdentifierExpr; +class Pattern; +class Type; +class TypeNoBounds; +class TypeParamBound; +class Lifetime; +class GenericParam; +class LifetimeParam; +class ConstGenericParam; +class MacroItem; +class TraitItem; +class InherentImplItem; +class TraitImplItem; +struct Crate; +class PathExpr; + +// rust-path.h +class PathIdentSegment; +struct GenericArgsBinding; +struct GenericArgs; +class PathExprSegment; +class PathPattern; +class PathInExpression; +class TypePathSegment; +class TypePathSegmentGeneric; +struct TypePathFunction; +class TypePathSegmentFunction; +class TypePath; +struct QualifiedPathType; +class QualifiedPathInExpression; +class QualifiedPathInType; + +// rust-expr.h +class ExprWithBlock; +class LiteralExpr; +class AttrInputLiteral; +class MetaItemLitExpr; +class MetaItemPathLit; +class OperatorExpr; +class BorrowExpr; +class DereferenceExpr; +class ErrorPropagationExpr; +class NegationExpr; +class ArithmeticOrLogicalExpr; +class ComparisonExpr; +class LazyBooleanExpr; +class TypeCastExpr; +class AssignmentExpr; +class CompoundAssignmentExpr; +class GroupedExpr; +class ArrayElems; +class ArrayElemsValues; +class ArrayElemsCopied; +class ArrayExpr; +class ArrayIndexExpr; +class TupleExpr; +class TupleIndexExpr; +class StructExpr; +class StructExprStruct; +struct StructBase; +class StructExprField; +class StructExprFieldIdentifier; +class StructExprFieldWithVal; +class StructExprFieldIdentifierValue; +class StructExprFieldIndexValue; +class StructExprStructFields; +class StructExprStructBase; +class CallExpr; +class MethodCallExpr; +class FieldAccessExpr; +struct ClosureParam; +class ClosureExpr; +class ClosureExprInner; +class BlockExpr; +class ClosureExprInnerTyped; +class ContinueExpr; +class BreakExpr; +class RangeExpr; +class RangeFromToExpr; +class RangeFromExpr; +class RangeToExpr; +class RangeFullExpr; +class RangeFromToInclExpr; +class RangeToInclExpr; +class ReturnExpr; +class UnsafeBlockExpr; +class LoopLabel; +class BaseLoopExpr; +class LoopExpr; +class WhileLoopExpr; +class WhileLetLoopExpr; +class ForLoopExpr; +class IfExpr; +class IfExprConseqElse; +class IfExprConseqIf; +class IfLetExpr; +class IfExprConseqIfLet; +class IfLetExprConseqElse; +class IfLetExprConseqIf; +class IfLetExprConseqIfLet; +struct MatchArm; +// class MatchCase; +// class MatchCaseBlockExpr; +// class MatchCaseExpr; +struct MatchCase; +class MatchExpr; +class AwaitExpr; +class AsyncBlockExpr; + +// rust-stmt.h +class EmptyStmt; +class LetStmt; +class ExprStmt; +class ExprStmtWithoutBlock; +class ExprStmtWithBlock; + +// rust-item.h +class TypeParam; +class WhereClauseItem; +class LifetimeWhereClauseItem; +class TypeBoundWhereClauseItem; +struct WhereClause; +struct SelfParam; +struct FunctionQualifiers; +struct FunctionParam; +struct Visibility; +class Method; +class VisItem; +class Module; +class ExternCrate; +class UseTree; +class UseTreeGlob; +class UseTreeList; +class UseTreeRebind; +class UseDeclaration; +class Function; +class TypeAlias; +class Struct; +struct StructField; +class StructStruct; +struct TupleField; +class TupleStruct; +class EnumItem; +class EnumItemTuple; +class EnumItemStruct; +class EnumItemDiscriminant; +class Enum; +class Union; +class ConstantItem; +class StaticItem; +struct TraitFunctionDecl; +class TraitItemFunc; +struct TraitMethodDecl; +class TraitItemMethod; +class TraitItemConst; +class TraitItemType; +class Trait; +class Impl; +class InherentImpl; +class TraitImpl; +class ExternalItem; +class ExternalStaticItem; +struct NamedFunctionParam; +class ExternalFunctionItem; +class ExternBlock; + +// rust-macro.h +class MacroMatchFragment; +class MacroMatchRepetition; +class MacroMatcher; +struct MacroTranscriber; +struct MacroRule; +class MacroRulesDefinition; +class MacroInvocation; +class MetaItemPath; +class MetaItemSeq; +class MetaWord; +class MetaNameValueStr; +class MetaListPaths; +class MetaListNameValueStr; + +// rust-pattern.h +class LiteralPattern; +class IdentifierPattern; +class WildcardPattern; +class RangePatternBound; +class RangePatternBoundLiteral; +class RangePatternBoundPath; +class RangePatternBoundQualPath; +class RangePattern; +class ReferencePattern; +struct StructPatternEtc; +class StructPatternField; +class StructPatternFieldTuplePat; +class StructPatternFieldIdentPat; +class StructPatternFieldIdent; +struct StructPatternElements; +class StructPattern; +class TupleStructItems; +class TupleStructItemsNoRange; +class TupleStructItemsRange; +class TupleStructPattern; +class TuplePatternItems; +class TuplePatternItemsMultiple; +class TuplePatternItemsRanged; +class TuplePattern; +class GroupedPattern; +class SlicePattern; + +// rust-type.h +class TraitBound; +class ImplTraitType; +class TraitObjectType; +class ParenthesisedType; +class ImplTraitTypeOneBound; +class TraitObjectTypeOneBound; +class TupleType; +class NeverType; +class RawPointerType; +class ReferenceType; +class ArrayType; +class SliceType; +class InferredType; +struct MaybeNamedParam; +class BareFunctionType; +} // namespace AST +} // namespace Rust + +#endif diff --git a/gcc/rust/ast/rust-ast-full-test.cc b/gcc/rust/ast/rust-ast-full-test.cc new file mode 100644 index 00000000000..1e8a93d462f --- /dev/null +++ b/gcc/rust/ast/rust-ast-full-test.cc @@ -0,0 +1,5810 @@ +/* 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 +. */ + +#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 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 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 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 +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 + "("; + + 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 &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_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 + // //.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 + 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 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 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 (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 +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 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 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 == "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> +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, 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, 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, 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, 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 () != "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::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 (new IdentifierExpr (path, {}, locus))); +} + +GenericArg +GenericArg::disambiguate_to_type () const +{ + rust_assert (get_kind () == Kind::Either); + + auto segment = std::unique_ptr ( + new TypePathSegment (path, false, locus)); + auto segments = std::vector> (); + segments.emplace_back (std::move (segment)); + + return GenericArg::create_type ( + std::unique_ptr (new TypePath (std::move (segments), locus))); +} + +} // namespace AST +} // namespace Rust diff --git a/gcc/rust/ast/rust-ast-full.h b/gcc/rust/ast/rust-ast-full.h new file mode 100644 index 00000000000..5ab136c61b6 --- /dev/null +++ b/gcc/rust/ast/rust-ast-full.h @@ -0,0 +1,31 @@ +// 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 +// . + +#ifndef RUST_AST_FULL_H +#define RUST_AST_FULL_H +// Use as a fast way of including all aspects of the AST (i.e. all headers) +#include "rust-ast.h" +#include "rust-expr.h" +#include "rust-item.h" +#include "rust-path.h" +#include "rust-pattern.h" +#include "rust-stmt.h" +#include "rust-type.h" +#include "rust-macro.h" + +#endif diff --git a/gcc/rust/ast/rust-ast.h b/gcc/rust/ast/rust-ast.h new file mode 100644 index 00000000000..461a2460f8f --- /dev/null +++ b/gcc/rust/ast/rust-ast.h @@ -0,0 +1,2007 @@ +// 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 +// . + +#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; + +// 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 clone_token_tree () const + { + return std::unique_ptr (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 > 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 clone_macro_match () const + { + return std::unique_ptr (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 clone_token () const + { + return std::unique_ptr (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 > 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 segments; + Location locus; + NodeId node_id; + +public: + // Constructor + SimplePath (std::vector 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 ()); + } + + // 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 single_segments + = {AST::SimplePathSegment (std::move (str), locus)}; + return SimplePath (std::move (single_segments)); + } + + const std::vector &get_segments () const + { + return segments; + } + + std::vector &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 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 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 clone_attribute () const + { + return std::unique_ptr (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 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 clone_attr_input () const + { + return std::unique_ptr (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 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 clone_meta_item_inner () const + { + return std::unique_ptr (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 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 > items; + +public: + AttrInputMetaItemContainer ( + std::vector > 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 + clone_attr_input_meta_item_container () const + { + return std::unique_ptr ( + clone_attr_input_meta_item_container_impl ()); + } + + std::vector separate_cfg_attrs () const override; + + bool is_meta_item () const override { return true; } + + // TODO: this mutable getter seems dodgy + std::vector > &get_items () { return items; } + const std::vector > &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 > 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 > token_trees + = std::vector > (), + 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 > to_token_stream () const override; + + std::unique_ptr clone_delim_token_tree () const + { + return std::unique_ptr (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 > &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 clone_stmt () const + { + return std::unique_ptr (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 clone_item () const + { + return std::unique_ptr (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 &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 clone_expr () const + { + return std::unique_ptr (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) = 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 clone_expr_without_block () const + { + return std::unique_ptr (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 outer_attrs; + Identifier ident; + Location locus; + +public: + IdentifierExpr (Identifier ident, std::vector 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 clone_identifier_expr () const + { + return std::unique_ptr (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 &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector 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 clone_pattern () const + { + return std::unique_ptr (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 clone_type () const + { + return std::unique_ptr (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 clone_type_no_bounds () const + { + return std::unique_ptr (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 clone_type_param_bound () const + { + return std::unique_ptr (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 clone_generic_param () const + { + return std::unique_ptr (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_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_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 clone_trait_item () const + { + return std::unique_ptr (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 clone_inherent_impl_item () const + { + return std::unique_ptr (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 clone_trait_impl_item () const + { + return std::unique_ptr (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 clone_external_item () const + { + return std::unique_ptr (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 > 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 > new_items) + { + parsed_items = std::move (new_items); + } + // TODO: mutable getter seems kinda dodgy + std::vector > &get_meta_items () + { + return parsed_items; + } + const std::vector > &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; + std::unique_ptr item; + std::unique_ptr stmt; + std::unique_ptr external_item; + std::unique_ptr trait_item; + std::unique_ptr impl_item; + std::unique_ptr trait_impl_item; + std::unique_ptr type; + +public: + SingleASTNode (std::unique_ptr expr) + : kind (EXPRESSION), expr (std::move (expr)) + {} + + SingleASTNode (std::unique_ptr item) + : kind (ITEM), item (std::move (item)) + {} + + SingleASTNode (std::unique_ptr stmt) + : kind (STMT), stmt (std::move (stmt)) + {} + + SingleASTNode (std::unique_ptr item) + : kind (EXTERN), external_item (std::move (item)) + {} + + SingleASTNode (std::unique_ptr item) + : kind (TRAIT), trait_item (std::move (item)) + {} + + SingleASTNode (std::unique_ptr item) + : kind (IMPL), impl_item (std::move (item)) + {} + + SingleASTNode (std::unique_ptr trait_impl_item) + : kind (TRAIT_IMPL), trait_impl_item (std::move (trait_impl_item)) + {} + + SingleASTNode (std::unique_ptr 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 &get_expr () + { + rust_assert (kind == EXPRESSION); + return expr; + } + + std::unique_ptr &get_item () + { + rust_assert (kind == ITEM); + return item; + } + + std::unique_ptr &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 take_stmt () + { + rust_assert (!is_error ()); + return std::move (stmt); + } + + std::unique_ptr take_expr () + { + rust_assert (!is_error ()); + return std::move (expr); + } + + std::unique_ptr take_item () + { + rust_assert (!is_error ()); + return std::move (item); + } + + std::unique_ptr take_trait_item () + { + rust_assert (!is_error ()); + return std::move (trait_item); + } + + std::unique_ptr take_external_item () + { + rust_assert (!is_error ()); + return std::move (external_item); + } + + std::unique_ptr take_impl_item () + { + rust_assert (!is_error ()); + return std::move (impl_item); + } + + std::unique_ptr take_trait_impl_item () + { + rust_assert (!is_error ()); + return std::move (trait_impl_item); + } + + std::unique_ptr 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 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 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 &get_nodes () { return nodes; } + bool is_error () const { return fragment_is_error; } + + bool should_expand () const { return !is_error (); } + + std::unique_ptr take_expression_fragment () + { + rust_assert (is_single_fragment_kind (SingleASTNode::NodeType::EXPRESSION)); + return nodes[0].take_expr (); + } + + std::unique_ptr 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 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 > items; + + NodeId node_id; + +public: + // Constructor + Crate (std::vector > items, + std::vector 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 &get_inner_attrs () const { return inner_attrs; } +}; + +// Base path expression AST node - abstract +class PathExpr : public ExprWithoutBlock +{ +}; +} // namespace AST +} // namespace Rust + +#endif diff --git a/gcc/rust/operator.h b/gcc/rust/operator.h new file mode 100644 index 00000000000..6813db3ed13 --- /dev/null +++ b/gcc/rust/operator.h @@ -0,0 +1,72 @@ +// 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 +// . + +#ifndef RUST_OPERATOR_H +#define RUST_OPERATOR_H + +enum class NegationOperator +{ + NEGATE, + NOT +}; + +enum class ArithmeticOrLogicalOperator +{ + ADD, // std::ops::Add + SUBTRACT, // std::ops::Sub + MULTIPLY, // std::ops::Mul + DIVIDE, // std::ops::Div + MODULUS, // std::ops::Rem + BITWISE_AND, // std::ops::BitAnd + BITWISE_OR, // std::ops::BitOr + BITWISE_XOR, // std::ops::BitXor + LEFT_SHIFT, // std::ops::Shl + RIGHT_SHIFT // std::ops::Shr +}; + +enum class ComparisonOperator +{ + EQUAL, // std::cmp::PartialEq::eq + NOT_EQUAL, // std::cmp::PartialEq::ne + GREATER_THAN, // std::cmp::PartialEq::gt + LESS_THAN, // std::cmp::PartialEq::lt + GREATER_OR_EQUAL, // std::cmp::PartialEq::ge + LESS_OR_EQUAL // std::cmp::PartialEq::le +}; + +enum class LazyBooleanOperator +{ + LOGICAL_OR, + LOGICAL_AND +}; + +enum class CompoundAssignmentOperator +{ + ADD, // std::ops::AddAssign + SUBTRACT, // std::ops::SubAssign + MULTIPLY, // std::ops::MulAssign + DIVIDE, // std::ops::DivAssign + MODULUS, // std::ops::RemAssign + BITWISE_AND, // std::ops::BitAndAssign + BITWISE_OR, // std::ops::BitOrAssign + BITWISE_XOR, // std::ops::BitXorAssign + LEFT_SHIFT, // std::ops::ShlAssign + RIGHT_SHIFT // std::ops::ShrAssign +}; + +#endif // RUST_OPERATOR_H -- 2.38.1