Skip to content

Commit 778f733

Browse files
max-sixtyclaude
andauthored
Fix for code before macros, such as let foo = assert_snapshot! (#835)
Co-authored-by: Claude <[email protected]>
1 parent 6cb41af commit 778f733

File tree

1 file changed

+252
-1
lines changed

1 file changed

+252
-1
lines changed

cargo-insta/src/inline.rs

Lines changed: 252 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,12 @@ impl FilePatcher {
128128
struct Visitor<'a>(usize, Option<InlineSnapshot>, &'a [String]);
129129

130130
fn indentation(macro_start: LineColumn, code_lines: &[String]) -> String {
131-
code_lines[macro_start.line - 1][..macro_start.column].to_owned()
131+
// Only capture leading whitespace from the line, not arbitrary code
132+
// that might precede the macro (fixes issue #833)
133+
code_lines[macro_start.line - 1]
134+
.chars()
135+
.take_while(|c| c.is_whitespace())
136+
.collect()
132137
}
133138

134139
fn scan_for_path_start(tokens: &[TokenTree], pos: usize, code_lines: &[String]) -> String {
@@ -413,4 +418,250 @@ fn test_function() {
413418
assert_debug_snapshot!(snapshot5.indentation, @r#"" ""#);
414419
assert_debug_snapshot!(snapshot6.indentation, @r#"" ""#);
415420
}
421+
422+
#[test]
423+
fn test_find_snapshot_macro_with_code_before_macro() {
424+
// Regression test for issue #833
425+
// When there's code before the macro (not just whitespace), the indentation
426+
// should only capture the leading whitespace, not the code.
427+
let content = r######"
428+
use insta::assert_snapshot;
429+
430+
fn test_function() {
431+
let output = assert_snapshot!("test\ntest", @r###"
432+
test
433+
test
434+
"###);
435+
}
436+
"######;
437+
438+
let file_patcher = FilePatcher {
439+
filename: PathBuf::new(),
440+
lines: content.lines().map(String::from).collect(),
441+
source: syn::parse_file(content).unwrap(),
442+
inline_snapshots: vec![],
443+
};
444+
445+
// The snapshot macro starts on line 5 (1-based index)
446+
let snapshot = file_patcher.find_snapshot_macro(5).unwrap();
447+
448+
// The indentation should only be the leading whitespace (" "),
449+
// NOT " let output = " which would cause the regression described in #833
450+
assert_debug_snapshot!(snapshot.indentation, @r#"" ""#);
451+
}
452+
453+
#[test]
454+
fn test_find_snapshot_macro_in_if_block() {
455+
// Corner case: macro inside if block with code before it on same line
456+
let content = r######"
457+
use insta::assert_snapshot;
458+
459+
fn test_function() {
460+
if true { assert_snapshot!("test", @"test"); }
461+
}
462+
"######;
463+
464+
let file_patcher = FilePatcher {
465+
filename: PathBuf::new(),
466+
lines: content.lines().map(String::from).collect(),
467+
source: syn::parse_file(content).unwrap(),
468+
inline_snapshots: vec![],
469+
};
470+
471+
let snapshot = file_patcher.find_snapshot_macro(5).unwrap();
472+
// Should only capture leading whitespace, not " if true { "
473+
assert_debug_snapshot!(snapshot.indentation, @r#"" ""#);
474+
}
475+
476+
#[test]
477+
fn test_find_snapshot_macro_in_match_arm() {
478+
// Corner case: macro in match arm
479+
let content = r######"
480+
use insta::assert_snapshot;
481+
482+
fn test_function() {
483+
match x {
484+
_ => assert_snapshot!("test", @"test"),
485+
}
486+
}
487+
"######;
488+
489+
let file_patcher = FilePatcher {
490+
filename: PathBuf::new(),
491+
lines: content.lines().map(String::from).collect(),
492+
source: syn::parse_file(content).unwrap(),
493+
inline_snapshots: vec![],
494+
};
495+
496+
let snapshot = file_patcher.find_snapshot_macro(6).unwrap();
497+
// Should only capture leading whitespace, not " _ => "
498+
assert_debug_snapshot!(snapshot.indentation, @r#"" ""#);
499+
}
500+
501+
#[test]
502+
fn test_find_snapshot_macro_no_indentation() {
503+
// Corner case: macro at column 0 (no indentation)
504+
let content = r######"
505+
use insta::assert_snapshot;
506+
507+
assert_snapshot!("test", @"test");
508+
"######;
509+
510+
let file_patcher = FilePatcher {
511+
filename: PathBuf::new(),
512+
lines: content.lines().map(String::from).collect(),
513+
source: syn::parse_file(content).unwrap(),
514+
inline_snapshots: vec![],
515+
};
516+
517+
let snapshot = file_patcher.find_snapshot_macro(4).unwrap();
518+
// No indentation at all
519+
assert_debug_snapshot!(snapshot.indentation, @r#""""#);
520+
}
521+
522+
#[test]
523+
fn test_find_snapshot_macro_after_method_chain() {
524+
// Corner case: macro result used in method chain
525+
let content = r######"
526+
use insta::assert_snapshot;
527+
528+
fn test_function() {
529+
let _ = assert_snapshot!("test", @"test");
530+
foo.bar().baz(assert_snapshot!("nested", @"nested"));
531+
}
532+
"######;
533+
534+
let file_patcher = FilePatcher {
535+
filename: PathBuf::new(),
536+
lines: content.lines().map(String::from).collect(),
537+
source: syn::parse_file(content).unwrap(),
538+
inline_snapshots: vec![],
539+
};
540+
541+
// First snapshot at line 5
542+
let snapshot1 = file_patcher.find_snapshot_macro(5).unwrap();
543+
assert_debug_snapshot!(snapshot1.indentation, @r#"" ""#);
544+
545+
// Second snapshot at line 6 (nested in method call)
546+
let snapshot2 = file_patcher.find_snapshot_macro(6).unwrap();
547+
// Should only capture leading whitespace, not " foo.bar().baz("
548+
assert_debug_snapshot!(snapshot2.indentation, @r#"" ""#);
549+
}
550+
551+
#[test]
552+
fn test_find_snapshot_macro_mixed_whitespace() {
553+
// Corner case: mixed tabs and spaces (tab then spaces)
554+
let content = "
555+
use insta::assert_snapshot;
556+
557+
fn test_function() {
558+
\t assert_snapshot!(\"test\", @\"test\");
559+
}
560+
";
561+
562+
let file_patcher = FilePatcher {
563+
filename: PathBuf::new(),
564+
lines: content.lines().map(String::from).collect(),
565+
source: syn::parse_file(content).unwrap(),
566+
inline_snapshots: vec![],
567+
};
568+
569+
let snapshot = file_patcher.find_snapshot_macro(5).unwrap();
570+
// Should capture the tab and spaces
571+
assert_debug_snapshot!(snapshot.indentation, @r#""\t ""#);
572+
}
573+
574+
#[test]
575+
fn test_find_snapshot_macro_closure() {
576+
// Corner case: macro inside closure
577+
let content = r######"
578+
use insta::assert_snapshot;
579+
580+
fn test_function() {
581+
let f = || assert_snapshot!("test", @"test");
582+
}
583+
"######;
584+
585+
let file_patcher = FilePatcher {
586+
filename: PathBuf::new(),
587+
lines: content.lines().map(String::from).collect(),
588+
source: syn::parse_file(content).unwrap(),
589+
inline_snapshots: vec![],
590+
};
591+
592+
let snapshot = file_patcher.find_snapshot_macro(5).unwrap();
593+
// Should only capture leading whitespace, not " let f = || "
594+
assert_debug_snapshot!(snapshot.indentation, @r#"" ""#);
595+
}
596+
597+
#[test]
598+
fn test_find_snapshot_macro_multiple_on_same_line() {
599+
// Corner case: multiple macros on the same line
600+
let content = r######"
601+
use insta::assert_snapshot;
602+
603+
fn test_function() {
604+
if true { assert_snapshot!("a", @"a"); assert_snapshot!("b", @"b"); }
605+
}
606+
"######;
607+
608+
let file_patcher = FilePatcher {
609+
filename: PathBuf::new(),
610+
lines: content.lines().map(String::from).collect(),
611+
source: syn::parse_file(content).unwrap(),
612+
inline_snapshots: vec![],
613+
};
614+
615+
// Both snapshots are on line 5
616+
// Note: find_snapshot_macro returns the first macro found on a line
617+
let snapshot = file_patcher.find_snapshot_macro(5).unwrap();
618+
// Should only capture leading whitespace
619+
assert_debug_snapshot!(snapshot.indentation, @r#"" ""#);
620+
}
621+
622+
#[test]
623+
fn test_find_snapshot_macro_deeply_nested() {
624+
// Corner case: macro deeply nested in expressions
625+
let content = r######"
626+
use insta::assert_snapshot;
627+
628+
fn test_function() {
629+
foo.bar(|x| x.baz().qux(|| assert_snapshot!("test", @"test")));
630+
}
631+
"######;
632+
633+
let file_patcher = FilePatcher {
634+
filename: PathBuf::new(),
635+
lines: content.lines().map(String::from).collect(),
636+
source: syn::parse_file(content).unwrap(),
637+
inline_snapshots: vec![],
638+
};
639+
640+
let snapshot = file_patcher.find_snapshot_macro(5).unwrap();
641+
// Should only capture leading whitespace, not the deeply nested expression
642+
assert_debug_snapshot!(snapshot.indentation, @r#"" ""#);
643+
}
644+
645+
#[test]
646+
fn test_find_snapshot_macro_qualified_path_with_code_before() {
647+
// Corner case: fully qualified path (insta::assert_snapshot!) with code before it
648+
// This exercises the scan_for_path_start function which walks back through :: tokens
649+
let content = r######"
650+
fn test_function() {
651+
let output = insta::assert_snapshot!("test", @"test");
652+
}
653+
"######;
654+
655+
let file_patcher = FilePatcher {
656+
filename: PathBuf::new(),
657+
lines: content.lines().map(String::from).collect(),
658+
source: syn::parse_file(content).unwrap(),
659+
inline_snapshots: vec![],
660+
};
661+
662+
let snapshot = file_patcher.find_snapshot_macro(3).unwrap();
663+
// Should only capture leading whitespace, not " let output = "
664+
// even though scan_for_path_start finds "insta" as the path start
665+
assert_debug_snapshot!(snapshot.indentation, @r#"" ""#);
666+
}
416667
}

0 commit comments

Comments
 (0)