// Copyright (C) 2024 Free Software Foundation, Inc.
// This file is part of GCC.
// GCC is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3, or (at your option) any later
// version.
// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
// You should have received a copy of the GNU General Public License
// along with GCC; see the file COPYING3. If not see
// .
#ifndef RUST_AST_BUILTIN_NODES_H
#define RUST_AST_BUILTIN_NODES_H
#include "rust-system.h"
#include "line-map.h"
#include "optional.h"
#include "rust-ast.h"
#include "rust-fmt.h"
namespace Rust {
namespace AST {
// Definitions, from rustc's `FormatArgs` AST struct
// https://github.com/rust-lang/rust/blob/1be468815c/compiler/rustc_ast/src/format.rs
//
// format_args!("hello {abc:.xyz$}!!", abc="world");
// └──────────────────────────────────────────────┘
// FormatArgs
//
// format_args!("hello {abc:.xyz$}!!", abc="world");
// └─────────┘
// argument
//
// format_args!("hello {abc:.xyz$}!!", abc="world");
// └───────────────────┘
// template
//
// format_args!("hello {abc:.xyz$}!!", abc="world");
// └────┘└─────────┘└┘
// pieces
//
// format_args!("hello {abc:.xyz$}!!", abc="world");
// └────┘ └┘
// literal pieces
//
// format_args!("hello {abc:.xyz$}!!", abc="world");
// └─────────┘
// placeholder
//
// format_args!("hello {abc:.xyz$}!!", abc="world");
// └─┘ └─┘
// positions (could be names, numbers, empty, or `*`)
// FIXME: Merge with the class below this one?
class FormatArgumentKind
{
public:
enum class Kind
{
Normal,
Named,
Captured,
} kind;
Identifier &get_ident ()
{
rust_assert (kind == Kind::Captured || kind == Kind::Named);
return ident.value ();
}
FormatArgumentKind (Kind kind, tl::optional ident)
: kind (kind), ident (ident)
{}
FormatArgumentKind (const FormatArgumentKind &other)
{
kind = other.kind;
ident = other.ident;
}
FormatArgumentKind operator= (const FormatArgumentKind &other)
{
kind = other.kind;
ident = other.ident;
return *this;
}
private:
tl::optional ident;
};
class FormatArgument
{
public:
static FormatArgument normal (std::unique_ptr expr)
{
return FormatArgument (FormatArgumentKind::Kind::Normal, tl::nullopt,
std::move (expr));
}
static FormatArgument named (Identifier ident, std::unique_ptr expr)
{
return FormatArgument (FormatArgumentKind::Kind::Named, ident,
std::move (expr));
}
static FormatArgument captured (Identifier ident, std::unique_ptr expr)
{
return FormatArgument (FormatArgumentKind::Kind::Captured, ident,
std::move (expr));
}
FormatArgument (const FormatArgument &other)
: kind (other.kind), expr (other.expr->clone_expr ())
{}
FormatArgument operator= (const FormatArgument &other)
{
kind = other.kind;
expr = other.expr->clone_expr ();
return *this;
}
FormatArgumentKind get_kind () const { return kind; }
const Expr &get_expr () const { return *expr; }
private:
FormatArgument (FormatArgumentKind::Kind kind, tl::optional ident,
std::unique_ptr expr)
: kind (FormatArgumentKind (kind, ident)), expr (std::move (expr))
{}
FormatArgumentKind kind;
std::unique_ptr expr;
};
class FormatArguments
{
public:
FormatArguments () {}
FormatArguments (FormatArguments &&) = default;
FormatArguments (const FormatArguments &other)
{
args = std::vector ();
args.reserve (other.args.size ());
for (const auto &arg : other.args)
args.emplace_back (arg);
};
FormatArguments &operator= (const FormatArguments &other) = default;
void push (FormatArgument &&elt) { args.emplace_back (std::move (elt)); }
const FormatArgument at (size_t idx) const { return args.at (idx); }
private:
std::vector args;
};
// TODO: Format documentation better
// Having a separate AST node for `format_args!()` expansion allows some
// important optimizations which help reduce generated code a lot. For example,
// turning `format_args!("a {} {} {}", 15, "hey", 'a')` directly into
// `format_args!("a 15 hey a")`, since all arguments are literals. Or,
// flattening imbricated `format_args!()` calls: `format_args!("heyo {}",
// format_args!("result: {}", some_result))` -> `format_args!("heyo result: {}",
// some_result)`
// FIXME: Move to rust-macro.h
class FormatArgs : public Expr
{
public:
enum class Newline
{
Yes,
No
};
FormatArgs (location_t loc, Fmt::Pieces &&template_str,
FormatArguments &&arguments)
: loc (loc), template_pieces (std::move (template_str)),
arguments (std::move (arguments))
{}
FormatArgs (FormatArgs &&other) = default;
FormatArgs (const FormatArgs &other) = default;
FormatArgs &operator= (const FormatArgs &other) = default;
void accept_vis (AST::ASTVisitor &vis) override;
const Fmt::Pieces &get_template () const { return template_pieces; }
const FormatArguments &get_arguments () const { return arguments; }
virtual location_t get_locus () const override;
private:
location_t loc;
// FIXME: This probably needs to be a separate type - it is one in rustc's
// expansion of format_args!(). There is extra handling associated with it.
// we can maybe do that in rust-fmt.cc? in collect_pieces()? like do the
// transformation into something we can handle better
Fmt::Pieces template_pieces;
FormatArguments arguments;
bool marked_for_strip = false;
protected:
virtual std::string as_string () const override;
virtual bool is_expr_without_block () const override;
virtual void mark_for_strip () override;
virtual bool is_marked_for_strip () const override;
virtual std::vector &get_outer_attrs () override;
virtual void set_outer_attrs (std::vector) override;
virtual Expr *clone_expr_impl () const override;
};
} // namespace AST
} // namespace Rust
#endif // ! RUST_AST_BUILTIN_NODES_H