// Copyright (C) 2020-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
// .
#include "rust-ast-validation.h"
#include "rust-common.h"
#include "rust-diagnostics.h"
#include "rust-item.h"
#include "rust-keyword-values.h"
namespace Rust {
void
ASTValidation::visit (AST::Lifetime &lifetime)
{
auto name = lifetime.get_lifetime_name ();
auto valid = std::set{"static", "_"};
auto &keywords = Values::Keywords::keywords;
if (valid.find (name) == valid.end ()
&& keywords.find (name) != keywords.end ())
rust_error_at (lifetime.get_locus (), "lifetimes cannot use keyword names");
AST::ContextualASTVisitor::visit (lifetime);
}
void
ASTValidation::visit (AST::LoopLabel &label)
{
auto name = label.get_lifetime ().get_lifetime_name ();
auto lifetime_name = '\'' + name;
auto &keywords = Values::Keywords::keywords;
if (keywords.find (name) != keywords.end ())
rust_error_at (label.get_lifetime ().get_locus (), "invalid label name %qs",
lifetime_name.c_str ());
// WARNING: Do not call ContextualASTVisitor, we don't want to visit the
// lifetime
// Maybe we should refactor LoopLabel instead ?
}
void
ASTValidation::visit (AST::ConstantItem &const_item)
{
if (!const_item.has_expr () && ctx.peek () != Kind::TRAIT_IMPL)
{
rust_error_at (const_item.get_locus (),
"associated constant in % without body");
}
AST::ContextualASTVisitor::visit (const_item);
}
void
ASTValidation::visit (AST::Union &item)
{
if (item.get_variants ().empty ())
rust_error_at (item.get_locus (), "unions cannot have zero fields");
AST::ContextualASTVisitor::visit (item);
}
void
ASTValidation::visit (AST::Function &function)
{
const auto &qualifiers = function.get_qualifiers ();
if (qualifiers.is_async () && qualifiers.is_const ())
rust_error_at (function.get_locus (),
"functions cannot be both % and %");
if (qualifiers.is_const ()
&& (ctx.peek () == Kind::TRAIT_IMPL || ctx.peek () == Kind::TRAIT))
rust_error_at (function.get_locus (), ErrorCode::E0379,
"functions in traits cannot be declared %");
// may change soon
if (qualifiers.is_async ()
&& (ctx.peek () == Kind::TRAIT_IMPL || ctx.peek () == Kind::TRAIT))
rust_error_at (function.get_locus (), ErrorCode::E0706,
"functions in traits cannot be declared %");
// if not an associated function but has a self parameter
if (ctx.peek () != Kind::TRAIT && ctx.peek () != Kind::TRAIT_IMPL
&& ctx.peek () != Kind::INHERENT_IMPL && function.has_self_param ())
rust_error_at (
function.get_self_param ().get_locus (),
"% parameter is only allowed in associated functions");
if (function.is_external ())
{
if (function.has_body ())
rust_error_at (function.get_locus (), "cannot have a body");
auto ¶ms = function.get_function_params ();
if (params.size () == 1 && function.is_variadic ())
rust_error_at (function.get_locus (),
"C-variadic function must be declared with at least one "
"named argument");
for (auto it = params.begin (); it != params.end (); it++)
{
if (it->get ()->is_variadic () && it + 1 != params.end ())
rust_error_at (
it->get ()->get_locus (),
"%<...%> must be the last argument of a C-variadic function");
// if functional parameter
if (!it->get ()->is_self () && !it->get ()->is_variadic ())
{
auto ¶m = static_cast (**it);
auto kind = param.get_pattern ().get_pattern_kind ();
if (kind != AST::Pattern::Kind::Identifier
&& kind != AST::Pattern::Kind::Wildcard)
rust_error_at (it->get ()->get_locus (), ErrorCode::E0130,
"pattern not allowed in foreign function");
}
}
}
else
{
if (!function.has_body ())
{
if (ctx.peek () == Kind::INHERENT_IMPL
|| ctx.peek () == Kind::TRAIT_IMPL)
rust_error_at (function.get_locus (),
"associated function in % without body");
else if (ctx.peek () != Kind::TRAIT)
rust_error_at (function.get_locus (),
"free function without a body");
}
auto &function_params = function.get_function_params ();
for (auto it = function_params.begin (); it != function_params.end ();
it++)
{
if (it->get ()->is_variadic ())
rust_error_at (
it->get ()->get_locus (),
"only foreign or % functions may "
"be C-variadic");
}
}
AST::ContextualASTVisitor::visit (function);
}
void
ASTValidation::visit (AST::Trait &trait)
{
if (trait.is_auto ())
{
if (trait.has_generics ())
rust_error_at (trait.get_generic_params ()[0]->get_locus (),
ErrorCode::E0567,
"auto traits cannot have generic parameters");
if (trait.has_type_param_bounds ())
rust_error_at (trait.get_type_param_bounds ()[0]->get_locus (),
ErrorCode::E0568,
"auto traits cannot have super traits");
if (trait.has_trait_items ())
{
rust_error_at (trait.get_identifier ().get_locus (), ErrorCode::E0380,
"auto traits cannot have methods or associated items");
for (const auto &item : trait.get_trait_items ())
Error::Hint (item->get_locus (), "remove this item").emit ();
}
}
AST::ContextualASTVisitor::visit (trait);
}
void
ASTValidation::visit (AST::Module &module)
{
if (module.get_unsafety () == Unsafety::Unsafe)
rust_error_at (module.get_locus (), "module cannot be declared unsafe");
AST::ContextualASTVisitor::visit (module);
}
} // namespace Rust