This commit is contained in:
132ikl 2025-03-10 10:10:32 -04:00 committed by GitHub
commit 6e6da88422
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 105 additions and 18 deletions

View File

@ -11,8 +11,14 @@ fn quickcheck_parse(data: String) -> bool {
let mut working_set = StateWorkingSet::new(&context); let mut working_set = StateWorkingSet::new(&context);
let _ = working_set.add_file("quickcheck".into(), data.as_bytes()); let _ = working_set.add_file("quickcheck".into(), data.as_bytes());
let _ = let _ = nu_parser::parse_block(
nu_parser::parse_block(&mut working_set, &tokens, Span::new(0, 0), false, false); &mut working_set,
&tokens,
Span::new(0, 0),
false,
false,
false,
);
} }
} }
true true

View File

@ -23,7 +23,7 @@ fn catch_can_access_error() {
#[test] #[test]
fn catch_can_access_error_as_dollar_in() { fn catch_can_access_error_as_dollar_in() {
let output = nu!("try { foobarbaz } catch { $in | get raw }"); let output = nu!("try { foobarbaz } catch { get raw }");
assert!(output.err.contains("External command failed")); assert!(output.err.contains("External command failed"));
} }

View File

@ -3339,7 +3339,8 @@ pub fn parse_let(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipeline
} }
let rvalue_span = Span::concat(&spans[(span.0 + 1)..]); let rvalue_span = Span::concat(&spans[(span.0 + 1)..]);
let rvalue_block = parse_block(working_set, &tokens, rvalue_span, false, true); let rvalue_block =
parse_block(working_set, &tokens, rvalue_span, false, true, false);
let output_type = rvalue_block.output_type(); let output_type = rvalue_block.output_type();
@ -3459,7 +3460,7 @@ pub fn parse_const(working_set: &mut StateWorkingSet, spans: &[Span]) -> (Pipeli
trace!("parsing: const right-hand side subexpression"); trace!("parsing: const right-hand side subexpression");
let rvalue_block = let rvalue_block =
parse_block(working_set, &rvalue_tokens, rvalue_span, false, true); parse_block(working_set, &rvalue_tokens, rvalue_span, false, true, false);
let rvalue_ty = rvalue_block.output_type(); let rvalue_ty = rvalue_block.output_type();
let rvalue_block_id = working_set.add_block(Arc::new(rvalue_block)); let rvalue_block_id = working_set.add_block(Arc::new(rvalue_block));
let rvalue = Expression::new( let rvalue = Expression::new(
@ -3621,7 +3622,8 @@ pub fn parse_mut(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipeline
} }
let rvalue_span = Span::concat(&spans[(span.0 + 1)..]); let rvalue_span = Span::concat(&spans[(span.0 + 1)..]);
let rvalue_block = parse_block(working_set, &tokens, rvalue_span, false, true); let rvalue_block =
parse_block(working_set, &tokens, rvalue_span, false, true, false);
let output_type = rvalue_block.output_type(); let output_type = rvalue_block.output_type();

View File

@ -14,8 +14,8 @@ use log::trace;
use nu_engine::DIR_VAR_PARSER_INFO; use nu_engine::DIR_VAR_PARSER_INFO;
use nu_protocol::{ use nu_protocol::{
ast::*, engine::StateWorkingSet, eval_const::eval_constant, BlockId, DeclId, DidYouMean, ast::*, engine::StateWorkingSet, eval_const::eval_constant, BlockId, DeclId, DidYouMean,
FilesizeUnit, Flag, ParseError, PositionalArg, ShellError, Signature, Span, Spanned, FilesizeUnit, Flag, ParseError, ParseWarning, PositionalArg, ShellError, Signature, Span,
SyntaxShape, Type, Value, VarId, ENV_VARIABLE_ID, IN_VARIABLE_ID, Spanned, SyntaxShape, Type, Value, VarId, ENV_VARIABLE_ID, IN_VARIABLE_ID,
}; };
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
@ -2452,7 +2452,7 @@ pub fn parse_full_cell_path(
// Creating a Type scope to parse the new block. This will keep track of // Creating a Type scope to parse the new block. This will keep track of
// the previous input type found in that block // the previous input type found in that block
let output = parse_block(working_set, &output, span, true, true); let output = parse_block(working_set, &output, span, true, true, false);
let ty = output.output_type(); let ty = output.output_type();
@ -4596,7 +4596,14 @@ pub fn parse_block_expression(working_set: &mut StateWorkingSet, span: Span) ->
_ => (None, 0), _ => (None, 0),
}; };
let mut output = parse_block(working_set, &output[amt_to_skip..], span, false, false); let mut output = parse_block(
working_set,
&output[amt_to_skip..],
span,
false,
false,
false,
);
if let Some(signature) = signature { if let Some(signature) = signature {
output.signature = signature.0; output.signature = signature.0;
@ -4915,7 +4922,14 @@ pub fn parse_closure_expression(
} }
} }
let mut output = parse_block(working_set, &output[amt_to_skip..], span, false, false); let mut output = parse_block(
working_set,
&output[amt_to_skip..],
span,
false,
false,
true,
);
if let Some(signature) = signature { if let Some(signature) = signature {
output.signature = signature.0; output.signature = signature.0;
@ -5213,7 +5227,7 @@ pub fn parse_assignment_expression(
working_set.parse_errors.extend(rhs_error); working_set.parse_errors.extend(rhs_error);
trace!("parsing: assignment right-hand side subexpression"); trace!("parsing: assignment right-hand side subexpression");
let rhs_block = parse_block(working_set, &rhs_tokens, rhs_span, false, true); let rhs_block = parse_block(working_set, &rhs_tokens, rhs_span, false, true, false);
let rhs_ty = rhs_block.output_type(); let rhs_ty = rhs_block.output_type();
// TEMP: double-check that if the RHS block starts with an external call, it must start with a // TEMP: double-check that if the RHS block starts with an external call, it must start with a
@ -6305,6 +6319,7 @@ pub fn parse_block(
span: Span, span: Span,
scoped: bool, scoped: bool,
is_subexpression: bool, is_subexpression: bool,
is_closure: bool,
) -> Block { ) -> Block {
let (lite_block, err) = lite_parse(tokens, working_set); let (lite_block, err) = lite_parse(tokens, working_set);
if let Some(err) = err { if let Some(err) = err {
@ -6342,6 +6357,10 @@ pub fn parse_block(
.flat_map(|pipeline| pipeline.elements.first()) .flat_map(|pipeline| pipeline.elements.first())
.any(|element| element.has_in_variable(working_set)) .any(|element| element.has_in_variable(working_set))
{ {
if is_closure {
check_block_pipes_in(working_set, &block);
}
// Move the block out to prepare it to become a subexpression // Move the block out to prepare it to become a subexpression
let inner_block = std::mem::take(&mut block); let inner_block = std::mem::take(&mut block);
block.span = inner_block.span; block.span = inner_block.span;
@ -6851,6 +6870,36 @@ fn wrap_expr_with_collect(working_set: &mut StateWorkingSet, expr: Expression) -
) )
} }
// Report a warning if the first pipeline element of a block is `$in` followed by a pipe
fn check_block_pipes_in(working_set: &mut StateWorkingSet, block: &Block) {
if let Some(elements) = block.pipelines.first().map(|pipeline| &pipeline.elements) {
// only warn if we're also piping into something
let element = match elements.len() {
..2 => return,
2.. => elements.first().expect("at least two elements"),
};
if let Expr::FullCellPath(cell_path) = &element.expr.expr {
if matches!(
**cell_path,
FullCellPath {
head: Expression {
expr: Expr::Var(id),
..
},
..
} if id == IN_VARIABLE_ID
) {
working_set
.parse_warnings
.push(ParseWarning::UnnecessaryInVariable {
span: element.expr.span(working_set),
})
}
}
}
}
// Parses a vector of u8 to create an AST Block. If a file name is given, then // Parses a vector of u8 to create an AST Block. If a file name is given, then
// the name is stored in the working set. When parsing a source without a file // the name is stored in the working set. When parsing a source without a file
// name, the source of bytes is stored as "source" // name, the source of bytes is stored as "source"
@ -6889,7 +6938,14 @@ pub fn parse(
working_set.error(err) working_set.error(err)
} }
Arc::new(parse_block(working_set, &output, new_span, scoped, false)) Arc::new(parse_block(
working_set,
&output,
new_span,
scoped,
false,
false,
))
} }
}; };

