mirror of
				https://github.com/rust-lang/rust-analyzer.git
				synced 2025-10-31 12:04:43 +00:00 
			
		
		
		
	Minic rustc's new format_args! expansion
				
					
				
			This commit is contained in:
		
							parent
							
								
									c13859903c
								
							
						
					
					
						commit
						4f8767d790
					
				
					 7 changed files with 366 additions and 41 deletions
				
			
		|  | @ -2815,6 +2815,51 @@ impl ExprCollector<'_> { | |||
|                 mutability: Mutability::Shared, | ||||
|             }) | ||||
|         }; | ||||
| 
 | ||||
|         // Assume that rustc version >= 1.89.0 iff lang item `format_arguments` exists
 | ||||
|         // but `format_unsafe_arg` does not
 | ||||
|         let fmt_args = | ||||
|             || crate::lang_item::lang_item(self.db, self.module.krate(), LangItem::FormatArguments); | ||||
|         let fmt_unsafe_arg = | ||||
|             || crate::lang_item::lang_item(self.db, self.module.krate(), LangItem::FormatUnsafeArg); | ||||
|         let use_format_args_since_1_89_0 = fmt_args().is_some() && fmt_unsafe_arg().is_none(); | ||||
| 
 | ||||
|         let idx = if use_format_args_since_1_89_0 { | ||||
|             self.collect_format_args_impl( | ||||
|                 syntax_ptr, | ||||
|                 fmt, | ||||
|                 hygiene, | ||||
|                 argmap, | ||||
|                 lit_pieces, | ||||
|                 format_options, | ||||
|             ) | ||||
|         } else { | ||||
|             self.collect_format_args_before_1_89_0_impl( | ||||
|                 syntax_ptr, | ||||
|                 fmt, | ||||
|                 argmap, | ||||
|                 lit_pieces, | ||||
|                 format_options, | ||||
|             ) | ||||
|         }; | ||||
| 
 | ||||
|         self.source_map | ||||
|             .template_map | ||||
|             .get_or_insert_with(Default::default) | ||||
|             .format_args_to_captures | ||||
|             .insert(idx, (hygiene, mappings)); | ||||
|         idx | ||||
|     } | ||||
| 
 | ||||
|     /// `format_args!` expansion implementation for rustc versions < `1.89.0`
 | ||||
|     fn collect_format_args_before_1_89_0_impl( | ||||
|         &mut self, | ||||
|         syntax_ptr: AstPtr<ast::Expr>, | ||||
|         fmt: FormatArgs, | ||||
|         argmap: FxIndexSet<(usize, ArgumentType)>, | ||||
|         lit_pieces: ExprId, | ||||
|         format_options: ExprId, | ||||
|     ) -> ExprId { | ||||
|         let arguments = &*fmt.arguments.arguments; | ||||
| 
 | ||||
|         let args = if arguments.is_empty() { | ||||
|  | @ -2902,19 +2947,181 @@ impl ExprCollector<'_> { | |||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         let idx = self.alloc_expr( | ||||
|         self.alloc_expr( | ||||
|             Expr::Call { | ||||
|                 callee: new_v1_formatted, | ||||
|                 args: Box::new([lit_pieces, args, format_options, unsafe_arg_new]), | ||||
|             }, | ||||
|             syntax_ptr, | ||||
|         ); | ||||
|         self.source_map | ||||
|             .template_map | ||||
|             .get_or_insert_with(Default::default) | ||||
|             .format_args_to_captures | ||||
|             .insert(idx, (hygiene, mappings)); | ||||
|         idx | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     /// `format_args!` expansion implementation for rustc versions >= `1.89.0`,
 | ||||
|     /// especially since [this PR](https://github.com/rust-lang/rust/pull/140748)
 | ||||
|     fn collect_format_args_impl( | ||||
|         &mut self, | ||||
|         syntax_ptr: AstPtr<ast::Expr>, | ||||
|         fmt: FormatArgs, | ||||
|         hygiene: HygieneId, | ||||
|         argmap: FxIndexSet<(usize, ArgumentType)>, | ||||
|         lit_pieces: ExprId, | ||||
|         format_options: ExprId, | ||||
|     ) -> ExprId { | ||||
|         let arguments = &*fmt.arguments.arguments; | ||||
| 
 | ||||
|         let (let_stmts, args) = if arguments.is_empty() { | ||||
|             ( | ||||
|                 // Generate:
 | ||||
|                 //     []
 | ||||
|                 vec![], | ||||
|                 self.alloc_expr_desugared(Expr::Array(Array::ElementList { | ||||
|                     elements: Box::default(), | ||||
|                 })), | ||||
|             ) | ||||
|         } else if argmap.len() == 1 && arguments.len() == 1 { | ||||
|             // Only one argument, so we don't need to make the `args` tuple.
 | ||||
|             //
 | ||||
|             // Generate:
 | ||||
|             //     super let args = [<core::fmt::Arguments>::new_display(&arg)];
 | ||||
|             let args = argmap | ||||
|                 .iter() | ||||
|                 .map(|&(arg_index, ty)| { | ||||
|                     let ref_arg = self.alloc_expr_desugared(Expr::Ref { | ||||
|                         expr: arguments[arg_index].expr, | ||||
|                         rawness: Rawness::Ref, | ||||
|                         mutability: Mutability::Shared, | ||||
|                     }); | ||||
|                     self.make_argument(ref_arg, ty) | ||||
|                 }) | ||||
|                 .collect(); | ||||
|             let args = | ||||
|                 self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args })); | ||||
|             let args_name = Name::new_symbol_root(sym::args); | ||||
|             let args_binding = | ||||
|                 self.alloc_binding(args_name.clone(), BindingAnnotation::Unannotated, hygiene); | ||||
|             let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None }); | ||||
|             self.add_definition_to_binding(args_binding, args_pat); | ||||
|             // TODO: We don't have `super let` yet.
 | ||||
|             let let_stmt = Statement::Let { | ||||
|                 pat: args_pat, | ||||
|                 type_ref: None, | ||||
|                 initializer: Some(args), | ||||
|                 else_branch: None, | ||||
|             }; | ||||
|             (vec![let_stmt], self.alloc_expr_desugared(Expr::Path(Path::from(args_name)))) | ||||
|         } else { | ||||
|             // Generate:
 | ||||
|             //     super let args = (&arg0, &arg1, &...);
 | ||||
|             let args_name = Name::new_symbol_root(sym::args); | ||||
|             let args_binding = | ||||
|                 self.alloc_binding(args_name.clone(), BindingAnnotation::Unannotated, hygiene); | ||||
|             let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None }); | ||||
|             self.add_definition_to_binding(args_binding, args_pat); | ||||
|             let elements = arguments | ||||
|                 .iter() | ||||
|                 .map(|arg| { | ||||
|                     self.alloc_expr_desugared(Expr::Ref { | ||||
|                         expr: arg.expr, | ||||
|                         rawness: Rawness::Ref, | ||||
|                         mutability: Mutability::Shared, | ||||
|                     }) | ||||
|                 }) | ||||
|                 .collect(); | ||||
|             let args_tuple = self.alloc_expr_desugared(Expr::Tuple { exprs: elements }); | ||||
|             // TODO: We don't have `super let` yet
 | ||||
|             let let_stmt1 = Statement::Let { | ||||
|                 pat: args_pat, | ||||
|                 type_ref: None, | ||||
|                 initializer: Some(args_tuple), | ||||
|                 else_branch: None, | ||||
|             }; | ||||
| 
 | ||||
|             // Generate:
 | ||||
|             //     super let args = [
 | ||||
|             //         <core::fmt::Argument>::new_display(args.0),
 | ||||
|             //         <core::fmt::Argument>::new_lower_hex(args.1),
 | ||||
|             //         <core::fmt::Argument>::new_debug(args.0),
 | ||||
|             //         …
 | ||||
|             //     ];
 | ||||
