proc-macro-workshop:sorted-6

godme發表於2022-07-06
// When we checked enum definitions for sortedness, it was sufficient to compare
// a single identifier (the name of the variant) for each variant. Match
// expressions are different in that each arm may have a pattern that consists
// of more than just one identifier.
//
// Ensure that patterns consisting of a path are correctly tested for
// sortedness. These patterns will be of type Pat::Path, Pat::TupleStruct, or
// Pat::Struct.
//
//
// Resources:
//
//   - The syn::Pat syntax tree which forms the left hand side of a match arm:
//     https://docs.rs/syn/1.0/syn/enum.Pat.html

use sorted::sorted;

use std::fmt::{self, Display};
use std::io;

#[sorted]
pub enum Error {
    Fmt(fmt::Error),
    Io(io::Error),
}

impl Display for Error {
    #[sorted::check]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        #[sorted]
        match self {
            Error::Io(e) => write!(f, "{}", e),
            Error::Fmt(e) => write!(f, "{}", e),
        }
    }
}

fn main() {}

好嘛,提示都沒了。

error: Error::Fmt should sort before Error::Io
  --> tests/06-pattern-path.rs:33:13
   |
33 |             Error::Fmt(e) => write!(f, "{}", e),
   |             ^^^^^^^^^^

可以看到,不論是span還是ident都是全路徑的。
因此我們需要把pathsegments全部進行拼接。

但是之前已經說明,sorted已經完結了,是因為在上一題已經做了解答


impl syn::visit_mut::VisitMut for MatchVisitor {
    fn visit_expr_match_mut(&mut self, i: &mut syn::ExprMatch) {
        let mut target_idx: isize = -1;
        for (idx, attr) in i.attrs.iter().enumerate() {
            if path_to_string(&attr.path) == "sorted" {
                target_idx = idx as isize;
                break;
            }
        }
        if target_idx != -1 {
            i.attrs.remove(target_idx as usize);
            let mut match_arm_names: Vec<(String, &dyn quote::ToTokens)> = Vec::new();
            for arm in i.arms.iter() {
                // 全路徑解析
                match &arm.pat {
                    syn::Pat::Path(p) => {
                        match_arm_names.push((path_to_string(&p.path), &p.path));
                    }
                    syn::Pat::TupleStruct(p) => {
                        match_arm_names.push((path_to_string(&p.path), &p.path));
                    }
                    syn::Pat::Struct(p) => {
                        match_arm_names.push((path_to_string(&p.path), &p.path));
                    }
                    syn::Pat::Ident(p) => {
                        match_arm_names.push((p.ident.to_string(), &p.ident));
                    }
                    syn::Pat::Wild(p) => {
                        match_arm_names.push(("_".to_string(), &p.underscore_token));
                    }
                    _ => {
                        self.err = std::option::Option::Some(syn::Error::new_spanned(
                            &arm.pat,
                            "unsupported by #[sorted]",
                        ));
                        return;
                    }
                }
            }
            if let Some(e) = check_order(match_arm_names) {
                self.err = std::option::Option::Some(e);
                return;
            }
        }
        syn::visit_mut::visit_expr_match_mut(self, i)
    }
}
// 全路徑拼接
fn path_to_string(path: &syn::Path) -> String {
    path.segments
        .iter()
        .map(|s| s.ident.to_string())
        .collect::<Vec<String>>()
        .join("::")
}

不過仔細核對錯誤,我們能更加的深刻的理解

// 因為做了path_to_string,所以這裡提示才會全路徑
error: Error::Fmt should sort before Error::Io 
  --> tests/06-pattern-path.rs:33:13
   |                // 因為使用的&p.path,所以span才會剛好
33 |             Error::Fmt(e) => write!(f, "{}", e),
   |             ^^^^^^^^^^

瞎貓碰上死耗子

本作品採用《CC 協議》,轉載必須註明作者和本文連結