View File

@ -1132,6 +1132,21 @@ impl<'a> GetSpan for &'a StateWorkingSet<'a> {
} }
} }
impl GetSpan for StateWorkingSet<'_> {
fn get_span(&self, span_id: SpanId) -> Span {
let num_permanent_spans = self.permanent_state.num_spans();
if span_id.get() < num_permanent_spans {
self.permanent_state.get_span(span_id)
} else {
*self
.delta
.spans
.get(span_id.get() - num_permanent_spans)
.expect("internal error: missing span")
}
}
}
impl miette::SourceCode for &StateWorkingSet<'_> { impl miette::SourceCode for &StateWorkingSet<'_> {
fn read_span<'b>( fn read_span<'b>(
&'b self, &'b self,

View File

@ -14,12 +14,20 @@ pub enum ParseWarning {
span: Span, span: Span,
url: String, url: String,
}, },
#[error("Found $in at the start of a command.")]
#[diagnostic(help("Using $in at the start of a command collects the pipeline input.\nIf you did mean to collect the pipeline input, replace this with the `collect` command."))]
UnnecessaryInVariable {
#[label("try removing this")]
span: Span,
},
} }
impl ParseWarning { impl ParseWarning {
pub fn span(&self) -> Span { pub fn span(&self) -> Span {
match self { match self {
ParseWarning::DeprecatedWarning { span, .. } => *span, ParseWarning::DeprecatedWarning { span, .. } => *span,
ParseWarning::UnnecessaryInVariable { span, .. } => *span,
} }
} }
} }

