use core::fmt;
use proc_macro2::{
Ident as Ident2, Span as Span2, TokenStream as TokenStream2, TokenTree as TokenTree2,
};
use quote::quote_spanned;
pub enum Error {
EmptyParams,
SegmentParameterTypeMismatch(TokenTree2),
InvalidSegmentValue(Ident2),
GroupParameterTypeMismatch(Span2),
ReservedGroupParameter(Span2),
InvalidGroupDelimiter(Span2),
InvalidGroupValue(Ident2, InvalidGroupParameterReason),
MissingGroupParameter(Span2),
ExtraneousParameter(TokenTree2),
}
#[derive(Copy, Clone)]
pub enum InvalidGroupParameterReason {
TooLong(usize),
InvalidSymbol(char),
}
impl fmt::Display for InvalidGroupParameterReason {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::TooLong(len) => f.write_fmt(format_args!(
"`group` string value contains more than 15 characters: {len}"
)),
Self::InvalidSymbol(ch) => f.write_fmt(format_args!(
"\
`group` string contains invalid symbol: {ch}\n\
- allowed characters: [a-z][0-9]_\n\
"
)),
}
}
}
impl quote::ToTokens for Error {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let span = self.span();
let message = self.to_string();
tokens.extend(quote_spanned!(span=>
const _: () = {
::core::compile_error!(
::core::concat!("#[portable_link_section]: ", #message)
)
};
))
}
}
impl Error {
fn span(&self) -> Span2 {
match self {
Self::EmptyParams => Span2::call_site(),
Self::SegmentParameterTypeMismatch(invalid) => invalid.span(),
Self::InvalidSegmentValue(invalid) => invalid.span(),
Self::InvalidGroupDelimiter(span) => *span,
Self::GroupParameterTypeMismatch(span) => *span,
Self::ReservedGroupParameter(span) => *span,
Self::InvalidGroupValue(invalid, _reason) => invalid.span(),
Self::MissingGroupParameter(span) => *span,
Self::ExtraneousParameter(extraneous) => extraneous.span(),
}
}
}
struct Indent;
impl fmt::Display for Indent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(" ")
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let indent = Indent;
match self {
Self::EmptyParams => f.write_fmt(format_args!(
"\
missing segment parameter, example usage:\n\
{indent}- #[portable_link_section(text(group))]\n\
{indent}- #[portable_link_section(data(group))]\n\
{indent}- #[portable_link_section(rodata)]\n\
{indent}- #[portable_link_section(bss)]\n\
"
)),
Self::SegmentParameterTypeMismatch(invalid) => f.write_fmt(format_args!(
"expected identifier for `segment` parameter but found: {invalid}"
)),
Self::InvalidSegmentValue(invalid) => f.write_fmt(format_args!(
"\
invalid value for `segment` parameter: {invalid}\n\
{indent}- accepted values: text, data, rodata, bss\n\
"
)),
Self::InvalidGroupDelimiter(_span) => f.write_fmt(format_args!(
"\
segment group parameter must be given inside `(` and `)`\n\
{indent}- example usage: #[portable_link_section(text(hot))]\n\
"
)),
Self::GroupParameterTypeMismatch(_span) => f.write_fmt(format_args!(
"\
expected segment group identifier, example usage\n\
{indent}- #[portable_link_section(text(group))]\n\
{indent}- #[portable_link_section(data(group))]\n\
"
)),
Self::ReservedGroupParameter(_span) => f.write_str("reserved group parameter"),
Self::InvalidGroupValue(invalid, reason) => f.write_fmt(format_args!(
"\
invalid value for `group` parameter: {invalid}\n\
{indent}- reason: {reason}
"
)),
Self::MissingGroupParameter(_) => f.write_fmt(format_args!(
"\
missing segment group parameter. example usage:\n\
{indent}- #[portable_link_section(text(group))]\n\
{indent}- #[portable_link_section(data(group))]\n\
"
)),
Self::ExtraneousParameter(extraneous) => f.write_fmt(format_args!(
"\
unexpected extraneous parameter: {extraneous} example usage:\n\
{indent}- #[portable_link_section(text(group))]\n\
{indent}- #[portable_link_section(data(group))]\n\
{indent}- #[portable_link_section(rodata)]\n\
{indent}- #[portable_link_section(bss)]\n\
"
)),
}
}
}