Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  repr.rs   Sprache: unbekannt

 
// Copyright 2019 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.

use core::fmt::{self, Display, Formatter};

use {
    proc_macro2::Span,
    syn::punctuated::Punctuated,
    syn::spanned::Spanned,
    syn::token::Comma,
    syn::{Attribute, DeriveInput, Error, LitInt, Meta},
};

pub struct Config<Repr: KindRepr> {
    // A human-readable message describing what combinations of representations
    // are allowed. This will be printed to the user if they use an invalid
    // combination.
    pub allowed_combinations_message: &'static str,
    // Whether we're checking as part of `derive(Unaligned)`. If not, we can
    // ignore `repr(align)`, which makes the code (and the list of valid repr
    // combinations we have to enumerate) somewhat simpler. If we're checking
    // for `Unaligned`, then in addition to checking against illegal
    // combinations, we also check to see if there exists a `repr(align(N > 1))`
    // attribute.
    pub derive_unaligned: bool,
    // Combinations which are valid for the trait.
    pub allowed_combinations: &'static [&'static [Repr]],
    // Combinations which are not valid for the trait, but are legal according
    // to Rust. Any combination not in this or `allowed_combinations` is either
    // illegal according to Rust or the behavior is unspecified. If the behavior
    // is unspecified, it might become specified in the future, and that
    // specification might not play nicely with our requirements. Thus, we
    // reject combinations with unspecified behavior in addition to illegal
    // combinations.
    pub disallowed_but_legal_combinations: &'static [&'static [Repr]],
}

impl<R: KindRepr> Config<R> {
    /// Validate that `input`'s representation attributes conform to the
    /// requirements specified by this `Config`.
    ///
    /// `validate_reprs` extracts the `repr` attributes, validates that they
    /// conform to the requirements of `self`, and returns them. Regardless of
    /// whether `align` attributes are considered during validation, they are
    /// stripped out of the returned value since no callers care about them.
    pub fn validate_reprs(&self, input: &DeriveInput) -> Result<Vec<R>, Vec<Error>> {
        let mut metas_reprs = reprs(&input.attrs)?;
        metas_reprs.sort_by(|a: &(_, R), b| a.1.partial_cmp(&b.1).unwrap());

        if self.derive_unaligned {
            if let Some((meta, _)) =
                metas_reprs.iter().find(|&repr: &&(_, R)| repr.1.is_align_gt_one())
            {
                return Err(vec![Error::new_spanned(
                    meta,
                    "cannot derive Unaligned with repr(align(N > 1))",
                )]);
            }
        }

        let mut metas = Vec::new();
        let mut reprs = Vec::new();
        metas_reprs.into_iter().filter(|(_, repr)| !repr.is_align()).for_each(|(meta, repr)| {
            metas.push(meta);
            reprs.push(repr)
        });

        if reprs.is_empty() {
            // Use `Span::call_site` to report this error on the
            // `#[derive(...)]` itself.
            return Err(vec![Error::new(Span::call_site(), "must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout")]);
        }

        let initial_sp = metas[0].span();
        let err_span = metas.iter().skip(1).try_fold(initial_sp, |sp, meta| sp.join(meta.span()));

        if self.allowed_combinations.contains(&reprs.as_slice()) {
            Ok(reprs)
        } else if self.disallowed_but_legal_combinations.contains(&reprs.as_slice()) {
            Err(vec![Error::new(
                err_span.unwrap_or_else(|| input.span()),
                self.allowed_combinations_message,
            )])
        } else {
            Err(vec![Error::new(
                err_span.unwrap_or_else(|| input.span()),
                "conflicting representation hints",
            )])
        }
    }
}

// The type of valid reprs for a particular kind (enum, struct, union).
pub trait KindRepr: 'static + Sized + Ord {
    fn is_align(&self) -> bool;
    fn is_align_gt_one(&self) -> bool;
    fn parse(meta: &Meta) -> syn::Result<Self>;
}

// Defines an enum for reprs which are valid for a given kind (structs, enums,
// etc), and provide implementations of `KindRepr`, `Ord`, and `Display`, and
// those traits' super-traits.
macro_rules! define_kind_specific_repr {
    ($type_name:expr, $repr_name:ident, [ $($repr_variant:ident),* ] , [ $($repr_variant_aligned:ident),* ]) => {
        #[derive(Copy, Clone, Debug, Eq, PartialEq)]
        pub enum $repr_name {
            $($repr_variant,)*
            $($repr_variant_aligned(u64),)*
        }

        impl KindRepr for $repr_name {
            fn is_align(&self) -> bool {
                match self {
                    $($repr_name::$repr_variant_aligned(_) => true,)*
                    _ => false,
                }
            }

            fn is_align_gt_one(&self) -> bool {
                match self {
                    // `packed(n)` only lowers alignment
                    $repr_name::Align(n) => n > &1,
                    _ => false,
                }
            }

            fn parse(meta: &Meta) -> syn::Result<$repr_name> {
                match Repr::from_meta(meta)? {
                    $(Repr::$repr_variant => Ok($repr_name::$repr_variant),)*
                    $(Repr::$repr_variant_aligned(u) => Ok($repr_name::$repr_variant_aligned(u)),)*
                    _ => Err(Error::new_spanned(meta, concat!("unsupported representation for deriving FromBytes, AsBytes, or Unaligned on ", $type_name)))
                }
            }
        }

        // Define a stable ordering so we can canonicalize lists of reprs. The
        // ordering itself doesn't matter so long as it's stable.
        impl PartialOrd for $repr_name {
            fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
                Some(self.cmp(other))
            }
        }

        impl Ord for $repr_name {
            fn cmp(&self, other: &Self) -> core::cmp::Ordering {
                format!("{:?}", self).cmp(&format!("{:?}", other))
            }
        }

        impl core::fmt::Display for $repr_name {
            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
                match self {
                    $($repr_name::$repr_variant => Repr::$repr_variant,)*
                    $($repr_name::$repr_variant_aligned(u) => Repr::$repr_variant_aligned(*u),)*
                }.fmt(f)
            }
        }
    }
}

