1#![allow(clippy::result_large_err)]
2pub use unsynn::Error as ParseError;
58pub use unsynn::ToTokens;
59
60use proc_macro2::TokenStream as TokenStream2;
61use unsynn::operator::names::{
62 Assign, Colon, Comma, Gt, LifetimeTick, Lt, PathSep, Pound, RArrow, Semicolon,
63};
64use unsynn::{
65 Any, BraceGroupContaining, BracketGroupContaining, CommaDelimitedVec, Cons, Either,
66 EndOfStream, Except, Ident, LiteralString, Many, Optional, ParenthesisGroupContaining, Parse,
67 ToTokenIter, TokenStream, keyword, unsynn,
68};
69
70keyword! {
71 pub KAsync = "async";
72 pub KFn = "fn";
73 pub KTrait = "trait";
74 pub KSelfKw = "self";
75 pub KMut = "mut";
76 pub KDoc = "doc";
77 pub KPub = "pub";
78 pub KWhere = "where";
79}
80
81type VerbatimUntil<C> = Many<Cons<Except<C>, AngleTokenTree>>;
83
84unsynn! {
85 #[derive(Clone)]
87 pub struct AngleTokenTree(
88 pub Either<Cons<Lt, Vec<Cons<Except<Gt>, AngleTokenTree>>, Gt>, unsynn::TokenTree>,
89 );
90
91 pub struct RawAttribute {
92 pub _pound: Pound,
93 pub body: BracketGroupContaining<TokenStream>,
94 }
95
96 pub struct DocAttribute {
97 pub _doc: KDoc,
98 pub _assign: Assign,
99 pub value: LiteralString,
100 }
101
102 pub enum Visibility {
103 Pub(KPub),
104 PubRestricted(Cons<KPub, ParenthesisGroupContaining<TokenStream>>),
105 }
106
107 pub struct RefSelf {
108 pub _amp: unsynn::operator::names::And,
109 pub lifetime: Option<Cons<LifetimeTick, Ident>>,
110 pub mutability: Option<KMut>,
111 pub name: KSelfKw,
112 }
113
114 pub struct ValueSelf {
115 pub mutability: Option<KMut>,
116 pub name: KSelfKw,
117 }
118
119 pub struct TypedSelf {
120 pub mutability: Option<KMut>,
121 pub name: KSelfKw,
122 pub _colon: Colon,
123 pub ty: Type,
124 }
125
126 pub enum MethodReceiver {
127 Ref(RefSelf),
128 Typed(TypedSelf),
129 Value(ValueSelf),
130 }
131
132 pub struct MethodParam {
133 pub name: Ident,
134 pub _colon: Colon,
135 pub ty: Type,
136 }
137
138 pub struct GenericParams {
139 pub _lt: Lt,
140 pub params: VerbatimUntil<Gt>,
141 pub _gt: Gt,
142 }
143
144 #[derive(Clone)]
145 pub struct TypePath {
146 pub leading: Option<PathSep>,
147 pub first: Ident,
148 pub rest: Any<Cons<PathSep, Ident>>,
149 }
150
151 #[derive(Clone)]
152 pub struct Lifetime {
153 pub _apo: LifetimeTick,
154 pub ident: Ident,
155 }
156
157 #[derive(Clone)]
158 pub enum GenericArgument {
159 Lifetime(Lifetime),
160 Type(Type),
161 }
162
163 #[derive(Clone)]
164 pub enum Type {
165 Reference(TypeRef),
166 Tuple(TypeTuple),
167 PathWithGenerics(PathWithGenerics),
168 Path(TypePath),
169 }
170
171 #[derive(Clone)]
172 pub struct TypeRef {
173 pub _amp: unsynn::operator::names::And,
174 pub lifetime: Option<Cons<LifetimeTick, Ident>>,
175 pub mutable: Option<KMut>,
176 pub inner: Box<Type>,
177 }
178
179 #[derive(Clone)]
180 pub struct TypeTuple(
181 pub ParenthesisGroupContaining<CommaDelimitedVec<Type>>,
182 );
183
184 #[derive(Clone)]
185 pub struct PathWithGenerics {
186 pub path: TypePath,
187 pub _lt: Lt,
188 pub args: CommaDelimitedVec<GenericArgument>,
189 pub _gt: Gt,
190 }
191
192 pub struct ReturnType {
193 pub _arrow: RArrow,
194 pub ty: Type,
195 }
196
197 pub struct WhereClause {
198 pub _where: KWhere,
199 pub bounds: VerbatimUntil<Semicolon>,
200 }
201
202 pub struct MethodParams {
203 pub receiver: MethodReceiver,
204 pub rest: Optional<Cons<Comma, CommaDelimitedVec<MethodParam>>>,
205 }
206
207 pub struct ServiceMethod {
208 pub attributes: Any<RawAttribute>,
209 pub _async: Optional<KAsync>,
210 pub _fn: KFn,
211 pub name: Ident,
212 pub generics: Optional<GenericParams>,
213 pub params: ParenthesisGroupContaining<MethodParams>,
214 pub return_type: Optional<ReturnType>,
215 pub where_clause: Optional<WhereClause>,
216 pub _semi: Semicolon,
217 }
218
219 pub struct ServiceTrait {
220 pub attributes: Any<RawAttribute>,
221 pub vis: Optional<Visibility>,
222 pub _trait: KTrait,
223 pub name: Ident,
224 pub generics: Optional<GenericParams>,
225 pub body: BraceGroupContaining<Any<ServiceMethod>>,
226 pub _eos: EndOfStream,
227 }
228}
229
230impl GenericArgument {
235 pub fn has_lifetime(&self) -> bool {
236 match self {
237 GenericArgument::Lifetime(_) => true,
238 GenericArgument::Type(ty) => ty.has_lifetime(),
239 }
240 }
241
242 pub fn has_named_lifetime(&self, name: &str) -> bool {
243 match self {
244 GenericArgument::Lifetime(lifetime) => lifetime.ident == name,
245 GenericArgument::Type(ty) => ty.has_named_lifetime(name),
246 }
247 }
248
249 pub fn has_non_named_lifetime(&self, name: &str) -> bool {
250 match self {
251 GenericArgument::Lifetime(lifetime) => lifetime.ident != name,
252 GenericArgument::Type(ty) => ty.has_non_named_lifetime(name),
253 }
254 }
255
256 pub fn has_elided_reference_lifetime(&self) -> bool {
257 match self {
258 GenericArgument::Lifetime(_) => false,
259 GenericArgument::Type(ty) => ty.has_elided_reference_lifetime(),
260 }
261 }
262
263 pub fn contains_channel(&self) -> bool {
264 match self {
265 GenericArgument::Lifetime(_) => false,
266 GenericArgument::Type(ty) => ty.contains_channel(),
267 }
268 }
269}
270
271impl Type {
276 pub fn as_result(&self) -> Option<(&Type, &Type)> {
278 match self {
279 Type::PathWithGenerics(PathWithGenerics { path, args, .. })
280 if path.last_segment().as_str() == "Result" && args.len() == 2 =>
281 {
282 let args_slice = args.as_slice();
283 match (&args_slice[0].value, &args_slice[1].value) {
284 (GenericArgument::Type(ok), GenericArgument::Type(err)) => Some((ok, err)),
285 _ => None,
286 }
287 }
288 _ => None,
289 }
290 }
291
292 pub fn has_lifetime(&self) -> bool {
294 match self {
295 Type::Reference(TypeRef {
296 lifetime: Some(_), ..
297 }) => true,
298 Type::Reference(TypeRef { inner, .. }) => inner.has_lifetime(),
299 Type::PathWithGenerics(PathWithGenerics { args, .. }) => {
300 args.iter().any(|t| t.value.has_lifetime())
301 }
302 Type::Tuple(TypeTuple(group)) => group.content.iter().any(|t| t.value.has_lifetime()),
303 Type::Path(_) => false,
304 }
305 }
306
307 pub fn has_named_lifetime(&self, name: &str) -> bool {
309 match self {
310 Type::Reference(TypeRef {
311 lifetime: Some(lifetime),
312 ..
313 }) => lifetime.second == name,
314 Type::Reference(TypeRef { inner, .. }) => inner.has_named_lifetime(name),
315 Type::PathWithGenerics(PathWithGenerics { args, .. }) => {
316 args.iter().any(|t| t.value.has_named_lifetime(name))
317 }
318 Type::Tuple(TypeTuple(group)) => group
319 .content
320 .iter()
321 .any(|t| t.value.has_named_lifetime(name)),
322 Type::Path(_) => false,
323 }
324 }
325
326 pub fn has_non_named_lifetime(&self, name: &str) -> bool {
328 match self {
329 Type::Reference(TypeRef {
330 lifetime: Some(lifetime),
331 ..
332 }) => lifetime.second != name,
333 Type::Reference(TypeRef { inner, .. }) => inner.has_non_named_lifetime(name),
334 Type::PathWithGenerics(PathWithGenerics { args, .. }) => {
335 args.iter().any(|t| t.value.has_non_named_lifetime(name))
336 }
337 Type::Tuple(TypeTuple(group)) => group
338 .content
339 .iter()
340 .any(|t| t.value.has_non_named_lifetime(name)),
341 Type::Path(_) => false,
342 }
343 }
344
345 pub fn has_elided_reference_lifetime(&self) -> bool {
349 match self {
350 Type::Reference(TypeRef { lifetime: None, .. }) => true,
351 Type::Reference(TypeRef { inner, .. }) => inner.has_elided_reference_lifetime(),
352 Type::PathWithGenerics(PathWithGenerics { args, .. }) => {
353 args.iter().any(|t| t.value.has_elided_reference_lifetime())
354 }
355 Type::Tuple(TypeTuple(group)) => group
356 .content
357 .iter()
358 .any(|t| t.value.has_elided_reference_lifetime()),
359 Type::Path(_) => false,
360 }
361 }
362
363 pub fn contains_channel(&self) -> bool {
368 match self {
369 Type::Reference(TypeRef { inner, .. }) => inner.contains_channel(),
370 Type::Tuple(TypeTuple(group)) => {
371 group.content.iter().any(|t| t.value.contains_channel())
372 }
373 Type::PathWithGenerics(PathWithGenerics { path, args, .. }) => {
374 let seg = path.last_segment();
375 if seg == "Tx" || seg == "Rx" {
376 return true;
377 }
378 args.iter().any(|t| t.value.contains_channel())
379 }
380 Type::Path(path) => {
381 let seg = path.last_segment();
382 seg == "Tx" || seg == "Rx"
383 }
384 }
385 }
386}
387
388impl TypePath {
393 pub fn last_segment(&self) -> String {
395 self.rest
396 .iter()
397 .last()
398 .map(|seg| seg.value.second.to_string())
399 .unwrap_or_else(|| self.first.to_string())
400 }
401}
402
403impl ServiceTrait {
408 pub fn name(&self) -> String {
410 self.name.to_string()
411 }
412
413 pub fn doc(&self) -> Option<String> {
415 collect_doc_string(&self.attributes)
416 }
417
418 pub fn methods(&self) -> impl Iterator<Item = &ServiceMethod> {
420 self.body.content.iter().map(|entry| &entry.value)
421 }
422}
423
424impl ServiceMethod {
429 pub fn name(&self) -> String {
431 self.name.to_string()
432 }
433
434 pub fn doc(&self) -> Option<String> {
436 collect_doc_string(&self.attributes)
437 }
438
439 pub fn args(&self) -> impl Iterator<Item = &MethodParam> {
441 self.params
442 .content
443 .rest
444 .iter()
445 .flat_map(|rest| rest.value.second.iter().map(|entry| &entry.value))
446 }
447
448 pub fn return_type(&self) -> Type {
450 self.return_type
451 .iter()
452 .next()
453 .map(|r| r.value.ty.clone())
454 .unwrap_or_else(unit_type)
455 }
456
457 pub fn receiver_kind(&self) -> ReceiverKind {
458 match &self.params.content.receiver {
459 MethodReceiver::Ref(RefSelf {
460 mutability: Some(_),
461 ..
462 }) => ReceiverKind::RefMutSelf,
463 MethodReceiver::Ref(_) => ReceiverKind::RefSelf,
464 MethodReceiver::Value(ValueSelf {
465 mutability: Some(_),
466 ..
467 }) => ReceiverKind::MutSelfValue,
468 MethodReceiver::Value(_) => ReceiverKind::SelfValue,
469 MethodReceiver::Typed(TypedSelf {
470 mutability: Some(_),
471 ..
472 }) => ReceiverKind::MutTypedSelf,
473 MethodReceiver::Typed(_) => ReceiverKind::TypedSelf,
474 }
475 }
476
477 pub fn is_mut_receiver(&self) -> bool {
479 matches!(
480 self.receiver_kind(),
481 ReceiverKind::RefMutSelf | ReceiverKind::MutSelfValue
482 )
483 }
484
485 pub fn has_generics(&self) -> bool {
487 !self.generics.is_empty()
488 }
489
490 pub fn is_async(&self) -> bool {
492 !self._async.is_empty()
493 }
494
495 pub fn wants_context(&self) -> bool {
497 has_attr_path(&self.attributes, &["vox", "context"])
498 }
499
500 pub fn is_idem(&self) -> bool {
502 has_attr_helper(&self.attributes, &["vox"], "idem")
503 }
504
505 pub fn is_persist(&self) -> bool {
507 has_attr_helper(&self.attributes, &["vox"], "persist")
508 }
509}
510
511#[derive(Debug, Clone, Copy, PartialEq, Eq)]
512pub enum ReceiverKind {
513 RefSelf,
514 RefMutSelf,
515 SelfValue,
516 MutSelfValue,
517 TypedSelf,
518 MutTypedSelf,
519}
520
521impl MethodParam {
526 pub fn name(&self) -> String {
528 self.name.to_string()
529 }
530}
531
532pub fn method_ok_and_err_types(return_ty: &Type) -> (&Type, Option<&Type>) {
539 if let Some((ok, err)) = return_ty.as_result() {
540 (ok, Some(err))
541 } else {
542 (return_ty, None)
543 }
544}
545
546fn unit_type() -> Type {
548 let mut iter = "()".to_token_iter();
549 Type::parse(&mut iter).expect("unit type should always parse")
550}
551
552fn collect_doc_string(attrs: &Any<RawAttribute>) -> Option<String> {
554 let mut docs = Vec::new();
555
556 for attr in attrs.iter() {
557 let mut body_iter = attr.value.body.content.clone().to_token_iter();
558 if let Ok(doc_attr) = DocAttribute::parse(&mut body_iter) {
559 let line = doc_attr
560 .value
561 .as_str()
562 .replace("\\\"", "\"")
563 .replace("\\'", "'");
564 docs.push(line);
565 }
566 }
567
568 if docs.is_empty() {
569 None
570 } else {
571 Some(docs.join("\n"))
572 }
573}
574
575fn has_attr_path(attrs: &Any<RawAttribute>, expected: &[&str]) -> bool {
576 attrs
577 .iter()
578 .any(|attr| attr_path_matches(&attr.value, expected))
579}
580
581fn has_attr_helper(attrs: &Any<RawAttribute>, path: &[&str], helper: &str) -> bool {
582 attrs
583 .iter()
584 .any(|attr| attr_helper_matches(&attr.value, path, helper))
585}
586
587fn attr_path_matches(attr: &RawAttribute, expected: &[&str]) -> bool {
588 let mut iter = attr.body.content.clone().to_token_iter();
589 let Ok(path) = TypePath::parse(&mut iter) else {
590 return false;
591 };
592 if EndOfStream::parse(&mut iter).is_err() {
593 return false;
594 }
595 path_matches(&path, expected)
596}
597
598fn attr_helper_matches(attr: &RawAttribute, expected_path: &[&str], expected_helper: &str) -> bool {
599 let mut iter = attr.body.content.clone().to_token_iter();
600 let Ok(path) = TypePath::parse(&mut iter) else {
601 return false;
602 };
603 if !path_matches(&path, expected_path) {
604 return false;
605 }
606
607 let Ok(group) = ParenthesisGroupContaining::<TokenStream>::parse(&mut iter) else {
608 return false;
609 };
610 if EndOfStream::parse(&mut iter).is_err() {
611 return false;
612 }
613
614 let mut inner = group.content.to_token_iter();
615 let Ok(helper) = Ident::parse(&mut inner) else {
616 return false;
617 };
618 if EndOfStream::parse(&mut inner).is_err() {
619 return false;
620 }
621 helper == expected_helper
622}
623
624fn path_matches(path: &TypePath, expected: &[&str]) -> bool {
625 let actual = std::iter::once(path.first.to_string())
626 .chain(path.rest.iter().map(|seg| seg.value.second.to_string()))
627 .collect::<Vec<_>>();
628
629 actual.len() == expected.len()
630 && actual
631 .iter()
632 .zip(expected.iter())
633 .all(|(actual, expected)| actual == expected)
634}
635
636#[allow(clippy::result_large_err)] pub fn parse_trait(tokens: &TokenStream2) -> Result<ServiceTrait, unsynn::Error> {
639 let mut iter = tokens.clone().to_token_iter();
640 ServiceTrait::parse(&mut iter)
641}
642
643#[cfg(test)]
644mod tests {
645 use super::*;
646
647 fn parse(src: &str) -> ServiceTrait {
648 let ts: TokenStream2 = src.parse().expect("tokenstream parse");
649 parse_trait(&ts).expect("trait parse")
650 }
651
652 #[test]
653 fn parse_trait_exposes_docs_methods_and_args() {
654 let trait_def = parse(
655 r#"
656 #[doc = "Calculator service."]
657 pub trait Calculator {
658 #[doc = "Adds two numbers."]
659 async fn add(&self, a: i32, b: i32) -> Result<i64, String>;
660 }
661 "#,
662 );
663
664 assert_eq!(trait_def.name(), "Calculator");
665 assert_eq!(trait_def.doc(), Some("Calculator service.".to_string()));
666
667 let method = trait_def.methods().next().expect("method");
668 assert_eq!(method.name(), "add");
669 assert_eq!(method.doc(), Some("Adds two numbers.".to_string()));
670 assert_eq!(
671 method.args().map(|arg| arg.name()).collect::<Vec<_>>(),
672 vec!["a", "b"]
673 );
674
675 let ret = method.return_type();
676 let (ok, err) = method_ok_and_err_types(&ret);
677 assert!(ok.as_result().is_none());
678 assert!(err.is_some());
679 }
680
681 #[test]
682 fn return_type_defaults_to_unit_when_omitted() {
683 let trait_def = parse(
684 r#"
685 trait Svc {
686 async fn ping(&self);
687 }
688 "#,
689 );
690 let method = trait_def.methods().next().expect("method");
691 let ret = method.return_type();
692 match ret {
693 Type::Tuple(TypeTuple(group)) => assert!(group.content.is_empty()),
694 other => panic!(
695 "expected unit tuple return, got {}",
696 other.to_token_stream()
697 ),
698 }
699 }
700
701 #[test]
702 fn method_helpers_detect_generics_and_mut_receiver() {
703 let trait_def = parse(
704 r#"
705 trait Svc {
706 async fn bad<T>(&mut self, value: T) -> T;
707 }
708 "#,
709 );
710 let method = trait_def.methods().next().expect("method");
711 assert!(method.has_generics());
712 assert!(method.is_mut_receiver());
713 }
714
715 #[test]
716 fn method_helpers_detect_async_keyword_presence() {
717 let trait_def = parse(
718 r#"
719 trait Svc {
720 fn plain(&self) -> u32;
721 async fn async_one(&self) -> u32;
722 }
723 "#,
724 );
725 let mut methods = trait_def.methods();
726 assert!(!methods.next().expect("plain method").is_async());
727 assert!(methods.next().expect("async method").is_async());
728 }
729
730 #[test]
731 fn method_helpers_detect_receiver_kinds() {
732 let trait_def = parse(
733 r#"
734 trait Svc {
735 async fn by_ref(&self) -> u32;
736 async fn by_ref_lifetime(&'a self) -> u32;
737 async fn by_mut_ref(&mut self) -> u32;
738 async fn by_mut_ref_lifetime(&'a mut self) -> u32;
739 async fn by_value(self) -> u32;
740 async fn by_mut_value(mut self) -> u32;
741 async fn by_typed(self: Box<Self>) -> u32;
742 async fn by_mut_typed(mut self: Box<Self>) -> u32;
743 }
744 "#,
745 );
746 let mut methods = trait_def.methods();
747 assert_eq!(
748 methods.next().expect("by_ref").receiver_kind(),
749 ReceiverKind::RefSelf
750 );
751 assert_eq!(
752 methods.next().expect("by_ref_lifetime").receiver_kind(),
753 ReceiverKind::RefSelf
754 );
755 assert_eq!(
756 methods.next().expect("by_mut_ref").receiver_kind(),
757 ReceiverKind::RefMutSelf
758 );
759 assert_eq!(
760 methods.next().expect("by_mut_ref_lifetime").receiver_kind(),
761 ReceiverKind::RefMutSelf
762 );
763 assert_eq!(
764 methods.next().expect("by_value").receiver_kind(),
765 ReceiverKind::SelfValue
766 );
767 assert_eq!(
768 methods.next().expect("by_mut_value").receiver_kind(),
769 ReceiverKind::MutSelfValue
770 );
771 assert_eq!(
772 methods.next().expect("by_typed").receiver_kind(),
773 ReceiverKind::TypedSelf
774 );
775 assert_eq!(
776 methods.next().expect("by_mut_typed").receiver_kind(),
777 ReceiverKind::MutTypedSelf
778 );
779 }
780
781 #[test]
782 fn method_helpers_detect_explicit_request_context_opt_in() {
783 let trait_def = parse(
784 r#"
785 trait Svc {
786 #[vox::context]
787 async fn contextual(&self) -> u32;
788
789 async fn plain(&self) -> u32;
790 }
791 "#,
792 );
793 let mut methods = trait_def.methods();
794 assert!(methods.next().expect("contextual method").wants_context());
795 assert!(!methods.next().expect("plain method").wants_context());
796 }
797
798 #[test]
799 fn method_helpers_detect_retry_helper_attributes() {
800 let trait_def = parse(
801 r#"
802 trait Svc {
803 #[vox(idem)]
804 async fn cached(&self) -> u32;
805
806 #[vox(persist)]
807 async fn durable(&self) -> u32;
808
809 async fn plain(&self) -> u32;
810 }
811 "#,
812 );
813 let mut methods = trait_def.methods();
814 let cached = methods.next().expect("cached");
815 assert!(cached.is_idem());
816 assert!(!cached.is_persist());
817
818 let durable = methods.next().expect("durable");
819 assert!(!durable.is_idem());
820 assert!(durable.is_persist());
821
822 let plain = methods.next().expect("plain");
823 assert!(!plain.is_idem());
824 assert!(!plain.is_persist());
825 }
826
827 #[test]
828 fn type_helpers_detect_result_lifetime_and_channel_nesting() {
829 let trait_def = parse(
830 r#"
831 trait Svc {
832 async fn stream(&self, input: &'static str) -> Result<Option<Tx<Vec<u8>>>, Rx<u32>>;
833 }
834 "#,
835 );
836 let method = trait_def.methods().next().expect("method");
837 let arg = method.args().next().expect("arg");
838 assert!(arg.ty.has_lifetime());
839 assert!(!arg.ty.contains_channel());
840
841 let ret = method.return_type();
842 let (ok, err) = method_ok_and_err_types(&ret);
843 assert!(ok.contains_channel());
844 assert!(err.expect("result err type").contains_channel());
845 }
846
847 #[test]
848 fn type_helpers_detect_named_and_elided_lifetimes() {
849 let trait_def = parse(
850 r#"
851 trait Svc {
852 async fn borrowed(&self) -> Result<&'vox str, Error>;
853 async fn bad_lifetime(&self) -> Result<&'a str, Error>;
854 async fn elided(&self) -> Result<&str, Error>;
855 }
856 "#,
857 );
858 let mut methods = trait_def.methods();
859
860 let borrowed = methods.next().expect("borrowed method").return_type();
861 let (borrowed_ok, _) = method_ok_and_err_types(&borrowed);
862 assert!(borrowed_ok.has_named_lifetime("vox"));
863 assert!(!borrowed_ok.has_non_named_lifetime("vox"));
864 assert!(!borrowed_ok.has_elided_reference_lifetime());
865
866 let bad_lifetime = methods.next().expect("bad_lifetime method").return_type();
867 let (bad_ok, _) = method_ok_and_err_types(&bad_lifetime);
868 assert!(!bad_ok.has_named_lifetime("vox"));
869 assert!(bad_ok.has_non_named_lifetime("vox"));
870 assert!(!bad_ok.has_elided_reference_lifetime());
871
872 let elided = methods.next().expect("elided method").return_type();
873 let (elided_ok, _) = method_ok_and_err_types(&elided);
874 assert!(!elided_ok.has_named_lifetime("vox"));
875 assert!(!elided_ok.has_non_named_lifetime("vox"));
876 assert!(elided_ok.has_elided_reference_lifetime());
877 }
878
879 #[test]
880 fn type_path_last_segment_uses_trailing_segment() {
881 let trait_def = parse(
882 r#"
883 trait Svc {
884 async fn f(&self) -> std::result::Result<u8, u8>;
885 }
886 "#,
887 );
888 let method = trait_def.methods().next().expect("method");
889 let ret = method.return_type();
890 let Type::PathWithGenerics(path_with_generics) = ret else {
891 panic!("expected path with generics");
892 };
893 assert_eq!(path_with_generics.path.last_segment(), "Result");
894 }
895}