mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-06-09 20:21:16 -03:00
ast: adopt Kind in yet more places
This commit is contained in:
@@ -1965,7 +1965,7 @@ pub fn dump(&self, orig: &wstr) -> WString {
|
||||
// dot-| padding
|
||||
result += &str::repeat("! ", depth)[..];
|
||||
|
||||
if let Some(n) = node.as_argument() {
|
||||
if let Kind::Argument(n) = node.kind() {
|
||||
result += "argument";
|
||||
if let Some(argsrc) = n.try_source(orig) {
|
||||
sprintf!(=> &mut result, ": '%ls'", argsrc);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::prelude::*;
|
||||
use crate::ast::{Ast, Leaf};
|
||||
use crate::ast::{Ast, Kind, Leaf};
|
||||
use crate::common::{unescape_string, UnescapeFlags, UnescapeStringStyle};
|
||||
use crate::complete::Completion;
|
||||
use crate::expand::{expand_string, ExpandFlags, ExpandResultCode};
|
||||
@@ -116,7 +116,7 @@ fn strip_dollar_prefixes(insert_mode: AppendMode, prefix: &wstr, insert: &wstr)
|
||||
let mut stripped = WString::new();
|
||||
let mut have = prefix.len();
|
||||
for node in ast.walk() {
|
||||
let Some(ds) = node.as_decorated_statement() else {
|
||||
let Kind::DecoratedStatement(ds) = node.kind() else {
|
||||
continue;
|
||||
};
|
||||
let Some(range) = ds.command.range() else {
|
||||
|
||||
@@ -351,7 +351,7 @@ fn compute_multi_line_brace_statement_locations(&self) -> Vec<usize> {
|
||||
.collect();
|
||||
let mut next_newline = 0;
|
||||
for node in Traversal::new(self.ast.top()) {
|
||||
let Some(brace_statement) = node.as_brace_statement() else {
|
||||
let Kind::BraceStatement(brace_statement) = node.kind() else {
|
||||
continue;
|
||||
};
|
||||
while next_newline != newline_offsets.len()
|
||||
@@ -402,15 +402,15 @@ fn gap_text_flags_before_node(&self, node: &dyn Node) -> GapFlags {
|
||||
let p = self.traversal.parent(p);
|
||||
assert_eq!(p.typ(), Type::statement);
|
||||
let p = self.traversal.parent(p);
|
||||
if let Some(job) = p.as_job_pipeline() {
|
||||
if let Kind::JobPipeline(job) = p.kind() {
|
||||
if !job.variables.is_empty() {
|
||||
result.allow_escaped_newlines = true;
|
||||
}
|
||||
} else if let Some(job_cnt) = p.as_job_continuation() {
|
||||
} else if let Kind::JobContinuation(job_cnt) = p.kind() {
|
||||
if !job_cnt.variables.is_empty() {
|
||||
result.allow_escaped_newlines = true;
|
||||
}
|
||||
} else if let Some(not_stmt) = p.as_not_statement() {
|
||||
} else if let Kind::NotStatement(not_stmt) = p.kind() {
|
||||
if !not_stmt.variables.is_empty() {
|
||||
result.allow_escaped_newlines = true;
|
||||
}
|
||||
@@ -727,14 +727,12 @@ fn visit_semi_nl(&mut self, node: &dyn ast::Token) {
|
||||
}
|
||||
|
||||
fn is_multi_line_brace(&self, node: &dyn ast::Token) -> bool {
|
||||
self.traversal
|
||||
.parent(node.as_node())
|
||||
.as_brace_statement()
|
||||
.is_some_and(|brace_statement| {
|
||||
self.multi_line_brace_statement_locations
|
||||
.binary_search(&brace_statement.source_range().start())
|
||||
.is_ok()
|
||||
})
|
||||
let Kind::BraceStatement(brace) = self.traversal.parent(node.as_node()).kind() else {
|
||||
return false;
|
||||
};
|
||||
self.multi_line_brace_statement_locations
|
||||
.binary_search(&brace.source_range().start())
|
||||
.is_ok()
|
||||
}
|
||||
fn visit_left_brace(&mut self, node: &dyn ast::Token) {
|
||||
let range = node.source_range();
|
||||
|
||||
@@ -278,7 +278,10 @@ fn autosuggest_parse_command(
|
||||
);
|
||||
|
||||
// Find the first statement.
|
||||
let jc = ast.top().as_job_list().unwrap().get(0)?;
|
||||
let Kind::JobList(job_list) = ast.top().kind() else {
|
||||
panic!("Expected job list");
|
||||
};
|
||||
let jc = job_list.get(0)?;
|
||||
let first_statement = jc.job.statement.as_decorated_statement()?;
|
||||
|
||||
if let Some(expanded_command) = statement_get_expanded_command(buff, first_statement, ctx) {
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
use rand::Rng;
|
||||
|
||||
use crate::{
|
||||
ast::{Ast, Node},
|
||||
ast::{Ast, Kind, Node},
|
||||
common::{
|
||||
str2wcstring, unescape_string, valid_var_name, wcs2zstring, CancelChecker,
|
||||
UnescapeStringStyle,
|
||||
@@ -1585,12 +1585,12 @@ pub fn add_pending_with_file_detection(
|
||||
|
||||
let mut potential_paths = Vec::new();
|
||||
for node in ast.walk() {
|
||||
if let Some(arg) = node.as_argument() {
|
||||
if let Kind::Argument(arg) = node.kind() {
|
||||
let potential_path = arg.source(s);
|
||||
if string_could_be_path(potential_path) {
|
||||
potential_paths.push(potential_path.to_owned());
|
||||
}
|
||||
} else if let Some(stmt) = node.as_decorated_statement() {
|
||||
} else if let Kind::DecoratedStatement(stmt) = node.kind() {
|
||||
// Hack hack hack - if the command is likely to trigger an exit, then don't do
|
||||
// background file detection, because we won't be able to write it to our history file
|
||||
// before we exit.
|
||||
|
||||
@@ -1335,7 +1335,8 @@ pub fn parse_util_detect_errors_in_argument_list(
|
||||
|
||||
// Get the root argument list and extract arguments from it.
|
||||
// Test each of these.
|
||||
let args = &ast.top().as_freestanding_argument_list().unwrap().arguments;
|
||||
let arg_list: &ast::FreestandingArgumentList = ast.top().cast().unwrap();
|
||||
let args = &arg_list.arguments;
|
||||
for arg in args.iter() {
|
||||
let arg_src = arg.source(arg_list_src);
|
||||
if parse_util_detect_errors_in_argument(arg, arg_src, &mut Some(&mut errors)).is_err() {
|
||||
@@ -1565,19 +1566,22 @@ fn detect_errors_in_backgrounded_job(
|
||||
// foo & ; or bar
|
||||
// if foo & ; end
|
||||
// while foo & ; end
|
||||
let Some(job_conj) = traversal.parent(job).as_job_conjunction() else {
|
||||
let Kind::JobConjunction(job_conj) = traversal.parent(job).kind() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let job_conj_parent = traversal.parent(job_conj);
|
||||
if job_conj_parent.as_if_clause().is_some() || job_conj_parent.as_while_header().is_some() {
|
||||
if matches!(
|
||||
job_conj_parent.kind(),
|
||||
Kind::IfClause(_) | Kind::WhileHeader(_)
|
||||
) {
|
||||
errored = append_syntax_error!(
|
||||
parse_errors,
|
||||
source_range.start(),
|
||||
source_range.length(),
|
||||
BACKGROUND_IN_CONDITIONAL_ERROR_MSG
|
||||
);
|
||||
} else if let Some(jlist) = job_conj_parent.as_job_list() {
|
||||
} else if let Kind::JobList(jlist) = job_conj_parent.kind() {
|
||||
// This isn't very complete, e.g. we don't catch 'foo & ; not and bar'.
|
||||
// Find the index of ourselves in the job list.
|
||||
let index = jlist
|
||||
@@ -1631,13 +1635,18 @@ fn detect_errors_in_decorated_statement(
|
||||
}
|
||||
|
||||
// Get the statement we are part of.
|
||||
let st = traversal.parent(dst).as_statement().unwrap();
|
||||
let Kind::Statement(st) = traversal.parent(dst).kind() else {
|
||||
panic!();
|
||||
};
|
||||
|
||||
// Walk up to the job.
|
||||
let job = traversal
|
||||
.parent_nodes()
|
||||
.find_map(|n| n.as_job_pipeline())
|
||||
.expect("Should have found the job");
|
||||
.find_map(|n| match n.kind() {
|
||||
Kind::JobPipeline(job) => Some(job),
|
||||
_ => None,
|
||||
})
|
||||
.expect("should have found the job");
|
||||
|
||||
// Check our pipeline position.
|
||||
let pipe_pos = if job.continuation.is_empty() {
|
||||
@@ -1748,10 +1757,10 @@ fn detect_errors_in_decorated_statement(
|
||||
// loop from the ancestor alone; we need the header. That is, we hit a
|
||||
// block_statement, and have to check its header.
|
||||
let mut found_loop = false;
|
||||
for block in traversal
|
||||
.parent_nodes()
|
||||
.filter_map(|anc| anc.as_block_statement())
|
||||
{
|
||||
for block in traversal.parent_nodes().filter_map(|anc| match anc.kind() {
|
||||
Kind::BlockStatement(block) => Some(block),
|
||||
_ => None,
|
||||
}) {
|
||||
match block.header {
|
||||
ast::BlockStatementHeader::For(_) | ast::BlockStatementHeader::While(_) => {
|
||||
// This is a loop header, so we can break or continue.
|
||||
@@ -1832,7 +1841,7 @@ fn detect_errors_in_block_redirection_list(
|
||||
return false;
|
||||
};
|
||||
let r = first_arg.source_range();
|
||||
if parent.as_brace_statement().is_some() {
|
||||
if let Kind::BraceStatement(_) = parent.kind() {
|
||||
append_syntax_error!(out_errors, r.start(), r.length(), RIGHT_BRACE_ARG_ERR_MSG);
|
||||
} else {
|
||||
append_syntax_error!(out_errors, r.start(), r.length(), END_ARG_ERR_MSG);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// The fish parser. Contains functions for parsing and evaluating code.
|
||||
|
||||
use crate::ast::{self, Ast, Node};
|
||||
use crate::ast::{self, Ast, Kind, Node};
|
||||
use crate::builtins::shared::STATUS_ILLEGAL_CMD;
|
||||
use crate::common::{
|
||||
escape_string, wcs2string, CancelChecker, EscapeFlags, EscapeStringStyle, FilenameRef,
|
||||
@@ -556,7 +556,9 @@ pub fn eval_parsed_source(
|
||||
block_type: BlockType,
|
||||
) -> EvalRes {
|
||||
assert!([BlockType::top, BlockType::subst].contains(&block_type));
|
||||
let job_list = ps.ast.top().as_job_list().unwrap();
|
||||
let Kind::JobList(job_list) = ps.ast.top().kind() else {
|
||||
panic!("Expected a job list");
|
||||
};
|
||||
if !job_list.is_empty() {
|
||||
// Execute the top job list.
|
||||
self.eval_node(ps, job_list, io, job_group, block_type)
|
||||
@@ -734,7 +736,9 @@ pub fn expand_argument_list(
|
||||
|
||||
// Get the root argument list and extract arguments from it.
|
||||
let mut result = vec![];
|
||||
let list = ast.top().as_freestanding_argument_list().unwrap();
|
||||
let Kind::FreestandingArgumentList(list) = ast.top().kind() else {
|
||||
panic!("Expected a freestanding argument list");
|
||||
};
|
||||
for arg in &list.arguments {
|
||||
let arg_src = arg.source(arg_list_src);
|
||||
if matches!(
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
use errno::{errno, Errno};
|
||||
|
||||
use crate::abbrs::abbrs_match;
|
||||
use crate::ast::{is_same_node, Ast};
|
||||
use crate::ast::{is_same_node, Ast, Kind};
|
||||
use crate::builtins::shared::ErrorCode;
|
||||
use crate::builtins::shared::STATUS_CMD_ERROR;
|
||||
use crate::builtins::shared::STATUS_CMD_OK;
|
||||
@@ -5381,10 +5381,10 @@ fn extract_tokens(s: &wstr) -> Vec<PositionedToken> {
|
||||
if !has_cmd_subs {
|
||||
// Common case of no command substitutions in this leaf node.
|
||||
// Check if a node is the command portion of a decorated statement.
|
||||
let is_cmd = traversal
|
||||
.parent(node)
|
||||
.as_decorated_statement()
|
||||
.is_some_and(|stmt| is_same_node(node, &stmt.command));
|
||||
let mut is_cmd = false;
|
||||
if let Kind::DecoratedStatement(stmt) = traversal.parent(node).kind() {
|
||||
is_cmd = is_same_node(node, &stmt.command);
|
||||
}
|
||||
result.push(PositionedToken { range, is_cmd })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::ast::{self, is_same_node, Ast, JobPipeline, Kind, Node, Traversal};
|
||||
use crate::ast::{self, is_same_node, Ast, Castable, JobList, JobPipeline, Kind, Node, Traversal};
|
||||
use crate::common::ScopeGuard;
|
||||
use crate::env::EnvStack;
|
||||
use crate::expand::ExpandFlags;
|
||||
@@ -35,7 +35,10 @@ fn detect_argument_errors(src: &str) -> Result<(), ParserTestErrorBits> {
|
||||
if ast.errored() {
|
||||
return Err(ParserTestErrorBits::ERROR);
|
||||
}
|
||||
let args = &ast.top().as_freestanding_argument_list().unwrap().arguments;
|
||||
let Kind::FreestandingArgumentList(args) = ast.top().kind() else {
|
||||
panic!("Expected free standing argument list");
|
||||
};
|
||||
let args = &args.arguments;
|
||||
let first_arg = args.get(0).expect("Failed to parse an argument");
|
||||
let mut errors = None;
|
||||
parse_util_detect_errors_in_argument(first_arg, first_arg.source(&src), &mut errors)
|
||||
@@ -424,7 +427,7 @@ fn test_1_parse_ll2(src: &wstr) -> Option<(WString, WString, StatementDecoration
|
||||
// Get the statement. Should only have one.
|
||||
let mut statement = None;
|
||||
for n in Traversal::new(ast.top()) {
|
||||
if let Some(tmp) = n.as_decorated_statement() {
|
||||
if let Kind::DecoratedStatement(tmp) = n.kind() {
|
||||
assert!(
|
||||
statement.is_none(),
|
||||
"More than one decorated statement found in '{}'",
|
||||
@@ -786,7 +789,7 @@ fn test_line_counter() {
|
||||
assert_eq!(line_offset, expected);
|
||||
}
|
||||
|
||||
let pipelines: Vec<_> = ps.ast.walk().filter_map(|n| n.as_job_pipeline()).collect();
|
||||
let pipelines: Vec<_> = ps.ast.walk().filter_map(ast::JobPipeline::cast).collect();
|
||||
assert_eq!(pipelines.len(), 3);
|
||||
let src_offsets = [0, 0, 2];
|
||||
assert_eq!(line_counter.source_offset_of_node(), None);
|
||||
@@ -832,7 +835,7 @@ struct TrueSemiAstTester<'a> {
|
||||
impl<'a> TrueSemiAstTester<'a> {
|
||||
const TRUE_SEMI: &'static wstr = L!("true;");
|
||||
fn new(ast: &'a Ast) -> Self {
|
||||
let job_list = ast.top().as_job_list().expect("Expected job_list");
|
||||
let job_list: &JobList = ast.top().cast().unwrap();
|
||||
let job_conjunction = &job_list[0];
|
||||
let job_pipeline = &job_conjunction.job;
|
||||
let variable_assignment_list = &job_pipeline.variables;
|
||||
@@ -894,7 +897,7 @@ fn test_ast() {
|
||||
// Light testing of the AST and traversals.
|
||||
let ast = Ast::parse(TrueSemiAstTester::TRUE_SEMI, ParseTreeFlags::empty(), None);
|
||||
let tester = TrueSemiAstTester::new(&ast);
|
||||
assert!(ast.top().as_job_list().is_some(), "Expected job_list");
|
||||
assert!(ast.top().cast::<JobList>().is_some(), "Expected job_list");
|
||||
|
||||
// Walk the AST and collect all nodes.
|
||||
// See is_same_node comments for why we can't use assert_eq! here.
|
||||
|
||||
Reference in New Issue
Block a user