Skip to content

Commit 8ab12f6

Browse files
committed
Optimize snapshot usage.
This commit implements a suggestion from @estebank that optimizes the use of snapshots. Instead of creating a snapshot for each recursion in `parse_path_segment` and then replacing `self` with them until the first invocation where if leading angle brackets are detected, `self` is not replaced and instead the snapshot is used to inform how parsing should continue. Now, a snapshot is created in the first invocation that acts as a backup of the parser state before any generic arguments are parsed (and therefore, before recursion starts). This backup replaces `self` if after all parsing of generic arguments has concluded we can determine that there are leading angle brackets. Parsing can then proceed from the backup state making use of the now known number of unmatched leading angle brackets to recover.
1 parent 22f794b commit 8ab12f6

File tree

1 file changed

+57
-68
lines changed

1 file changed

+57
-68
lines changed

src/libsyntax/parse/parser.rs

+57-68
Original file line numberDiff line numberDiff line change
@@ -5428,87 +5428,76 @@ impl<'a> Parser<'a> {
54285428
//
54295429
// In doing this, we have managed to work out how many unmatched leading left angle
54305430
// brackets there are, but we cannot recover as the unmatched angle brackets have
5431-
// already been consumed. To remedy this, whenever `parse_generic_args` is invoked, we
5432-
// make a snapshot of the current parser state and invoke it on that and inspect
5433-
// the result:
5434-
//
5435-
// - If success (ie. when it found a matching `>` character) then the snapshot state
5436-
// is kept (this is required to propagate the count upwards).
5437-
//
5438-
// - If error and in was in a recursive call, then the snapshot state is kept (this is
5439-
// required to propagate the count upwards).
5440-
//
5441-
// - If error and this was the first invocation (before any recursion had taken place)
5442-
// then we choose not to keep the snapshot state - that way we haven't actually
5443-
// consumed any of the `<` characters, but can still inspect the count from the
5444-
// snapshot to know how many `<` characters to remove. Using this information, we can
5445-
// emit an error and consume the extra `<` characters before attempting to parse
5446-
// the generic arguments again (this time hopefullt successfully as the unmatched `<`
5447-
// characters are gone).
5431+
// already been consumed. To remedy this, we keep a snapshot of the parser state
5432+
// before we do the above. We can then inspect whether we ended up with a parsing error
5433+
// and unmatched left angle brackets and if so, restore the parser state before we
5434+
// consumed any `<` characters to emit an error and consume the erroneous tokens to
5435+
// recover by attempting to parse again.
54485436
//
54495437
// In practice, the recursion of this function is indirect and there will be other
54505438
// locations that consume some `<` characters - as long as we update the count when
54515439
// this happens, it isn't an issue.
5452-
let mut snapshot = self.clone();
5440+
5441+
let is_first_invocation = style == PathStyle::Expr;
5442+
// Take a snapshot before attempting to parse - we can restore this later.
5443+
let snapshot = if is_first_invocation {
5444+
Some(self.clone())
5445+
} else {
5446+
None
5447+
};
5448+
54535449
debug!("parse_generic_args_with_leading_angle_bracket_recovery: (snapshotting)");
5454-
match snapshot.parse_generic_args() {
5455-
Ok(value) => {
5456-
debug!(
5457-
"parse_generic_args_with_leading_angle_bracket_recovery: (snapshot success) \
5458-
snapshot.count={:?}",
5459-
snapshot.unmatched_angle_bracket_count,
5460-
);
5461-
mem::replace(self, snapshot);
5462-
Ok(value)
5463-
},
5464-
Err(mut e) => {
5450+
match self.parse_generic_args() {
5451+
Ok(value) => Ok(value),
5452+
Err(ref mut e) if is_first_invocation && self.unmatched_angle_bracket_count > 0 => {
5453+
// Cancel error from being unable to find `>`. We know the error
5454+
// must have been this due to a non-zero unmatched angle bracket
5455+
// count.
5456+
e.cancel();
5457+
5458+
// Swap `self` with our backup of the parser state before attempting to parse
5459+
// generic arguments.
5460+
let snapshot = mem::replace(self, snapshot.unwrap());
5461+
54655462
debug!(
54665463
"parse_generic_args_with_leading_angle_bracket_recovery: (snapshot failure) \
54675464
snapshot.count={:?}",
54685465
snapshot.unmatched_angle_bracket_count,
54695466
);
5470-
if style == PathStyle::Expr && snapshot.unmatched_angle_bracket_count > 0 {
5471-
// Cancel error from being unable to find `>`. We know the error
5472-
// must have been this due to a non-zero unmatched angle bracket
5473-
// count.
5474-
e.cancel();
5475-
5476-
// Eat the unmatched angle brackets.
5477-
for _ in 0..snapshot.unmatched_angle_bracket_count {
5478-
self.eat_lt();
5479-
}
54805467

5481-
// Make a span over ${unmatched angle bracket count} characters.
5482-
let span = lo.with_hi(
5483-
lo.lo() + BytePos(snapshot.unmatched_angle_bracket_count)
5484-
);
5485-
let plural = snapshot.unmatched_angle_bracket_count > 1;
5486-
self.diagnostic()
5487-
.struct_span_err(
5488-
span,
5489-
&format!(
5490-
"unmatched angle bracket{}",
5491-
if plural { "s" } else { "" }
5492-
),
5493-
)
5494-
.span_suggestion_with_applicability(
5495-
span,
5496-
&format!(
5497-
"remove extra angle bracket{}",
5498-
if plural { "s" } else { "" }
5499-
),
5500-
String::new(),
5501-
Applicability::MachineApplicable,
5502-
)
5503-
.emit();
5504-
5505-
// Try again without unmatched angle bracket characters.
5506-
self.parse_generic_args()
5507-
} else {
5508-
mem::replace(self, snapshot);
5509-
Err(e)
5468+
// Eat the unmatched angle brackets.
5469+
for _ in 0..snapshot.unmatched_angle_bracket_count {
5470+
self.eat_lt();
55105471
}
5472+
5473+
// Make a span over ${unmatched angle bracket count} characters.
5474+
let span = lo.with_hi(
5475+
lo.lo() + BytePos(snapshot.unmatched_angle_bracket_count)
5476+
);
5477+
let plural = snapshot.unmatched_angle_bracket_count > 1;
5478+
self.diagnostic()
5479+
.struct_span_err(
5480+
span,
5481+
&format!(
5482+
"unmatched angle bracket{}",
5483+
if plural { "s" } else { "" }
5484+
),
5485+
)
5486+
.span_suggestion_with_applicability(
5487+
span,
5488+
&format!(
5489+
"remove extra angle bracket{}",
5490+
if plural { "s" } else { "" }
5491+
),
5492+
String::new(),
5493+
Applicability::MachineApplicable,
5494+
)
5495+
.emit();
5496+
5497+
// Try again without unmatched angle bracket characters.
5498+
self.parse_generic_args()
55115499
},
5500+
Err(e) => Err(e),
55125501
}
55135502
}
55145503

0 commit comments

Comments
 (0)