| use Piece::*; | |
| use super::*; | |
| #[track_caller] | |
| fn same(fmt: &'static str, p: &[Piece<'static>]) { | |
| let parser = Parser::new(fmt, None, None, false, ParseMode::Format); | |
| assert_eq!(parser.collect::<Vec<Piece<'static>>>(), p); | |
| } | |
| fn fmtdflt() -> FormatSpec<'static> { | |
| return FormatSpec { | |
| fill: None, | |
| fill_span: None, | |
| align: AlignUnknown, | |
| sign: None, | |
| alternate: false, | |
| zero_pad: false, | |
| debug_hex: None, | |
| precision: CountImplied, | |
| width: CountImplied, | |
| precision_span: None, | |
| width_span: None, | |
| ty: "", | |
| ty_span: None, | |
| }; | |
| } | |
| fn musterr(s: &str) { | |
| let mut p = Parser::new(s, None, None, false, ParseMode::Format); | |
| p.next(); | |
| assert!(!p.errors.is_empty()); | |
| } | |
| #[test] | |
| fn simple() { | |
| same("asdf", &[Lit("asdf")]); | |
| same("a{{b", &[Lit("a"), Lit("{b")]); | |
| same("a}}b", &[Lit("a"), Lit("}b")]); | |
| same("a}}", &[Lit("a"), Lit("}")]); | |
| same("}}", &[Lit("}")]); | |
| same("\\}}", &[Lit("\\"), Lit("}")]); | |
| } | |
| #[test] | |
| fn invalid01() { | |
| musterr("{") | |
| } | |
| #[test] | |
| fn invalid02() { | |
| musterr("}") | |
| } | |
| #[test] | |
| fn invalid04() { | |
| musterr("{3a}") | |
| } | |
| #[test] | |
| fn invalid05() { | |
| musterr("{:|}") | |
| } | |
| #[test] | |
| fn invalid06() { | |
| musterr("{:>>>}") | |
| } | |
| #[test] | |
| fn invalid_position() { | |
| musterr("{18446744073709551616}"); | |
| } | |
| #[test] | |
| fn invalid_width() { | |
| musterr("{:18446744073709551616}"); | |
| } | |
| #[test] | |
| fn invalid_precision() { | |
| musterr("{:.18446744073709551616}"); | |
| } | |
| #[test] | |
| fn format_empty() { | |
| same( | |
| "{}", | |
| &[NextArgument(Box::new(Argument { | |
| position: ArgumentImplicitlyIs(0), | |
| position_span: 2..2, | |
| format: fmtdflt(), | |
| }))], | |
| ); | |
| } | |
| #[test] | |
| fn format_tab_empty() { | |
| let fmt_pre = r###""\t{}""###; | |
| let fmt = "\t{}"; | |
| let parser = Parser::new(fmt, None, Some(fmt_pre.into()), false, ParseMode::Format); | |
| assert_eq!( | |
| parser.collect::<Vec<Piece<'static>>>(), | |
| &[ | |
| Lit("\t"), | |
| NextArgument(Box::new(Argument { | |
| position: ArgumentImplicitlyIs(0), | |
| position_span: 4..4, | |
| format: fmtdflt(), | |
| })) | |
| ], | |
| ); | |
| } | |
| #[test] | |
| fn format_open_brace_tab() { | |
| let fmt_pre = r###""{\t""###; | |
| let fmt = "{\t"; | |
| let mut parser = Parser::new(fmt, None, Some(fmt_pre.into()), false, ParseMode::Format); | |
| let _ = parser.by_ref().collect::<Vec<Piece<'static>>>(); | |
| assert_eq!(parser.errors[0].span, 4..4); | |
| } | |
| #[test] | |
| fn format_position() { | |
| same( | |
| "{3}", | |
| &[NextArgument(Box::new(Argument { | |
| position: ArgumentIs(3), | |
| position_span: 2..3, | |
| format: fmtdflt(), | |
| }))], | |
| ); | |
| } | |
| #[test] | |
| fn format_position_nothing_else() { | |
| same( | |
| "{3:}", | |
| &[NextArgument(Box::new(Argument { | |
| position: ArgumentIs(3), | |
| position_span: 2..3, | |
| format: fmtdflt(), | |
| }))], | |
| ); | |
| } | |
| #[test] | |
| fn format_named() { | |
| same( | |
| "{name}", | |
| &[NextArgument(Box::new(Argument { | |
| position: ArgumentNamed("name"), | |
| position_span: 2..6, | |
| format: fmtdflt(), | |
| }))], | |
| ) | |
| } | |
| #[test] | |
| fn format_named_space_nothing() { | |
| same( | |
| "{name} {}", | |
| &[ | |
| NextArgument(Box::new(Argument { | |
| position: ArgumentNamed("name"), | |
| position_span: 2..6, | |
| format: fmtdflt(), | |
| })), | |
| Lit(" "), | |
| NextArgument(Box::new(Argument { | |
| position: ArgumentImplicitlyIs(0), | |
| position_span: 9..9, | |
| format: fmtdflt(), | |
| })), | |
| ], | |
| ) | |
| } | |
| #[test] | |
| fn format_raw() { | |
| let snippet = r###"r#"assertion `left {op} right` failed"#"###.into(); | |
| let source = r#"assertion `left {op} right` failed"#; | |
| let parser = Parser::new(source, Some(1), Some(snippet), true, ParseMode::Format); | |
| let expected = &[ | |
| Lit("assertion `left "), | |
| NextArgument(Box::new(Argument { | |
| position: ArgumentNamed("op"), | |
| position_span: 20..22, | |
| format: fmtdflt(), | |
| })), | |
| Lit(" right` failed"), | |
| ]; | |
| assert_eq!(parser.collect::<Vec<Piece<'static>>>(), expected); | |
| } | |
| #[test] | |
| fn format_type() { | |
| same( | |
| "{3:x}", | |
| &[NextArgument(Box::new(Argument { | |
| position: ArgumentIs(3), | |
| position_span: 2..3, | |
| format: FormatSpec { | |
| fill: None, | |
| fill_span: None, | |
| align: AlignUnknown, | |
| sign: None, | |
| alternate: false, | |
| zero_pad: false, | |
| debug_hex: None, | |
| precision: CountImplied, | |
| width: CountImplied, | |
| precision_span: None, | |
| width_span: None, | |
| ty: "x", | |
| ty_span: None, | |
| }, | |
| }))], | |
| ); | |
| } | |
| #[test] | |
| fn format_align_fill() { | |
| same( | |
| "{3:>}", | |
| &[NextArgument(Box::new(Argument { | |
| position: ArgumentIs(3), | |
| position_span: 2..3, | |
| format: FormatSpec { | |
| fill: None, | |
| fill_span: None, | |
| align: AlignRight, | |
| sign: None, | |
| alternate: false, | |
| zero_pad: false, | |
| debug_hex: None, | |
| precision: CountImplied, | |
| width: CountImplied, | |
| precision_span: None, | |
| width_span: None, | |
| ty: "", | |
| ty_span: None, | |
| }, | |
| }))], | |
| ); | |
| same( | |
| "{3:0<}", | |
| &[NextArgument(Box::new(Argument { | |
| position: ArgumentIs(3), | |
| position_span: 2..3, | |
| format: FormatSpec { | |
| fill: Some('0'), | |
| fill_span: Some(4..5), | |
| align: AlignLeft, | |
| sign: None, | |
| alternate: false, | |
| zero_pad: false, | |
| debug_hex: None, | |
| precision: CountImplied, | |
| width: CountImplied, | |
| precision_span: None, | |
| width_span: None, | |
| ty: "", | |
| ty_span: None, | |
| }, | |
| }))], | |
| ); | |
| same( | |
| "{3:*<abcd}", | |
| &[NextArgument(Box::new(Argument { | |
| position: ArgumentIs(3), | |
| position_span: 2..3, | |
| format: FormatSpec { | |
| fill: Some('*'), | |
| fill_span: Some(4..5), | |
| align: AlignLeft, | |
| sign: None, | |
| alternate: false, | |
| zero_pad: false, | |
| debug_hex: None, | |
| precision: CountImplied, | |
| width: CountImplied, | |
| precision_span: None, | |
| width_span: None, | |
| ty: "abcd", | |
| ty_span: Some(6..10), | |
| }, | |
| }))], | |
| ); | |
| } | |
| #[test] | |
| fn format_counts() { | |
| same( | |
| "{:10x}", | |
| &[NextArgument(Box::new(Argument { | |
| position: ArgumentImplicitlyIs(0), | |
| position_span: 2..2, | |
| format: FormatSpec { | |
| fill: None, | |
| fill_span: None, | |
| align: AlignUnknown, | |
| sign: None, | |
| alternate: false, | |
| zero_pad: false, | |
| debug_hex: None, | |
| precision: CountImplied, | |
| precision_span: None, | |
| width: CountIs(10), | |
| width_span: Some(3..5), | |
| ty: "x", | |
| ty_span: None, | |
| }, | |
| }))], | |
| ); | |
| same( | |
| "{:10$.10x}", | |
| &[NextArgument(Box::new(Argument { | |
| position: ArgumentImplicitlyIs(0), | |
| position_span: 2..2, | |
| format: FormatSpec { | |
| fill: None, | |
| fill_span: None, | |
| align: AlignUnknown, | |
| sign: None, | |
| alternate: false, | |
| zero_pad: false, | |
| debug_hex: None, | |
| precision: CountIs(10), | |
| precision_span: Some(6..9), | |
| width: CountIsParam(10), | |
| width_span: Some(3..6), | |
| ty: "x", | |
| ty_span: None, | |
| }, | |
| }))], | |
| ); | |
| same( | |
| "{1:0$.10x}", | |
| &[NextArgument(Box::new(Argument { | |
| position: ArgumentIs(1), | |
| position_span: 2..3, | |
| format: FormatSpec { | |
| fill: None, | |
| fill_span: None, | |
| align: AlignUnknown, | |
| sign: None, | |
| alternate: false, | |
| zero_pad: false, | |
| debug_hex: None, | |
| precision: CountIs(10), | |
| precision_span: Some(6..9), | |
| width: CountIsParam(0), | |
| width_span: Some(4..6), | |
| ty: "x", | |
| ty_span: None, | |
| }, | |
| }))], | |
| ); | |
| same( | |
| "{:.*x}", | |
| &[NextArgument(Box::new(Argument { | |
| position: ArgumentImplicitlyIs(1), | |
| position_span: 2..2, | |
| format: FormatSpec { | |
| fill: None, | |
| fill_span: None, | |
| align: AlignUnknown, | |
| sign: None, | |
| alternate: false, | |
| zero_pad: false, | |
| debug_hex: None, | |
| precision: CountIsStar(0), | |
| precision_span: Some(3..5), | |
| width: CountImplied, | |
| width_span: None, | |
| ty: "x", | |
| ty_span: None, | |
| }, | |
| }))], | |
| ); | |
| same( | |
| "{:.10$x}", | |
| &[NextArgument(Box::new(Argument { | |
| position: ArgumentImplicitlyIs(0), | |
| position_span: 2..2, | |
| format: FormatSpec { | |
| fill: None, | |
| fill_span: None, | |
| align: AlignUnknown, | |
| sign: None, | |
| alternate: false, | |
| zero_pad: false, | |
| debug_hex: None, | |
| precision: CountIsParam(10), | |
| width: CountImplied, | |
| precision_span: Some(3..7), | |
| width_span: None, | |
| ty: "x", | |
| ty_span: None, | |
| }, | |
| }))], | |
| ); | |
| same( | |
| "{:a$.b$?}", | |
| &[NextArgument(Box::new(Argument { | |
| position: ArgumentImplicitlyIs(0), | |
| position_span: 2..2, | |
| format: FormatSpec { | |
| fill: None, | |
| fill_span: None, | |
| align: AlignUnknown, | |
| sign: None, | |
| alternate: false, | |
| zero_pad: false, | |
| debug_hex: None, | |
| precision: CountIsName("b", 6..7), | |
| precision_span: Some(5..8), | |
| width: CountIsName("a", 3..4), | |
| width_span: Some(3..5), | |
| ty: "?", | |
| ty_span: None, | |
| }, | |
| }))], | |
| ); | |
| same( | |
| "{:.4}", | |
| &[NextArgument(Box::new(Argument { | |
| position: ArgumentImplicitlyIs(0), | |
| position_span: 2..2, | |
| format: FormatSpec { | |
| fill: None, | |
| fill_span: None, | |
| align: AlignUnknown, | |
| sign: None, | |
| alternate: false, | |
| zero_pad: false, | |
| debug_hex: None, | |
| precision: CountIs(4), | |
| precision_span: Some(3..5), | |
| width: CountImplied, | |
| width_span: None, | |
| ty: "", | |
| ty_span: None, | |
| }, | |
| }))], | |
| ) | |
| } | |
| #[test] | |
| fn format_flags() { | |
| same( | |
| "{:-}", | |
| &[NextArgument(Box::new(Argument { | |
| position: ArgumentImplicitlyIs(0), | |
| position_span: 2..2, | |
| format: FormatSpec { | |
| fill: None, | |
| fill_span: None, | |
| align: AlignUnknown, | |
| sign: Some(Sign::Minus), | |
| alternate: false, | |
| zero_pad: false, | |
| debug_hex: None, | |
| precision: CountImplied, | |
| width: CountImplied, | |
| precision_span: None, | |
| width_span: None, | |
| ty: "", | |
| ty_span: None, | |
| }, | |
| }))], | |
| ); | |
| same( | |
| "{:+#}", | |
| &[NextArgument(Box::new(Argument { | |
| position: ArgumentImplicitlyIs(0), | |
| position_span: 2..2, | |
| format: FormatSpec { | |
| fill: None, | |
| fill_span: None, | |
| align: AlignUnknown, | |
| sign: Some(Sign::Plus), | |
| alternate: true, | |
| zero_pad: false, | |
| debug_hex: None, | |
| precision: CountImplied, | |
| width: CountImplied, | |
| precision_span: None, | |
| width_span: None, | |
| ty: "", | |
| ty_span: None, | |
| }, | |
| }))], | |
| ); | |
| } | |
| #[test] | |
| fn format_mixture() { | |
| same( | |
| "abcd {3:x} efg", | |
| &[ | |
| Lit("abcd "), | |
| NextArgument(Box::new(Argument { | |
| position: ArgumentIs(3), | |
| position_span: 7..8, | |
| format: FormatSpec { | |
| fill: None, | |
| fill_span: None, | |
| align: AlignUnknown, | |
| sign: None, | |
| alternate: false, | |
| zero_pad: false, | |
| debug_hex: None, | |
| precision: CountImplied, | |
| width: CountImplied, | |
| precision_span: None, | |
| width_span: None, | |
| ty: "x", | |
| ty_span: None, | |
| }, | |
| })), | |
| Lit(" efg"), | |
| ], | |
| ); | |
| } | |
| #[test] | |
| fn format_whitespace() { | |
| same( | |
| "{ }", | |
| &[NextArgument(Box::new(Argument { | |
| position: ArgumentImplicitlyIs(0), | |
| position_span: 2..3, | |
| format: fmtdflt(), | |
| }))], | |
| ); | |
| same( | |
| "{ }", | |
| &[NextArgument(Box::new(Argument { | |
| position: ArgumentImplicitlyIs(0), | |
| position_span: 2..4, | |
| format: fmtdflt(), | |
| }))], | |
| ); | |
| } | |
| #[test] | |
| fn asm_linespans() { | |
| let asm_pre = r###"r" | |
| .intel_syntax noprefix | |
| nop""###; | |
| let asm = r" | |
| .intel_syntax noprefix | |
| nop"; | |
| let mut parser = Parser::new(asm, Some(0), Some(asm_pre.into()), false, ParseMode::InlineAsm); | |
| assert!(parser.is_source_literal); | |
| assert_eq!( | |
| parser.by_ref().collect::<Vec<Piece<'static>>>(), | |
| &[Lit("\n .intel_syntax noprefix\n nop")] | |
| ); | |
| assert_eq!(parser.line_spans, &[2..2, 11..33, 42..45]); | |
| } | |
| #[test] | |
| fn asm_concat() { | |
| let asm_pre = r###"concat!("invalid", "_", "instruction")"###; | |
| let asm = "invalid_instruction"; | |
| let mut parser = Parser::new(asm, None, Some(asm_pre.into()), false, ParseMode::InlineAsm); | |
| assert!(!parser.is_source_literal); | |
| assert_eq!(parser.by_ref().collect::<Vec<Piece<'static>>>(), &[Lit(asm)]); | |
| assert_eq!(parser.line_spans, &[]); | |
| } | |
| #[test] | |
| fn diagnostic_format_flags() { | |
| let lit = "{thing:blah}"; | |
| let mut parser = Parser::new(lit, None, None, false, ParseMode::Diagnostic); | |
| assert!(!parser.is_source_literal); | |
| let [NextArgument(arg)] = &*parser.by_ref().collect::<Vec<Piece<'static>>>() else { panic!() }; | |
| assert_eq!( | |
| **arg, | |
| Argument { | |
| position: ArgumentNamed("thing"), | |
| position_span: 2..7, | |
| format: FormatSpec { ty: "blah", ty_span: Some(7..12), ..Default::default() }, | |
| } | |
| ); | |
| assert_eq!(parser.line_spans, &[]); | |
| assert!(parser.errors.is_empty()); | |
| } | |
| #[test] | |
| fn diagnostic_format_mod() { | |
| let lit = "{thing:+}"; | |
| let mut parser = Parser::new(lit, None, None, false, ParseMode::Diagnostic); | |
| assert!(!parser.is_source_literal); | |
| let [NextArgument(arg)] = &*parser.by_ref().collect::<Vec<Piece<'static>>>() else { panic!() }; | |
| assert_eq!( | |
| **arg, | |
| Argument { | |
| position: ArgumentNamed("thing"), | |
| position_span: 2..7, | |
| format: FormatSpec { ty: "+", ty_span: Some(7..9), ..Default::default() }, | |
| } | |
| ); | |
| assert_eq!(parser.line_spans, &[]); | |
| assert!(parser.errors.is_empty()); | |
| } | |
| #[test] | |
| fn format_plus_sign_missing_colon_error() { | |
| // `{+}` should produce an error suggesting `:` before `+` | |
| let mut p = Parser::new("{+}", None, None, false, ParseMode::Format); | |
| let _ = p.by_ref().collect::<Vec<Piece<'static>>>(); | |
| assert!(!p.errors.is_empty()); | |
| assert!(p.errors[0].description.contains("`+` sign flag must appear after `:`")); | |
| assert!(p.errors[0].label.contains("expected `:` before `+` sign flag")); | |
| assert_eq!(p.errors[0].span, 2..3); | |
| } |