Improve spans for chained function calls

Fixes: #84180

For chained function calls separated by the `?` try operator, the
function call following the try operator produced a MIR `Call` span that
matched the span of the first call. The `?` try operator started a new
span, so the second call got no span.

It turns out the MIR `Call` terminator has a `func` `Operand`
for the `Constant` representing the function name, and the function
name's Span can be used to reset the starting position of the span.
This commit is contained in:
Rich Kadel 2021-04-24 13:55:10 -07:00
parent 2b68027841
commit 41667e8534
3 changed files with 182 additions and 3 deletions

View file

@ -717,11 +717,21 @@ pub(super) fn filtered_terminator_span(
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::Goto { .. } => None,
// Call `func` operand can have a more specific span when part of a chain of calls
| TerminatorKind::Call { ref func, .. } => {
let mut span = terminator.source_info.span;
if let mir::Operand::Constant(box constant) = func {
if constant.span.lo() > span.lo() {
span = span.with_lo(constant.span.lo());
}
}
Some(function_source_span(span, body_span))
}
// Retain spans from all other terminators
TerminatorKind::Resume
| TerminatorKind::Abort
| TerminatorKind::Return
| TerminatorKind::Call { .. }
| TerminatorKind::Yield { .. }
| TerminatorKind::GeneratorDrop
| TerminatorKind::FalseUnwind { .. }

View file

@ -9,7 +9,7 @@
9| | }
10| 6|}
11| |
12| 1|fn main() -> Result<(),()> {
12| 1|fn test1() -> Result<(),()> {
13| 1| let mut
14| 1| countdown = 10
15| | ;
@ -35,4 +35,91 @@
34| | }
35| 0| Ok(())
36| 1|}
37| |
38| |struct Thing1;
39| |impl Thing1 {
40| 18| fn get_thing_2(&self, return_error: bool) -> Result<Thing2,()> {
41| 18| if return_error {
42| 1| Err(())
43| | } else {
44| 17| Ok(Thing2{})
45| | }
46| 18| }
47| |}
48| |
49| |struct Thing2;
50| |impl Thing2 {
51| 17| fn call(&self, return_error: bool) -> Result<u32,()> {
52| 17| if return_error {
53| 2| Err(())
54| | } else {
55| 15| Ok(57)
56| | }
57| 17| }
58| |}
59| |
60| 1|fn test2() -> Result<(),()> {
61| 1| let thing1 = Thing1{};
62| 1| let mut
63| 1| countdown = 10
64| | ;
65| | for
66| 6| _
67| | in
68| 6| 0..10
69| | {
70| 6| countdown
71| 6| -= 1
72| 6| ;
73| 6| if
74| 6| countdown < 5
75| | {
76| 1| thing1.get_thing_2(/*err=*/ false)?.call(/*err=*/ true).expect_err("call should fail");
^0
77| 1| thing1
78| 1| .
79| 1| get_thing_2(/*return_error=*/ false)
80| 0| ?
81| | .
82| 1| call(/*return_error=*/ true)
83| 1| .
84| 1| expect_err(
85| 1| "call should fail"
86| 1| );
87| 1| let val = thing1.get_thing_2(/*return_error=*/ true)?.call(/*return_error=*/ true)?;
^0 ^0 ^0
88| 0| assert_eq!(val, 57);
89| 0| let val = thing1.get_thing_2(/*return_error=*/ true)?.call(/*return_error=*/ false)?;
90| 0| assert_eq!(val, 57);
91| | }
92| | else
93| | {
94| 5| let val = thing1.get_thing_2(/*return_error=*/ false)?.call(/*return_error=*/ false)?;
^0 ^0
95| 5| assert_eq!(val, 57);
96| 5| let val = thing1
97| 5| .get_thing_2(/*return_error=*/ false)?
^0
98| 5| .call(/*return_error=*/ false)?;
^0
99| 5| assert_eq!(val, 57);
100| 5| let val = thing1
101| 5| .get_thing_2(/*return_error=*/ false)
102| 0| ?
103| 5| .call(/*return_error=*/ false)
104| 0| ?
105| | ;
106| 5| assert_eq!(val, 57);
107| | }
108| | }
109| 0| Ok(())
110| 1|}
111| |
112| 1|fn main() -> Result<(),()> {
113| 1| test1().expect_err("test1 should fail");
114| 1| test2()
115| 1| ?
116| | ;
117| 0| Ok(())
118| 1|}

View file

@ -9,7 +9,7 @@ fn call(return_error: bool) -> Result<(),()> {
}
}
fn main() -> Result<(),()> {
fn test1() -> Result<(),()> {
let mut
countdown = 10
;
@ -34,3 +34,85 @@ fn main() -> Result<(),()> {
}
Ok(())
}
struct Thing1;
impl Thing1 {
fn get_thing_2(&self, return_error: bool) -> Result<Thing2,()> {
if return_error {
Err(())
} else {
Ok(Thing2{})
}
}
}
struct Thing2;
impl Thing2 {
fn call(&self, return_error: bool) -> Result<u32,()> {
if return_error {
Err(())
} else {
Ok(57)
}
}
}
fn test2() -> Result<(),()> {
let thing1 = Thing1{};
let mut
countdown = 10
;
for
_
in
0..10
{
countdown
-= 1
;
if
countdown < 5
{
thing1.get_thing_2(/*err=*/ false)?.call(/*err=*/ true).expect_err("call should fail");
thing1
.
get_thing_2(/*return_error=*/ false)
?
.
call(/*return_error=*/ true)
.
expect_err(
"call should fail"
);
let val = thing1.get_thing_2(/*return_error=*/ true)?.call(/*return_error=*/ true)?;
assert_eq!(val, 57);
let val = thing1.get_thing_2(/*return_error=*/ true)?.call(/*return_error=*/ false)?;
assert_eq!(val, 57);
}
else
{
let val = thing1.get_thing_2(/*return_error=*/ false)?.call(/*return_error=*/ false)?;
assert_eq!(val, 57);
let val = thing1
.get_thing_2(/*return_error=*/ false)?
.call(/*return_error=*/ false)?;
assert_eq!(val, 57);
let val = thing1
.get_thing_2(/*return_error=*/ false)
?
.call(/*return_error=*/ false)
?
;
assert_eq!(val, 57);
}
}
Ok(())
}
fn main() -> Result<(),()> {
test1().expect_err("test1 should fail");
test2()
?
;
Ok(())
}