define_kind_specific_repr!("a struct", StructRepr, [C, Transparent, Packed], [Align, PackedN]);
define_kind_specific_repr!(
    "an enum",
    EnumRepr,
    [C, U8, U16, U32, U64, Usize, I8, I16, I32, I64, Isize],
    [Align]
);

// All representations known to Rust.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum Repr {
    U8,
    U16,
    U32,
    U64,
    Usize,
    I8,
    I16,
    I32,
    I64,
    Isize,
    C,
    Transparent,
    Packed,
    PackedN(u64),
    Align(u64),
}

impl Repr {
    fn from_meta(meta: &Meta) -> Result<Repr, Error> {
        let (path, list) = match meta {
            Meta::Path(path) => (path, None),
            Meta::List(list) => (&list.path, Some(list)),
            _ => return Err(Error::new_spanned(meta, "unrecognized representation hint")),
        };

        let ident = path
            .get_ident()
            .ok_or_else(|| Error::new_spanned(meta, "unrecognized representation hint"))?;

        Ok(match (ident.to_string().as_str(), list) {
            ("u8", None) => Repr::U8,
            ("u16", None) => Repr::U16,
            ("u32", None) => Repr::U32,
            ("u64", None) => Repr::U64,
            ("usize", None) => Repr::Usize,
            ("i8", None) => Repr::I8,
            ("i16", None) => Repr::I16,
            ("i32", None) => Repr::I32,
            ("i64", None) => Repr::I64,
            ("isize", None) => Repr::Isize,
            ("C", None) => Repr::C,
            ("transparent", None) => Repr::Transparent,
            ("packed", None) => Repr::Packed,
            ("packed", Some(list)) => {
                Repr::PackedN(list.parse_args::<LitInt>()?.base10_parse::<u64>()?)
            }
            ("align", Some(list)) => {
                Repr::Align(list.parse_args::<LitInt>()?.base10_parse::<u64>()?)
            }
            _ => return Err(Error::new_spanned(meta, "unrecognized representation hint")),
        })
    }
}

impl KindRepr for Repr {
    fn is_align(&self) -> bool {
        false
    }

    fn is_align_gt_one(&self) -> bool {
        false
    }

    fn parse(meta: &Meta) -> syn::Result<Self> {
        Self::from_meta(meta)
    }
}

impl Display for Repr {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
        if let Repr::Align(n) = self {
            return write!(f, "repr(align({}))", n);
        }
        if let Repr::PackedN(n) = self {
            return write!(f, "repr(packed({}))", n);
        }
        write!(
            f,
            "repr({})",
            match self {
                Repr::U8 => "u8",
                Repr::U16 => "u16",
                Repr::U32 => "u32",
                Repr::U64 => "u64",
                Repr::Usize => "usize",
                Repr::I8 => "i8",
                Repr::I16 => "i16",
                Repr::I32 => "i32",
                Repr::I64 => "i64",
                Repr::Isize => "isize",
                Repr::C => "C",
                Repr::Transparent => "transparent",
                Repr::Packed => "packed",
                _ => unreachable!(),
            }
        )
    }
}

pub(crate) fn reprs<R: KindRepr>(attrs: &[Attribute]) -> Result<Vec<(Meta, R)>, Vec<Error>> {
    let mut reprs = Vec::new();
    let mut errors = Vec::new();
    for attr in attrs {
        // Ignore documentation attributes.
        if attr.path().is_ident("doc") {
            continue;
        }
        if let Meta::List(ref meta_list) = attr.meta {
            if meta_list.path.is_ident("repr") {
                let parsed: Punctuated<Meta, Comma> =
                    match meta_list.parse_args_with(Punctuated::parse_terminated) {
                        Ok(parsed) => parsed,
                        Err(_) => {
                            errors.push(Error::new_spanned(
                                &meta_list.tokens,
                                "unrecognized representation hint",
                            ));
                            continue;
                        }
                    };
                for meta in parsed {
                    match R::parse(&meta) {
                        Ok(repr) => reprs.push((meta, repr)),
                        Err(err) => errors.push(err),
                    }
                }
            }
        }
    }

    if !errors.is_empty() {
        return Err(errors);
    }
    Ok(reprs)
}

[ Dauer der Verarbeitung: 0.2 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge