Add explicit error handling for macro parsing

finchie
Apr 15, 2025, 9:42 AM
7JPOCQEISAIOD7LV4JYBE6NNUWUKKNE73MEPQYTIZ7PP44ZAD2RAC

Dependencies

  • [2] 73C6NOJ7 Fix minimal `Localize` implementation on errors
  • [3] YZ6PVVQC Add error handling for common unsupported Rust code
  • [4] VQBJBFEX Improve error handling for missing Fluent messages
  • [5] NO3PDO7P Refactor `fluent_embed` to support structs
  • [6] XGRU7WZE Add `expand` feature for proc-macro debugging
  • [7] QFPQZR4K Refactor `fluent_embed`
  • [8] K3G4HK2J Track Fluent files using `include!`
  • [9] O77KA6C4 Create `fluent_embed` crate
  • [10] LIO32J4B Fix compiler error when `expand` feature is disabled
  • [11] OWXLFLRM Merge `cli_macros` shim into `fluent_embed`
  • [12] 4BMW4JJO Add support for deriving items with generics
  • [13] F5LG7WEN Emit compilation errors from Fluent source code
  • [14] CESJ4CTO Move macro-specific code into `macro_impl` module
  • [15] TVRANPYB Ignore errors when setting `miette` panic hook
  • [16] NFV26FRQ Improve glob error handling

