| /* General AST-related method implementations for Rust frontend. |
| Copyright (C) 2009-2025 Free Software Foundation, Inc. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it under |
| the terms of the GNU General Public License as published by the Free |
| Software Foundation; either version 3, or (at your option) any later |
| version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "rust-path.h" |
| #include "rust-system.h" |
| #include "rust-ast-full.h" |
| #include "rust-diagnostics.h" |
| #include "rust-ast-visitor.h" |
| #include "rust-macro.h" |
| #include "rust-session-manager.h" |
| #include "rust-lex.h" |
| #include "rust-parse.h" |
| #include "rust-operators.h" |
| |
| namespace Rust { |
| namespace AST { |
| |
| 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; |
| } |
| |
| GenericArg |
| GenericArg::disambiguate_to_const () const |
| { |
| rust_assert (get_kind () == Kind::Either); |
| |
| // FIXME: is it fine to have no outer attributes? |
| return GenericArg::create_const ( |
| std::unique_ptr<Expr> (new IdentifierExpr (path, {}, locus))); |
| } |
| |
| GenericArg |
| GenericArg::disambiguate_to_type () const |
| { |
| rust_assert (get_kind () == Kind::Either); |
| |
| auto segment = std::unique_ptr<TypePathSegment> ( |
| new TypePathSegment (path.as_string (), false, locus)); |
| auto segments = std::vector<std::unique_ptr<TypePathSegment>> (); |
| segments.emplace_back (std::move (segment)); |
| |
| return GenericArg::create_type ( |
| std::unique_ptr<Type> (new TypePath (std::move (segments), locus))); |
| } |
| |
| std::string |
| GenericArgsBinding::as_string () const |
| { |
| // TODO: rewrite to work with non-literalisable types |
| return identifier.as_string () + " = " + type->as_string (); |
| } |
| |
| std::string |
| ConstGenericParam::as_string () const |
| { |
| std::string str ("ConstGenericParam: "); |
| str += "const " + name.as_string () + ": " + type->as_string (); |
| |
| if (has_default_value ()) |
| str += " = " + get_default_value_unchecked ().as_string (); |
| |
| return str; |
| } |
| |
| 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 |
| Path::as_string () const |
| { |
| // FIXME: Impl for lang items |
| rust_assert (kind == Kind::Regular); |
| |
| 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; |
| } |
| |
| SimplePath |
| Path::convert_to_simple_path (bool with_opening_scope_resolution) const |
| { |
| rust_assert (kind == Kind::Regular); |
| |
| if (!has_segments ()) |
| return SimplePath::create_empty (); |
| |
| // create vector of reserved size (to minimise reallocations) |
| std::vector<SimplePathSegment> simple_segments; |
| simple_segments.reserve (segments.size ()); |
| |
| for (const auto &segment : segments) |
| { |
| // return empty path if doesn't meet simple path segment requirements |
| if (segment.is_error () || segment.has_generic_args ()) |
| return SimplePath::create_empty (); |
| |
| // create segment and add to vector |
| std::string segment_str = segment.as_string (); |
| simple_segments.emplace_back (std::move (segment_str), |
| segment.get_locus ()); |
| } |
| |
| // kind of a HACK to get locus depending on opening scope resolution |
| location_t locus = 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); |
| } |
| |
| void |
| PathInExpression::accept_vis (ASTVisitor &vis) |
| { |
| vis.visit (*this); |
| } |
| |
| std::string |
| PathInExpression::as_string () const |
| { |
| std::string str; |
| |
| if (has_opening_scope_resolution) |
| str = "::"; |
| |
| return str + Path::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 |
| TypePathSegmentFunction::as_string () const |
| { |
| // TODO: rewrite to work with non-linearisable types |
| return TypePathSegment::as_string () + function_path.as_string (); |
| } |
| |
| 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; |
| } |
| |
| SimplePath |
| TypePath::as_simple_path () const |
| { |
| if (segments.empty ()) |
| return SimplePath::create_empty (); |
| |
| // create vector of reserved size (to minimise reallocations) |
| std::vector<SimplePathSegment> simple_segments; |
| simple_segments.reserve (segments.size ()); |
| |
| for (const auto &segment : segments) |
| { |
| // return empty path if doesn't meet simple path segment requirements |
| if (segment == nullptr || segment->is_error () |
| || !segment->is_ident_only () || segment->as_string () == "Self") |
| return SimplePath::create_empty (); |
| |
| // create segment and add to vector |
| std::string segment_str = segment->as_string (); |
| simple_segments.emplace_back (std::move (segment_str), |
| segment->get_locus ()); |
| } |
| |
| return SimplePath (std::move (simple_segments), has_opening_scope_resolution, |
| locus); |
| } |
| |
| std::string |
| TypePath::make_debug_string () const |
| { |
| rust_assert (!segments.empty ()); |
| |
| std::string output; |
| |
| for (const auto &segment : segments) |
| { |
| if (segment != nullptr && !segment->is_lang_item () |
| && !segment->is_error ()) |
| { |
| if (!output.empty () || has_opening_scope_resolution_op ()) |
| output.append ("::"); |
| output.append (segment->get_ident_segment ().as_string ()); |
| } |
| } |
| |
| return output; |
| } |
| |
| // hopefully definition here will prevent circular dependency issue |
| TraitBound * |
| TypePath::to_trait_bound (bool in_parens) const |
| { |
| // If already in parentheses, don't convert to trait bound |
| // This ensures (TypePath) stays as ParenthesisedType in the parser |
| if (in_parens) |
| return nullptr; |
| |
| return new TraitBound (TypePath (*this), get_locus (), in_parens); |
| } |
| |
| 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 |
| QualifiedPathInExpression::as_string () const |
| { |
| return path_type.as_string () + "::" + Path::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 (); |
| |
| str += "::" + associated_segment->as_string (); |
| for (const auto &segment : segments) |
| str += "::" + segment->as_string (); |
| |
| return str; |
| } |
| |
| void |
| ConstGenericParam::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); |
| } |
| |
| } // namespace AST |
| } // namespace Rust |