|             let args = argmap | ||||
|                 .iter() | ||||
|                 .map(|&(arg_index, ty)| { | ||||
|                     let args_ident_expr = | ||||
|                         self.alloc_expr_desugared(Expr::Path(args_name.clone().into())); | ||||
|                     let arg = self.alloc_expr_desugared(Expr::Field { | ||||
|                         expr: args_ident_expr, | ||||
|                         name: Name::new_tuple_field(arg_index), | ||||
|                     }); | ||||
|                     self.make_argument(arg, ty) | ||||
|                 }) | ||||
|                 .collect(); | ||||
|             let array = | ||||
|                 self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args })); | ||||
|             let args_binding = | ||||
|                 self.alloc_binding(args_name.clone(), BindingAnnotation::Unannotated, hygiene); | ||||
|             let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None }); | ||||
|             self.add_definition_to_binding(args_binding, args_pat); | ||||
|             let let_stmt2 = Statement::Let { | ||||
|                 pat: args_pat, | ||||
|                 type_ref: None, | ||||
|                 initializer: Some(array), | ||||
|                 else_branch: None, | ||||
|             }; | ||||
|             (vec![let_stmt1, let_stmt2], self.alloc_expr_desugared(Expr::Path(args_name.into()))) | ||||
|         }; | ||||
| 
 | ||||
|         // Generate:
 | ||||
|         //     &args
 | ||||
|         let args = self.alloc_expr_desugared(Expr::Ref { | ||||
|             expr: args, | ||||
|             rawness: Rawness::Ref, | ||||
|             mutability: Mutability::Shared, | ||||
|         }); | ||||
| 
 | ||||
|         let call_block = { | ||||
|             // Generate:
 | ||||
|             //     unsafe {
 | ||||
|             //         <core::fmt::Arguments>::new_v1_formatted(
 | ||||
|             //             lit_pieces,
 | ||||
|             //             args,
 | ||||
|             //             format_options,
 | ||||
|             //         )
 | ||||
|             //     }
 | ||||
| 
 | ||||
|             let new_v1_formatted = LangItem::FormatArguments.ty_rel_path( | ||||
|                 self.db, | ||||
|                 self.module.krate(), | ||||
|                 Name::new_symbol_root(sym::new_v1_formatted), | ||||
|             ); | ||||
|             let new_v1_formatted = | ||||
|                 self.alloc_expr_desugared(new_v1_formatted.map_or(Expr::Missing, Expr::Path)); | ||||
|             let args = [lit_pieces, args, format_options]; | ||||
|             let call = self | ||||
|                 .alloc_expr_desugared(Expr::Call { callee: new_v1_formatted, args: args.into() }); | ||||
| 
 | ||||
|             Expr::Unsafe { id: None, statements: Box::default(), tail: Some(call) } | ||||
|         }; | ||||
| 
 | ||||
