From 51e70d0c3bff9ba69b40f50c6fc4267056f32f89 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 24 Apr 2025 13:04:21 +0200 Subject: [PATCH 1/3] Rust: Add Copilot generated test for `?` operator expressions --- .../test/library-tests/type-inference/main.rs | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/rust/ql/test/library-tests/type-inference/main.rs b/rust/ql/test/library-tests/type-inference/main.rs index 1972b181c83c..7e8ab73d00d2 100644 --- a/rust/ql/test/library-tests/type-inference/main.rs +++ b/rust/ql/test/library-tests/type-inference/main.rs @@ -919,6 +919,99 @@ mod borrowed_typed { } } +mod try_expressions { + use std::fmt::Debug; + + #[derive(Debug)] + struct S1; + + #[derive(Debug)] + struct S2; + + #[derive(Debug)] + enum MyResult { + MyOk(T), + MyErr(E), + } + + impl MyResult { + fn map(self, op: F) -> MyResult + where + F: FnOnce(T) -> U, + { + match self { + MyResult::MyOk(t) => MyResult::MyOk(op(t)), + MyResult::MyErr(e) => MyResult::MyErr(e), + } + } + + fn and_then(self, op: F) -> MyResult + where + F: FnOnce(T) -> MyResult, + { + match self { + MyResult::MyOk(t) => op(t), + MyResult::MyErr(e) => MyResult::MyErr(e), + } + } + } + + // For the try operator to work, we need to implement From for OtherE + impl From for S2 { + fn from(s: S1) -> S2 { + S2 + } + } + + // Simple function using ? operator with same error types + fn try_same_error() -> MyResult { + let x = MyResult::MyOk(S1)?; // $ type=x:S1 + MyResult::MyOk(x) + } + + // Function using ? operator with different error types that need conversion + fn try_convert_error() -> MyResult { + let x: MyResult = MyResult::MyOk(S1); + let y = x?; // $ type=y:S1 + MyResult::MyOk(y) + } + + // Chained ? operations + fn try_chained() -> MyResult { + let x: MyResult, S1> = MyResult::MyOk(MyResult::MyOk(S1)); + let y = x?.map(|s| s)?; // First ? returns MyResult, second ? returns S1 + MyResult::MyOk(y) + } + + // Function that uses ? with closures and complex error cases + fn try_complex(input: MyResult) -> MyResult { + let value = input?; // $ method=From::from + let mapped = MyResult::MyOk(value).and_then(|v| { + println!("{:?}", v); + MyResult::MyOk::<_, S1>(v) + })?; // $ method=From::from + MyResult::MyOk(mapped) + } + + pub fn f() { + if let MyResult::MyOk(result) = try_same_error() { + println!("{:?}", result); + } + + if let MyResult::MyOk(result) = try_convert_error() { + println!("{:?}", result); + } + + if let MyResult::MyOk(result) = try_chained() { + println!("{:?}", result); + } + + if let MyResult::MyOk(result) = try_complex(MyResult::MyOk(S1)) { + println!("{:?}", result); + } + } +} + fn main() { field_access::f(); method_impl::f(); @@ -935,4 +1028,5 @@ fn main() { trait_implicit_self_borrow::f(); implicit_self_borrow::f(); borrowed_typed::f(); + try_expressions::f(); } From 88075c4c8cdd60ad9a7db5f93f3e9741ab64a5f5 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 24 Apr 2025 13:05:14 +0200 Subject: [PATCH 2/3] Rust: Make manual tweaks to Copilot generated code --- .../test/library-tests/type-inference/main.rs | 78 +++++------------ .../type-inference/type-inference.expected | 86 ++++++++++++++++++- 2 files changed, 104 insertions(+), 60 deletions(-) diff --git a/rust/ql/test/library-tests/type-inference/main.rs b/rust/ql/test/library-tests/type-inference/main.rs index 7e8ab73d00d2..0b93a8511a06 100644 --- a/rust/ql/test/library-tests/type-inference/main.rs +++ b/rust/ql/test/library-tests/type-inference/main.rs @@ -928,85 +928,51 @@ mod try_expressions { #[derive(Debug)] struct S2; - #[derive(Debug)] - enum MyResult { - MyOk(T), - MyErr(E), - } - - impl MyResult { - fn map(self, op: F) -> MyResult - where - F: FnOnce(T) -> U, - { - match self { - MyResult::MyOk(t) => MyResult::MyOk(op(t)), - MyResult::MyErr(e) => MyResult::MyErr(e), - } - } - - fn and_then(self, op: F) -> MyResult - where - F: FnOnce(T) -> MyResult, - { - match self { - MyResult::MyOk(t) => op(t), - MyResult::MyErr(e) => MyResult::MyErr(e), - } - } - } - - // For the try operator to work, we need to implement From for OtherE - impl From for S2 { - fn from(s: S1) -> S2 { - S2 - } - } - // Simple function using ? operator with same error types - fn try_same_error() -> MyResult { - let x = MyResult::MyOk(S1)?; // $ type=x:S1 - MyResult::MyOk(x) + fn try_same_error() -> Result { + let x = Result::Ok(S1)?; // $ MISSING: type=x:S1 + Result::Ok(S1) } // Function using ? operator with different error types that need conversion - fn try_convert_error() -> MyResult { - let x: MyResult = MyResult::MyOk(S1); - let y = x?; // $ type=y:S1 - MyResult::MyOk(y) + fn try_convert_error() -> Result { + let x = Result::Ok(S1); + let y = x?; // $ MISSING: type=y:S1 + Result::Ok(S1) } // Chained ? operations - fn try_chained() -> MyResult { - let x: MyResult, S1> = MyResult::MyOk(MyResult::MyOk(S1)); - let y = x?.map(|s| s)?; // First ? returns MyResult, second ? returns S1 - MyResult::MyOk(y) + fn try_chained() -> Result { + let x = Result::Ok(Result::Ok(S1)); + // First ? returns Result, second ? returns S1 + let y = x?.map(|s| s)?; // $ MISSING: method=map + Result::Ok(S1) } // Function that uses ? with closures and complex error cases - fn try_complex(input: MyResult) -> MyResult { - let value = input?; // $ method=From::from - let mapped = MyResult::MyOk(value).and_then(|v| { + fn try_complex(input: Result) -> Result { + let value = input?; + let mapped = Result::Ok(value).and_then(|v| { println!("{:?}", v); - MyResult::MyOk::<_, S1>(v) - })?; // $ method=From::from - MyResult::MyOk(mapped) + Result::Ok::<_, S1>(v) + })?; // $ method=and_then + Result::Err(S1) } pub fn f() { - if let MyResult::MyOk(result) = try_same_error() { + if let Result::Ok(result) = try_same_error() { println!("{:?}", result); } - if let MyResult::MyOk(result) = try_convert_error() { + if let Result::Ok(result) = try_convert_error() { println!("{:?}", result); } - if let MyResult::MyOk(result) = try_chained() { + if let Result::Ok(result) = try_chained() { println!("{:?}", result); } - if let MyResult::MyOk(result) = try_complex(MyResult::MyOk(S1)) { + if let Result::Ok(result) = try_complex(Result::Ok(S1)) { println!("{:?}", result); } } diff --git a/rust/ql/test/library-tests/type-inference/type-inference.expected b/rust/ql/test/library-tests/type-inference/type-inference.expected index c91b3cef3dc3..c9b7b349a70c 100644 --- a/rust/ql/test/library-tests/type-inference/type-inference.expected +++ b/rust/ql/test/library-tests/type-inference/type-inference.expected @@ -1005,7 +1005,85 @@ inferType | main.rs:918:15:918:16 | &x | | file://:0:0:0:0 | & | | main.rs:918:15:918:16 | &x | &T | main.rs:894:5:894:13 | S | | main.rs:918:16:918:16 | x | | main.rs:894:5:894:13 | S | -| main.rs:924:5:924:20 | ...::f(...) | | main.rs:67:5:67:21 | Foo | -| main.rs:925:5:925:60 | ...::g(...) | | main.rs:67:5:67:21 | Foo | -| main.rs:925:20:925:38 | ...::Foo {...} | | main.rs:67:5:67:21 | Foo | -| main.rs:925:41:925:59 | ...::Foo {...} | | main.rs:67:5:67:21 | Foo | +| main.rs:932:43:935:5 | { ... } | | file://:0:0:0:0 | Result | +| main.rs:932:43:935:5 | { ... } | E | main.rs:925:5:926:14 | S1 | +| main.rs:932:43:935:5 | { ... } | T | main.rs:925:5:926:14 | S1 | +| main.rs:933:17:933:30 | ...::Ok(...) | | file://:0:0:0:0 | Result | +| main.rs:933:17:933:30 | ...::Ok(...) | T | main.rs:925:5:926:14 | S1 | +| main.rs:933:28:933:29 | S1 | | main.rs:925:5:926:14 | S1 | +| main.rs:934:9:934:22 | ...::Ok(...) | | file://:0:0:0:0 | Result | +| main.rs:934:9:934:22 | ...::Ok(...) | E | main.rs:925:5:926:14 | S1 | +| main.rs:934:9:934:22 | ...::Ok(...) | T | main.rs:925:5:926:14 | S1 | +| main.rs:934:20:934:21 | S1 | | main.rs:925:5:926:14 | S1 | +| main.rs:938:46:942:5 | { ... } | | file://:0:0:0:0 | Result | +| main.rs:938:46:942:5 | { ... } | E | main.rs:928:5:929:14 | S2 | +| main.rs:938:46:942:5 | { ... } | T | main.rs:925:5:926:14 | S1 | +| main.rs:939:13:939:13 | x | | file://:0:0:0:0 | Result | +| main.rs:939:13:939:13 | x | T | main.rs:925:5:926:14 | S1 | +| main.rs:939:17:939:30 | ...::Ok(...) | | file://:0:0:0:0 | Result | +| main.rs:939:17:939:30 | ...::Ok(...) | T | main.rs:925:5:926:14 | S1 | +| main.rs:939:28:939:29 | S1 | | main.rs:925:5:926:14 | S1 | +| main.rs:940:17:940:17 | x | | file://:0:0:0:0 | Result | +| main.rs:940:17:940:17 | x | T | main.rs:925:5:926:14 | S1 | +| main.rs:941:9:941:22 | ...::Ok(...) | | file://:0:0:0:0 | Result | +| main.rs:941:9:941:22 | ...::Ok(...) | E | main.rs:928:5:929:14 | S2 | +| main.rs:941:9:941:22 | ...::Ok(...) | T | main.rs:925:5:926:14 | S1 | +| main.rs:941:20:941:21 | S1 | | main.rs:925:5:926:14 | S1 | +| main.rs:945:40:950:5 | { ... } | | file://:0:0:0:0 | Result | +| main.rs:945:40:950:5 | { ... } | E | main.rs:928:5:929:14 | S2 | +| main.rs:945:40:950:5 | { ... } | T | main.rs:925:5:926:14 | S1 | +| main.rs:946:13:946:13 | x | | file://:0:0:0:0 | Result | +| main.rs:946:13:946:13 | x | T | file://:0:0:0:0 | Result | +| main.rs:946:13:946:13 | x | T.T | main.rs:925:5:926:14 | S1 | +| main.rs:946:17:946:42 | ...::Ok(...) | | file://:0:0:0:0 | Result | +| main.rs:946:17:946:42 | ...::Ok(...) | T | file://:0:0:0:0 | Result | +| main.rs:946:17:946:42 | ...::Ok(...) | T.T | main.rs:925:5:926:14 | S1 | +| main.rs:946:28:946:41 | ...::Ok(...) | | file://:0:0:0:0 | Result | +| main.rs:946:28:946:41 | ...::Ok(...) | T | main.rs:925:5:926:14 | S1 | +| main.rs:946:39:946:40 | S1 | | main.rs:925:5:926:14 | S1 | +| main.rs:948:17:948:17 | x | | file://:0:0:0:0 | Result | +| main.rs:948:17:948:17 | x | T | file://:0:0:0:0 | Result | +| main.rs:948:17:948:17 | x | T.T | main.rs:925:5:926:14 | S1 | +| main.rs:949:9:949:22 | ...::Ok(...) | | file://:0:0:0:0 | Result | +| main.rs:949:9:949:22 | ...::Ok(...) | E | main.rs:928:5:929:14 | S2 | +| main.rs:949:9:949:22 | ...::Ok(...) | T | main.rs:925:5:926:14 | S1 | +| main.rs:949:20:949:21 | S1 | | main.rs:925:5:926:14 | S1 | +| main.rs:953:30:953:34 | input | | file://:0:0:0:0 | Result | +| main.rs:953:30:953:34 | input | E | main.rs:925:5:926:14 | S1 | +| main.rs:953:30:953:34 | input | T | main.rs:953:20:953:27 | T | +| main.rs:953:69:960:5 | { ... } | | file://:0:0:0:0 | Result | +| main.rs:953:69:960:5 | { ... } | E | main.rs:925:5:926:14 | S1 | +| main.rs:953:69:960:5 | { ... } | T | main.rs:953:20:953:27 | T | +| main.rs:954:21:954:25 | input | | file://:0:0:0:0 | Result | +| main.rs:954:21:954:25 | input | E | main.rs:925:5:926:14 | S1 | +| main.rs:954:21:954:25 | input | T | main.rs:953:20:953:27 | T | +| main.rs:955:22:955:38 | ...::Ok(...) | | file://:0:0:0:0 | Result | +| main.rs:955:22:958:10 | ... .and_then(...) | | file://:0:0:0:0 | Result | +| main.rs:955:53:958:9 | { ... } | | file://:0:0:0:0 | Result | +| main.rs:955:53:958:9 | { ... } | E | main.rs:925:5:926:14 | S1 | +| main.rs:957:13:957:34 | ...::Ok::<...>(...) | | file://:0:0:0:0 | Result | +| main.rs:957:13:957:34 | ...::Ok::<...>(...) | E | main.rs:925:5:926:14 | S1 | +| main.rs:959:9:959:23 | ...::Err(...) | | file://:0:0:0:0 | Result | +| main.rs:959:9:959:23 | ...::Err(...) | E | main.rs:925:5:926:14 | S1 | +| main.rs:959:9:959:23 | ...::Err(...) | T | main.rs:953:20:953:27 | T | +| main.rs:959:21:959:22 | S1 | | main.rs:925:5:926:14 | S1 | +| main.rs:963:37:963:52 | try_same_error(...) | | file://:0:0:0:0 | Result | +| main.rs:963:37:963:52 | try_same_error(...) | E | main.rs:925:5:926:14 | S1 | +| main.rs:963:37:963:52 | try_same_error(...) | T | main.rs:925:5:926:14 | S1 | +| main.rs:967:37:967:55 | try_convert_error(...) | | file://:0:0:0:0 | Result | +| main.rs:967:37:967:55 | try_convert_error(...) | E | main.rs:928:5:929:14 | S2 | +| main.rs:967:37:967:55 | try_convert_error(...) | T | main.rs:925:5:926:14 | S1 | +| main.rs:971:37:971:49 | try_chained(...) | | file://:0:0:0:0 | Result | +| main.rs:971:37:971:49 | try_chained(...) | E | main.rs:928:5:929:14 | S2 | +| main.rs:971:37:971:49 | try_chained(...) | T | main.rs:925:5:926:14 | S1 | +| main.rs:975:37:975:63 | try_complex(...) | | file://:0:0:0:0 | Result | +| main.rs:975:37:975:63 | try_complex(...) | E | main.rs:925:5:926:14 | S1 | +| main.rs:975:37:975:63 | try_complex(...) | T | main.rs:925:5:926:14 | S1 | +| main.rs:975:49:975:62 | ...::Ok(...) | | file://:0:0:0:0 | Result | +| main.rs:975:49:975:62 | ...::Ok(...) | E | main.rs:925:5:926:14 | S1 | +| main.rs:975:49:975:62 | ...::Ok(...) | T | main.rs:925:5:926:14 | S1 | +| main.rs:975:60:975:61 | S1 | | main.rs:925:5:926:14 | S1 | +| main.rs:983:5:983:20 | ...::f(...) | | main.rs:67:5:67:21 | Foo | +| main.rs:984:5:984:60 | ...::g(...) | | main.rs:67:5:67:21 | Foo | +| main.rs:984:20:984:38 | ...::Foo {...} | | main.rs:67:5:67:21 | Foo | +| main.rs:984:41:984:59 | ...::Foo {...} | | main.rs:67:5:67:21 | Foo | From a3c26b4bfe57958419b2d2432c9c5dbf98408637 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 24 Apr 2025 13:05:42 +0200 Subject: [PATCH 3/3] Rust: Type inference for `?` expressions --- .../lib/codeql/rust/internal/TypeInference.qll | 14 ++++++++++++++ .../PathResolutionConsistency.expected | 17 +++++++++++++++++ .../test/library-tests/type-inference/main.rs | 6 +++--- .../type-inference/type-inference.expected | 11 +++++++++++ 4 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 rust/ql/test/library-tests/frameworks/postgres/CONSISTENCY/PathResolutionConsistency.expected diff --git a/rust/ql/lib/codeql/rust/internal/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/TypeInference.qll index 0f0c92306d12..5c5f678950a2 100644 --- a/rust/ql/lib/codeql/rust/internal/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/TypeInference.qll @@ -6,6 +6,7 @@ private import Type private import Type as T private import TypeMention private import codeql.typeinference.internal.TypeInference +private import codeql.rust.frameworks.stdlib.Stdlib class Type = T::Type; @@ -891,6 +892,17 @@ private Type inferRefExprType(Expr e, TypePath path) { ) } +pragma[nomagic] +private Type inferTryExprType(TryExpr te, TypePath path) { + exists(TypeParam tp | + result = inferType(te.getExpr(), TypePath::cons(TTypeParamTypeParameter(tp), path)) + | + tp = any(ResultEnum r).getGenericParamList().getGenericParam(0) + or + tp = any(OptionEnum o).getGenericParamList().getGenericParam(0) + ) +} + cached private module Cached { private import codeql.rust.internal.CachedStages @@ -1008,6 +1020,8 @@ private module Cached { result = inferFieldExprType(n, path) or result = inferRefExprType(n, path) + or + result = inferTryExprType(n, path) } } diff --git a/rust/ql/test/library-tests/frameworks/postgres/CONSISTENCY/PathResolutionConsistency.expected b/rust/ql/test/library-tests/frameworks/postgres/CONSISTENCY/PathResolutionConsistency.expected new file mode 100644 index 000000000000..c47d16d68751 --- /dev/null +++ b/rust/ql/test/library-tests/frameworks/postgres/CONSISTENCY/PathResolutionConsistency.expected @@ -0,0 +1,17 @@ +multipleMethodCallTargets +| main.rs:11:5:18:5 | conn.execute(...) | file://:0:0:0:0 | fn execute | +| main.rs:11:5:18:5 | conn.execute(...) | file://:0:0:0:0 | fn execute | +| main.rs:22:5:22:37 | conn.execute(...) | file://:0:0:0:0 | fn execute | +| main.rs:22:5:22:37 | conn.execute(...) | file://:0:0:0:0 | fn execute | +| main.rs:23:5:23:38 | conn.batch_execute(...) | file://:0:0:0:0 | fn batch_execute | +| main.rs:23:5:23:38 | conn.batch_execute(...) | file://:0:0:0:0 | fn batch_execute | +| main.rs:25:5:25:32 | conn.prepare(...) | file://:0:0:0:0 | fn prepare | +| main.rs:25:5:25:32 | conn.prepare(...) | file://:0:0:0:0 | fn prepare | +| main.rs:28:5:28:35 | conn.query(...) | file://:0:0:0:0 | fn query | +| main.rs:28:5:28:35 | conn.query(...) | file://:0:0:0:0 | fn query | +| main.rs:29:5:29:39 | conn.query_one(...) | file://:0:0:0:0 | fn query_one | +| main.rs:29:5:29:39 | conn.query_one(...) | file://:0:0:0:0 | fn query_one | +| main.rs:30:5:30:39 | conn.query_opt(...) | file://:0:0:0:0 | fn query_opt | +| main.rs:30:5:30:39 | conn.query_opt(...) | file://:0:0:0:0 | fn query_opt | +| main.rs:35:17:35:67 | conn.query(...) | file://:0:0:0:0 | fn query | +| main.rs:35:17:35:67 | conn.query(...) | file://:0:0:0:0 | fn query | diff --git a/rust/ql/test/library-tests/type-inference/main.rs b/rust/ql/test/library-tests/type-inference/main.rs index 0b93a8511a06..fa16b6264740 100644 --- a/rust/ql/test/library-tests/type-inference/main.rs +++ b/rust/ql/test/library-tests/type-inference/main.rs @@ -930,14 +930,14 @@ mod try_expressions { // Simple function using ? operator with same error types fn try_same_error() -> Result { - let x = Result::Ok(S1)?; // $ MISSING: type=x:S1 + let x = Result::Ok(S1)?; // $ type=x:S1 Result::Ok(S1) } // Function using ? operator with different error types that need conversion fn try_convert_error() -> Result { let x = Result::Ok(S1); - let y = x?; // $ MISSING: type=y:S1 + let y = x?; // $ type=y:S1 Result::Ok(S1) } @@ -945,7 +945,7 @@ mod try_expressions { fn try_chained() -> Result { let x = Result::Ok(Result::Ok(S1)); // First ? returns Result, second ? returns S1 - let y = x?.map(|s| s)?; // $ MISSING: method=map + let y = x?.map(|s| s)?; // $ method=map Result::Ok(S1) } diff --git a/rust/ql/test/library-tests/type-inference/type-inference.expected b/rust/ql/test/library-tests/type-inference/type-inference.expected index c9b7b349a70c..42e5d90701b9 100644 --- a/rust/ql/test/library-tests/type-inference/type-inference.expected +++ b/rust/ql/test/library-tests/type-inference/type-inference.expected @@ -1008,8 +1008,10 @@ inferType | main.rs:932:43:935:5 | { ... } | | file://:0:0:0:0 | Result | | main.rs:932:43:935:5 | { ... } | E | main.rs:925:5:926:14 | S1 | | main.rs:932:43:935:5 | { ... } | T | main.rs:925:5:926:14 | S1 | +| main.rs:933:13:933:13 | x | | main.rs:925:5:926:14 | S1 | | main.rs:933:17:933:30 | ...::Ok(...) | | file://:0:0:0:0 | Result | | main.rs:933:17:933:30 | ...::Ok(...) | T | main.rs:925:5:926:14 | S1 | +| main.rs:933:17:933:31 | TryExpr | | main.rs:925:5:926:14 | S1 | | main.rs:933:28:933:29 | S1 | | main.rs:925:5:926:14 | S1 | | main.rs:934:9:934:22 | ...::Ok(...) | | file://:0:0:0:0 | Result | | main.rs:934:9:934:22 | ...::Ok(...) | E | main.rs:925:5:926:14 | S1 | @@ -1023,8 +1025,10 @@ inferType | main.rs:939:17:939:30 | ...::Ok(...) | | file://:0:0:0:0 | Result | | main.rs:939:17:939:30 | ...::Ok(...) | T | main.rs:925:5:926:14 | S1 | | main.rs:939:28:939:29 | S1 | | main.rs:925:5:926:14 | S1 | +| main.rs:940:13:940:13 | y | | main.rs:925:5:926:14 | S1 | | main.rs:940:17:940:17 | x | | file://:0:0:0:0 | Result | | main.rs:940:17:940:17 | x | T | main.rs:925:5:926:14 | S1 | +| main.rs:940:17:940:18 | TryExpr | | main.rs:925:5:926:14 | S1 | | main.rs:941:9:941:22 | ...::Ok(...) | | file://:0:0:0:0 | Result | | main.rs:941:9:941:22 | ...::Ok(...) | E | main.rs:928:5:929:14 | S2 | | main.rs:941:9:941:22 | ...::Ok(...) | T | main.rs:925:5:926:14 | S1 | @@ -1044,6 +1048,9 @@ inferType | main.rs:948:17:948:17 | x | | file://:0:0:0:0 | Result | | main.rs:948:17:948:17 | x | T | file://:0:0:0:0 | Result | | main.rs:948:17:948:17 | x | T.T | main.rs:925:5:926:14 | S1 | +| main.rs:948:17:948:18 | TryExpr | | file://:0:0:0:0 | Result | +| main.rs:948:17:948:18 | TryExpr | T | main.rs:925:5:926:14 | S1 | +| main.rs:948:17:948:29 | ... .map(...) | | file://:0:0:0:0 | Result | | main.rs:949:9:949:22 | ...::Ok(...) | | file://:0:0:0:0 | Result | | main.rs:949:9:949:22 | ...::Ok(...) | E | main.rs:928:5:929:14 | S2 | | main.rs:949:9:949:22 | ...::Ok(...) | T | main.rs:925:5:926:14 | S1 | @@ -1054,11 +1061,15 @@ inferType | main.rs:953:69:960:5 | { ... } | | file://:0:0:0:0 | Result | | main.rs:953:69:960:5 | { ... } | E | main.rs:925:5:926:14 | S1 | | main.rs:953:69:960:5 | { ... } | T | main.rs:953:20:953:27 | T | +| main.rs:954:13:954:17 | value | | main.rs:953:20:953:27 | T | | main.rs:954:21:954:25 | input | | file://:0:0:0:0 | Result | | main.rs:954:21:954:25 | input | E | main.rs:925:5:926:14 | S1 | | main.rs:954:21:954:25 | input | T | main.rs:953:20:953:27 | T | +| main.rs:954:21:954:26 | TryExpr | | main.rs:953:20:953:27 | T | | main.rs:955:22:955:38 | ...::Ok(...) | | file://:0:0:0:0 | Result | +| main.rs:955:22:955:38 | ...::Ok(...) | T | main.rs:953:20:953:27 | T | | main.rs:955:22:958:10 | ... .and_then(...) | | file://:0:0:0:0 | Result | +| main.rs:955:33:955:37 | value | | main.rs:953:20:953:27 | T | | main.rs:955:53:958:9 | { ... } | | file://:0:0:0:0 | Result | | main.rs:955:53:958:9 | { ... } | E | main.rs:925:5:926:14 | S1 | | main.rs:957:13:957:34 | ...::Ok::<...>(...) | | file://:0:0:0:0 | Result |