Protocol: debug_assert!() Span to reflect a valid slice (#6806)

Also enforce this by #[non_exhaustive] span such that going forward we
cannot, in debug builds (1), construct invalid spans.

The motivation for this stems from #6431 where I've seen crashes due to
invalid slice indexing.

My hope is this will mitigate such senarios

1. https://github.com/nushell/nushell/pull/6431#issuecomment-1278147241

# Description

(description of your pull request here)

# Tests

Make sure you've done the following:

- [ ] Add tests that cover your changes, either in the command examples,
the crate/tests folder, or in the /tests folder.
- [ ] Try to think about corner cases and various ways how your changes
could break. Cover them with tests.
- [ ] If adding tests is not possible, please document in the PR body a
minimal example with steps on how to reproduce so one can verify your
change works.

Make sure you've run and fixed any issues with these commands:

- [x] `cargo fmt --all -- --check` to check standard code formatting
(`cargo fmt --all` applies these changes)
- [ ] `cargo clippy --workspace --features=extra -- -D warnings -D
clippy::unwrap_used -A clippy::needless_collect` to check that you're
using the standard code style
- [ ] `cargo test --workspace --features=extra` to check that all the
tests pass

# Documentation

- [ ] If your PR touches a user-facing nushell feature then make sure
that there is an entry in the documentation
(https://github.com/nushell/nushell.github.io) for the feature, and
update it if necessary.
This commit is contained in:
Daniel Buch Hansen 2022-12-03 10:44:12 +01:00 committed by GitHub
parent e6cf18ea43
commit 850ecf648a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 200 additions and 418 deletions

View File

@ -94,10 +94,7 @@ impl CommandCompletion {
value: String::from_utf8_lossy(&x.0).to_string(), value: String::from_utf8_lossy(&x.0).to_string(),
description: x.1, description: x.1,
extra: None, extra: None,
span: reedline::Span { span: reedline::Span::new(span.start - offset, span.end - offset),
start: span.start - offset,
end: span.end - offset,
},
append_whitespace: true, append_whitespace: true,
}); });
@ -108,10 +105,7 @@ impl CommandCompletion {
value: String::from_utf8_lossy(&x).to_string(), value: String::from_utf8_lossy(&x).to_string(),
description: None, description: None,
extra: None, extra: None,
span: reedline::Span { span: reedline::Span::new(span.start - offset, span.end - offset),
start: span.start - offset,
end: span.end - offset,
},
append_whitespace: true, append_whitespace: true,
}); });
@ -128,10 +122,7 @@ impl CommandCompletion {
value: x, value: x,
description: None, description: None,
extra: None, extra: None,
span: reedline::Span { span: reedline::Span::new(span.start - offset, span.end - offset),
start: span.start - offset,
end: span.end - offset,
},
append_whitespace: true, append_whitespace: true,
}); });
@ -187,10 +178,7 @@ impl Completer for CommandCompletion {
let subcommands = if let Some(last) = last { let subcommands = if let Some(last) = last {
self.complete_commands( self.complete_commands(
working_set, working_set,
Span { Span::new(last.0.start, pos),
start: last.0.start,
end: pos,
},
offset, offset,
false, false,
options.match_algorithm, options.match_algorithm,

View File

@ -101,14 +101,8 @@ impl NuCompleter {
Ok(pd) => { Ok(pd) => {
let value = pd.into_value(span); let value = pd.into_value(span);
if let Value::List { vals, span: _ } = value { if let Value::List { vals, span: _ } = value {
let result = map_value_completions( let result =
vals.iter(), map_value_completions(vals.iter(), Span::new(span.start, span.end), offset);
Span {
start: span.start,
end: span.end,
},
offset,
);
return Some(result); return Some(result);
} }
@ -165,15 +159,12 @@ impl NuCompleter {
// Create a new span // Create a new span
let new_span = if flat_idx == 0 { let new_span = if flat_idx == 0 {
Span { Span::new(flat.0.start, flat.0.end - 1 - span_offset)
start: flat.0.start,
end: flat.0.end - 1 - span_offset,
}
} else { } else {
Span { Span::new(
start: flat.0.start - span_offset, flat.0.start - span_offset,
end: flat.0.end - 1 - span_offset, flat.0.end - 1 - span_offset,
} )
}; };
// Parses the prefix. Completion should look up to the cursor position, not after. // Parses the prefix. Completion should look up to the cursor position, not after.

View File

@ -52,13 +52,13 @@ impl Completer for CustomCompletion {
head: span, head: span,
arguments: vec![ arguments: vec![
Argument::Positional(Expression { Argument::Positional(Expression {
span: Span { start: 0, end: 0 }, span: Span::unknown(),
ty: Type::String, ty: Type::String,
expr: Expr::String(self.line.clone()), expr: Expr::String(self.line.clone()),
custom_completion: None, custom_completion: None,
}), }),
Argument::Positional(Expression { Argument::Positional(Expression {
span: Span { start: 0, end: 0 }, span: Span::unknown(),
ty: Type::Int, ty: Type::Int,
expr: Expr::Int(line_pos as i64), expr: Expr::Int(line_pos as i64),
custom_completion: None, custom_completion: None,

View File

@ -111,10 +111,7 @@ impl Completer for VariableCompletion {
&self.engine_state, &self.engine_state,
&self.stack, &self.stack,
nu_protocol::NU_VARIABLE_ID, nu_protocol::NU_VARIABLE_ID,
nu_protocol::Span { nu_protocol::Span::new(current_span.start, current_span.end),
start: current_span.start,
end: current_span.end,
},
) { ) {
for suggestion in for suggestion in
nested_suggestions(nuval, self.var_context.1.clone(), current_span) nested_suggestions(nuval, self.var_context.1.clone(), current_span)
@ -134,13 +131,7 @@ impl Completer for VariableCompletion {
// Completion other variable types // Completion other variable types
if let Some(var_id) = var_id { if let Some(var_id) = var_id {
// Extract the variable value from the stack // Extract the variable value from the stack
let var = self.stack.get_var( let var = self.stack.get_var(var_id, Span::new(span.start, span.end));
var_id,
Span {
start: span.start,
end: span.end,
},
);
// If the value exists and it's of type Record // If the value exists and it's of type Record
if let Ok(value) = var { if let Ok(value) = var {
@ -281,7 +272,7 @@ fn recursive_value(val: Value, sublevels: Vec<Vec<u8>>) -> Value {
// Current sublevel value not found // Current sublevel value not found
return Value::Nothing { return Value::Nothing {
span: Span { start: 0, end: 0 }, span: Span::unknown(),
}; };
} }
_ => return val, _ => return val,

View File

@ -76,7 +76,7 @@ pub fn evaluate_repl(
"CMD_DURATION_MS".into(), "CMD_DURATION_MS".into(),
Value::String { Value::String {
val: "0823".to_string(), val: "0823".to_string(),
span: Span { start: 0, end: 0 }, span: Span::unknown(),
}, },
); );
@ -84,7 +84,7 @@ pub fn evaluate_repl(
"LAST_EXIT_CODE".into(), "LAST_EXIT_CODE".into(),
Value::Int { Value::Int {
val: 0, val: 0,
span: Span { start: 0, end: 0 }, span: Span::unknown(),
}, },
); );
@ -375,7 +375,7 @@ pub fn evaluate_repl(
"OLDPWD".into(), "OLDPWD".into(),
Value::String { Value::String {
val: cwd.clone(), val: cwd.clone(),
span: Span { start: 0, end: 0 }, span: Span::unknown(),
}, },
); );
@ -385,7 +385,7 @@ pub fn evaluate_repl(
"PWD".into(), "PWD".into(),
Value::String { Value::String {
val: path.clone(), val: path.clone(),
span: Span { start: 0, end: 0 }, span: Span::unknown(),
}, },
); );
let cwd = Value::String { val: cwd, span }; let cwd = Value::String { val: cwd, span };
@ -440,7 +440,7 @@ pub fn evaluate_repl(
"CMD_DURATION_MS".into(), "CMD_DURATION_MS".into(),
Value::String { Value::String {
val: format!("{}", cmd_duration.as_millis()), val: format!("{}", cmd_duration.as_millis()),
span: Span { start: 0, end: 0 }, span: Span::unknown(),
}, },
); );
@ -997,7 +997,7 @@ fn run_ansi_sequence(seq: &str) -> Result<(), ShellError> {
return Err(ShellError::GenericError( return Err(ShellError::GenericError(
"Error writing ansi sequence".into(), "Error writing ansi sequence".into(),
err.to_string(), err.to_string(),
Some(Span { start: 0, end: 0 }), Some(Span::unknown()),
None, None,
Vec::new(), Vec::new(),
)); ));
@ -1007,7 +1007,7 @@ fn run_ansi_sequence(seq: &str) -> Result<(), ShellError> {
ShellError::GenericError( ShellError::GenericError(
"Error flushing stdio".into(), "Error flushing stdio".into(),
e.to_string(), e.to_string(),
Some(Span { start: 0, end: 0 }), Some(Span::unknown()),
None, None,
Vec::new(), Vec::new(),
) )

View File

@ -149,7 +149,7 @@ fn split_span_by_highlight_positions(
for pos in highlight_positions { for pos in highlight_positions {
if start <= *pos && pos < &span.end { if start <= *pos && pos < &span.end {
if start < *pos { if start < *pos {
result.push((Span { start, end: *pos }, false)); result.push((Span::new(start, *pos), false));
} }
let span_str = &line[pos - global_span_offset..span.end - global_span_offset]; let span_str = &line[pos - global_span_offset..span.end - global_span_offset];
let end = span_str let end = span_str
@ -157,18 +157,12 @@ fn split_span_by_highlight_positions(
.next() .next()
.map(|c| pos + get_char_length(c)) .map(|c| pos + get_char_length(c))
.unwrap_or(pos + 1); .unwrap_or(pos + 1);
result.push((Span { start: *pos, end }, true)); result.push((Span::new(*pos, end), true));
start = end; start = end;
} }
} }
if start < span.end { if start < span.end {
result.push(( result.push((Span::new(start, span.end), false));
Span {
start,
end: span.end,
},
false,
));
} }
result result
} }

View File

@ -291,7 +291,7 @@ fn set_last_exit_code(stack: &mut Stack, exit_code: i64) {
"LAST_EXIT_CODE".to_string(), "LAST_EXIT_CODE".to_string(),
Value::Int { Value::Int {
val: exit_code, val: exit_code,
span: Span { start: 0, end: 0 }, span: Span::unknown(),
}, },
); );
} }

View File

@ -33,20 +33,14 @@ pub fn new_engine() -> (PathBuf, String, EngineState, Stack) {
"PWD".to_string(), "PWD".to_string(),
Value::String { Value::String {
val: dir_str.clone(), val: dir_str.clone(),
span: nu_protocol::Span { span: nu_protocol::Span::new(0, dir_str.len()),
start: 0,
end: dir_str.len(),
},
}, },
); );
stack.add_env_var( stack.add_env_var(
"TEST".to_string(), "TEST".to_string(),
Value::String { Value::String {
val: "NUSHELL".to_string(), val: "NUSHELL".to_string(),
span: nu_protocol::Span { span: nu_protocol::Span::new(0, dir_str.len()),
start: 0,
end: dir_str.len(),
},
}, },
); );
@ -112,7 +106,7 @@ pub fn merge_input(
&block, &block,
PipelineData::Value( PipelineData::Value(
Value::Nothing { Value::Nothing {
span: Span { start: 0, end: 0 }, span: Span::unknown(),
}, },
None None
), ),

View File

@ -53,7 +53,7 @@ impl Default for HashableValue {
fn default() -> Self { fn default() -> Self {
HashableValue::Bool { HashableValue::Bool {
val: false, val: false,
span: Span { start: 0, end: 0 }, span: Span::unknown(),
} }
} }
} }
@ -214,7 +214,7 @@ mod test {
]; ];
for (val, expect_hashable_val) in values.into_iter() { for (val, expect_hashable_val) in values.into_iter() {
assert_eq!( assert_eq!(
HashableValue::from_value(val, Span { start: 0, end: 0 }).unwrap(), HashableValue::from_value(val, Span::unknown()).unwrap(),
expect_hashable_val expect_hashable_val
); );
} }
@ -245,7 +245,7 @@ mod test {
}, },
]; ];
for v in values { for v in values {
assert!(HashableValue::from_value(v, Span { start: 0, end: 0 }).is_err()) assert!(HashableValue::from_value(v, Span::unknown()).is_err())
} }
} }
@ -266,7 +266,7 @@ mod test {
for val in values.into_iter() { for val in values.into_iter() {
let expected_val = val.clone(); let expected_val = val.clone();
assert_eq!( assert_eq!(
HashableValue::from_value(val, Span { start: 0, end: 0 }) HashableValue::from_value(val, Span::unknown())
.unwrap() .unwrap()
.into_value(), .into_value(),
expected_val expected_val
@ -279,14 +279,11 @@ mod test {
assert_eq!( assert_eq!(
HashableValue::Bool { HashableValue::Bool {
val: true, val: true,
span: Span { start: 0, end: 1 } span: Span::new(0, 1)
}, },
HashableValue::Bool { HashableValue::Bool {
val: true, val: true,
span: Span { span: Span::new(90, 1000)
start: 90,
end: 1000
}
} }
) )
} }
@ -299,7 +296,7 @@ mod test {
assert!(set.contains(&HashableValue::Bool { val: true, span })); assert!(set.contains(&HashableValue::Bool { val: true, span }));
// hashable value doesn't care about span. // hashable value doesn't care about span.
let diff_span = Span { start: 1, end: 2 }; let diff_span = Span::new(1, 2);
set.insert(HashableValue::Bool { set.insert(HashableValue::Bool {
val: true, val: true,
span: diff_span, span: diff_span,

View File

@ -502,7 +502,7 @@ mod test {
#[test] #[test]
fn turns_ns_to_duration() { fn turns_ns_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 2);
let word = Value::test_string("3ns"); let word = Value::test_string("3ns");
let expected = Value::Duration { val: 3, span }; let expected = Value::Duration { val: 3, span };
let convert_duration = None; let convert_duration = None;
@ -513,7 +513,7 @@ mod test {
#[test] #[test]
fn turns_us_to_duration() { fn turns_us_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 2);
let word = Value::test_string("4us"); let word = Value::test_string("4us");
let expected = Value::Duration { let expected = Value::Duration {
val: 4 * 1000, val: 4 * 1000,
@ -527,7 +527,7 @@ mod test {
#[test] #[test]
fn turns_ms_to_duration() { fn turns_ms_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 2);
let word = Value::test_string("5ms"); let word = Value::test_string("5ms");
let expected = Value::Duration { let expected = Value::Duration {
val: 5 * 1000 * 1000, val: 5 * 1000 * 1000,
@ -541,7 +541,7 @@ mod test {
#[test] #[test]
fn turns_sec_to_duration() { fn turns_sec_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 3);
let word = Value::test_string("1sec"); let word = Value::test_string("1sec");
let expected = Value::Duration { let expected = Value::Duration {
val: 1000 * 1000 * 1000, val: 1000 * 1000 * 1000,
@ -555,7 +555,7 @@ mod test {
#[test] #[test]
fn turns_min_to_duration() { fn turns_min_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 3);
let word = Value::test_string("7min"); let word = Value::test_string("7min");
let expected = Value::Duration { let expected = Value::Duration {
val: 7 * 60 * 1000 * 1000 * 1000, val: 7 * 60 * 1000 * 1000 * 1000,
@ -569,7 +569,7 @@ mod test {
#[test] #[test]
fn turns_hr_to_duration() { fn turns_hr_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 3);
let word = Value::test_string("42hr"); let word = Value::test_string("42hr");
let expected = Value::Duration { let expected = Value::Duration {
val: 42 * 60 * 60 * 1000 * 1000 * 1000, val: 42 * 60 * 60 * 1000 * 1000 * 1000,
@ -583,7 +583,7 @@ mod test {
#[test] #[test]
fn turns_day_to_duration() { fn turns_day_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 5);
let word = Value::test_string("123day"); let word = Value::test_string("123day");
let expected = Value::Duration { let expected = Value::Duration {
val: 123 * 24 * 60 * 60 * 1000 * 1000 * 1000, val: 123 * 24 * 60 * 60 * 1000 * 1000 * 1000,
@ -597,7 +597,7 @@ mod test {
#[test] #[test]
fn turns_wk_to_duration() { fn turns_wk_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 2);
let word = Value::test_string("3wk"); let word = Value::test_string("3wk");
let expected = Value::Duration { let expected = Value::Duration {
val: 3 * 7 * 24 * 60 * 60 * 1000 * 1000 * 1000, val: 3 * 7 * 24 * 60 * 60 * 1000 * 1000 * 1000,

View File

@ -108,10 +108,7 @@ fn make_error(value: &Value, throw_span: Option<Span>) -> Option<ShellError> {
) => Some(ShellError::GenericError( ) => Some(ShellError::GenericError(
message, message,
label_text, label_text,
Some(Span { Some(Span::new(start as usize, end as usize)),
start: start as usize,
end: end as usize,
}),
None, None,
Vec::new(), Vec::new(),
)), )),

View File

@ -39,7 +39,7 @@ impl Display for DataFrameValue {
impl Default for DataFrameValue { impl Default for DataFrameValue {
fn default() -> Self { fn default() -> Self {
Self(Value::Nothing { Self(Value::Nothing {
span: Span { start: 0, end: 0 }, span: Span::unknown(),
}) })
} }
} }

View File

@ -64,7 +64,7 @@ impl Command for ConfigEnv {
let args = vec![Spanned { let args = vec![Spanned {
item: nu_config.to_string_lossy().to_string(), item: nu_config.to_string_lossy().to_string(),
span: Span { start: 0, end: 0 }, span: Span::unknown(),
}]; }];
let command = ExternalCommand { let command = ExternalCommand {

View File

@ -64,7 +64,7 @@ impl Command for ConfigNu {
let args = vec![Spanned { let args = vec![Spanned {
item: nu_config.to_string_lossy().to_string(), item: nu_config.to_string_lossy().to_string(),
span: Span { start: 0, end: 0 }, span: Span::unknown(),
}]; }];
let command = ExternalCommand { let command = ExternalCommand {

View File

@ -518,7 +518,7 @@ pub(crate) fn dir_entry_dict(
if md.is_dir() { if md.is_dir() {
if du { if du {
let params = DirBuilder::new(Span { start: 0, end: 2 }, None, false, None, false); let params = DirBuilder::new(Span::new(0, 2), None, false, None, false);
let dir_size = DirInfo::new(filename, &params, None, ctrl_c).get_size(); let dir_size = DirInfo::new(filename, &params, None, ctrl_c).get_size();
vals.push(Value::Filesize { vals.push(Value::Filesize {

View File

@ -199,18 +199,18 @@ fn rm(
)); ));
} }
let targets_span = Span { let targets_span = Span::new(
start: targets targets
.iter() .iter()
.map(|x| x.span.start) .map(|x| x.span.start)
.min() .min()
.expect("targets were empty"), .expect("targets were empty"),
end: targets targets
.iter() .iter()
.map(|x| x.span.end) .map(|x| x.span.end)
.max() .max()
.expect("targets were empty"), .expect("targets were empty"),
}; );
let path = current_dir(engine_state, stack)?; let path = current_dir(engine_state, stack)?;

View File

@ -154,7 +154,7 @@ fn item_mapper_by_col(cols: Vec<String>) -> impl Fn(crate::ItemMapperState) -> c
let col_vals = Value::List { let col_vals = Value::List {
vals: item_column_values, vals: item_column_values,
span: Span { start: 0, end: 0 }, span: Span::unknown(),
}; };
crate::ValueCounter::new_vals_to_compare(ms.item, ms.flag_ignore_case, col_vals) crate::ValueCounter::new_vals_to_compare(ms.item, ms.flag_ignore_case, col_vals)

View File

@ -166,19 +166,13 @@ fn convert_row_column_to_span(row: usize, col: usize, contents: &str) -> Span {
cur_col = 0; cur_col = 0;
} }
if cur_row >= row && cur_col >= col { if cur_row >= row && cur_col >= col {
return Span { return Span::new(offset, offset);
start: offset,
end: offset,
};
} else { } else {
cur_col += 1; cur_col += 1;
} }
} }
Span { Span::new(contents.len(), contents.len())
start: contents.len(),
end: contents.len(),
}
} }
fn convert_string_to_value(string_input: String, span: Span) -> Result<Value, ShellError> { fn convert_string_to_value(string_input: String, span: Span) -> Result<Value, ShellError> {

View File

@ -420,11 +420,11 @@ fn display(help: &str, engine_state: &EngineState, stack: &mut Stack, span: Span
&Call::new(span), &Call::new(span),
Value::String { Value::String {
val: item.to_string(), val: item.to_string(),
span: Span { start: 0, end: 0 }, span: Span::unknown(),
} }
.into_pipeline_data(), .into_pipeline_data(),
) { ) {
let result = output.into_value(Span { start: 0, end: 0 }); let result = output.into_value(Span::unknown());
match result.as_string() { match result.as_string() {
Ok(s) => { Ok(s) => {
build.push_str(&s); build.push_str(&s);

View File

@ -152,18 +152,12 @@ fn extract_formatting_operations(input: String, span_start: usize) -> Vec<Format
if column_need_eval { if column_need_eval {
output.push(FormatOperation::ValueNeedEval( output.push(FormatOperation::ValueNeedEval(
column_name.clone(), column_name.clone(),
Span { Span::new(span_start + column_span_start, span_start + column_span_end),
start: span_start + column_span_start,
end: span_start + column_span_end,
},
)); ));
} else { } else {
output.push(FormatOperation::ValueFromColumn( output.push(FormatOperation::ValueFromColumn(
column_name.clone(), column_name.clone(),
Span { Span::new(span_start + column_span_start, span_start + column_span_end),
start: span_start + column_span_start,
end: span_start + column_span_end,
},
)); ));
} }
} }

View File

@ -186,15 +186,15 @@ fn get_documentation(
match decl.run( match decl.run(
engine_state, engine_state,
stack, stack,
&Call::new(Span::new(0, 0)), &Call::new(Span::unknown()),
Value::String { Value::String {
val: example.example.to_string(), val: example.example.to_string(),
span: Span { start: 0, end: 0 }, span: Span::unknown(),
} }
.into_pipeline_data(), .into_pipeline_data(),
) { ) {
Ok(output) => { Ok(output) => {
let result = output.into_value(Span { start: 0, end: 0 }); let result = output.into_value(Span::unknown());
match result.as_string() { match result.as_string() {
Ok(s) => { Ok(s) => {
let _ = write!(long_desc, "\n > {}\n", s); let _ = write!(long_desc, "\n > {}\n", s);

View File

@ -1096,7 +1096,7 @@ pub fn eval_block(
} }
} }
input = PipelineData::new(Span { start: 0, end: 0 }) input = PipelineData::new(Span::unknown())
} }
} }

View File

@ -102,10 +102,7 @@ pub fn flatten_expression(
} }
Expr::UnaryNot(inner_expr) => { Expr::UnaryNot(inner_expr) => {
let mut output = vec![( let mut output = vec![(
Span { Span::new(expr.span.start, expr.span.start + 3),
start: expr.span.start,
end: expr.span.start + 3,
},
FlatShape::Operator, FlatShape::Operator,
)]; )];
output.extend(flatten_expression(working_set, inner_expr)); output.extend(flatten_expression(working_set, inner_expr));
@ -123,25 +120,13 @@ pub fn flatten_expression(
if let Some(first) = flattened.first() { if let Some(first) = flattened.first() {
if first.0.start > outer_span.start { if first.0.start > outer_span.start {
output.push(( output.push((Span::new(outer_span.start, first.0.start), FlatShape::Block));
Span {
start: outer_span.start,
end: first.0.start,
},
FlatShape::Block,
));
} }
} }
let last = if let Some(last) = flattened.last() { let last = if let Some(last) = flattened.last() {
if last.0.end < outer_span.end { if last.0.end < outer_span.end {
Some(( Some((Span::new(last.0.end, outer_span.end), FlatShape::Block))
Span {
start: last.0.end,
end: outer_span.end,
},
FlatShape::Block,
))
} else { } else {
None None
} }
@ -313,13 +298,7 @@ pub fn flatten_expression(
if let Some(first) = flattened.first() { if let Some(first) = flattened.first() {
if first.0.start > last_end { if first.0.start > last_end {
output.push(( output.push((Span::new(last_end, first.0.start), FlatShape::List));
Span {
start: last_end,
end: first.0.start,
},
FlatShape::List,
));
} }
} }
@ -331,13 +310,7 @@ pub fn flatten_expression(
} }
if last_end < outer_span.end { if last_end < outer_span.end {
output.push(( output.push((Span::new(last_end, outer_span.end), FlatShape::List));
Span {
start: last_end,
end: outer_span.end,
},
FlatShape::List,
));
} }
output output
} }
@ -353,18 +326,12 @@ pub fn flatten_expression(
output.insert( output.insert(
0, 0,
( (
Span { Span::new(expr.span.start, expr.span.start + 2),
start: expr.span.start,
end: expr.span.start + 2,
},
FlatShape::StringInterpolation, FlatShape::StringInterpolation,
), ),
); );
output.push(( output.push((
Span { Span::new(expr.span.end - 1, expr.span.end),
start: expr.span.end - 1,
end: expr.span.end,
},
FlatShape::StringInterpolation, FlatShape::StringInterpolation,
)); ));
} }
@ -382,13 +349,7 @@ pub fn flatten_expression(
if let Some(first) = flattened_lhs.first() { if let Some(first) = flattened_lhs.first() {
if first.0.start > last_end { if first.0.start > last_end {
output.push(( output.push((Span::new(last_end, first.0.start), FlatShape::Record));
Span {
start: last_end,
end: first.0.start,
},
FlatShape::Record,
));
} }
} }
if let Some(last) = flattened_lhs.last() { if let Some(last) = flattened_lhs.last() {
@ -398,13 +359,7 @@ pub fn flatten_expression(
if let Some(first) = flattened_rhs.first() { if let Some(first) = flattened_rhs.first() {
if first.0.start > last_end { if first.0.start > last_end {
output.push(( output.push((Span::new(last_end, first.0.start), FlatShape::Record));
Span {
start: last_end,
end: first.0.start,
},
FlatShape::Record,
));
} }
} }
if let Some(last) = flattened_rhs.last() { if let Some(last) = flattened_rhs.last() {
@ -414,13 +369,7 @@ pub fn flatten_expression(
output.extend(flattened_rhs); output.extend(flattened_rhs);
} }
if last_end < outer_span.end { if last_end < outer_span.end {
output.push(( output.push((Span::new(last_end, outer_span.end), FlatShape::Record));
Span {
start: last_end,
end: outer_span.end,
},
FlatShape::Record,
));
} }
output output
@ -448,13 +397,7 @@ pub fn flatten_expression(
let flattened = flatten_expression(working_set, e); let flattened = flatten_expression(working_set, e);
if let Some(first) = flattened.first() { if let Some(first) = flattened.first() {
if first.0.start > last_end { if first.0.start > last_end {
output.push(( output.push((Span::new(last_end, first.0.start), FlatShape::Table));
Span {
start: last_end,
end: first.0.start,
},
FlatShape::Table,
));
} }
} }
@ -469,13 +412,7 @@ pub fn flatten_expression(
let flattened = flatten_expression(working_set, expr); let flattened = flatten_expression(working_set, expr);
if let Some(first) = flattened.first() { if let Some(first) = flattened.first() {
if first.0.start > last_end { if first.0.start > last_end {
output.push(( output.push((Span::new(last_end, first.0.start), FlatShape::Table));
Span {
start: last_end,
end: first.0.start,
},
FlatShape::Table,
));
} }
} }
@ -488,13 +425,7 @@ pub fn flatten_expression(
} }
if last_end < outer_span.end { if last_end < outer_span.end {
output.push(( output.push((Span::new(last_end, outer_span.end), FlatShape::Table));
Span {
start: last_end,
end: outer_span.end,
},
FlatShape::Table,
));
} }
output output

View File

@ -122,10 +122,7 @@ pub fn lex_item(
}, },
Some(ParseError::UnexpectedEof( Some(ParseError::UnexpectedEof(
(start as char).to_string(), (start as char).to_string(),
Span { Span::new(span.end, span.end),
start: span.end,
end: span.end,
},
)), )),
); );
} }
@ -193,13 +190,8 @@ pub fn lex_item(
// If there is still unclosed opening delimiters, remember they were missing // If there is still unclosed opening delimiters, remember they were missing
if let Some(block) = block_level.last() { if let Some(block) = block_level.last() {
let delim = block.closing(); let delim = block.closing();
let cause = ParseError::UnexpectedEof( let cause =
(delim as char).to_string(), ParseError::UnexpectedEof((delim as char).to_string(), Span::new(span.end, span.end));
Span {
start: span.end,
end: span.end,
},
);
return ( return (
Token { Token {
@ -221,10 +213,7 @@ pub fn lex_item(
}, },
Some(ParseError::UnexpectedEof( Some(ParseError::UnexpectedEof(
(delim as char).to_string(), (delim as char).to_string(),
Span { Span::new(span.end, span.end),
start: span.end,
end: span.end,
},
)), )),
); );
} }

View File

@ -1179,10 +1179,7 @@ pub fn parse_export_in_module(
error = error.or_else(|| { error = error.or_else(|| {
Some(ParseError::MissingPositional( Some(ParseError::MissingPositional(
"def, def-env, alias, or env keyword".into(), // TODO: keep filling more keywords as they come "def, def-env, alias, or env keyword".into(), // TODO: keep filling more keywords as they come
Span { Span::new(export_span.end, export_span.end),
start: export_span.end,
end: export_span.end,
},
"'def', `def-env`, `alias`, or 'env' keyword.".to_string(), "'def', `def-env`, `alias`, or 'env' keyword.".to_string(),
)) ))
}); });
@ -1472,11 +1469,10 @@ pub fn parse_module(
if block_bytes.ends_with(b"}") { if block_bytes.ends_with(b"}") {
end -= 1; end -= 1;
} else { } else {
error = error = error.or_else(|| Some(ParseError::Unclosed("}".into(), Span::new(end, end))));
error.or_else(|| Some(ParseError::Unclosed("}".into(), Span { start: end, end })));
} }
let block_span = Span { start, end }; let block_span = Span::new(start, end);
let (block, module, err) = let (block, module, err) =
parse_module_block(working_set, block_span, expand_aliases_denylist); parse_module_block(working_set, block_span, expand_aliases_denylist);

View File

@ -173,19 +173,13 @@ pub fn check_call(command: Span, sig: &Signature, call: &Call) -> Option<ParseEr
if let Some(last) = call.positional_iter().last() { if let Some(last) = call.positional_iter().last() {
return Some(ParseError::MissingPositional( return Some(ParseError::MissingPositional(
argument.name.clone(), argument.name.clone(),
Span { Span::new(last.span.end, last.span.end),
start: last.span.end,
end: last.span.end,
},
sig.call_signature(), sig.call_signature(),
)); ));
} else { } else {
return Some(ParseError::MissingPositional( return Some(ParseError::MissingPositional(
argument.name.clone(), argument.name.clone(),
Span { Span::new(command.end, command.end),
start: command.end,
end: command.end,
},
sig.call_signature(), sig.call_signature(),
)); ));
} }
@ -196,19 +190,13 @@ pub fn check_call(command: Span, sig: &Signature, call: &Call) -> Option<ParseEr
if let Some(last) = call.positional_iter().last() { if let Some(last) = call.positional_iter().last() {
Some(ParseError::MissingPositional( Some(ParseError::MissingPositional(
missing.name.clone(), missing.name.clone(),
Span { Span::new(last.span.end, last.span.end),
start: last.span.end,
end: last.span.end,
},
sig.call_signature(), sig.call_signature(),
)) ))
} else { } else {
Some(ParseError::MissingPositional( Some(ParseError::MissingPositional(
missing.name.clone(), missing.name.clone(),
Span { Span::new(command.end, command.end),
start: command.end,
end: command.end,
},
sig.call_signature(), sig.call_signature(),
)) ))
} }
@ -285,10 +273,7 @@ pub fn parse_external_call(
let head_contents = working_set.get_span_contents(spans[0]); let head_contents = working_set.get_span_contents(spans[0]);
let head_span = if head_contents.starts_with(b"^") { let head_span = if head_contents.starts_with(b"^") {
Span { Span::new(spans[0].start + 1, spans[0].end)
start: spans[0].start + 1,
end: spans[0].end,
}
} else { } else {
spans[0] spans[0]
}; };
@ -345,7 +330,7 @@ pub fn parse_external_call(
span: *span, span: *span,
ty: Type::String, ty: Type::String,
custom_completion: None, custom_completion: None,
}) });
} }
} }
( (
@ -393,10 +378,7 @@ fn parse_long_flag(
( (
Some(Spanned { Some(Spanned {
item: long_name, item: long_name,
span: Span { span: Span::new(arg_span.start, arg_span.start + long_name_len + 2),
start: arg_span.start,
end: arg_span.start + long_name_len + 2,
},
}), }),
Some(arg), Some(arg),
err, err,
@ -486,10 +468,10 @@ fn parse_short_flags(
for short_flag in short_flags.iter().enumerate() { for short_flag in short_flags.iter().enumerate() {
let short_flag_char = char::from(*short_flag.1); let short_flag_char = char::from(*short_flag.1);
let orig = arg_span; let orig = arg_span;
let short_flag_span = Span { let short_flag_span = Span::new(
start: orig.start + 1 + short_flag.0, orig.start + 1 + short_flag.0,
end: orig.start + 1 + short_flag.0 + 1, orig.start + 1 + short_flag.0 + 1,
}; );
if let Some(flag) = sig.get_short_flag(short_flag_char) { if let Some(flag) = sig.get_short_flag(short_flag_char) {
// If we require an arg and are in a batch of short flags, error // If we require an arg and are in a batch of short flags, error
if !found_short_flags.is_empty() && flag.arg.is_some() { if !found_short_flags.is_empty() && flag.arg.is_some() {
@ -715,10 +697,7 @@ pub fn parse_multispan_value(
Some(ParseError::KeywordMissingArgument( Some(ParseError::KeywordMissingArgument(
arg.to_string(), arg.to_string(),
String::from_utf8_lossy(keyword).into(), String::from_utf8_lossy(keyword).into(),
Span { Span::new(spans[*spans_idx - 1].end, spans[*spans_idx - 1].end),
start: spans[*spans_idx - 1].end,
end: spans[*spans_idx - 1].end,
},
)) ))
}); });
return ( return (
@ -925,10 +904,7 @@ pub fn parse_internal_call(
error = error.or_else(|| { error = error.or_else(|| {
Some(ParseError::MissingPositional( Some(ParseError::MissingPositional(
positional.name.clone(), positional.name.clone(),
Span { Span::new(spans[spans_idx].end, spans[spans_idx].end),
start: spans[spans_idx].end,
end: spans[spans_idx].end,
},
signature.call_signature(), signature.call_signature(),
)) ))
}); });
@ -1621,7 +1597,7 @@ pub fn parse_string_interpolation(
(span.start, span.end) (span.start, span.end)
}; };
let inner_span = Span { start, end }; let inner_span = Span::new(start, end);
let contents = working_set.get_span_contents(inner_span).to_vec(); let contents = working_set.get_span_contents(inner_span).to_vec();
let mut output = vec![]; let mut output = vec![];
@ -1642,10 +1618,7 @@ pub fn parse_string_interpolation(
{ {
mode = InterpolationMode::Expression; mode = InterpolationMode::Expression;
if token_start < b { if token_start < b {
let span = Span { let span = Span::new(token_start, b);
start: token_start,
end: b,
};
let str_contents = working_set.get_span_contents(span); let str_contents = working_set.get_span_contents(span);
let str_contents = if double_quote { let str_contents = if double_quote {
@ -1696,10 +1669,7 @@ pub fn parse_string_interpolation(
mode = InterpolationMode::String; mode = InterpolationMode::String;
if token_start < b { if token_start < b {
let span = Span { let span = Span::new(token_start, b + 1);
start: token_start,
end: b + 1,
};
let (expr, err) = let (expr, err) =
parse_full_cell_path(working_set, None, span, expand_aliases_denylist); parse_full_cell_path(working_set, None, span, expand_aliases_denylist);
@ -1718,10 +1688,7 @@ pub fn parse_string_interpolation(
match mode { match mode {
InterpolationMode::String => { InterpolationMode::String => {
if token_start < end { if token_start < end {
let span = Span { let span = Span::new(token_start, end);
start: token_start,
end,
};
let str_contents = working_set.get_span_contents(span); let str_contents = working_set.get_span_contents(span);
let str_contents = if double_quote { let str_contents = if double_quote {
@ -1743,10 +1710,7 @@ pub fn parse_string_interpolation(
} }
InterpolationMode::Expression => { InterpolationMode::Expression => {
if token_start < end { if token_start < end {
let span = Span { let span = Span::new(token_start, end);
start: token_start,
end,
};
let (expr, err) = let (expr, err) =
parse_full_cell_path(working_set, None, span, expand_aliases_denylist); parse_full_cell_path(working_set, None, span, expand_aliases_denylist);
@ -1923,11 +1887,11 @@ pub fn parse_full_cell_path(
if bytes.ends_with(b")") { if bytes.ends_with(b")") {
end -= 1; end -= 1;
} else { } else {
error = error error =
.or_else(|| Some(ParseError::Unclosed(")".into(), Span { start: end, end }))); error.or_else(|| Some(ParseError::Unclosed(")".into(), Span::new(end, end))));
} }
let span = Span { start, end }; let span = Span::new(start, end);
let source = working_set.get_span_contents(span); let source = working_set.get_span_contents(span);
@ -2568,19 +2532,13 @@ pub fn unescape_string(bytes: &[u8], span: Span) -> (Vec<u8>, Option<ParseError>
} }
err = Some(ParseError::Expected( err = Some(ParseError::Expected(
"unicode hex value".into(), "unicode hex value".into(),
Span { Span::new(span.start + idx, span.end),
start: (span.start + idx),
end: span.end,
},
)); ));
} }
_ => { _ => {
err = Some(ParseError::Expected( err = Some(ParseError::Expected(
"unicode hex value".into(), "unicode hex value".into(),
Span { Span::new(span.start + idx, span.end),
start: (span.start + idx),
end: span.end,
},
)); ));
} }
} }
@ -2589,10 +2547,7 @@ pub fn unescape_string(bytes: &[u8], span: Span) -> (Vec<u8>, Option<ParseError>
_ => { _ => {
err = Some(ParseError::Expected( err = Some(ParseError::Expected(
"supported escape character".into(), "supported escape character".into(),
Span { Span::new(span.start + idx, span.end),
start: (span.start + idx),
end: span.end,
},
)); ));
} }
} }
@ -2772,10 +2727,7 @@ pub fn parse_shape_name(
let (shape, err) = parse_shape_name( let (shape, err) = parse_shape_name(
working_set, working_set,
split[0].as_bytes(), split[0].as_bytes(),
Span { Span::new(span.start, span.start + split[0].len()),
start: span.start,
end: span.start + split[0].len(),
},
); );
let command_name = trim_quotes(split[1].as_bytes()); let command_name = trim_quotes(split[1].as_bytes());
@ -2786,10 +2738,10 @@ pub fn parse_shape_name(
} else { } else {
return ( return (
shape, shape,
Some(ParseError::UnknownCommand(Span { Some(ParseError::UnknownCommand(Span::new(
start: span.start + split[0].len() + 1, span.start + split[0].len() + 1,
end: span.end, span.end,
})), ))),
); );
} }
} else { } else {
@ -3143,10 +3095,7 @@ pub fn parse_signature(
error = error.or_else(|| { error = error.or_else(|| {
Some(ParseError::Expected( Some(ParseError::Expected(
"[ or (".into(), "[ or (".into(),
Span { Span::new(start, start + 1),
start,
end: start + 1,
},
)) ))
}); });
} }
@ -3154,16 +3103,11 @@ pub fn parse_signature(
if (has_paren && bytes.ends_with(b")")) || (!has_paren && bytes.ends_with(b"]")) { if (has_paren && bytes.ends_with(b")")) || (!has_paren && bytes.ends_with(b"]")) {
end -= 1; end -= 1;
} else { } else {
error = error.or_else(|| { error = error.or_else(|| Some(ParseError::Unclosed("] or )".into(), Span::new(end, end))));
Some(ParseError::Unclosed(
"] or )".into(),
Span { start: end, end },
))
});
} }
let (sig, err) = let (sig, err) =
parse_signature_helper(working_set, Span { start, end }, expand_aliases_denylist); parse_signature_helper(working_set, Span::new(start, end), expand_aliases_denylist);
error = error.or(err); error = error.or(err);
( (
@ -3631,10 +3575,7 @@ pub fn parse_signature_helper(
contents: crate::TokenContents::Comment, contents: crate::TokenContents::Comment,
span, span,
} => { } => {
let contents = working_set.get_span_contents(Span { let contents = working_set.get_span_contents(Span::new(span.start + 1, span.end));
start: span.start + 1,
end: span.end,
});
let mut contents = String::from_utf8_lossy(contents).to_string(); let mut contents = String::from_utf8_lossy(contents).to_string();
contents = contents.trim().into(); contents = contents.trim().into();
@ -3724,10 +3665,10 @@ pub fn parse_list_expression(
if bytes.ends_with(b"]") { if bytes.ends_with(b"]") {
end -= 1; end -= 1;
} else { } else {
error = error.or_else(|| Some(ParseError::Unclosed("]".into(), Span { start: end, end }))); error = error.or_else(|| Some(ParseError::Unclosed("]".into(), Span::new(end, end))));
} }
let inner_span = Span { start, end }; let inner_span = Span::new(start, end);
let source = working_set.get_span_contents(inner_span); let source = working_set.get_span_contents(inner_span);
let (output, err) = lex(source, inner_span.start, &[b'\n', b'\r', b','], &[], true); let (output, err) = lex(source, inner_span.start, &[b'\n', b'\r', b','], &[], true);
@ -3803,10 +3744,10 @@ pub fn parse_table_expression(
if bytes.ends_with(b"]") { if bytes.ends_with(b"]") {
end -= 1; end -= 1;
} else { } else {
error = error.or_else(|| Some(ParseError::Unclosed("]".into(), Span { start: end, end }))); error = error.or_else(|| Some(ParseError::Unclosed("]".into(), Span::new(end, end))));
} }
let inner_span = Span { start, end }; let inner_span = Span::new(start, end);
let source = working_set.get_span_contents(inner_span); let source = working_set.get_span_contents(inner_span);
@ -3938,10 +3879,10 @@ pub fn parse_block_expression(
if bytes.ends_with(b"}") { if bytes.ends_with(b"}") {
end -= 1; end -= 1;
} else { } else {
error = error.or_else(|| Some(ParseError::Unclosed("}".into(), Span { start: end, end }))); error = error.or_else(|| Some(ParseError::Unclosed("}".into(), Span::new(end, end))));
} }
let inner_span = Span { start, end }; let inner_span = Span::new(start, end);
let source = working_set.get_span_contents(inner_span); let source = working_set.get_span_contents(inner_span);
@ -4069,10 +4010,10 @@ pub fn parse_closure_expression(
if bytes.ends_with(b"}") { if bytes.ends_with(b"}") {
end -= 1; end -= 1;
} else { } else {
error = error.or_else(|| Some(ParseError::Unclosed("}".into(), Span { start: end, end }))); error = error.or_else(|| Some(ParseError::Unclosed("}".into(), Span::new(end, end))));
} }
let inner_span = Span { start, end }; let inner_span = Span::new(start, end);
let source = working_set.get_span_contents(inner_span); let source = working_set.get_span_contents(inner_span);
@ -4111,10 +4052,7 @@ pub fn parse_closure_expression(
end end
}; };
let signature_span = Span { let signature_span = Span::new(start_point, end_point);
start: start_point,
end: end_point,
};
let (signature, err) = let (signature, err) =
parse_signature_helper(working_set, signature_span, expand_aliases_denylist); parse_signature_helper(working_set, signature_span, expand_aliases_denylist);
error = error.or(err); error = error.or(err);
@ -4673,10 +4611,7 @@ pub fn parse_math_expression(
garbage(spans[0]), garbage(spans[0]),
Some(ParseError::Expected( Some(ParseError::Expected(
"expression".into(), "expression".into(),
Span { Span::new(spans[0].end, spans[0].end),
start: spans[0].end,
end: spans[0].end,
},
)), )),
); );
} }
@ -4825,16 +4760,10 @@ pub fn parse_expression(
let lhs = parse_string_strict( let lhs = parse_string_strict(
working_set, working_set,
Span { Span::new(spans[pos].start, spans[pos].start + point - 1),
start: spans[pos].start,
end: spans[pos].start + point - 1,
},
); );
let rhs = if spans[pos].start + point < spans[pos].end { let rhs = if spans[pos].start + point < spans[pos].end {
let rhs_span = Span { let rhs_span = Span::new(spans[pos].start + point, spans[pos].end);
start: spans[pos].start + point,
end: spans[pos].end,
};
if working_set.get_span_contents(rhs_span).starts_with(b"$") { if working_set.get_span_contents(rhs_span).starts_with(b"$") {
parse_dollar_expr(working_set, rhs_span, expand_aliases_denylist) parse_dollar_expr(working_set, rhs_span, expand_aliases_denylist)
@ -4845,7 +4774,7 @@ pub fn parse_expression(
( (
Expression { Expression {
expr: Expr::String(String::new()), expr: Expr::String(String::new()),
span: Span { start: 0, end: 0 }, span: Span::unknown(),
ty: Type::Nothing, ty: Type::Nothing,
custom_completion: None, custom_completion: None,
}, },
@ -5124,7 +5053,7 @@ pub fn parse_expression(
]; ];
let expr = Expr::Call(Box::new(Call { let expr = Expr::Call(Box::new(Call {
head: Span { start: 0, end: 0 }, head: Span::unknown(),
decl_id, decl_id,
arguments, arguments,
redirect_stdout: true, redirect_stdout: true,
@ -5232,10 +5161,7 @@ pub fn parse_record(
error = error.or_else(|| { error = error.or_else(|| {
Some(ParseError::Expected( Some(ParseError::Expected(
"{".into(), "{".into(),
Span { Span::new(start, start + 1),
start,
end: start + 1,
},
)) ))
}); });
} }
@ -5243,10 +5169,10 @@ pub fn parse_record(
if bytes.ends_with(b"}") { if bytes.ends_with(b"}") {
end -= 1; end -= 1;
} else { } else {
error = error.or_else(|| Some(ParseError::Unclosed("}".into(), Span { start: end, end }))); error = error.or_else(|| Some(ParseError::Unclosed("}".into(), Span::new(end, end))));
} }
let inner_span = Span { start, end }; let inner_span = Span::new(start, end);
let source = working_set.get_span_contents(inner_span); let source = working_set.get_span_contents(inner_span);
let (tokens, err) = lex(source, start, &[b'\n', b'\r', b','], &[b':'], true); let (tokens, err) = lex(source, start, &[b'\n', b'\r', b','], &[b':'], true);

View File

@ -18,7 +18,7 @@ fn lex_newline() {
assert!(output.0.contains(&Token { assert!(output.0.contains(&Token {
contents: TokenContents::Eol, contents: TokenContents::Eol,
span: Span { start: 11, end: 12 } span: Span::new(11, 12)
})); }));
} }
@ -43,7 +43,7 @@ fn lex_parenthesis() {
output.0.get(3).unwrap(), output.0.get(3).unwrap(),
&Token { &Token {
contents: TokenContents::Item, contents: TokenContents::Item,
span: Span { start: 8, end: 27 } span: Span::new(8, 27)
} }
); );
} }
@ -58,7 +58,7 @@ fn lex_comment() {
output.0.get(4).unwrap(), output.0.get(4).unwrap(),
&Token { &Token {
contents: TokenContents::Comment, contents: TokenContents::Comment,
span: Span { start: 12, end: 24 } span: Span::new(12, 24)
} }
); );
} }
@ -108,14 +108,14 @@ fn lex_comments() {
output.0.get(4).unwrap(), output.0.get(4).unwrap(),
&Token { &Token {
contents: TokenContents::Comment, contents: TokenContents::Comment,
span: Span { start: 10, end: 19 } span: Span::new(10, 19)
} }
); );
assert_eq!( assert_eq!(
output.0.get(5).unwrap(), output.0.get(5).unwrap(),
&Token { &Token {
contents: TokenContents::Eol, contents: TokenContents::Eol,
span: Span { start: 19, end: 20 } span: Span::new(19, 20)
} }
); );
@ -125,14 +125,14 @@ fn lex_comments() {
output.0.get(10).unwrap(), output.0.get(10).unwrap(),
&Token { &Token {
contents: TokenContents::Comment, contents: TokenContents::Comment,
span: Span { start: 31, end: 40 } span: Span::new(31, 40)
} }
); );
assert_eq!( assert_eq!(
output.0.get(11).unwrap(), output.0.get(11).unwrap(),
&Token { &Token {
contents: TokenContents::Eol, contents: TokenContents::Eol,
span: Span { start: 40, end: 41 } span: Span::new(40, 41)
} }
); );
} }

View File

@ -115,32 +115,32 @@ mod test {
#[test] #[test]
fn call_to_value() { fn call_to_value() {
let call = EvaluatedCall { let call = EvaluatedCall {
head: Span { start: 0, end: 10 }, head: Span::new(0, 10),
positional: vec![ positional: vec![
Value::Float { Value::Float {
val: 1.0, val: 1.0,
span: Span { start: 0, end: 10 }, span: Span::new(0, 10),
}, },
Value::String { Value::String {
val: "something".into(), val: "something".into(),
span: Span { start: 0, end: 10 }, span: Span::new(0, 10),
}, },
], ],
named: vec![ named: vec![
( (
Spanned { Spanned {
item: "name".to_string(), item: "name".to_string(),
span: Span { start: 0, end: 10 }, span: Span::new(0, 10),
}, },
Some(Value::Float { Some(Value::Float {
val: 1.0, val: 1.0,
span: Span { start: 0, end: 10 }, span: Span::new(0, 10),
}), }),
), ),
( (
Spanned { Spanned {
item: "flag".to_string(), item: "flag".to_string(),
span: Span { start: 0, end: 10 }, span: Span::new(0, 10),
}, },
None, None,
), ),

View File

@ -79,29 +79,29 @@ mod tests {
let input = Value::Bool { let input = Value::Bool {
val: false, val: false,
span: Span { start: 1, end: 20 }, span: Span::new(1, 20),
}; };
let call = EvaluatedCall { let call = EvaluatedCall {
head: Span { start: 0, end: 10 }, head: Span::new(0, 10),
positional: vec![ positional: vec![
Value::Float { Value::Float {
val: 1.0, val: 1.0,
span: Span { start: 0, end: 10 }, span: Span::new(0, 10),
}, },
Value::String { Value::String {
val: "something".into(), val: "something".into(),
span: Span { start: 0, end: 10 }, span: Span::new(0, 10),
}, },
], ],
named: vec![( named: vec![(
Spanned { Spanned {
item: "name".to_string(), item: "name".to_string(),
span: Span { start: 0, end: 10 }, span: Span::new(0, 10),
}, },
Some(Value::Float { Some(Value::Float {
val: 1.0, val: 1.0,
span: Span { start: 0, end: 10 }, span: Span::new(0, 10),
}), }),
)], )],
}; };
@ -155,7 +155,7 @@ mod tests {
#[test] #[test]
fn callinfo_round_trip_collapsecustomvalue() { fn callinfo_round_trip_collapsecustomvalue() {
let data = vec![1, 2, 3, 4, 5, 6, 7]; let data = vec![1, 2, 3, 4, 5, 6, 7];
let span = Span { start: 0, end: 20 }; let span = Span::new(0, 20);
let collapse_custom_value = PluginCall::CollapseCustomValue(PluginData { let collapse_custom_value = PluginCall::CollapseCustomValue(PluginData {
data: data.clone(), data: data.clone(),
@ -247,7 +247,7 @@ mod tests {
fn response_round_trip_value() { fn response_round_trip_value() {
let value = Value::Int { let value = Value::Int {
val: 10, val: 10,
span: Span { start: 2, end: 30 }, span: Span::new(2, 30),
}; };
let response = PluginResponse::Value(Box::new(value.clone())); let response = PluginResponse::Value(Box::new(value.clone()));
@ -276,7 +276,7 @@ mod tests {
let name = "test".to_string(); let name = "test".to_string();
let data = vec![1, 2, 3, 4, 5]; let data = vec![1, 2, 3, 4, 5];
let span = Span { start: 2, end: 30 }; let span = Span::new(2, 30);
let response = PluginResponse::PluginData( let response = PluginResponse::PluginData(
name.clone(), name.clone(),
@ -312,7 +312,7 @@ mod tests {
let error = LabeledError { let error = LabeledError {
label: "label".into(), label: "label".into(),
msg: "msg".into(), msg: "msg".into(),
span: Some(Span { start: 2, end: 30 }), span: Some(Span::new(2, 30)),
}; };
let response = PluginResponse::Error(error.clone()); let response = PluginResponse::Error(error.clone());

View File

@ -78,29 +78,29 @@ mod tests {
let input = Value::Bool { let input = Value::Bool {
val: false, val: false,
span: Span { start: 1, end: 20 }, span: Span::new(1, 20),
}; };
let call = EvaluatedCall { let call = EvaluatedCall {
head: Span { start: 0, end: 10 }, head: Span::new(0, 10),
positional: vec![ positional: vec![
Value::Float { Value::Float {
val: 1.0, val: 1.0,
span: Span { start: 0, end: 10 }, span: Span::new(0, 10),
}, },
Value::String { Value::String {
val: "something".into(), val: "something".into(),
span: Span { start: 0, end: 10 }, span: Span::new(0, 10),
}, },
], ],
named: vec![( named: vec![(
Spanned { Spanned {
item: "name".to_string(), item: "name".to_string(),
span: Span { start: 0, end: 10 }, span: Span::new(0, 10),
}, },
Some(Value::Float { Some(Value::Float {
val: 1.0, val: 1.0,
span: Span { start: 0, end: 10 }, span: Span::new(0, 10),
}), }),
)], )],
}; };
@ -154,7 +154,7 @@ mod tests {
#[test] #[test]
fn callinfo_round_trip_collapsecustomvalue() { fn callinfo_round_trip_collapsecustomvalue() {
let data = vec![1, 2, 3, 4, 5, 6, 7]; let data = vec![1, 2, 3, 4, 5, 6, 7];
let span = Span { start: 0, end: 20 }; let span = Span::new(0, 20);
let collapse_custom_value = PluginCall::CollapseCustomValue(PluginData { let collapse_custom_value = PluginCall::CollapseCustomValue(PluginData {
data: data.clone(), data: data.clone(),
@ -246,7 +246,7 @@ mod tests {
fn response_round_trip_value() { fn response_round_trip_value() {
let value = Value::Int { let value = Value::Int {
val: 10, val: 10,
span: Span { start: 2, end: 30 }, span: Span::new(2, 30),
}; };
let response = PluginResponse::Value(Box::new(value.clone())); let response = PluginResponse::Value(Box::new(value.clone()));
@ -275,7 +275,7 @@ mod tests {
let name = "test".to_string(); let name = "test".to_string();
let data = vec![1, 2, 3, 4, 5]; let data = vec![1, 2, 3, 4, 5];
let span = Span { start: 2, end: 30 }; let span = Span::new(2, 30);
let response = PluginResponse::PluginData( let response = PluginResponse::PluginData(
name.clone(), name.clone(),
@ -311,7 +311,7 @@ mod tests {
let error = LabeledError { let error = LabeledError {
label: "label".into(), label: "label".into(),
msg: "msg".into(), msg: "msg".into(),
span: Some(Span { start: 2, end: 30 }), span: Some(Span::new(2, 30)),
}; };
let response = PluginResponse::Error(error.clone()); let response = PluginResponse::Error(error.clone());

View File

@ -32,7 +32,7 @@ impl ImportPattern {
head: ImportPatternHead { head: ImportPatternHead {
name: vec![], name: vec![],
id: None, id: None,
span: Span { start: 0, end: 0 }, span: Span::unknown(),
}, },
members: vec![], members: vec![],
hidden: HashSet::new(), hidden: HashSet::new(),

View File

@ -981,7 +981,7 @@ fn create_hooks(value: &Value) -> Result<Hooks, ShellError> {
_ => Err(ShellError::UnsupportedConfigValue( _ => Err(ShellError::UnsupportedConfigValue(
"record for 'hooks' config".into(), "record for 'hooks' config".into(),
"non-record value".into(), "non-record value".into(),
Span { start: 0, end: 0 }, Span::unknown(),
)), )),
}, },
} }

View File

@ -755,10 +755,7 @@ impl EngineState {
pub fn get_file_source(&self, file_id: usize) -> String { pub fn get_file_source(&self, file_id: usize) -> String {
for file in self.files.iter().enumerate() { for file in self.files.iter().enumerate() {
if file.0 == file_id { if file.0 == file_id {
let contents = self.get_span_contents(&Span { let contents = self.get_span_contents(&Span::new(file.1 .1, file.1 .2));
start: file.1 .1,
end: file.1 .2,
});
let output = String::from_utf8_lossy(contents).to_string(); let output = String::from_utf8_lossy(contents).to_string();
return output; return output;
@ -1318,10 +1315,9 @@ impl<'a> StateWorkingSet<'a> {
pub fn get_file_source(&self, file_id: usize) -> String { pub fn get_file_source(&self, file_id: usize) -> String {
for file in self.files().enumerate() { for file in self.files().enumerate() {
if file.0 == file_id { if file.0 == file_id {
let output = String::from_utf8_lossy(self.get_span_contents(Span { let output = String::from_utf8_lossy(
start: file.1 .1, self.get_span_contents(Span::new(file.1 .1, file.1 .2)),
end: file.1 .2, )
}))
.to_string(); .to_string();
return output; return output;
@ -2041,10 +2037,7 @@ impl<'a> miette::SourceCode for &StateWorkingSet<'a> {
let found_file = "Found matching file"; let found_file = "Found matching file";
dbg!(found_file); dbg!(found_file);
} }
let our_span = Span { let our_span = Span::new(*start, *end);
start: *start,
end: *end,
};
// We need to move to a local span because we're only reading // We need to move to a local span because we're only reading
// the specific file contents via self.get_span_contents. // the specific file contents via self.get_span_contents.
let local_span = (span.offset() - *start, span.len()).into(); let local_span = (span.offset() - *start, span.len()).into();

View File

@ -308,7 +308,7 @@ impl PipelineData {
} }
PipelineData::ListStream(stream, ..) => Ok(stream.map(f).into_pipeline_data(ctrlc)), PipelineData::ListStream(stream, ..) => Ok(stream.map(f).into_pipeline_data(ctrlc)),
PipelineData::ExternalStream { stdout: None, .. } => { PipelineData::ExternalStream { stdout: None, .. } => {
Ok(PipelineData::new(Span { start: 0, end: 0 })) Ok(PipelineData::new(Span::unknown()))
} }
PipelineData::ExternalStream { PipelineData::ExternalStream {
stdout: Some(stream), stdout: Some(stream),
@ -366,7 +366,7 @@ impl PipelineData {
Ok(stream.flat_map(f).into_pipeline_data(ctrlc)) Ok(stream.flat_map(f).into_pipeline_data(ctrlc))
} }
PipelineData::ExternalStream { stdout: None, .. } => { PipelineData::ExternalStream { stdout: None, .. } => {
Ok(PipelineData::new(Span { start: 0, end: 0 })) Ok(PipelineData::new(Span::unknown()))
} }
PipelineData::ExternalStream { PipelineData::ExternalStream {
stdout: Some(stream), stdout: Some(stream),
@ -419,7 +419,7 @@ impl PipelineData {
} }
PipelineData::ListStream(stream, ..) => Ok(stream.filter(f).into_pipeline_data(ctrlc)), PipelineData::ListStream(stream, ..) => Ok(stream.filter(f).into_pipeline_data(ctrlc)),
PipelineData::ExternalStream { stdout: None, .. } => { PipelineData::ExternalStream { stdout: None, .. } => {
Ok(PipelineData::new(Span { start: 0, end: 0 })) Ok(PipelineData::new(Span::unknown()))
} }
PipelineData::ExternalStream { PipelineData::ExternalStream {
stdout: Some(stream), stdout: Some(stream),

View File

@ -14,6 +14,7 @@ where
/// Spans are a global offset across all seen files, which are cached in the engine's state. The start and /// Spans are a global offset across all seen files, which are cached in the engine's state. The start and
/// end offset together make the inclusive start/exclusive end pair for where to underline to highlight /// end offset together make the inclusive start/exclusive end pair for where to underline to highlight
/// a given point of interest. /// a given point of interest.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Span { pub struct Span {
pub start: usize, pub start: usize,
@ -28,24 +29,28 @@ impl From<Span> for SourceSpan {
impl Span { impl Span {
pub fn new(start: usize, end: usize) -> Span { pub fn new(start: usize, end: usize) -> Span {
debug_assert!(
end >= start,
"Can't create a Span whose end < start, start={}, end={}",
start,
end
);
Span { start, end } Span { start, end }
} }
pub fn unknown() -> Self { pub const fn unknown() -> Span {
Self::new(0, 0) Span { start: 0, end: 0 }
} }
/// Note: Only use this for test data, *not* live data, as it will point into unknown source /// Note: Only use this for test data, *not* live data, as it will point into unknown source
/// when used in errors. /// when used in errors.
pub fn test_data() -> Span { pub const fn test_data() -> Span {
Span { start: 0, end: 0 } Self::unknown()
} }
pub fn offset(&self, offset: usize) -> Span { pub fn offset(&self, offset: usize) -> Span {
Span { Span::new(self.start - offset, self.end - offset)
start: self.start - offset,
end: self.end - offset,
}
} }
pub fn contains(&self, pos: usize) -> bool { pub fn contains(&self, pos: usize) -> bool {
@ -70,15 +75,17 @@ impl Span {
pub fn span(spans: &[Span]) -> Span { pub fn span(spans: &[Span]) -> Span {
let length = spans.len(); let length = spans.len();
//TODO debug_assert!(length > 0, "expect spans > 0");
if length == 0 { if length == 0 {
// TODO: do this for now, but we might also want to protect against this case Span::unknown()
Span { start: 0, end: 0 }
} else if length == 1 { } else if length == 1 {
spans[0] spans[0]
} else { } else {
Span { let end = spans
start: spans[0].start, .iter()
end: spans[length - 1].end, .map(|s| s.end)
} .max()
.expect("Must be an end. Length > 0");
Span::new(spans[0].start, end)
} }
} }

View File

@ -1391,7 +1391,7 @@ impl Value {
impl Default for Value { impl Default for Value {
fn default() -> Self { fn default() -> Self {
Value::Nothing { Value::Nothing {
span: Span { start: 0, end: 0 }, span: Span::unknown(),
} }
} }
} }