|         if !let_stmts.is_empty() { | ||||
|             // Generate:
 | ||||
|             //     {
 | ||||
|             //         super let …
 | ||||
|             //         super let …
 | ||||
|             //         <core::fmt::Arguments>::new_…(…)
 | ||||
|             //     }
 | ||||
|             let call = self.alloc_expr_desugared(call_block); | ||||
|             self.alloc_expr( | ||||
|                 Expr::Block { | ||||
|                     id: None, | ||||
|                     statements: let_stmts.into(), | ||||
|                     tail: Some(call), | ||||
|                     label: None, | ||||
|                 }, | ||||
|                 syntax_ptr, | ||||
|             ) | ||||
|         } else { | ||||
|             self.alloc_expr(call_block, syntax_ptr) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Generate a hir expression for a format_args placeholder specification.
 | ||||
|  |  | |||
|  | @ -178,14 +178,14 @@ fn main() { | |||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn desugar_builtin_format_args() { | ||||
| fn desugar_builtin_format_args_before_1_89_0() { | ||||
|     let (db, body, def) = lower( | ||||
|         r#" | ||||
| //- minicore: fmt
 | ||||
| //- minicore: fmt_before_1_89_0
 | ||||
| fn main() { | ||||
|     let are = "are"; | ||||
|     let count = 10; | ||||
|     builtin#format_args("\u{1b}hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!"); | ||||
|     builtin#format_args("\u{1b}hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", orphan = (), last = "!"); | ||||
| } | ||||
| "#,
 | ||||
|     ); | ||||
|  | @ -249,14 +249,100 @@ fn main() { | |||
|                         builtin#lang(Count::Implied), | ||||
|                     ), | ||||
|                 ], | ||||
|                 unsafe { | ||||
|                     builtin#lang(UnsafeArg::new)() | ||||
|                 { | ||||
|                     (); | ||||
|                     unsafe { | ||||
|                         builtin#lang(UnsafeArg::new)() | ||||
|                     } | ||||
|                 }, | ||||
|             ); | ||||
|         }"#]]
 | ||||
|     .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT)) | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn desugar_builtin_format_args() { | ||||
|     let (db, body, def) = lower( | ||||
|         r#" | ||||
| //- minicore: fmt
 | ||||
| fn main() { | ||||
|     let are = "are"; | ||||
|     let count = 10; | ||||
|     builtin#format_args("\u{1b}hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", orphan = (), last = "!"); | ||||
| } | ||||
| "#,
 | ||||
|     ); | ||||
| 
 | ||||
|     expect![[r#" | ||||
|         fn main() { | ||||
|             let are = "are"; | ||||
|             let count = 10; | ||||
|             { | ||||
|                 let args = (&"fancy", &(), &"!", &count, &are, ); | ||||
|                 let args = [ | ||||
|                     builtin#lang(Argument::new_display)( | ||||
|                         args.3, | ||||
|                     ), builtin#lang(Argument::new_display)( | ||||
|                         args.0, | ||||
|                     ), builtin#lang(Argument::new_debug)( | ||||
|                         args.4, | ||||
|                     ), builtin#lang(Argument::new_display)( | ||||
|                         args.2, | ||||
|                     ), | ||||
|                 ]; | ||||
|                 unsafe { | ||||
|                     builtin#lang(Arguments::new_v1_formatted)( | ||||
|                         &[ | ||||
|                             "\u{1b}hello ", " ", " friends, we ", " ", "", | ||||
|                         ], | ||||
|                         &args, | ||||
|                         &[ | ||||
|                             builtin#lang(Placeholder::new)( | ||||
|                                 0usize, | ||||
|                                 ' ', | ||||
|                                 builtin#lang(Alignment::Unknown), | ||||
|                                 8u32, | ||||
|                                 builtin#lang(Count::Implied), | ||||
|                                 builtin#lang(Count::Is)( | ||||
|                                     2, | ||||
|                                 ), | ||||
|                             ), builtin#lang(Placeholder::new)( | ||||
|                                 1usize, | ||||
|                                 ' ', | ||||
|                                 builtin#lang(Alignment::Unknown), | ||||
|                                 0u32, | ||||
|                                 builtin#lang(Count::Implied), | ||||
|                                 builtin#lang(Count::Implied), | ||||
|                             ), builtin#lang(Placeholder::new)( | ||||
|                                 2usize, | ||||
|                                 ' ', | ||||
|                                 builtin#lang(Alignment::Unknown), | ||||
|                                 0u32, | ||||
|                                 builtin#lang(Count::Implied), | ||||
|                                 builtin#lang(Count::Implied), | ||||
|                             ), builtin#lang(Placeholder::new)( | ||||
|                                 1usize, | ||||
|                                 ' ', | ||||
|                                 builtin#lang(Alignment::Unknown), | ||||
|                                 0u32, | ||||
|                                 builtin#lang(Count::Implied), | ||||
|                                 builtin#lang(Count::Implied), | ||||
|                             ), builtin#lang(Placeholder::new)( | ||||
|                                 3usize, | ||||
|                                 ' ', | ||||
|                                 builtin#lang(Alignment::Unknown), | ||||
|                                 0u32, | ||||
|                                 builtin#lang(Count::Implied), | ||||
|                                 builtin#lang(Count::Implied), | ||||
|                             ), | ||||
|                         ], | ||||
|                     ) | ||||
|                 } | ||||
|             }; | ||||
|         }"#]]
 | ||||
