diff --git a/src/ast.rs b/src/ast.rs index f1490e9e0..4f37e626e 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -25,7 +25,8 @@ }; use crate::wchar::prelude::*; use std::borrow::Cow; -use std::ops::{ControlFlow, Index, IndexMut}; +use std::convert::AsMut; +use std::ops::{ControlFlow, Deref}; /** * A NodeVisitor is something which can visit an AST node. @@ -486,28 +487,6 @@ fn allows_keyword(&self, kw: ParseKeyword) -> bool { } } -// A simple variable-sized array, possibly empty. -pub trait List: Node { - type ContentsNode: Node + Default; - fn contents(&self) -> &[Self::ContentsNode]; - fn contents_mut(&mut self) -> &mut Box<[Self::ContentsNode]>; - /// Return our count. - fn count(&self) -> usize { - self.contents().len() - } - /// Return whether we are empty. - fn is_empty(&self) -> bool { - self.contents().is_empty() - } - /// Iteration support. - fn iter(&self) -> std::slice::Iter { - self.contents().iter() - } - fn get(&self, index: usize) -> Option<&Self::ContentsNode> { - self.contents().get(index) - } -} - /// This is for optional values and for lists. trait CheckParse { /// A true return means we should descend into the production, false means stop. @@ -642,7 +621,7 @@ impl $name { } } -/// Define a node that implements the list trait. +/// Define a list node. macro_rules! define_list_node { ( $name:ident, @@ -650,97 +629,51 @@ macro_rules! define_list_node { $contents:ident ) => { #[derive(Default, Debug)] - pub struct $name { - list_contents: Box<[$contents]>, - } + pub struct $name(Box<[$contents]>); + implement_node!($name, $type); - impl List for $name { - type ContentsNode = $contents; - fn contents(&self) -> &[Self::ContentsNode] { - &self.list_contents - } - fn contents_mut(&mut self) -> &mut Box<[Self::ContentsNode]> { - &mut self.list_contents + + impl Deref for $name { + type Target = Box<[$contents]>; + fn deref(&self) -> &Self::Target { + &self.0 } } + impl<'a> IntoIterator for &'a $name { type Item = &'a $contents; type IntoIter = std::slice::Iter<'a, $contents>; + fn into_iter(self) -> Self::IntoIter { - self.contents().into_iter() + self.0.iter() } } - impl Index for $name { - type Output = <$name as List>::ContentsNode; - fn index(&self, index: usize) -> &Self::Output { - &self.contents()[index] - } - } - impl IndexMut for $name { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.contents_mut()[index] + + impl AsMut> for $name { + fn as_mut(&mut self) -> &mut Box<[$contents]> { + &mut self.0 } } + impl Acceptor for $name { - #[allow(unused_variables)] fn accept<'a>(&'a self, visitor: &mut dyn NodeVisitor<'a>) { - let _ = accept_list_visitor!(Self, accept, visit, self, visitor, $contents); + self.iter().for_each(|item| visitor.visit(item)); } } + impl AcceptorMut for $name { - #[allow(unused_variables)] fn accept_mut(&mut self, visitor: &mut dyn NodeVisitorMut) { visitor.will_visit_fields_of(self); - let flow = - accept_list_visitor!(Self, accept_mut, visit_mut, self, visitor, $contents); + let flow = self + .0 + .iter_mut() + .try_for_each(|item| visitor.visit_mut(item)); visitor.did_visit_fields_of(self, flow); } } }; } -macro_rules! accept_list_visitor { - ( - $Self:ident, - $accept:ident, - $visit:ident, - $self:ident, - $visitor:ident, - $list_element:ident - ) => { - loop { - let mut result = VisitResult::Continue(()); - // list types pretend their child nodes are direct embeddings. - // This isn't used during AST construction because we need to construct the list. - for i in 0..$self.count() { - result = accept_list_visitor_impl!($self, $visitor, $visit, $self[i]); - if result.is_break() { - break; - } - } - break result; - } - }; -} - -macro_rules! accept_list_visitor_impl { - ( - $self:ident, - $visitor:ident, - visit, - $child:expr) => {{ - $visitor.visit(&$child); - VisitResult::Continue(()) - }}; - ( - $self:ident, - $visitor:ident, - visit_mut, - $child:expr) => { - $visitor.visit_mut(&mut $child) - }; -} - /// Implement the acceptor trait for the given branch node. macro_rules! implement_acceptor_for_branch { ( @@ -2734,41 +2667,31 @@ fn visit_mut(&mut self, node: &mut dyn NodeMut) -> VisitResult { // This field is an embedding of an array of (pointers to) ContentsNode. // Parse as many as we can. match node.typ() { - Type::andor_job_list => self.populate_list::( - node.as_mut_andor_job_list().unwrap(), - false, - ), - Type::argument_list => self - .populate_list::(node.as_mut_argument_list().unwrap(), false), - Type::argument_or_redirection_list => self - .populate_list::( - node.as_mut_argument_or_redirection_list().unwrap(), - false, - ), - Type::case_item_list => self.populate_list::( - node.as_mut_case_item_list().unwrap(), - false, - ), - Type::elseif_clause_list => self.populate_list::( - node.as_mut_elseif_clause_list().unwrap(), - false, - ), - Type::job_conjunction_continuation_list => self - .populate_list::( - node.as_mut_job_conjunction_continuation_list().unwrap(), - false, - ), - Type::job_continuation_list => self.populate_list::( - node.as_mut_job_continuation_list().unwrap(), - false, - ), - Type::job_list => { - self.populate_list::(node.as_mut_job_list().unwrap(), false) + Type::andor_job_list => { + self.populate_list(node.as_mut_andor_job_list().unwrap(), false) } - Type::variable_assignment_list => self.populate_list::( - node.as_mut_variable_assignment_list().unwrap(), + Type::argument_list => { + self.populate_list(node.as_mut_argument_list().unwrap(), false) + } + Type::argument_or_redirection_list => self + .populate_list(node.as_mut_argument_or_redirection_list().unwrap(), false), + Type::case_item_list => { + self.populate_list(node.as_mut_case_item_list().unwrap(), false) + } + Type::elseif_clause_list => { + self.populate_list(node.as_mut_elseif_clause_list().unwrap(), false) + } + Type::job_conjunction_continuation_list => self.populate_list( + node.as_mut_job_conjunction_continuation_list().unwrap(), false, ), + Type::job_continuation_list => { + self.populate_list(node.as_mut_job_continuation_list().unwrap(), false) + } + Type::job_list => self.populate_list(node.as_mut_job_list().unwrap(), false), + Type::variable_assignment_list => { + self.populate_list(node.as_mut_variable_assignment_list().unwrap(), false) + } _ => (), } } @@ -3309,11 +3232,14 @@ fn consume_excess_token_generating_error(&mut self) { /// Given that we are a list of type ListNodeType, whose contents type is ContentsNode, /// populate as many elements as we can. /// If exhaust_stream is set, then keep going until we get parse_token_type_t::terminate. - fn populate_list(&mut self, list: &mut ListType, exhaust_stream: bool) + fn populate_list(&mut self, list: &mut ListType, exhaust_stream: bool) where - ::ContentsNode: NodeMut + CheckParse, + ContentsType: NodeMut + CheckParse + Default, + ListType: Node + AsMut>, { - assert!(list.contents().is_empty(), "List is not initially empty"); + let typ = list.typ(); + let list = list.as_mut(); + assert!(list.is_empty(), "List is not initially empty"); // Do not attempt to parse a list if we are unwinding. if self.unwinding { @@ -3327,9 +3253,9 @@ fn populate_list(&mut self, list: &mut ListType, exhaust_stream: "%*sunwinding %ls", self.spaces(), "", - ast_type_to_string(list.typ()) + ast_type_to_string(typ) ); - assert!(list.contents().is_empty(), "Should be an empty list"); + assert!(list.is_empty(), "Should be an empty list"); return; } @@ -3340,7 +3266,7 @@ fn populate_list(&mut self, list: &mut ListType, exhaust_stream: // If we are unwinding, then either we recover or we break the loop, dependent on the // loop type. if self.unwinding { - if !self.list_type_stops_unwind(list.typ()) { + if !self.list_type_stops_unwind(typ) { break; } // We are going to stop unwinding. @@ -3372,10 +3298,10 @@ fn populate_list(&mut self, list: &mut ListType, exhaust_stream: } // Chomp semis and newlines. - self.chomp_extras(list.typ()); + self.chomp_extras(typ); // Now try parsing a node. - if let Some(node) = self.try_parse::() { + if let Some(node) = self.try_parse::() { // #7201: Minimize reallocations of contents vector // Empirically, 99.97% of cases are 16 elements or fewer, // with 75% being empty, so this works out best. @@ -3399,8 +3325,8 @@ fn populate_list(&mut self, list: &mut ListType, exhaust_stream: contents.len() <= u32::MAX.try_into().unwrap(), "Contents size out of bounds" ); - assert!(list.contents().is_empty(), "List should still be empty"); - *list.contents_mut() = contents.into_boxed_slice(); + assert!(list.is_empty(), "List should still be empty"); + *list = contents.into_boxed_slice(); } FLOGF!( @@ -3408,8 +3334,8 @@ fn populate_list(&mut self, list: &mut ListType, exhaust_stream: "%*s%ls size: %lu", self.spaces(), "", - ast_type_to_string(list.typ()), - list.count() + ast_type_to_string(typ), + list.len() ); } diff --git a/src/builtins/fish_indent.rs b/src/builtins/fish_indent.rs index 98db5c4cb..66ea4373b 100644 --- a/src/builtins/fish_indent.rs +++ b/src/builtins/fish_indent.rs @@ -15,9 +15,7 @@ use libc::LC_ALL; use super::prelude::*; -use crate::ast::{ - self, Ast, Category, Leaf, List, Node, NodeVisitor, SourceRangeList, Traversal, Type, -}; +use crate::ast::{self, Ast, Category, Leaf, Node, NodeVisitor, SourceRangeList, Traversal, Type}; use crate::common::{ str2wcstring, unescape_string, wcs2string, UnescapeFlags, UnescapeStringStyle, PROGRAM_NAME, }; @@ -279,10 +277,10 @@ fn compute_preferred_semi_locations(&self) -> Vec { } // If there is no and-or tail then we always use a newline. - if andors.count() > 0 { + if !andors.is_empty() { condition.map(&mut mark_semi_from_input); // Mark all but last of the andor list. - for andor in andors.iter().take(andors.count() - 1) { + for andor in andors.iter().take(andors.len() - 1) { mark_semi_from_input(andor.job.semi_nl.as_ref().unwrap()); } } diff --git a/src/highlight/highlight.rs b/src/highlight/highlight.rs index 06b88a872..2f52fbe5c 100644 --- a/src/highlight/highlight.rs +++ b/src/highlight/highlight.rs @@ -2,8 +2,7 @@ use crate::abbrs::{self, with_abbrs}; use crate::ast::{ self, Argument, Ast, BlockStatement, BlockStatementHeaderVariant, BraceStatement, - DecoratedStatement, Keyword, List, Node, NodeVisitor, Redirection, Token, Type, - VariableAssignment, + DecoratedStatement, Keyword, Node, NodeVisitor, Redirection, Token, Type, VariableAssignment, }; use crate::builtins::shared::builtin_exists; use crate::color::Color; diff --git a/src/parse_execution.rs b/src/parse_execution.rs index 9f6039343..017fc5e20 100644 --- a/src/parse_execution.rs +++ b/src/parse_execution.rs @@ -1,8 +1,8 @@ //! Provides the "linkage" between an ast and actual execution structures (job_t, etc.). use crate::ast::{ - self, unescape_keyword, BlockStatementHeaderVariant, Keyword, Leaf, List, Node, - StatementVariant, Token, + self, unescape_keyword, BlockStatementHeaderVariant, Keyword, Leaf, Node, StatementVariant, + Token, }; use crate::builtins; use crate::builtins::shared::{ diff --git a/src/parse_util.rs b/src/parse_util.rs index fd2eb7a46..de17975be 100644 --- a/src/parse_util.rs +++ b/src/parse_util.rs @@ -1,7 +1,5 @@ //! Various mostly unrelated utility functions related to parsing, loading and evaluating fish code. -use crate::ast::{ - self, is_same_node, Ast, Keyword, Leaf, List, Node, NodeVisitor, Token, Traversal, -}; +use crate::ast::{self, is_same_node, Ast, Keyword, Leaf, Node, NodeVisitor, Token, Traversal}; use crate::builtins::shared::builtin_exists; use crate::common::{ escape_string, unescape_string, valid_var_name, valid_var_name_char, EscapeFlags, diff --git a/src/parser.rs b/src/parser.rs index 0456a7fa9..033216ca5 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,6 +1,6 @@ // The fish parser. Contains functions for parsing and evaluating code. -use crate::ast::{self, Ast, List, Node}; +use crate::ast::{self, Ast, Node}; use crate::builtins::shared::STATUS_ILLEGAL_CMD; use crate::common::{ escape_string, wcs2string, CancelChecker, EscapeFlags, EscapeStringStyle, FilenameRef, diff --git a/src/tests/parser.rs b/src/tests/parser.rs index 9b151f6f4..e8ebab6df 100644 --- a/src/tests/parser.rs +++ b/src/tests/parser.rs @@ -1,4 +1,4 @@ -use crate::ast::{self, is_same_node, Ast, JobPipeline, List, Node, Traversal}; +use crate::ast::{self, is_same_node, Ast, JobPipeline, Node, Traversal}; use crate::common::ScopeGuard; use crate::env::EnvStack; use crate::expand::ExpandFlags;