View File

@ -4,7 +4,7 @@ def format-event [ ] {
# Replace numeric value of raw_code with hex string # Replace numeric value of raw_code with hex string
let record = match $record { let record = match $record {
{raw_code: $code} => { {raw_code: $code} => {
$record | update raw_code {|| $in | format number | get upperhex} $record | update raw_code {|| format number | get upperhex}
} }
_ => $record _ => $record
} }
@ -12,7 +12,7 @@ def format-event [ ] {
# Replace numeric value of raw_modifiers with binary string # Replace numeric value of raw_modifiers with binary string
let record = match $record { let record = match $record {
{raw_modifiers: $flags} => { {raw_modifiers: $flags} => {
$record | update raw_modifiers {|| $in | format number | get binary} $record | update raw_modifiers {|| format number | get binary}
} }
_ => $record _ => $record
} }

View File

@ -95,7 +95,7 @@ def xupdate-string-step [ step: string rest: list updater: closure ] {
} }
def xupdate-int-step [ step: int rest: list updater: closure ] { def xupdate-int-step [ step: int rest: list updater: closure ] {
$in | enumerate | each {|it| enumerate | each {|it|
let item = $it.item let item = $it.item
let idx = $it.index let idx = $it.index
@ -108,7 +108,7 @@ def xupdate-int-step [ step: int rest: list updater: closure ] {
} }
def xupdate-closure-step [ step: closure rest: list updater: closure ] { def xupdate-closure-step [ step: closure rest: list updater: closure ] {
$in | each {|it| each {|it|
if (do $step $it) { if (do $step $it) {
[ $it ] | xupdate-internal $rest $updater | get 0 [ $it ] | xupdate-internal $rest $updater | get 0
} else { } else {
@ -194,7 +194,7 @@ export def xinsert [
# position is greater than number of elements) in content of all entries of input matched by # position is greater than number of elements) in content of all entries of input matched by
# path. If not specified inserts at the end. # path. If not specified inserts at the end.
] { ] {
$in | xupdate $path {|entry| xupdate $path {|entry|
match ($entry | xtype) { match ($entry | xtype) {
'tag' => { 'tag' => {
let new_content = if $position == null { let new_content = if $position == null {