|     .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT)) | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn test_macro_hygiene() { | ||||
|     let (db, body, def) = lower( | ||||
|  | @ -295,29 +381,31 @@ impl SsrError { | |||
|     expect![[r#" | ||||
|         fn main() { | ||||
|             _ = ra_test_fixture::error::SsrError::new( | ||||
|                 builtin#lang(Arguments::new_v1_formatted)( | ||||
|                     &[ | ||||
|                         "Failed to resolve path `", "`", | ||||
|                     ], | ||||
|                     &[ | ||||
|                 { | ||||
|                     let args = [ | ||||
|                         builtin#lang(Argument::new_display)( | ||||
|                             &node.text(), | ||||
|                         ), | ||||
|                     ], | ||||
|                     &[ | ||||
|                         builtin#lang(Placeholder::new)( | ||||
|                             0usize, | ||||
|                             ' ', | ||||
|                             builtin#lang(Alignment::Unknown), | ||||
|                             0u32, | ||||
|                             builtin#lang(Count::Implied), | ||||
|                             builtin#lang(Count::Implied), | ||||
|                         ), | ||||
|                     ], | ||||
|                     ]; | ||||
|                     unsafe { | ||||
|                         builtin#lang(UnsafeArg::new)() | ||||
|                     }, | ||||
|                 ), | ||||
|                         builtin#lang(Arguments::new_v1_formatted)( | ||||
|                             &[ | ||||
|                                 "Failed to resolve path `", "`", | ||||
|                             ], | ||||
|                             &args, | ||||
|                             &[ | ||||
|                                 builtin#lang(Placeholder::new)( | ||||
|                                     0usize, | ||||
|                                     ' ', | ||||
|                                     builtin#lang(Alignment::Unknown), | ||||
|                                     0u32, | ||||
|                                     builtin#lang(Count::Implied), | ||||
|                                     builtin#lang(Count::Implied), | ||||
|                                 ), | ||||
|                             ], | ||||
|                         ) | ||||
|                     } | ||||
|                 }, | ||||
|             ); | ||||
|         }"#]]
 | ||||
