1
0
mirror of https://github.com/nushell/nushell.git synced 2025-04-04 06:30:45 +02:00

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

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

View File

@ -101,14 +101,8 @@ impl NuCompleter {
Ok(pd) => {
let value = pd.into_value(span);
if let Value::List { vals, span: _ } = value {
let result = map_value_completions(
vals.iter(),
Span {
start: span.start,
end: span.end,
},
offset,
);
let result =
map_value_completions(vals.iter(), Span::new(span.start, span.end), offset);
return Some(result);
}
@ -165,15 +159,12 @@ impl NuCompleter {
// Create a new span
let new_span = if flat_idx == 0 {
Span {
start: flat.0.start,
end: flat.0.end - 1 - span_offset,
}
Span::new(flat.0.start, flat.0.end - 1 - span_offset)
} else {
Span {
start: flat.0.start - span_offset,
end: flat.0.end - 1 - span_offset,
}
Span::new(
flat.0.start - span_offset,
flat.0.end - 1 - span_offset,
)
};
// 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,
arguments: vec![
Argument::Positional(Expression {
span: Span { start: 0, end: 0 },
span: Span::unknown(),
ty: Type::String,
expr: Expr::String(self.line.clone()),
custom_completion: None,
}),
Argument::Positional(Expression {
span: Span { start: 0, end: 0 },
span: Span::unknown(),
ty: Type::Int,
expr: Expr::Int(line_pos as i64),
custom_completion: None,

View File

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

View File

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

View File

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

View File

@ -291,7 +291,7 @@ fn set_last_exit_code(stack: &mut Stack, exit_code: i64) {
"LAST_EXIT_CODE".to_string(),
Value::Int {
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(),
Value::String {
val: dir_str.clone(),
span: nu_protocol::Span {
start: 0,
end: dir_str.len(),
},
span: nu_protocol::Span::new(0, dir_str.len()),
},
);
stack.add_env_var(
"TEST".to_string(),
Value::String {
val: "NUSHELL".to_string(),
span: nu_protocol::Span {
start: 0,
end: dir_str.len(),
},
span: nu_protocol::Span::new(0, dir_str.len()),
},
);
@ -112,7 +106,7 @@ pub fn merge_input(
&block,
PipelineData::Value(
Value::Nothing {
span: Span { start: 0, end: 0 },
span: Span::unknown(),
},
None
),

View File

@ -53,7 +53,7 @@ impl Default for HashableValue {
fn default() -> Self {
HashableValue::Bool {
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() {
assert_eq!(
HashableValue::from_value(val, Span { start: 0, end: 0 }).unwrap(),
HashableValue::from_value(val, Span::unknown()).unwrap(),
expect_hashable_val
);
}
@ -245,7 +245,7 @@ mod test {
},
];
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() {
let expected_val = val.clone();
assert_eq!(
HashableValue::from_value(val, Span { start: 0, end: 0 })
HashableValue::from_value(val, Span::unknown())
.unwrap()
.into_value(),
expected_val
@ -279,14 +279,11 @@ mod test {
assert_eq!(
HashableValue::Bool {
val: true,
span: Span { start: 0, end: 1 }
span: Span::new(0, 1)
},
HashableValue::Bool {
val: true,
span: Span {
start: 90,
end: 1000
}
span: Span::new(90, 1000)
}
)
}
@ -299,7 +296,7 @@ mod test {
assert!(set.contains(&HashableValue::Bool { val: true, 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 {
val: true,
span: diff_span,

View File

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

View File

@ -39,7 +39,7 @@ impl Display for DataFrameValue {
impl Default for DataFrameValue {
fn default() -> Self {
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 {
item: nu_config.to_string_lossy().to_string(),
span: Span { start: 0, end: 0 },
span: Span::unknown(),
}];
let command = ExternalCommand {

View File

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

View File

@ -518,7 +518,7 @@ pub(crate) fn dir_entry_dict(
if md.is_dir() {
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();
vals.push(Value::Filesize {

View File

@ -199,18 +199,18 @@ fn rm(
));
}
let targets_span = Span {
start: targets
let targets_span = Span::new(
targets
.iter()
.map(|x| x.span.start)
.min()
.expect("targets were empty"),
end: targets
targets
.iter()
.map(|x| x.span.end)
.max()
.expect("targets were empty"),
};
);
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 {
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)

View File

@ -166,19 +166,13 @@ fn convert_row_column_to_span(row: usize, col: usize, contents: &str) -> Span {
cur_col = 0;
}
if cur_row >= row && cur_col >= col {
return Span {
start: offset,
end: offset,
};
return Span::new(offset, offset);
} else {
cur_col += 1;
}
}
Span {
start: contents.len(),
end: contents.len(),
}
Span::new(contents.len(), contents.len())
}
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),
Value::String {
val: item.to_string(),
span: Span { start: 0, end: 0 },
span: Span::unknown(),
}
.into_pipeline_data(),
) {
let result = output.into_value(Span { start: 0, end: 0 });
let result = output.into_value(Span::unknown());
match result.as_string() {
Ok(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 {
output.push(FormatOperation::ValueNeedEval(
column_name.clone(),
Span {
start: span_start + column_span_start,
end: span_start + column_span_end,
},
Span::new(span_start + column_span_start, span_start + column_span_end),
));
} else {
output.push(FormatOperation::ValueFromColumn(
column_name.clone(),
Span {
start: span_start + column_span_start,
end: span_start + column_span_end,
},
Span::new(span_start + column_span_start, span_start + column_span_end),
));
}
}

View File

@ -186,15 +186,15 @@ fn get_documentation(
match decl.run(
engine_state,
stack,
&Call::new(Span::new(0, 0)),
&Call::new(Span::unknown()),
Value::String {
val: example.example.to_string(),
span: Span { start: 0, end: 0 },
span: Span::unknown(),
}
.into_pipeline_data(),
) {
Ok(output) => {
let result = output.into_value(Span { start: 0, end: 0 });
let result = output.into_value(Span::unknown());
match result.as_string() {
Ok(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) => {
let mut output = vec![(
Span {
start: expr.span.start,
end: expr.span.start + 3,
},
Span::new(expr.span.start, expr.span.start + 3),
FlatShape::Operator,
)];
output.extend(flatten_expression(working_set, inner_expr));
@ -123,25 +120,13 @@ pub fn flatten_expression(
if let Some(first) = flattened.first() {
if first.0.start > outer_span.start {
output.push((
Span {
start: outer_span.start,
end: first.0.start,
},
FlatShape::Block,
));
output.push((Span::new(outer_span.start, first.0.start), FlatShape::Block));
}
}
let last = if let Some(last) = flattened.last() {
if last.0.end < outer_span.end {
Some((
Span {
start: last.0.end,
end: outer_span.end,
},
FlatShape::Block,
))
Some((Span::new(last.0.end, outer_span.end), FlatShape::Block))
} else {
None
}
@ -313,13 +298,7 @@ pub fn flatten_expression(
if let Some(first) = flattened.first() {
if first.0.start > last_end {
output.push((
Span {
start: last_end,
end: first.0.start,
},
FlatShape::List,
));
output.push((Span::new(last_end, first.0.start), FlatShape::List));
}
}
@ -331,13 +310,7 @@ pub fn flatten_expression(
}
if last_end < outer_span.end {
output.push((
Span {
start: last_end,
end: outer_span.end,
},
FlatShape::List,
));
output.push((Span::new(last_end, outer_span.end), FlatShape::List));
}
output
}
@ -353,18 +326,12 @@ pub fn flatten_expression(
output.insert(
0,
(
Span {
start: expr.span.start,
end: expr.span.start + 2,
},
Span::new(expr.span.start, expr.span.start + 2),
FlatShape::StringInterpolation,
),
);
output.push((
Span {
start: expr.span.end - 1,
end: expr.span.end,
},
Span::new(expr.span.end - 1, expr.span.end),
FlatShape::StringInterpolation,
));
}
@ -382,13 +349,7 @@ pub fn flatten_expression(
if let Some(first) = flattened_lhs.first() {
if first.0.start > last_end {
output.push((
Span {
start: last_end,
end: first.0.start,
},
FlatShape::Record,
));
output.push((Span::new(last_end, first.0.start), FlatShape::Record));
}
}
if let Some(last) = flattened_lhs.last() {
@ -398,13 +359,7 @@ pub fn flatten_expression(
if let Some(first) = flattened_rhs.first() {
if first.0.start > last_end {
output.push((
Span {
start: last_end,
end: first.0.start,
},
FlatShape::Record,
));
output.push((Span::new(last_end, first.0.start), FlatShape::Record));
}
}
if let Some(last) = flattened_rhs.last() {
@ -414,13 +369,7 @@ pub fn flatten_expression(
output.extend(flattened_rhs);
}
if last_end < outer_span.end {
output.push((
Span {
start: last_end,
end: outer_span.end,
},
FlatShape::Record,
));
output.push((Span::new(last_end, outer_span.end), FlatShape::Record));
}
output
@ -448,13 +397,7 @@ pub fn flatten_expression(
let flattened = flatten_expression(working_set, e);
if let Some(first) = flattened.first() {
if first.0.start > last_end {
output.push((
Span {
start: last_end,
end: first.0.start,
},
FlatShape::Table,
));
output.push((Span::new(last_end, first.0.start), FlatShape::Table));
}
}
@ -469,13 +412,7 @@ pub fn flatten_expression(
let flattened = flatten_expression(working_set, expr);
if let Some(first) = flattened.first() {
if first.0.start > last_end {
output.push((
Span {
start: last_end,
end: first.0.start,
},
FlatShape::Table,
));
output.push((Span::new(last_end, first.0.start), FlatShape::Table));
}
}
@ -488,13 +425,7 @@ pub fn flatten_expression(
}
if last_end < outer_span.end {
output.push((
Span {
start: last_end,
end: outer_span.end,
},
FlatShape::Table,
));
output.push((Span::new(last_end, outer_span.end), FlatShape::Table));
}
output

View File

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

View File

@ -1179,10 +1179,7 @@ pub fn parse_export_in_module(
error = error.or_else(|| {
Some(ParseError::MissingPositional(
"def, def-env, alias, or env keyword".into(), // TODO: keep filling more keywords as they come
Span {
start: export_span.end,
end: export_span.end,
},
Span::new(export_span.end, export_span.end),
"'def', `def-env`, `alias`, or 'env' keyword.".to_string(),
))
});
@ -1472,11 +1469,10 @@ pub fn parse_module(
if block_bytes.ends_with(b"}") {
end -= 1;
} 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 block_span = Span { start, end };
let block_span = Span::new(start, end);
let (block, module, err) =
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() {
return Some(ParseError::MissingPositional(
argument.name.clone(),
Span {
start: last.span.end,
end: last.span.end,
},
Span::new(last.span.end, last.span.end),
sig.call_signature(),
));
} else {
return Some(ParseError::MissingPositional(
argument.name.clone(),
Span {
start: command.end,
end: command.end,
},
Span::new(command.end, command.end),
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() {
Some(ParseError::MissingPositional(
missing.name.clone(),
Span {
start: last.span.end,
end: last.span.end,
},
Span::new(last.span.end, last.span.end),
sig.call_signature(),
))
} else {
Some(ParseError::MissingPositional(
missing.name.clone(),
Span {
start: command.end,
end: command.end,
},
Span::new(command.end, command.end),
sig.call_signature(),
))
}
@ -285,10 +273,7 @@ pub fn parse_external_call(
let head_contents = working_set.get_span_contents(spans[0]);
let head_span = if head_contents.starts_with(b"^") {
Span {
start: spans[0].start + 1,
end: spans[0].end,
}
Span::new(spans[0].start + 1, spans[0].end)
} else {
spans[0]
};
@ -345,7 +330,7 @@ pub fn parse_external_call(
span: *span,
ty: Type::String,
custom_completion: None,
})
});
}
}
(
@ -393,10 +378,7 @@ fn parse_long_flag(
(
Some(Spanned {
item: long_name,
span: Span {
start: arg_span.start,
end: arg_span.start + long_name_len + 2,
},
span: Span::new(arg_span.start, arg_span.start + long_name_len + 2),
}),
Some(arg),
err,
@ -486,10 +468,10 @@ fn parse_short_flags(
for short_flag in short_flags.iter().enumerate() {
let short_flag_char = char::from(*short_flag.1);
let orig = arg_span;
let short_flag_span = Span {
start: orig.start + 1 + short_flag.0,
end: orig.start + 1 + short_flag.0 + 1,
};
let short_flag_span = Span::new(
orig.start + 1 + short_flag.0,
orig.start + 1 + short_flag.0 + 1,
);
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 !found_short_flags.is_empty() && flag.arg.is_some() {
@ -715,10 +697,7 @@ pub fn parse_multispan_value(
Some(ParseError::KeywordMissingArgument(
arg.to_string(),
String::from_utf8_lossy(keyword).into(),
Span {
start: spans[*spans_idx - 1].end,
end: spans[*spans_idx - 1].end,
},
Span::new(spans[*spans_idx - 1].end, spans[*spans_idx - 1].end),
))
});
return (
@ -925,10 +904,7 @@ pub fn parse_internal_call(
error = error.or_else(|| {
Some(ParseError::MissingPositional(
positional.name.clone(),
Span {
start: spans[spans_idx].end,
end: spans[spans_idx].end,
},
Span::new(spans[spans_idx].end, spans[spans_idx].end),
signature.call_signature(),
))
});
@ -1621,7 +1597,7 @@ pub fn parse_string_interpolation(
(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 mut output = vec![];
@ -1642,10 +1618,7 @@ pub fn parse_string_interpolation(
{
mode = InterpolationMode::Expression;
if token_start < b {
let span = Span {
start: token_start,
end: b,
};
let span = Span::new(token_start, b);
let str_contents = working_set.get_span_contents(span);
let str_contents = if double_quote {
@ -1696,10 +1669,7 @@ pub fn parse_string_interpolation(
mode = InterpolationMode::String;
if token_start < b {
let span = Span {
start: token_start,
end: b + 1,
};
let span = Span::new(token_start, b + 1);
let (expr, err) =
parse_full_cell_path(working_set, None, span, expand_aliases_denylist);
@ -1718,10 +1688,7 @@ pub fn parse_string_interpolation(
match mode {
InterpolationMode::String => {
if token_start < end {
let span = Span {
start: token_start,
end,
};
let span = Span::new(token_start, end);
let str_contents = working_set.get_span_contents(span);
let str_contents = if double_quote {
@ -1743,10 +1710,7 @@ pub fn parse_string_interpolation(
}
InterpolationMode::Expression => {
if token_start < end {
let span = Span {
start: token_start,
end,
};
let span = Span::new(token_start, end);
let (expr, err) =
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")") {
end -= 1;
} 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 span = Span { start, end };
let span = Span::new(start, end);
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(
"unicode hex value".into(),
Span {
start: (span.start + idx),
end: span.end,
},
Span::new(span.start + idx, span.end),
));
}
_ => {
err = Some(ParseError::Expected(
"unicode hex value".into(),
Span {
start: (span.start + idx),
end: span.end,
},
Span::new(span.start + idx, span.end),
));
}
}
@ -2589,10 +2547,7 @@ pub fn unescape_string(bytes: &[u8], span: Span) -> (Vec<u8>, Option<ParseError>
_ => {
err = Some(ParseError::Expected(
"supported escape character".into(),
Span {
start: (span.start + idx),
end: span.end,
},
Span::new(span.start + idx, span.end),
));
}
}
@ -2772,10 +2727,7 @@ pub fn parse_shape_name(
let (shape, err) = parse_shape_name(
working_set,
split[0].as_bytes(),
Span {
start: span.start,
end: span.start + split[0].len(),
},
Span::new(span.start, span.start + split[0].len()),
);
let command_name = trim_quotes(split[1].as_bytes());
@ -2786,10 +2738,10 @@ pub fn parse_shape_name(
} else {
return (
shape,
Some(ParseError::UnknownCommand(Span {
start: span.start + split[0].len() + 1,
end: span.end,
})),
Some(ParseError::UnknownCommand(Span::new(
span.start + split[0].len() + 1,
span.end,
))),
);
}
} else {
@ -3143,10 +3095,7 @@ pub fn parse_signature(
error = error.or_else(|| {
Some(ParseError::Expected(
"[ or (".into(),
Span {
start,
end: start + 1,
},
Span::new(start, start + 1),
))
});
}
@ -3154,16 +3103,11 @@ pub fn parse_signature(
if (has_paren && bytes.ends_with(b")")) || (!has_paren && bytes.ends_with(b"]")) {
end -= 1;
} else {
error = error.or_else(|| {
Some(ParseError::Unclosed(
"] or )".into(),
Span { start: end, end },
))
});
error = error.or_else(|| Some(ParseError::Unclosed("] or )".into(), Span::new(end, end))));
}
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);
(
@ -3631,10 +3575,7 @@ pub fn parse_signature_helper(
contents: crate::TokenContents::Comment,
span,
} => {
let contents = working_set.get_span_contents(Span {
start: span.start + 1,
end: span.end,
});
let contents = working_set.get_span_contents(Span::new(span.start + 1, span.end));
let mut contents = String::from_utf8_lossy(contents).to_string();
contents = contents.trim().into();
@ -3724,10 +3665,10 @@ pub fn parse_list_expression(
if bytes.ends_with(b"]") {
end -= 1;
} 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 (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"]") {
end -= 1;
} 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);
@ -3938,10 +3879,10 @@ pub fn parse_block_expression(
if bytes.ends_with(b"}") {
end -= 1;
} 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);
@ -4069,10 +4010,10 @@ pub fn parse_closure_expression(
if bytes.ends_with(b"}") {
end -= 1;
} 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);
@ -4111,10 +4052,7 @@ pub fn parse_closure_expression(
end
};
let signature_span = Span {
start: start_point,
end: end_point,
};
let signature_span = Span::new(start_point, end_point);
let (signature, err) =
parse_signature_helper(working_set, signature_span, expand_aliases_denylist);
error = error.or(err);
@ -4673,10 +4611,7 @@ pub fn parse_math_expression(
garbage(spans[0]),
Some(ParseError::Expected(
"expression".into(),
Span {
start: spans[0].end,
end: spans[0].end,
},
Span::new(spans[0].end, spans[0].end),
)),
);
}
@ -4825,16 +4760,10 @@ pub fn parse_expression(
let lhs = parse_string_strict(
working_set,
Span {
start: spans[pos].start,
end: spans[pos].start + point - 1,
},
Span::new(spans[pos].start, spans[pos].start + point - 1),
);
let rhs = if spans[pos].start + point < spans[pos].end {
let rhs_span = Span {
start: spans[pos].start + point,
end: spans[pos].end,
};
let rhs_span = Span::new(spans[pos].start + point, spans[pos].end);
if working_set.get_span_contents(rhs_span).starts_with(b"$") {
parse_dollar_expr(working_set, rhs_span, expand_aliases_denylist)
@ -4845,7 +4774,7 @@ pub fn parse_expression(
(
Expression {
expr: Expr::String(String::new()),
span: Span { start: 0, end: 0 },
span: Span::unknown(),
ty: Type::Nothing,
custom_completion: None,
},
@ -5124,7 +5053,7 @@ pub fn parse_expression(
];
let expr = Expr::Call(Box::new(Call {
head: Span { start: 0, end: 0 },
head: Span::unknown(),
decl_id,
arguments,
redirect_stdout: true,
@ -5232,10 +5161,7 @@ pub fn parse_record(
error = error.or_else(|| {
Some(ParseError::Expected(
"{".into(),
Span {
start,
end: start + 1,
},
Span::new(start, start + 1),
))
});
}
@ -5243,10 +5169,10 @@ pub fn parse_record(
if bytes.ends_with(b"}") {
end -= 1;
} 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 (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 {
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(),
&Token {
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(),
&Token {
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(),
&Token {
contents: TokenContents::Comment,
span: Span { start: 10, end: 19 }
span: Span::new(10, 19)
}
);
assert_eq!(
output.0.get(5).unwrap(),
&Token {
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(),
&Token {
contents: TokenContents::Comment,
span: Span { start: 31, end: 40 }
span: Span::new(31, 40)
}
);
assert_eq!(
output.0.get(11).unwrap(),
&Token {
contents: TokenContents::Eol,
span: Span { start: 40, end: 41 }
span: Span::new(40, 41)
}
);
}

View File

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

View File

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

View File

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

View File

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

View File

@ -981,7 +981,7 @@ fn create_hooks(value: &Value) -> Result<Hooks, ShellError> {
_ => Err(ShellError::UnsupportedConfigValue(
"record for 'hooks' config".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 {
for file in self.files.iter().enumerate() {
if file.0 == file_id {
let contents = self.get_span_contents(&Span {
start: file.1 .1,
end: file.1 .2,
});
let contents = self.get_span_contents(&Span::new(file.1 .1, file.1 .2));
let output = String::from_utf8_lossy(contents).to_string();
return output;
@ -1318,10 +1315,9 @@ impl<'a> StateWorkingSet<'a> {
pub fn get_file_source(&self, file_id: usize) -> String {
for file in self.files().enumerate() {
if file.0 == file_id {
let output = String::from_utf8_lossy(self.get_span_contents(Span {
start: file.1 .1,
end: file.1 .2,
}))
let output = String::from_utf8_lossy(
self.get_span_contents(Span::new(file.1 .1, file.1 .2)),
)
.to_string();
return output;
@ -2041,10 +2037,7 @@ impl<'a> miette::SourceCode for &StateWorkingSet<'a> {
let found_file = "Found matching file";
dbg!(found_file);
}
let our_span = Span {
start: *start,
end: *end,
};
let our_span = Span::new(*start, *end);
// We need to move to a local span because we're only reading
// the specific file contents via self.get_span_contents.
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::ExternalStream { stdout: None, .. } => {
Ok(PipelineData::new(Span { start: 0, end: 0 }))
Ok(PipelineData::new(Span::unknown()))
}
PipelineData::ExternalStream {
stdout: Some(stream),
@ -366,7 +366,7 @@ impl PipelineData {
Ok(stream.flat_map(f).into_pipeline_data(ctrlc))
}
PipelineData::ExternalStream { stdout: None, .. } => {
Ok(PipelineData::new(Span { start: 0, end: 0 }))
Ok(PipelineData::new(Span::unknown()))
}
PipelineData::ExternalStream {
stdout: Some(stream),
@ -419,7 +419,7 @@ impl PipelineData {
}
PipelineData::ListStream(stream, ..) => Ok(stream.filter(f).into_pipeline_data(ctrlc)),
PipelineData::ExternalStream { stdout: None, .. } => {
Ok(PipelineData::new(Span { start: 0, end: 0 }))
Ok(PipelineData::new(Span::unknown()))
}
PipelineData::ExternalStream {
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
/// end offset together make the inclusive start/exclusive end pair for where to underline to highlight
/// a given point of interest.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Span {
pub start: usize,
@ -28,24 +29,28 @@ impl From<Span> for SourceSpan {
impl 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 }
}
pub fn unknown() -> Self {
Self::new(0, 0)
pub const fn unknown() -> Span {
Span { start: 0, end: 0 }
}
/// Note: Only use this for test data, *not* live data, as it will point into unknown source
/// when used in errors.
pub fn test_data() -> Span {
Span { start: 0, end: 0 }
pub const fn test_data() -> Span {
Self::unknown()
}
pub fn offset(&self, offset: usize) -> Span {
Span {
start: self.start - offset,
end: self.end - offset,
}
Span::new(self.start - offset, self.end - offset)
}
pub fn contains(&self, pos: usize) -> bool {
@ -70,15 +75,17 @@ impl Span {
pub fn span(spans: &[Span]) -> Span {
let length = spans.len();
//TODO debug_assert!(length > 0, "expect spans > 0");
if length == 0 {
// TODO: do this for now, but we might also want to protect against this case
Span { start: 0, end: 0 }
Span::unknown()
} else if length == 1 {
spans[0]
} else {
Span {
start: spans[0].start,
end: spans[length - 1].end,
}
let end = spans
.iter()
.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 {
fn default() -> Self {
Value::Nothing {
span: Span { start: 0, end: 0 },
span: Span::unknown(),
}
}
}