tamer: nir::desugar::interp: Proper span offsets

The spans were previously not being calculated relative to the offset of the
original symbol span.  Tests were passing because all of those spans began
at offset 0.

DEV-13156
main
Mike Gerwitz 2022-11-08 00:55:04 -05:00
parent 6b9979da9a
commit 5c5041f90e
4 changed files with 97 additions and 22 deletions

View File

@ -160,6 +160,20 @@ macro_rules! diagnostic_panic {
}}
}
/// Produce a panic with diagnostic information and a rather obnoxious
/// message describing this issue as a bug in TAMER,
/// but only if debug assertions are enabled.
///
/// This simply gates [`diagnostic_panic!`] behind a `debug_assertions` cfg
/// check.
#[macro_export]
macro_rules! debug_diagnostic_panic {
($desc_data:expr, $($panic_args:tt)*) => {
#[cfg(debug_assertions)]
$crate::diagnostic_panic!($desc_data, $($panic_args)*);
}
}
/// Alternatives to `unwrap` and `expect` that utilize
/// [`diagnostic_panic!`].
pub trait DiagnosticPanic {

View File

@ -321,8 +321,7 @@ impl<const TY: NirSymbolTy> ParseState for InterpState<TY> {
let end = offset + rel_pos;
let literal = s[offset..end].intern();
let span_text =
span.context().span_or_zz(offset, rel_pos);
let span_text = span.slice(offset, rel_pos);
let text = PlainNir::TplParamText(
PlainNirSymbol::Todo(literal, span_text),
@ -336,8 +335,7 @@ impl<const TY: NirSymbolTy> ParseState for InterpState<TY> {
// The remainder of the specification is a literal.
None => {
let literal = s[offset..].intern();
let span_text =
span.context().span_or_zz(offset, s.len() - offset);
let span_text = span.slice(offset, s.len() - offset);
let text = PlainNir::TplParamText(
PlainNirSymbol::Todo(literal, span_text),
@ -377,8 +375,7 @@ impl<const TY: NirSymbolTy> ParseState for InterpState<TY> {
// Since rel_pos is 0-indexed,
// it is also the length of the value string.
let span_value =
span.context().span_or_zz(offset, rel_pos);
let span_value = span.slice(offset, rel_pos);
let param_value = PlainNir::TplParamValue(
PlainNirSymbol::Todo(value, span_value),

View File

@ -64,9 +64,11 @@ fn desugars_literal_with_ending_var() {
// 0 9
// A
let a = DC.span(0, 10);
let b = DC.span(0, 3);
let c = DC.span(4, 5);
// Non-zero span offset ensures that derived spans properly consider
// parent offset.
let a = DC.span(10, 10);
let b = DC.span(10, 3);
let c = DC.span(14, 5);
let given_sym = SugaredNirSymbol::<{ StringLiteral }>(given_val.into(), a);
let toks = vec![given_sym];
@ -143,9 +145,9 @@ fn desugars_var_with_ending_literal() {
// 0 9
// A
let a = DC.span(0, 10);
let b = DC.span(1, 5);
let c = DC.span(7, 3);
let a = DC.span(20, 10);
let b = DC.span(21, 5);
let c = DC.span(27, 3);
let given_sym = SugaredNirSymbol::<{ StringLiteral }>(given_val.into(), a);
let toks = vec![given_sym];
@ -208,11 +210,11 @@ fn desugars_many_vars_and_literals() {
// 0 20
// A
let a = DC.span(0, 21);
let b = DC.span(0, 3);
let c = DC.span(4, 5);
let d = DC.span(10, 3);
let e = DC.span(14, 6);
let a = DC.span(30, 21);
let b = DC.span(30, 3);
let c = DC.span(34, 5);
let d = DC.span(40, 3);
let e = DC.span(44, 6);
let given_sym = SugaredNirSymbol::<{ StringLiteral }>(given_val.into(), a);
let toks = vec![given_sym];
@ -280,10 +282,10 @@ fn desugars_adjacent_interpolated_vars() {
// 0 20
// A
let a = DC.span(0, 21);
let b = DC.span(1, 5);
let c = DC.span(8, 5);
let d = DC.span(15, 5);
let a = DC.span(40, 21);
let b = DC.span(41, 5);
let c = DC.span(48, 5);
let d = DC.span(55, 5);
let given_sym = SugaredNirSymbol::<{ StringLiteral }>(given_val.into(), a);
let toks = vec![given_sym];

View File

@ -184,7 +184,7 @@
//! [rustc-span]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/struct.Span.html
use crate::{
global,
debug_diagnostic_panic, global,
sym::{st16, ContextStaticSymbolId, GlobalSymbolResolve, SymbolId},
};
use std::{convert::TryInto, fmt::Display, path::Path};
@ -395,6 +395,47 @@ impl Span {
)
}
/// Create a new span that is a slice of this one.
///
/// If either `rel_offset` or `len` are too large,
/// then a copy of the span will be returned unsliced.
///
/// Panics (Debug Mode)
/// -------------------
/// If the offset and length exceeds the bounds of the span,
/// then the system has an arithmetic bug that ought to be corrected,
/// and so this will panic with a diagnostic message.
/// This check does not occur on release builds since this is not a
/// safety issue and should be caught by tests.
pub fn slice(self, rel_offset: usize, len: usize) -> Self {
let (irel_offset, ilen) = match (rel_offset.try_into(), len.try_into())
{
(Ok(x), Ok(y)) => (x, y),
_ => (0, self.len()),
};
// We shouldn't ignore slices that exceed the length of the span,
// since this represents a bug that'll cause nonsense diagnostic
// data and it represents an arithmetic bug in the system
// (but there are no safety concerns).
if ((irel_offset as usize).saturating_add(ilen as usize))
> self.len() as usize
{
use crate::diagnose::Annotate;
debug_diagnostic_panic!(
self.error("attempting to slice this span").into(),
"length {len} at offset {rel_offset} \
exceeds bounds of span {self}",
);
}
Self {
ctx: self.ctx,
offset: self.offset.saturating_add(irel_offset),
len: ilen,
}
}
/// Adjust span such that its offset is relative to the provided span.
///
/// If the provide `rel_span` does not precede this span,
@ -781,4 +822,25 @@ mod test {
assert_eq!(start, Span::new(offset, 0, ctx));
assert_eq!(end, Span::new(SpanOffsetSize::MAX, 0, ctx));
}
#[test]
fn span_slice_yields_slice_within_original() {
let ctx = Context::from("slice");
let span = ctx.span(10, 10);
assert_eq!(ctx.span(15, 5), span.slice(5, 5));
}
#[test]
fn span_slice_large_values_yield_original() {
let span = Context::from("slice").span(0, 50);
// Too large of an offset should return original even though legnth
// is okay.
assert_eq!(span, span.slice(usize::MAX, 5));
// Too large of length should return original even though offset is
// okay.
assert_eq!(span, span.slice(0, usize::MAX));
}
}