Change contents

  • edit in fluent_embed_derive/src/macro_impl/mod.rs at line 28
    [3.320]
    [4.295]
    #[error("Failed to parse macro input")]
    pub enum ParseError {
    #[error("invalid attribute")]
    InvalidAttribute(syn::Error),
    #[error("invalid item")]
    InvalidDeriveInput(syn::Error),
    }
    #[derive(Debug, Error)]
  • edit in fluent_embed_derive/src/macro_impl/mod.rs at line 42
    [3.364]
    [4.458]
    ParseError(#[from] ParseError),
  • replacement in fluent_embed_derive/src/macro_impl/mod.rs at line 46
    [4.478][4.478:544]()
    attribute: &syn::LitStr,
    derive_input: &syn::DeriveInput,
    [4.478]
    [4.544]
    attribute_stream: proc_macro2::TokenStream,
    derive_input_stream: proc_macro2::TokenStream,
  • replacement in fluent_embed_derive/src/macro_impl/mod.rs at line 61
    [4.360][4.596:646](),[4.596][4.596:646]()
    let locales = attribute::locales(attribute)?;
    [4.360]
    [4.0]
    // Parse the token streams
    let attribute: syn::LitStr = syn::parse2(attribute_stream)
    .map_err(|parse_error| ParseError::InvalidAttribute(parse_error))?;
    let derive_input: syn::DeriveInput = syn::parse2(derive_input_stream)
    .map_err(|parse_error| ParseError::InvalidDeriveInput(parse_error))?;
    let locales = attribute::locales(&attribute)?;
  • replacement in fluent_embed_derive/src/macro_impl/error.rs at line 4
    [3.1562][3.1562:1615]()
    MacroError, UnsupportedError, UnsupportedReason,
    [3.1562]
    [3.1615]
    MacroError, ParseError, UnsupportedError, UnsupportedReason,
  • replacement in fluent_embed_derive/src/macro_impl/error.rs at line 115
    [3.2111][3.2111:2207]()
    note = "There must be at least one named field (unit structs are unsupported!)"
    [3.2111]
    [3.2207]
    note = "There must be at least one named field (unit structs are unsupported!)";
    }
    }
    }
    }
    fn parse(error: ParseError, attribute_stream: TokenStream, derive_input_stream: TokenStream) {
    match error {
    ParseError::InvalidAttribute(invalid_attribute) => {
    emit_error! { attribute_stream, invalid_attribute;
    help = "Expected a path glob, for example {}",
    r#"#[localize("i18n/**/strings.ftl")]"#;
    }
    }
    ParseError::InvalidDeriveInput(invalid_derive_input) => {
    emit_error! { derive_input_stream, invalid_derive_input;
    help = "This macro can only be used on structs or enums";
  • replacement in fluent_embed_derive/src/macro_impl/error.rs at line 137
    [4.4667][4.2971:3035]()
    pub fn emit(error: MacroError, attribute_stream: TokenStream) {
    [4.4667]
    [4.4797]
    pub fn emit(error: MacroError, attribute_stream: TokenStream, derive_input_stream: TokenStream) {
  • edit in fluent_embed_derive/src/macro_impl/error.rs at line 142
    [3.2294]
    [4.5206]
    MacroError::ParseError(error) => parse(error, attribute_stream, derive_input_stream),
  • replacement in fluent_embed_derive/src/lib.rs at line 2
    [4.75][4.75:115]()
    use proc_macro_error::proc_macro_error;
    [4.75]
    [4.4348]
    use proc_macro_error::{proc_macro_error};
  • edit in fluent_embed_derive/src/lib.rs at line 4
    [4.4366][4.116:144]()
    use syn::parse_macro_input;
  • replacement in fluent_embed_derive/src/lib.rs at line 11
    [4.510][4.510:580](),[4.580][4.5096:5174](),[4.5174][4.580:700](),[4.580][4.580:700]()
    let original_item = proc_macro2::TokenStream::from(item.clone());
    let attribute_stream = proc_macro2::TokenStream::from(attribute.clone());
    let derive_attribute: syn::LitStr = parse_macro_input!(attribute);
    let derive_input = parse_macro_input!(item);
    [4.510]
    [4.700]
    let attribute_stream = proc_macro2::TokenStream::from(attribute);
    let derive_input_stream = proc_macro2::TokenStream::from(item);
  • replacement in fluent_embed_derive/src/lib.rs at line 14
    [4.701][4.5175:5242]()
    match macro_impl::localize(&derive_attribute, &derive_input) {
    [4.701]
    [4.5242]
    match macro_impl::localize(attribute_stream.clone(), derive_input_stream.clone()) {
  • replacement in fluent_embed_derive/src/lib.rs at line 19
    [4.3498][4.789:819]()
    #derive_input
    [4.3498]
    [4.869]
    #derive_input_stream
  • replacement in fluent_embed_derive/src/lib.rs at line 36
    [4.5545][4.5545:5607]()
    macro_impl::error::emit(error, attribute_stream);
    [4.5545]
    [4.906]
    macro_impl::error::emit(error, attribute_stream, derive_input_stream.clone());
  • replacement in fluent_embed_derive/src/lib.rs at line 39
    [2.91][4.5701:5766](),[4.5701][4.5701:5766](),[4.5766][2.92:574]()
    let ident = derive_input.ident;
    quote! {
    #original_item
    impl<W: ::std::io::Write> ::fluent_embed::Localize<W> for #ident {
    const CANONICAL_LOCALE: ::fluent_embed::icu_locale::LanguageIdentifier =
    ::fluent_embed::icu_locale::langid!("en-US");
    fn available_locales(&self) -> Vec<::fluent_embed::icu_locale::LanguageIdentifier> {
    unimplemented!("Encountered error in derive macro")
    }
    [2.91]
    [2.574]
    match syn::parse2::<syn::DeriveInput>(derive_input_stream.clone()) {
    Ok(derive_input) => {
    let ident = derive_input.ident;
    quote! {
    #derive_input_stream
    impl<W: ::std::io::Write> ::fluent_embed::Localize<W> for #ident {
    const CANONICAL_LOCALE: ::fluent_embed::icu_locale::LanguageIdentifier =
    ::fluent_embed::icu_locale::langid!("en-US");
  • replacement in fluent_embed_derive/src/lib.rs at line 49
    [2.583][2.583:851](),[2.851][4.5847:5923](),[4.5847][4.5847:5923]()
    fn message_for_locale(
    &self,
    writer: &mut W,
    locale: &::fluent_embed::icu_locale::LanguageIdentifier,
    ) -> Result<(), ::fluent_embed::LocalizationError> {
    unimplemented!("Encountered error in derive macro")
    [2.583]
    [4.5923]
    fn available_locales(&self) -> Vec<::fluent_embed::icu_locale::LanguageIdentifier> {
    unimplemented!("Encountered error in derive macro")
    }
    fn message_for_locale(
    &self,
    writer: &mut W,
    locale: &::fluent_embed::icu_locale::LanguageIdentifier,
    ) -> Result<(), ::fluent_embed::LocalizationError> {
    unimplemented!("Encountered error in derive macro")
    }
    }
  • replacement in fluent_embed_derive/src/lib.rs at line 62
    [4.5945][4.5945:5963]()
    }
    [4.5945]
    [4.5963]
    },
    // Unable to fail gracefully if the ident is unknown, so just return the original input
    Err(_) => derive_input_stream,
  • edit in fluent_embed_derive/src/lib.rs at line 66
    [4.5977]
    [4.5977]