|     .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT)) | ||||
|  | @ -327,7 +415,7 @@ impl SsrError { | |||
| fn regression_10300() { | ||||
|     let (db, body, def) = lower( | ||||
|         r#" | ||||
| //- minicore: concat, panic
 | ||||
| //- minicore: concat, panic, fmt_before_1_89_0
 | ||||
| mod private { | ||||
|     pub use core::concat; | ||||
| } | ||||
|  |  | |||
|  | @ -100,7 +100,9 @@ fn f() { let a: u128 = 1; let b: u128 = todo$0!() }"#, | |||
|     fn test_complete_todo_with_msg() { | ||||
|         check_assist( | ||||
|             term_search, | ||||
|             r#"//- minicore: todo, unimplemented
 | ||||
|             // FIXME: Since we are lacking of `super let`, term search fails due to borrowck failure.
 | ||||
|             // Should implement super let and remove `fmt_before_1_89_0`
 | ||||
|             r#"//- minicore: todo, unimplemented, fmt_before_1_89_0
 | ||||
| fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#,
 | ||||
|             r#"fn f() { let a: u128 = 1; let b: u128 = a }"#, | ||||
|         ) | ||||
|  | @ -110,7 +112,9 @@ fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#, | |||
|     fn test_complete_unimplemented_with_msg() { | ||||
|         check_assist( | ||||
|             term_search, | ||||
|             r#"//- minicore: todo, unimplemented
 | ||||
|             // FIXME: Since we are lacking of `super let`, term search fails due to borrowck failure.
 | ||||
|             // Should implement super let and remove `fmt_before_1_89_0`
 | ||||
|             r#"//- minicore: todo, unimplemented, fmt_before_1_89_0
 | ||||
| fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#,
 | ||||
|             r#"fn f() { let a: u128 = 1; let b: u128 = a }"#, | ||||
|         ) | ||||
|  | @ -120,7 +124,9 @@ fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#, | |||
|     fn test_complete_unimplemented() { | ||||
|         check_assist( | ||||
|             term_search, | ||||
|             r#"//- minicore: todo, unimplemented
 | ||||
|             // FIXME: Since we are lacking of `super let`, term search fails due to borrowck failure.
 | ||||
|             // Should implement super let and remove `fmt_before_1_89_0`
 | ||||
|             r#"//- minicore: todo, unimplemented, fmt_before_1_89_0
 | ||||
| fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#,
 | ||||
|             r#"fn f() { let a: u128 = 1; let b: u128 = a }"#, | ||||
|         ) | ||||
|  |  | |||
|  | @ -1474,20 +1474,18 @@ fn main() { | |||
| } | ||||
| "#,
 | ||||
|         expect![[r#" | ||||
|             me foo()     fn(&self) | ||||
|             sn box  Box::new(expr) | ||||
|             sn call function(expr) | ||||
|             sn const      const {} | ||||
|             sn dbg      dbg!(expr) | ||||
|             sn dbgr    dbg!(&expr) | ||||
|             sn deref         *expr | ||||
|             sn if       if expr {} | ||||
|             sn match match expr {} | ||||
|             sn not           !expr | ||||
|             sn ref           &expr | ||||
|             sn refm      &mut expr | ||||
|             sn return  return expr | ||||
|             sn unsafe    unsafe {} | ||||
|             sn while while expr {} | ||||
|         "#]],
 | ||||
|     ); | ||||
| } | ||||
|  |  | |||
|  | @ -628,6 +628,17 @@ fn main() { | |||
|     #[test] | ||||
|     fn orphan_unsafe_format_args() { | ||||
|         // Checks that we don't place orphan arguments for formatting under an unsafe block.
 | ||||
|         check_diagnostics( | ||||
|             r#" | ||||
| //- minicore: fmt_before_1_89_0
 | ||||
| fn foo() { | ||||
|     let p = 0xDEADBEEF as *const i32; | ||||
|     format_args!("", *p); | ||||
|                   // ^^ error: dereference of raw pointer is unsafe and requires an unsafe function or block
 | ||||
| } | ||||
|         "#,
 | ||||
|         ); | ||||
| 
 | ||||
|         check_diagnostics( | ||||
|             r#" | ||||
| //- minicore: fmt
 | ||||
|  | @ -958,4 +969,18 @@ impl FooTrait for S2 { | |||
|         "#,
 | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn no_false_positive_on_format_args_since_1_89_0() { | ||||
|         check_diagnostics( | ||||
|             r#" | ||||
| //- minicore: fmt
 | ||||
| fn test() { | ||||
|     let foo = 10; | ||||
|     let bar = true; | ||||
|     let _x = format_args!("{} {0} {} {last}", foo, bar, last = "!"); | ||||
| } | ||||
|             "#,
 | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -496,6 +496,7 @@ define_symbols! { | |||
|     vectorcall, | ||||
|     wasm, | ||||
|     win64, | ||||
|     args, | ||||
|     array, | ||||
|     boxed_slice, | ||||
|     completions, | ||||
|  |  | |||
|  | @ -446,13 +446,13 @@ impl MiniCore { | |||
|             } | ||||
| 
 | ||||
|             let mut keep = true; | ||||
|             for ®ion in active_regions.iter() { | ||||
|             for ®ion in &active_regions { | ||||
|                 assert!(!region.starts_with(' '), "region marker starts with a space: {region:?}"); | ||||
|                 self.assert_valid_flag(region); | ||||
|                 seen_regions.push(region); | ||||
|                 keep &= self.has_flag(region); | ||||
|             } | ||||
|             for ®ion in inactive_regions.iter() { | ||||
|             for ®ion in &inactive_regions { | ||||
|                 assert!(!region.starts_with(' '), "region marker starts with a space: {region:?}"); | ||||
|                 self.assert_valid_flag(region); | ||||
|                 seen_regions.push(region); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Shoyu Vanilla
						Shoyu Vanilla