1use core::error;
2use std::{panic::Location, rc::Rc, sync::Arc};
3
4#[diagnostic::on_unimplemented(
12 message = "`{Self}` cannot build an error from a source of type `{E}`",
13 label = "this selector does not accept a `{E}` source",
14 note = "a selector whose variant has a `source` field builds from that field's exact type — \
15 `.context(...)` on a `Result` requires the selector's source type to match the \
16 `Result`'s error",
17 note = "a selector with no `source` field is a leaf: build it with `.build()` / `.fail()`, or \
18 attach it to an `Option` with `.context(...)` — not to a `Result`'s error"
19)]
20pub trait Contextual<E> {
21 type Destination;
23
24 #[track_caller]
26 fn build_error(self, source: E) -> Self::Destination;
27}
28
29pub trait Capturable {
43 #[track_caller]
45 fn capture() -> Self;
46
47 #[track_caller]
54 #[inline]
55 fn capture_or_extract(source: &dyn crate::Diagnostic) -> Self
56 where
57 Self: Sized,
58 {
59 let _ = source;
60 Self::capture()
61 }
62}
63
64impl<T: Capturable> Capturable for Box<T> {
65 #[track_caller]
66 #[inline]
67 fn capture() -> Self {
68 Self::new(T::capture())
69 }
70
71 #[track_caller]
72 #[inline]
73 fn capture_or_extract(source: &dyn crate::Diagnostic) -> Self {
74 Self::new(T::capture_or_extract(source))
75 }
76}
77
78impl<T: Capturable> Capturable for Rc<T> {
79 #[track_caller]
80 #[inline]
81 fn capture() -> Self {
82 Self::new(T::capture())
83 }
84
85 #[track_caller]
86 #[inline]
87 fn capture_or_extract(source: &dyn crate::Diagnostic) -> Self {
88 Self::new(T::capture_or_extract(source))
89 }
90}
91
92impl<T: Capturable> Capturable for Arc<T> {
93 #[track_caller]
94 #[inline]
95 fn capture() -> Self {
96 Self::new(T::capture())
97 }
98
99 #[track_caller]
100 #[inline]
101 fn capture_or_extract(source: &dyn crate::Diagnostic) -> Self {
102 Self::new(T::capture_or_extract(source))
103 }
104}
105
106macro_rules! impl_capturable_tuples {
107 ($($name:ident),* $(,)?) => {
109 impl_capturable_tuples!(@acc [] $($name,)*);
110 };
111 (@acc [$($acc:ident,)*] $head:ident, $($rest:ident,)*) => {
113 impl_capturable_tuples!(@impl $($acc,)* $head,);
114 impl_capturable_tuples!(@acc [$($acc,)* $head,] $($rest,)*);
115 };
116 (@acc [$($acc:ident,)*]) => {};
118 (@impl $($name:ident,)+) => {
119 impl<$($name: Capturable),+> Capturable for ($($name,)+) {
120 #[track_caller]
121 #[inline]
122 fn capture() -> Self {
123 ($($name::capture(),)+)
124 }
125 #[track_caller]
126 #[inline]
127 fn capture_or_extract(source: &dyn crate::Diagnostic) -> Self {
128 ($($name::capture_or_extract(source),)+)
129 }
130 }
131 };
132}
133impl_capturable_tuples!(A, B, C, D, E, F, G, H, I);
134
135impl Capturable for std::time::SystemTime {
136 #[inline]
137 fn capture() -> Self {
138 Self::now()
139 }
140}
141
142impl Capturable for std::time::Instant {
143 #[inline]
144 fn capture() -> Self {
145 Self::now()
146 }
147}
148impl Capturable for &'static Location<'static> {
149 #[inline]
150 #[track_caller]
151 fn capture() -> Self {
152 Location::caller()
153 }
154
155 #[track_caller]
156 #[inline]
157 fn capture_or_extract(source: &dyn crate::Diagnostic) -> Self {
158 let here = Location::caller();
162 source.oopsie_location().unwrap_or(here)
163 }
164}
165
166#[cfg(feature = "chrono")]
167impl Capturable for chrono::DateTime<chrono::Local> {
168 #[inline]
169 fn capture() -> Self {
170 chrono::Local::now()
171 }
172}
173
174#[cfg(feature = "jiff")]
175impl Capturable for jiff::Timestamp {
176 #[inline]
177 fn capture() -> Self {
178 Self::now()
179 }
180}
181
182#[cfg(feature = "jiff")]
183impl Capturable for jiff::Zoned {
184 #[inline]
185 fn capture() -> Self {
186 Self::now()
187 }
188}
189
190#[derive(Debug, Copy, Clone, PartialEq, Eq)]
195pub struct NoSource;
196
197pub trait AsErrorSource {
199 fn as_error_source(&self) -> &(dyn error::Error + 'static);
201}
202
203impl<T: error::Error + 'static> AsErrorSource for T {
204 #[inline]
205 fn as_error_source(&self) -> &(dyn error::Error + 'static) {
206 self
207 }
208}
209
210impl AsErrorSource for dyn error::Error + 'static {
211 #[inline]
212 fn as_error_source(&self) -> &(dyn error::Error + 'static) {
213 self
214 }
215}
216
217impl AsErrorSource for dyn error::Error + Send + 'static {
218 #[inline]
219 fn as_error_source(&self) -> &(dyn error::Error + 'static) {
220 self
221 }
222}
223
224impl AsErrorSource for dyn error::Error + Send + Sync + 'static {
225 #[inline]
226 fn as_error_source(&self) -> &(dyn error::Error + 'static) {
227 self
228 }
229}
230
231pub trait ResultExt<T, E> {
254 fn context<C>(self, context: C) -> Result<T, C::Destination>
256 where
257 C: Contextual<E>;
258
259 fn with_context<F, C>(self, context: F) -> Result<T, C::Destination>
261 where
262 F: FnOnce(&E) -> C,
263 C: Contextual<E>;
264
265 fn boxed(self) -> Result<T, Box<dyn error::Error + Send + Sync + 'static>>
270 where
271 E: error::Error + Send + Sync + 'static;
272
273 fn boxed_local(self) -> Result<T, Box<dyn error::Error + 'static>>
278 where
279 E: error::Error + 'static;
280}
281
282impl<T, E> ResultExt<T, E> for Result<T, E> {
283 #[inline]
284 #[track_caller]
285 fn context<C>(self, context: C) -> Result<T, C::Destination>
286 where
287 C: Contextual<E>,
288 {
289 match self {
293 Ok(value) => Ok(value),
294 Err(error) => Err(context.build_error(error)),
295 }
296 }
297
298 #[inline]
299 #[track_caller]
300 fn with_context<F, C>(self, context: F) -> Result<T, C::Destination>
301 where
302 F: FnOnce(&E) -> C,
303 C: Contextual<E>,
304 {
305 match self {
306 Ok(value) => Ok(value),
307 Err(error) => {
308 let selector = context(&error);
309 Err(selector.build_error(error))
310 }
311 }
312 }
313
314 #[inline]
315 fn boxed(self) -> Result<T, Box<dyn error::Error + Send + Sync + 'static>>
316 where
317 E: error::Error + Send + Sync + 'static,
318 {
319 self.map_err(|error| Box::new(error) as Box<dyn error::Error + Send + Sync + 'static>)
320 }
321
322 #[inline]
323 fn boxed_local(self) -> Result<T, Box<dyn error::Error + 'static>>
324 where
325 E: error::Error + 'static,
326 {
327 self.map_err(|error| Box::new(error) as Box<dyn error::Error + 'static>)
328 }
329}
330
331pub trait OptionExt<T> {
354 fn context<C>(self, context: C) -> Result<T, C::Destination>
356 where
357 C: Contextual<NoSource>;
358
359 fn with_context<F, C>(self, context: F) -> Result<T, C::Destination>
361 where
362 F: FnOnce() -> C,
363 C: Contextual<NoSource>;
364}
365
366impl<T> OptionExt<T> for Option<T> {
367 #[inline]
368 #[track_caller]
369 fn context<C>(self, context: C) -> Result<T, C::Destination>
370 where
371 C: Contextual<NoSource>,
372 {
373 match self {
374 Some(value) => Ok(value),
375 None => Err(context.build_error(NoSource)),
376 }
377 }
378
379 #[inline]
380 #[track_caller]
381 fn with_context<F, C>(self, context: F) -> Result<T, C::Destination>
382 where
383 F: FnOnce() -> C,
384 C: Contextual<NoSource>,
385 {
386 match self {
387 Some(value) => Ok(value),
388 None => Err(context().build_error(NoSource)),
389 }
390 }
391}
392
393#[cfg(test)]
394mod tests {
395 use super::*;
396 use std::error::Error as StdError;
397 use std::fmt;
398 use std::time::SystemTime;
399
400 #[cfg(feature = "jiff")]
401 #[test]
402 fn captures_current_jiff_timestamp() {
403 let before = jiff::Timestamp::now();
406 let captured = <jiff::Timestamp as Capturable>::capture();
407 assert!(captured >= before, "captured {captured} predates {before}");
408 }
409
410 #[cfg(feature = "jiff")]
411 #[test]
412 fn captures_current_jiff_zoned() {
413 let before = jiff::Timestamp::now();
414 let captured = <jiff::Zoned as Capturable>::capture().timestamp();
415 assert!(captured >= before, "captured {captured} predates {before}");
416 }
417
418 #[derive(Debug)]
419 struct DiagOnly;
420
421 impl fmt::Display for DiagOnly {
422 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
423 f.write_str("diag only")
424 }
425 }
426
427 impl std::error::Error for DiagOnly {}
428 impl crate::Diagnostic for DiagOnly {}
429
430 #[derive(Debug)]
432 struct SimpleError {
433 message: String,
434 }
435
436 impl fmt::Display for SimpleError {
437 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
438 write!(f, "{}", self.message)
439 }
440 }
441
442 impl std::error::Error for SimpleError {}
443
444 #[derive(Debug)]
445 struct SourceError {
446 message: String,
447 }
448
449 impl fmt::Display for SourceError {
450 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
451 write!(f, "{}", self.message)
452 }
453 }
454
455 impl std::error::Error for SourceError {}
456
457 #[derive(Debug)]
458 struct ChainError {
459 message: String,
460 source: Box<SourceError>,
461 }
462
463 impl fmt::Display for ChainError {
464 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
465 write!(f, "{}", self.message)
466 }
467 }
468
469 impl std::error::Error for ChainError {
470 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
471 Some(&*self.source)
472 }
473 }
474
475 struct SimpleSelector;
477
478 impl Contextual<NoSource> for SimpleSelector {
479 type Destination = SimpleError;
480
481 fn build_error(self, _source: NoSource) -> SimpleError {
482 SimpleError {
483 message: "simple error".to_owned(),
484 }
485 }
486 }
487
488 struct ChainSelector;
489
490 impl Contextual<SourceError> for ChainSelector {
491 type Destination = ChainError;
492
493 fn build_error(self, source: SourceError) -> ChainError {
494 ChainError {
495 message: "chain error".to_owned(),
496 source: Box::new(source),
497 }
498 }
499 }
500
501 #[test]
502 fn test_result_ext_context_ok() {
503 let result: Result<i32, SourceError> = Ok(42);
504 let chained: Result<i32, ChainError> = result.context(ChainSelector);
505 assert!(chained.is_ok());
506 assert_eq!(chained.unwrap(), 42);
507 }
508
509 #[test]
510 fn test_result_ext_context_err() {
511 let result: Result<i32, SourceError> = Err(SourceError {
512 message: "source failed".to_owned(),
513 });
514 let chained: Result<i32, ChainError> = result.context(ChainSelector);
515 assert!(chained.is_err());
516 let err = chained.unwrap_err();
517 assert_eq!(err.message, "chain error");
518 assert_eq!(StdError::source(&err).unwrap().to_string(), "source failed");
519 }
520
521 #[test]
522 fn test_result_ext_with_context_ok() {
523 let result: Result<i32, SourceError> = Ok(42);
524 let chained: Result<i32, ChainError> = result.with_context(|_| ChainSelector);
525 assert!(chained.is_ok());
526 assert_eq!(chained.unwrap(), 42);
527 }
528
529 #[test]
530 fn test_result_ext_with_context_err_closure_called() {
531 let mut closure_called = false;
532 let result: Result<i32, SourceError> = Err(SourceError {
533 message: "source failed".to_owned(),
534 });
535 let chained: Result<i32, ChainError> = result.with_context(|source| {
536 closure_called = true;
537 assert_eq!(source.message, "source failed");
539 ChainSelector
540 });
541 assert!(closure_called);
542 chained.unwrap_err();
543 }
544
545 #[test]
546 fn test_option_ext_context_some() {
547 let option: Option<i32> = Some(42);
548 let result: Result<i32, SimpleError> = option.context(SimpleSelector);
549 assert!(result.is_ok());
550 assert_eq!(result.unwrap(), 42);
551 }
552
553 #[test]
554 fn test_option_ext_context_none() {
555 let option: Option<i32> = None;
556 let result: Result<i32, SimpleError> = option.context(SimpleSelector);
557 assert!(result.is_err());
558 assert_eq!(result.unwrap_err().message, "simple error");
559 }
560
561 #[test]
562 fn test_option_ext_with_context_some() {
563 let option: Option<i32> = Some(42);
564 let result: Result<i32, SimpleError> = option.with_context(|| SimpleSelector);
565 assert!(result.is_ok());
566 assert_eq!(result.unwrap(), 42);
567 }
568
569 #[test]
570 fn test_option_ext_with_context_none_closure_called() {
571 let mut closure_called = false;
572 let option: Option<i32> = None;
573 let result: Result<i32, SimpleError> = option.with_context(|| {
574 closure_called = true;
575 SimpleSelector
576 });
577 assert!(closure_called);
578 result.unwrap_err();
579 }
580
581 const _: () = {
582 const fn is_capturable<T: Capturable>() {}
584 is_capturable::<Box<crate::Backtrace>>();
585 };
586
587 #[cfg(feature = "tracing")]
588 #[test]
589 fn tuple_capture_produces_both_elements() {
590 use crate::{Backtrace, SpanTrace};
591 let (bt, st): (Backtrace, SpanTrace) = <(Backtrace, SpanTrace) as Capturable>::capture();
593 let _ = bt.frames();
594 let _ = st.status();
595 }
596
597 #[cfg(feature = "tracing")]
598 const _: () = {
599 const fn is_capturable<T: Capturable>() {}
600 is_capturable::<Box<(crate::Backtrace, crate::SpanTrace)>>();
601 is_capturable::<(crate::Backtrace, crate::SpanTrace)>();
602 };
603
604 #[cfg(feature = "tracing")]
605 #[test]
606 fn tuple_capture_ext_extracts_both_from_source() {
607 use crate::{
608 Backtrace, Capturable, Diagnostic, RustBacktrace, SpanTrace,
609 with_rust_backtrace_override,
610 };
611 use std::fmt;
612
613 #[derive(Debug)]
614 struct Src {
615 backtrace: Backtrace,
616 spantrace: SpanTrace,
617 }
618 impl fmt::Display for Src {
619 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
620 f.write_str("src")
621 }
622 }
623 impl std::error::Error for Src {}
624 impl Diagnostic for Src {
625 fn oopsie_backtrace(&self) -> Option<&Backtrace> {
626 Some(&self.backtrace)
627 }
628 fn oopsie_spantrace(&self) -> Option<&SpanTrace> {
629 Some(&self.spantrace)
630 }
631 }
632
633 with_rust_backtrace_override(RustBacktrace::Enabled, || {
634 let src = Src {
635 backtrace: Backtrace::capture(),
636 spantrace: SpanTrace::capture(),
637 };
638 let source_frames = src.backtrace.frames().len();
639 assert!(
640 source_frames > 0,
641 "backtrace must be enabled for this test to be probative"
642 );
643 let extracted = <(Backtrace, SpanTrace) as Capturable>::capture_or_extract(&src);
644 assert_eq!(extracted.0.frames().len(), source_frames);
647 });
648 }
649
650 #[cfg(feature = "tracing")]
651 const _: () = {
652 const fn is_capturable<T: Capturable>() {}
653 is_capturable::<Box<(crate::Backtrace, crate::SpanTrace)>>();
654 is_capturable::<(crate::Backtrace, crate::SpanTrace)>();
655 };
656
657 #[derive(Debug)]
661 struct BoxedChainError {
662 message: String,
663 source: Box<dyn StdError + Send + Sync + 'static>,
664 }
665
666 impl fmt::Display for BoxedChainError {
667 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
668 write!(f, "{}", self.message)
669 }
670 }
671
672 impl StdError for BoxedChainError {
673 fn source(&self) -> Option<&(dyn StdError + 'static)> {
674 Some(&*self.source)
675 }
676 }
677
678 struct BoxedSelector;
679
680 impl Contextual<Box<dyn StdError + Send + Sync + 'static>> for BoxedSelector {
681 type Destination = BoxedChainError;
682
683 fn build_error(self, source: Box<dyn StdError + Send + Sync + 'static>) -> BoxedChainError {
684 BoxedChainError {
685 message: "boxed chain error".to_owned(),
686 source,
687 }
688 }
689 }
690
691 #[derive(Debug)]
692 struct BoxedLocalChainError {
693 source: Box<dyn StdError + 'static>,
694 }
695
696 impl fmt::Display for BoxedLocalChainError {
697 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
698 write!(f, "boxed local chain error")
699 }
700 }
701
702 impl StdError for BoxedLocalChainError {
703 fn source(&self) -> Option<&(dyn StdError + 'static)> {
704 Some(&*self.source)
705 }
706 }
707
708 struct BoxedLocalSelector;
709
710 impl Contextual<Box<dyn StdError + 'static>> for BoxedLocalSelector {
711 type Destination = BoxedLocalChainError;
712
713 fn build_error(self, source: Box<dyn StdError + 'static>) -> BoxedLocalChainError {
714 BoxedLocalChainError { source }
715 }
716 }
717
718 #[test]
719 fn boxed_passes_through_ok() {
720 let result: Result<i32, SourceError> = Ok(7);
721 assert_eq!(result.boxed().unwrap(), 7);
722 }
723
724 #[test]
725 fn boxed_erases_err_but_preserves_display_and_concrete_type() {
726 let result: Result<i32, SourceError> = Err(SourceError {
727 message: "boom".to_owned(),
728 });
729 let err = result.boxed().unwrap_err();
730 assert_eq!(err.to_string(), "boom");
731 assert!(err.downcast_ref::<SourceError>().is_some());
733 }
734
735 #[test]
736 fn boxed_local_erases_err_but_preserves_display_and_concrete_type() {
737 let result: Result<i32, SourceError> = Err(SourceError {
738 message: "boom".to_owned(),
739 });
740 let err = result.boxed_local().unwrap_err();
741 assert_eq!(err.to_string(), "boom");
742 assert!(err.downcast_ref::<SourceError>().is_some());
743 }
744
745 #[test]
746 fn boxed_then_context_chains_erased_source() {
747 let result: Result<i32, SourceError> = Err(SourceError {
748 message: "io broke".to_owned(),
749 });
750 let chained: Result<i32, BoxedChainError> = result.boxed().context(BoxedSelector);
751 let err = chained.unwrap_err();
752 assert_eq!(err.message, "boxed chain error");
753 assert_eq!(StdError::source(&err).unwrap().to_string(), "io broke");
754 }
755
756 #[test]
757 fn boxed_then_context_passes_through_ok() {
758 let result: Result<i32, SourceError> = Ok(42);
759 let chained: Result<i32, BoxedChainError> = result.boxed().context(BoxedSelector);
760 assert_eq!(chained.unwrap(), 42);
761 }
762
763 #[test]
764 fn boxed_then_with_context_chains_erased_source() {
765 let result: Result<i32, SourceError> = Err(SourceError {
766 message: "io broke".to_owned(),
767 });
768 let chained: Result<i32, BoxedChainError> = result.boxed().with_context(|source| {
769 assert_eq!(source.to_string(), "io broke");
771 BoxedSelector
772 });
773 assert_eq!(
774 StdError::source(&chained.unwrap_err()).unwrap().to_string(),
775 "io broke"
776 );
777 }
778
779 #[test]
780 fn boxed_local_then_context_chains_erased_source() {
781 let result: Result<i32, SourceError> = Err(SourceError {
782 message: "io broke".to_owned(),
783 });
784 let chained: Result<i32, BoxedLocalChainError> =
785 result.boxed_local().context(BoxedLocalSelector);
786 assert_eq!(
787 StdError::source(&chained.unwrap_err()).unwrap().to_string(),
788 "io broke"
789 );
790 }
791
792 #[test]
793 fn boxed_unifies_heterogeneous_error_types() {
794 fn run(fail_with_source: bool) -> Result<(), Box<dyn StdError + Send + Sync + 'static>> {
797 if fail_with_source {
798 Err(SourceError {
799 message: "source".to_owned(),
800 })
801 .boxed()?;
802 } else {
803 Err(SimpleError {
804 message: "simple".to_owned(),
805 })
806 .boxed()?;
807 }
808 Ok(())
809 }
810 assert_eq!(run(true).unwrap_err().to_string(), "source");
811 assert_eq!(run(false).unwrap_err().to_string(), "simple");
812 }
813
814 #[test]
815 fn boxed_on_boxed_error_downcasts_to_the_box() {
816 let result: Result<(), Box<SourceError>> = Err(Box::new(SourceError {
817 message: "boom".to_owned(),
818 }));
819 let err = result.boxed().unwrap_err();
820 assert!(err.downcast_ref::<SourceError>().is_none());
821 assert!(err.downcast_ref::<Box<SourceError>>().is_some());
822 }
823
824 const _: () = {
825 const fn is_send_sync<T: Send + Sync>() {}
827 is_send_sync::<Box<dyn StdError + Send + Sync + 'static>>();
828 };
829
830 #[derive(Debug)]
831 struct WithLoc(&'static std::panic::Location<'static>);
832
833 impl fmt::Display for WithLoc {
834 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
835 f.write_str("with loc")
836 }
837 }
838
839 impl StdError for WithLoc {}
840
841 impl crate::Diagnostic for WithLoc {
842 fn oopsie_location(&self) -> Option<&'static std::panic::Location<'static>> {
843 Some(self.0)
844 }
845 }
846
847 #[test]
848 fn location_captures_caller_and_extracts_from_source() {
849 let line = line!() + 1;
851 let loc = <&'static std::panic::Location<'static> as Capturable>::capture();
852 assert!(loc.file().ends_with("traits.rs"));
853 assert_eq!(loc.line(), line);
854
855 let src = WithLoc(std::panic::Location::caller());
857 let extracted =
858 <&'static std::panic::Location<'static> as Capturable>::capture_or_extract(&src);
859 assert!(
860 std::ptr::eq(extracted, src.0),
861 "must reuse the source's location"
862 );
863
864 let fresh =
866 <&'static std::panic::Location<'static> as Capturable>::capture_or_extract(&DiagOnly);
867 assert!(fresh.file().ends_with("traits.rs"));
868 }
869
870 #[test]
871 fn system_time_captures_now_and_never_extracts() {
872 let before = SystemTime::now();
873 let captured = <SystemTime as Capturable>::capture();
874 assert!(captured >= before);
875 let extracted = <SystemTime as Capturable>::capture_or_extract(&DiagOnly);
876 assert!(extracted >= before);
877 }
878
879 #[test]
880 fn location_captures() {
881 let location = <&'static Location<'static> as Capturable>::capture();
882 let expected_line = line!() - 1; assert_eq!(location.line(), expected_line);
884 assert_eq!(location.file(), file!());
885 }
886
887 #[test]
888 fn location_captures_nested() {
889 #[track_caller]
890 fn capture_location() -> &'static Location<'static> {
891 <&'static Location<'static> as Capturable>::capture()
892 }
893 let location = capture_location();
894 let expected_line = line!() - 1; assert_eq!(location.line(), expected_line);
896 assert_eq!(location.file(), file!());
897 }
898
899 #[test]
900 fn complex_location_captures() {
901 type Loc = &'static Location<'static>;
902 type LocTimeLoc = (Loc, SystemTime, Loc);
903 let location =
904 <(Box<LocTimeLoc>, Rc<LocTimeLoc>, Arc<LocTimeLoc>) as Capturable>::capture();
905 let expected_line = line!() - 1; let expected_col = 3 * 4 + 1; let (box_loc, rc_loc, arc_loc) = location;
908 for (loc1, _, loc2) in [&*box_loc, &*rc_loc, &*arc_loc] {
909 for loc in [loc1, loc2] {
910 assert_eq!(loc.file(), file!());
911 assert_eq!(loc.line(), expected_line);
912 assert_eq!(loc.column(), expected_col);
913 }
914 }
915 }
916}