tamer: Integrate clippy

This invokes clippy as part of `make check` now, which I had previously
avoided doing (I'll elaborate on that below).

This commit represents the changes needed to resolve all the warnings
presented by clippy.  Many changes have been made where I find the lints to
be useful and agreeable, but there are a number of lints, rationalized in
`src/lib.rs`, where I found the lints to be disagreeable.  I have provided
rationale, primarily for those wondering why I desire to deviate from the
default lints, though it does feel backward to rationalize why certain lints
ought to be applied (the reverse should be true).

With that said, this did catch some legitimage issues, and it was also
helpful in getting some older code up-to-date with new language additions
that perhaps I used in new code but hadn't gone back and updated old code
for.  My goal was to get clippy working without errors so that, in the
future, when others get into TAMER and are still getting used to Rust,
clippy is able to help guide them in the right direction.

One of the reasons I went without clippy for so long (though I admittedly
forgot I wasn't using it for a period of time) was because there were a
number of suggestions that I found disagreeable, and I didn't take the time
to go through them and determine what I wanted to follow.  Furthermore, it
was hard to make that judgment when I was new to the language and lacked
the necessary experience to do so.

One thing I would like to comment further on is the use of `format!` with
`expect`, which is also what the diagnostic system convenience methods
do (which clippy does not cover).  Because of all the work I've done trying
to understand Rust and looking at disassemblies and seeing what it
optimizes, I falsely assumed that Rust would convert such things into
conditionals in my otherwise-pure code...but apparently that's not the case,
when `format!` is involved.

I noticed that, after making the suggested fix with `get_ident`, Rust
proceeded to then inline it into each call site and then apply further
optimizations.  It was also previously invoking the thread lock (for the
interner) unconditionally and invoking the `Display` implementation.  That
is not at all what I intended for, despite knowing the eager semantics of
function calls in Rust.

Anyway, possibly more to come on that, I'm just tired of typing and need to
move on.  I'll be returning to investigate further diagnostic messages soon.
main
Mike Gerwitz 2023-01-12 10:46:48 -05:00
parent f1cf35f499
commit e6640c0019
34 changed files with 320 additions and 258 deletions

View File

@ -47,9 +47,12 @@ html-am:
# note that 'cargo check' is something else; see 'cargo --help'
test: check
check-am: check-fmt
check-am: check-lint check-fmt
RUSTFLAGS="$(RUSTFLAGS)" @CARGO@ +@RUST_TC@ @CARGO_FLAGS@ test --quiet @FEATURES@
check-lint:
@CARGO@ +@RUST_TC@ @CARGO_FLAGS@ clippy
check-fmt:
@CARGO@ +@RUST_TC@ @CARGO_FLAGS@ fmt -- --check

View File

@ -30,7 +30,7 @@ pub(crate) use tamer::{
num::Dtype,
parse::util::SPair,
span::UNKNOWN_SPAN,
sym::{GlobalSymbolIntern, SymbolId},
sym::GlobalSymbolIntern,
};
type TestAsg = DefaultAsg;

View File

@ -105,6 +105,13 @@ AS_IF([cargo "+$RUST_TC" $CARGO_DOC_FLAGS doc --help &>/dev/null],
AC_MSG_WARN([missing cargo-doc on $RUST_TC])
AC_MSG_WARN([`make html` will not work])])
AC_MSG_CHECKING([whether cargo-clippy is available on $RUST_TC])
AS_IF([cargo "+$RUST_TC" clippy --help &>/dev/null],
[AC_MSG_RESULT(yes)],
[AC_MSG_RESULT(no)
cargo "+$RUST_TC" clippy --help # run again so user can see output
AC_MSG_ERROR([missing cargo-fmt on $RUST_TC])])
AC_ARG_VAR([FEATURES],
[Cargo features flags for TAMER, comma-delimited])

View File

@ -52,9 +52,9 @@ impl Functor<Span> for Expr {
}
}
impl Into<Span> for &Expr {
fn into(self) -> Span {
self.span()
impl From<&Expr> for Span {
fn from(val: &Expr) -> Self {
val.span()
}
}

View File

@ -317,9 +317,9 @@ impl Asg {
let is_auto_root = kind.is_auto_root();
self.with_ident_lookup(name, |obj| obj.resolve(name.span(), kind, src))
.and_then(|node| {
.map(|node| {
is_auto_root.then(|| self.add_root(node));
Ok(node)
node
})
}

View File

@ -237,7 +237,7 @@ impl Ident {
let err = TransitionError::VirtualOverrideKind(
name,
orig_kind.clone(),
(kind.clone(), span),
(kind, span),
);
return Err((self, err));
@ -792,10 +792,10 @@ impl IdentKind {
/// Whether this identifier should be automatically added as a root when
/// declared.
pub fn is_auto_root(&self) -> bool {
match self {
Self::Meta | Self::Map | Self::RetMap | Self::Worksheet => true,
_ => false,
}
matches!(
self,
Self::Meta | Self::Map | Self::RetMap | Self::Worksheet
)
}
}

View File

@ -176,46 +176,46 @@ impl From<Expr> for Object {
}
}
impl Into<Ident> for Object {
impl From<Object> for Ident {
/// Narrow an object into an [`Ident`],
/// panicing if the object is not of that type.
fn into(self) -> Ident {
match self {
Self::Ident(ident) => ident,
_ => self.narrowing_panic("an identifier"),
}
}
}
impl Into<Expr> for Object {
/// Narrow an object into an [`Expr`],
/// panicing if the object is not of that type.
fn into(self) -> Expr {
match self {
Self::Expr(expr) => expr,
_ => self.narrowing_panic("an expression"),
}
}
}
impl<'a> Into<&'a Ident> for &'a Object {
/// Narrow an object into an [`Ident`],
/// panicing if the object is not of that type.
fn into(self) -> &'a Ident {
match self {
fn from(val: Object) -> Self {
match val {
Object::Ident(ident) => ident,
_ => self.narrowing_panic("an identifier"),
_ => val.narrowing_panic("an identifier"),
}
}
}
impl<'a> Into<&'a Expr> for &'a Object {
impl From<Object> for Expr {
/// Narrow an object into an [`Expr`],
/// panicing if the object is not of that type.
fn into(self) -> &'a Expr {
match self {
fn from(val: Object) -> Self {
match val {
Object::Expr(expr) => expr,
_ => self.narrowing_panic("an expression"),
_ => val.narrowing_panic("an expression"),
}
}
}
impl<'a> From<&'a Object> for &'a Ident {
/// Narrow an object into an [`Ident`],
/// panicing if the object is not of that type.
fn from(val: &'a Object) -> Self {
match val {
Object::Ident(ident) => ident,
_ => val.narrowing_panic("an identifier"),
}
}
}
impl<'a> From<&'a Object> for &'a Expr {
/// Narrow an object into an [`Expr`],
/// panicing if the object is not of that type.
fn from(val: &'a Object) -> Self {
match val {
Object::Expr(expr) => expr,
_ => val.narrowing_panic("an expression"),
}
}
}
@ -265,7 +265,7 @@ pub struct ObjectIndex<O: ObjectKind>(NodeIndex, Span, PhantomData<O>);
// (2022-12-22, Rust 1.68.0-nightly).
impl<O: ObjectKind> Clone for ObjectIndex<O> {
fn clone(&self) -> Self {
Self(self.0.clone(), self.1.clone(), self.2.clone())
Self(self.0, self.1, self.2)
}
}

View File

@ -17,6 +17,10 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// Use your judgment;
// a `match` may be more clear within a given context.
#![allow(clippy::single_match)]
//! This is the TAME compiler.
//!
//! `tamec` compiles source code into object files that are later linked
@ -181,20 +185,19 @@ pub fn main() -> Result<(), UnrecoverableError> {
let args: Vec<String> = env::args().collect();
let program = &args[0];
let opts = get_opts();
let usage = opts.usage(&format!("Usage: {} [OPTIONS] INPUT", program));
let usage = opts.usage(&format!("Usage: {program} [OPTIONS] INPUT"));
match parse_options(opts, args) {
Ok(Command::Compile(src_path, _, dest_path)) => {
let mut reporter = VisualReporter::new(FsSpanResolver);
compile(&src_path, &dest_path, &mut reporter).or_else(
compile(&src_path, &dest_path, &mut reporter).map_err(
|e: UnrecoverableError| {
// Rendering to a string ensures buffering so that we
// don't interleave output between processes.
let report = reporter.render(&e).to_string();
println!(
"{report}\nfatal: failed to compile `{}`",
dest_path
"{report}\nfatal: failed to compile `{dest_path}`",
);
std::process::exit(1);
@ -202,12 +205,12 @@ pub fn main() -> Result<(), UnrecoverableError> {
)
}
Ok(Command::Usage) => {
println!("{}", usage);
println!("{usage}");
std::process::exit(exitcode::OK);
}
Err(e) => {
eprintln!("{}", e);
println!("{}", usage);
eprintln!("{e}");
println!("{usage}");
std::process::exit(exitcode::USAGE);
}
}
@ -263,7 +266,7 @@ fn parse_options(opts: Options, args: Vec<String>) -> Result<Command, Fail> {
let output = match matches.opt_str("o") {
Some(m) => m,
// we check that the extension is "xml" later
None => format!("{}o", input),
None => format!("{input}o"),
};
Ok(Command::Compile(input, emit, output))

View File

@ -57,7 +57,7 @@ pub fn main() -> Result<(), TameldError> {
let program = &args[0];
let opts = get_opts();
let usage =
opts.usage(&format!("Usage: {} [OPTIONS] -o OUTPUT FILE", program));
opts.usage(&format!("Usage: {program} [OPTIONS] -o OUTPUT FILE"));
let mut reporter = VisualReporter::new(FsSpanResolver);
@ -66,23 +66,23 @@ pub fn main() -> Result<(), TameldError> {
Emit::Xmle => poc::xmle(&input, &output),
Emit::Graphml => poc::graphml(&input, &output),
}
.or_else(|e| {
.map_err(|e| {
// POC: Rendering to a string ensures buffering so that we don't
// interleave output between processes,
// but we ought to reuse a buffer when we support multiple
// errors.
let report = reporter.render(&e).to_string();
println!("{report}\nfatal: failed to link `{}`", output);
println!("{report}\nfatal: failed to link `{output}`");
std::process::exit(1);
}),
Ok(Command::Usage) => {
println!("{}", usage);
println!("{usage}");
std::process::exit(exitcode::OK);
}
Err(e) => {
eprintln!("{}", e);
println!("{}", usage);
eprintln!("{e}");
println!("{usage}");
std::process::exit(exitcode::USAGE);
}
}
@ -139,7 +139,7 @@ fn parse_options(opts: Options, args: Vec<String>) -> Result<Command, Fail> {
Some(m) => match &m[..] {
"xmle" => Emit::Xmle,
"graphml" => Emit::Graphml,
em => return Err(Fail::ArgumentMissing(format!("--emit {}", em))),
em => return Err(Fail::ArgumentMissing(format!("--emit {em}"))),
},
None => Emit::Xmle,
};

View File

@ -69,8 +69,8 @@ where
/// Panics
/// ======
/// Causes a panic on failure.
fn expect_from(value: T, msg: &str) -> T {
T::try_from(value).diagnostic_expect(vec![], msg)
fn expect_from(value: T, msg: &str) -> Self {
Self::try_from(value).diagnostic_expect(vec![], msg)
}
/// Attempt to convert and unwrap `value` using `T::try_from`.
@ -78,8 +78,8 @@ where
/// Panics
/// ======
/// Causes a panic on failure.
fn unwrap_from(value: T) -> T {
T::try_from(value).diagnostic_unwrap(vec![])
fn unwrap_from(value: T) -> Self {
Self::try_from(value).diagnostic_unwrap(vec![])
}
}

View File

@ -127,14 +127,14 @@ macro_rules! diagnostic_panic {
};
(@$macro:ident!, $desc_data:expr, $($panic_args:tt)*) => {{
use crate::diagnose::Reporter;
use $crate::diagnose::Reporter;
let mut reporter = crate::diagnose::panic::PanicReporter::new(
let mut reporter = $crate::diagnose::panic::PanicReporter::new(
Default::default()
);
let summary = format!($($panic_args)*);
let desc = crate::diagnose::panic::DiagnosticDesc(
let desc = $crate::diagnose::panic::DiagnosticDesc(
summary,
std::cell::Cell::new($desc_data),
);
@ -211,8 +211,7 @@ pub trait DiagnosticPanic {
/// Panics if the inner value is not available.
/// For a custom message,
/// use [`DiagnosticPanic::diagnostic_expect`].
fn diagnostic_unwrap<'a>(self, desc: Vec<AnnotatedSpan<'a>>)
-> Self::Inner;
fn diagnostic_unwrap(self, desc: Vec<AnnotatedSpan>) -> Self::Inner;
/// Attempt to return the inner value,
/// consuming `self`.
@ -223,9 +222,9 @@ pub trait DiagnosticPanic {
///
/// # Panics
/// Panics if the inner value is not available with a custom `msg`.
fn diagnostic_expect<'a>(
fn diagnostic_expect(
self,
desc: Vec<AnnotatedSpan<'a>>,
desc: Vec<AnnotatedSpan>,
msg: &str,
) -> Self::Inner;
}
@ -233,10 +232,7 @@ pub trait DiagnosticPanic {
impl<T> DiagnosticPanic for Option<T> {
type Inner = T;
fn diagnostic_unwrap<'a>(
self,
desc: Vec<AnnotatedSpan<'a>>,
) -> Self::Inner {
fn diagnostic_unwrap(self, desc: Vec<AnnotatedSpan>) -> Self::Inner {
match self {
Some(val) => val,
// Same message as `Option::unwrap`
@ -247,9 +243,9 @@ impl<T> DiagnosticPanic for Option<T> {
}
}
fn diagnostic_expect<'a>(
fn diagnostic_expect(
self,
desc: Vec<AnnotatedSpan<'a>>,
desc: Vec<AnnotatedSpan>,
msg: &str,
) -> Self::Inner {
match self {
@ -265,10 +261,7 @@ where
{
type Inner = T;
fn diagnostic_unwrap<'a>(
self,
desc: Vec<AnnotatedSpan<'a>>,
) -> Self::Inner {
fn diagnostic_unwrap(self, desc: Vec<AnnotatedSpan>) -> Self::Inner {
match self {
Ok(val) => val,
// Same message as `Result::unwrap`
@ -279,9 +272,9 @@ where
}
}
fn diagnostic_expect<'a>(
fn diagnostic_expect(
self,
desc: Vec<AnnotatedSpan<'a>>,
desc: Vec<AnnotatedSpan>,
msg: &str,
) -> Self::Inner {
match self {

View File

@ -149,10 +149,10 @@ impl<R: SpanResolver> Reporter for VisualReporter<R> {
/// allocation for a span that was just processed and whose section will
/// be squashed anyway
/// (see [`Section::maybe_squash_into`]).
fn describe_resolved<'d, D, F>(
fn describe_resolved<D, F>(
mut resolve: F,
diagnostic: &'d D,
) -> impl Iterator<Item = MaybeResolvedSpan<'d, ResolvedSpan>>
diagnostic: &D,
) -> impl Iterator<Item = MaybeResolvedSpan<ResolvedSpan>>
where
D: Diagnostic,
F: FnMut(Span) -> Result<ResolvedSpan, SpanResolverError>,
@ -387,7 +387,7 @@ struct Section<'d> {
line_max: NonZeroU32,
}
impl<'s, 'd> Section<'d> {
impl<'d> Section<'d> {
/// Create a new section from a resolved span.
fn new_resolved<S: ResolvedSpanData>(
rspan: S,
@ -552,9 +552,8 @@ impl<'s, 'd> Section<'d> {
// Padding is added conservatively during generation,
// which may lead to adjacent padding for multi-line spans.
// That padding can be merged into a single line.
self.body.dedup_by(|a, b| match (a, b) {
(SourceLinePadding, SourceLinePadding) => true,
_ => false,
self.body.dedup_by(|a, b| {
matches!((a, b), (SourceLinePadding, SourceLinePadding),)
});
// Padding at the very end of the section is not desirable since the
@ -565,7 +564,7 @@ impl<'s, 'd> Section<'d> {
}
}
impl<'d, 'a, S> From<MaybeResolvedSpan<'d, S>> for Section<'d>
impl<'d, S> From<MaybeResolvedSpan<'d, S>> for Section<'d>
where
S: ResolvedSpanData,
{
@ -692,7 +691,7 @@ enum HeadingColNum {
impl Display for HeadingColNum {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Resolved(col) => write!(f, ":{}", col),
Self::Resolved(col) => write!(f, ":{col}"),
// The column is unavailable,
// which means that the line must have contained invalid UTF-8.

View File

@ -290,7 +290,7 @@ impl Display for SourceLine {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let text = &self.text;
let disp = if text.chars().last() == Some('\n') {
let disp = if text.ends_with('\n') {
&text[0..text.len() - 1]
} else {
&text[..]
@ -491,7 +491,7 @@ impl LineBytes {
fn as_str(&self) -> Result<&str, Utf8Error> {
match self {
Self::Eof => Ok(&""),
Self::Eof => Ok(""),
Self::WithNewline(buf)
| Self::WithoutNewline(buf)
| Self::WithEof(buf) => std::str::from_utf8(buf),
@ -565,10 +565,7 @@ impl Line {
/// Line buffer as a UTF-8 slice.
fn line_as_str(&self) -> Result<&str, Utf8Error> {
self.bytes
.as_ref()
.map(LineBytes::as_str)
.unwrap_or(Ok(&""))
self.bytes.as_ref().map(LineBytes::as_str).unwrap_or(Ok(""))
}
/// Produce formatted output for a line containing invalid UTF-8 data.
@ -691,8 +688,8 @@ impl Line {
// we know specifically what use cases we'll be optimizing for.
let (start, end) = widths.fold((1, 0), |(start, end), (i, width)| {
(
(i < rel_start).then(|| start + width).unwrap_or(start),
(i <= rel_end).then(|| end + width).unwrap_or(end),
(i < rel_start).then_some(start + width).unwrap_or(start),
(i <= rel_end).then_some(end + width).unwrap_or(end),
)
});

View File

@ -225,7 +225,7 @@ pub trait ListDisplayWrapper {
// This can be further abstracted away using the above primitives,
// if ever we have a use.
for next in list.into_iter().enumerate() {
for next in list.iter().enumerate() {
match next {
(i, x) => Self::fmt_nth(lasti, i, x, f)?,
};

View File

@ -189,9 +189,9 @@ where
return Ok(VisitOnceFile::Visited);
}
VisitOnceFile::open(ostr).and_then(|file| {
VisitOnceFile::open(ostr).map(|file| {
self.visited.insert(ostr.to_os_string());
Ok(file)
file
})
}
}

View File

@ -96,7 +96,7 @@ impl<'a, I: ResultIterator<T, EI>, T, EI> TripIter<'a, I, T, EI> {
// after the caller is done with it,
// to check to see if we have tripped and force the caller to
// consider the error.
biter.state.map_err(EI::into).and_then(|_| fret)
biter.state.map_err(EI::into).and(fret)
}
/// Whether the iterator has been tripped by an [`Err`] on the

View File

@ -58,7 +58,7 @@ use std::{
fmt::{self, Display},
fs,
io::{self, BufReader, BufWriter, Write},
path::{Path, PathBuf},
path::Path,
};
type LinkerAsg = DefaultAsg;
@ -146,7 +146,7 @@ pub fn graphml(package_path: &str, output: &str) -> Result<(), TameldError> {
Ok(())
}
fn load_xmlo<'a, P: AsRef<Path>, S: Escaper>(
fn load_xmlo<P: AsRef<Path>, S: Escaper>(
path_str: P,
fs: &mut VisitOnceFilesystem<FsCanonicalizer, FxBuildHasher>,
asg: Asg,
@ -201,15 +201,14 @@ fn load_xmlo<'a, P: AsRef<Path>, S: Escaper>(
})
})?;
let mut dir: PathBuf = path.clone();
let mut dir = path;
dir.pop();
let found = state.found.take().unwrap_or_default();
for relpath in found.iter() {
let mut path_buf = dir.clone();
let str: &str = &relpath.lookup_str();
path_buf.push(str);
path_buf.push(relpath.lookup_str());
path_buf.set_extension("xmlo");
(asg, state) = load_xmlo(path_buf, fs, asg, escaper, state)?;

View File

@ -88,7 +88,7 @@ where
Ok(dest)
}
fn get_ident<'a, S>(depgraph: &'a Asg, name: S) -> &'a Ident
fn get_ident<S>(depgraph: &Asg, name: S) -> &Ident
where
S: Into<SymbolId>,
{
@ -97,10 +97,9 @@ where
depgraph
.lookup(sym)
.and_then(|id| depgraph.get(id))
.expect(&format!(
"missing internal identifier: {}",
sym.lookup_str()
))
.unwrap_or_else(|| {
panic!("missing internal identifier: {}", sym.lookup_str())
})
}
/// Check graph for cycles
@ -235,7 +234,7 @@ impl Diagnostic for SortError {
// error per cycle.
Cycles(cycles) => cycles
.iter()
.map(|cycle| {
.flat_map(|cycle| {
let n = cycle.len();
let ident = cycle.last().unwrap();
@ -273,7 +272,6 @@ impl Diagnostic for SortError {
])
.collect::<Vec<_>>()
})
.flatten()
.collect(),
}
}

View File

@ -125,7 +125,7 @@ impl<'a> DepListIter<'a> {
obj,
),
}
}).and_then(|(name, kind, src)| {
}).map(|(name, kind, src)| {
self.toks.push(Token::Close(None, CloseSpan::empty(LSPAN)));
self.toks_push_attr(QN_DESC, src.desc);
@ -149,7 +149,7 @@ impl<'a> DepListIter<'a> {
self.toks_push_attr(QN_NAME, Some(name.symbol()));
self.toks_push_obj_attrs(kind);
Some(Token::Open(QN_P_SYM, OpenSpan::without_name_span(LSPAN)))
Token::Open(QN_P_SYM, OpenSpan::without_name_span(LSPAN))
})
}
@ -228,25 +228,23 @@ struct MapFromsIter {
impl MapFromsIter {
#[inline]
fn new<'a>(iter: hash_set::IntoIter<SymbolId>) -> Self {
let iter = Self {
fn new(iter: hash_set::IntoIter<SymbolId>) -> Self {
Self {
iter,
// Most of the time we have a single `from` (4 tokens).
toks: ArrayVec::new(),
};
iter
}
}
#[inline]
fn refill_toks(&mut self) -> Option<Token> {
self.iter.next().and_then(|from| {
self.iter.next().map(|from| {
self.toks.push(Token::Close(None, CloseSpan::empty(LSPAN)));
self.toks.push(Token::AttrValue(from, LSPAN));
self.toks.push(Token::AttrName(QN_NAME, LSPAN));
Some(Token::Open(QN_L_FROM, OpenSpan::without_name_span(LSPAN)))
Token::Open(QN_L_FROM, OpenSpan::without_name_span(LSPAN))
})
}
}
@ -292,6 +290,7 @@ impl Iterator for FragmentIter {
/// having to resort to dynamic dispatch,
/// since this iterator will receive over a million calls on larger
/// programs (and hundreds of thousands on smaller).
#[allow(clippy::type_complexity)] // more clear as one type
pub struct LowerIter<'a>(
ElemWrapIter<
Chain<

View File

@ -80,6 +80,69 @@
#![allow(rustdoc::private_intra_doc_links)]
// For sym::prefill recursive macro `static_symbols!`.
#![recursion_limit = "512"]
//
// Clippy Lints
// ============
// This section contains rationale for deviating from standard lints.
// This reasoning applies to TAMER and may not be appropriate for other
// projects,
// or even other teams.
//
// These are presented in no particular order,
// but if you do rearrange them,
// be mindful of the comments that may reference preceding lints.
//
// Choosing not to inline format args sometimes adds to the clarity of the
// format string by emphasizing structure more concisely.
// Use your judgment.
#![allow(clippy::uninlined_format_args)]
// The rationale for this lint is that it may catch accidental semicolons,
// but the type system is plenty sufficient to catch unit types that were
// unintended.
#![allow(clippy::unit_arg)]
// Use your judgment;
// a `match` may be more clear within a given context.
// Or may simply be personal preference.
#![allow(clippy::single_match)]
// Same rationale as the previous,
// but additionally this clearly scopes pattern bindings to an inner
// block,
// which is not the case with a sibling `let` binding.
// This pattern was originally taken from `rustc` itself.
#![allow(clippy::match_single_binding)]
// This lint also seems to apply when dereferencing a double reference,
// for which the use of `cloned` would be far more confusing.
#![allow(clippy::map_clone)]
// Perhaps `is_empty` does not make sense for that particular trait/impl?
// We don't need a linter to guide these abstractions;
// an `is-empty` method will be added if it is needed and actually
// utilized.
#![allow(clippy::len_without_is_empty)]
// This is another case of a linter trying to guide abstractions.
// `Default` will be implemented if it both makes sense and is needed,
// not needlessly,
// as TAMER is not a library and its uses are statically known.
// Furthermore,
// `Default` is sometimes explicitly omitted to disallow automatic
// construction in various contexts.
#![allow(clippy::new_without_default)]
// When surrounding code uses `write!`,
// switching to `writeln!` for the last line adds an inconsistency that
// can make the code less clear,
// or possibly even introduce bugs by having the reader miss the change
// in pattern.
// `writeln!` also gives the impression that it's writing a line,
// when in actuality it may simply be appending to a partially-written
// line,
// making it feel like an inappropriate abstraction.
// Choose the abstraction that's most appropriate within a given context.
#![allow(clippy::write_with_newline)]
// Calling this "obfuscation" is hyperbole.
// Furthermore,
// `if` statements are expanded by `rustfmt` into something with a
// significantly larger footprint than this form,
// so this lint does _not_ suggest a suitable replacement.
#![allow(clippy::obfuscated_if_else)]
pub mod global;

View File

@ -246,9 +246,9 @@ impl Display for Nir {
}
}
impl<E> Into<Result<Nir, E>> for Nir {
fn into(self) -> Result<Nir, E> {
Ok(self)
impl<E> From<Nir> for Result<Nir, E> {
fn from(val: Nir) -> Self {
Ok(val)
}
}

View File

@ -81,9 +81,9 @@ impl Dtype {
}
}
impl Into<SymbolId> for Dtype {
fn into(self) -> SymbolId {
self.as_sym()
impl From<Dtype> for SymbolId {
fn from(val: Dtype) -> Self {
val.as_sym()
}
}

View File

@ -182,7 +182,7 @@ impl ParseState for XmloToAir {
// and spans are not retained.
(&attrs)
.try_into()
.and_then(|kindval| {
.map(|kindval| {
let mut src: Source = attrs.into();
// This used to come from SymAttrs in the old XmloReader.
@ -197,13 +197,13 @@ impl ParseState for XmloToAir {
}
if extern_ {
Ok(ParseStatus::Object(Air::IdentExternDecl(
ParseStatus::Object(Air::IdentExternDecl(
name, kindval, src,
)))
))
} else {
Ok(ParseStatus::Object(Air::IdentDecl(
ParseStatus::Object(Air::IdentDecl(
name, kindval, src,
)))
))
}
})
.transition(Package(pkg_name))

View File

@ -124,10 +124,10 @@ impl From<(SymbolId, Span)> for SPair {
}
}
impl Into<(SymbolId, Span)> for SPair {
fn into(self) -> (SymbolId, Span) {
match self {
Self(sym, span) => (sym, span),
impl From<SPair> for (SymbolId, Span) {
fn from(val: SPair) -> Self {
match val {
SPair(sym, span) => (sym, span),
}
}
}

View File

@ -501,9 +501,9 @@ impl Span {
}
}
impl Into<u64> for Span {
fn into(self) -> u64 {
self.as_u64()
impl From<Span> for u64 {
fn from(val: Span) -> Self {
val.as_u64()
}
}
@ -643,7 +643,7 @@ impl Display for Context {
impl AsRef<Path> for Context {
fn as_ref(&self) -> &Path {
&Path::new(self.0.lookup_str())
Path::new(self.0.lookup_str())
}
}

View File

@ -34,8 +34,10 @@ use crate::global;
///
/// This symbol id is allocated at compile-time.
///
/// _All objects implementing this trait must have the same byte
/// representation as its inner [`SymbolId`]_.
/// Safety
/// ======
/// All objects implementing this trait must have the same byte
/// representation as its inner [`SymbolId`].
pub unsafe trait StaticSymbolId<Ix: SymbolIndexSize = global::ProgSymSize>:
private::Sealed
{

View File

@ -125,6 +125,11 @@ pub trait SymbolIndexSize:
/// Construct a new non-zero value from the provided primitive value
/// without checking whether the value is non-zero.
///
/// Safety
/// ======
/// This method must only be used in a context where a non-zero check
/// has already been performed.
unsafe fn new_unchecked(n: Self) -> Self::NonZero;
/// Convert primitive value into a [`usize`].
@ -253,6 +258,7 @@ pub trait GlobalSymbolResolve {
/// when an index lookup fails;
/// [`Option`] would have been used,
/// but [`Result`] is idiomatic with `try_*` functions.
#[allow(clippy::result_unit_err)]
fn try_lookup_str(&self) -> Result<&'static str, ()>;
}
@ -295,7 +301,7 @@ impl<Ix: SymbolIndexSize> Debug for SymbolId<Ix> {
f,
"SymbolId({} \"{}\")",
self.0.into(),
self.try_lookup_str().unwrap_or(&"<#!UNKNOWN_SYMBOL>")
self.try_lookup_str().unwrap_or("<#!UNKNOWN_SYMBOL>")
)
}
}

View File

@ -287,9 +287,11 @@ impl From<quick_xml::Error> for QuickXmlError {
}
}
impl Into<quick_xml::Error> for QuickXmlError {
fn into(self) -> quick_xml::Error {
self.0
impl From<QuickXmlError> for quick_xml::Error {
fn from(e: QuickXmlError) -> Self {
match e {
QuickXmlError(e) => e,
}
}
}

View File

@ -492,7 +492,7 @@ where
}
// Final closing tag (for root node) completes the document.
(..) if stack.len() == 0 => Transition(Done).ok(
(..) if stack.is_empty() => Transition(Done).ok(
XirfToken::Close(close_oqname, close_span, Depth(0)),
),

View File

@ -107,8 +107,8 @@ macro_rules! attr_parse_stream {
// TODO: This can be extracted out of the macro.
#[derive(Debug, PartialEq, Eq)]
$vis enum $state_name {
Parsing(crate::xir::QName, crate::xir::OpenSpan),
Done(crate::xir::QName, crate::xir::OpenSpan),
Parsing($crate::xir::QName, $crate::xir::OpenSpan),
Done($crate::xir::QName, $crate::xir::OpenSpan),
}
/// Intermediate state of parser as fields are aggregated.
@ -118,24 +118,24 @@ macro_rules! attr_parse_stream {
#[derive(Debug, PartialEq, Eq, Default)]
$vis struct [<$state_name Fields>];
impl crate::xir::parse::AttrParseState for $state_name {
impl $crate::xir::parse::AttrParseState for $state_name {
type ValueError = $evty;
type Fields = [<$state_name Fields>];
fn with_element(
ele: crate::xir::QName,
span: crate::xir::OpenSpan
ele: $crate::xir::QName,
span: $crate::xir::OpenSpan
) -> Self {
Self::Parsing(ele, span)
}
fn element_name(&self) -> crate::xir::QName {
fn element_name(&self) -> $crate::xir::QName {
match self {
Self::Parsing(qname, _) | Self::Done(qname, _) => *qname,
}
}
fn element_span(&self) -> crate::xir::OpenSpan {
fn element_span(&self) -> $crate::xir::OpenSpan {
match self {
Self::Parsing(_, span) | Self::Done(_, span) => *span,
}
@ -148,8 +148,8 @@ macro_rules! attr_parse_stream {
///
/// [`ParseError`]: crate::parse::ParseError
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use crate::fmt::{DisplayWrapper, TtQuote};
use crate::xir::parse::AttrParseState;
use $crate::fmt::{DisplayWrapper, TtQuote};
use $crate::xir::parse::AttrParseState;
write!(
f,
@ -159,26 +159,26 @@ macro_rules! attr_parse_stream {
}
}
impl crate::parse::ParseState for $state_name {
type Token = crate::xir::flat::XirfToken<
crate::xir::flat::RefinedText
impl $crate::parse::ParseState for $state_name {
type Token = $crate::xir::flat::XirfToken<
$crate::xir::flat::RefinedText
>;
type Object = $objty;
type Error = crate::xir::parse::AttrParseError<Self>;
type Error = $crate::xir::parse::AttrParseError<Self>;
fn parse_token(
#[allow(unused_mut)]
mut self,
tok: Self::Token,
_ctx: &mut Self::Context,
) -> crate::parse::TransitionResult<Self> {
use crate::parse::Transition;
use crate::xir::{
) -> $crate::parse::TransitionResult<Self> {
use $crate::parse::Transition;
use $crate::xir::{
flat,
parse::{AttrParseError, AttrParseState}
};
#[allow(unused_imports)] // unused if no attrs
use crate::{
use $crate::{
parse::{Transitionable, ParseStatus, util::SPair},
xir::attr::{Attr, AttrSpan}
};

View File

@ -360,11 +360,11 @@ macro_rules! ele_parse {
// No explicit Close mapping defaults to doing nothing at all
// (so yield Incomplete).
(@!ele_close) => {
crate::parse::ParseStatus::Incomplete
$crate::parse::ParseStatus::Incomplete
};
(@!ele_close $close:expr) => {
crate::parse::ParseStatus::Object($close)
$crate::parse::ParseStatus::Object($close)
};
// Delegation when the destination type is `()`,
@ -438,7 +438,7 @@ macro_rules! ele_parse {
)*
}
) => { paste::paste! {
crate::attr_parse_stream! {
$crate::attr_parse_stream! {
/// Attribute parser for
#[doc=concat!("[`", stringify!($nt), "`].")]
type Object = $objty;
@ -459,18 +459,18 @@ macro_rules! ele_parse {
$(
$ntref(
(
crate::xir::QName,
crate::xir::OpenSpan,
crate::xir::flat::Depth
$crate::xir::QName,
$crate::xir::OpenSpan,
$crate::xir::flat::Depth
),
),
)*
ExpectClose_(
(
crate::xir::QName,
crate::xir::OpenSpan,
crate::xir::flat::Depth
$crate::xir::QName,
$crate::xir::OpenSpan,
$crate::xir::flat::Depth
),
),
}
@ -479,20 +479,20 @@ macro_rules! ele_parse {
///
#[doc=concat!("Parser for element [`", stringify!($qname), "`].")]
#[derive(Debug, PartialEq, Eq, Default)]
$vis struct $nt(crate::xir::parse::NtState<$nt>);
$vis struct $nt($crate::xir::parse::NtState<$nt>);
impl $nt {
/// A default state that cannot be preempted by the superstate.
#[allow(dead_code)] // not utilized for every NT
fn non_preemptable() -> Self {
Self(crate::xir::parse::NtState::NonPreemptableExpecting)
Self($crate::xir::parse::NtState::NonPreemptableExpecting)
}
/// Whether the given QName would be matched by any of the
/// parsers associated with this type.
#[inline]
fn matches(qname: crate::xir::QName) -> bool {
<Self as crate::xir::parse::Nt>::matcher().matches(qname)
fn matches(qname: $crate::xir::QName) -> bool {
<Self as $crate::xir::parse::Nt>::matcher().matches(qname)
}
/// Number of
@ -521,7 +521,7 @@ macro_rules! ele_parse {
i: &mut usize,
f: &mut std::fmt::Formatter
) -> std::fmt::Result {
use crate::{
use $crate::{
fmt::ListDisplayWrapper,
xir::{fmt::EleSumList, parse::Nt},
};
@ -550,7 +550,7 @@ macro_rules! ele_parse {
#[allow(dead_code)] // used only when there are child NTs
/// Whether the current state represents the last child NT.
fn is_last_nt(&self) -> bool {
use crate::xir::parse::NtState::*;
use $crate::xir::parse::NtState::*;
let Self(st) = self;
@ -572,13 +572,13 @@ macro_rules! ele_parse {
}
}
impl crate::xir::parse::Nt for $nt {
impl $crate::xir::parse::Nt for $nt {
type AttrState = [<$nt AttrState_>];
type ChildNt = [<$nt ChildNt_>];
#[inline]
fn matcher() -> crate::xir::parse::NodeMatcher {
crate::xir::parse::NodeMatcher::from($qname)
fn matcher() -> $crate::xir::parse::NodeMatcher {
$crate::xir::parse::NodeMatcher::from($qname)
}
}
@ -590,13 +590,13 @@ macro_rules! ele_parse {
}
}
impl crate::parse::ParseState for $nt {
type Token = crate::xir::flat::XirfToken<
crate::xir::flat::RefinedText
impl $crate::parse::ParseState for $nt {
type Token = $crate::xir::flat::XirfToken<
$crate::xir::flat::RefinedText
>;
type Object = $objty;
type Error = crate::xir::parse::NtError<$nt>;
type Context = crate::xir::parse::SuperStateContext<Self::Super>;
type Error = $crate::xir::parse::NtError<$nt>;
type Context = $crate::xir::parse::SuperStateContext<Self::Super>;
type Super = $super;
fn parse_token(
@ -604,8 +604,8 @@ macro_rules! ele_parse {
tok: Self::Token,
#[allow(unused_variables)] // used only if child NTs
stack: &mut Self::Context,
) -> crate::parse::TransitionResult<Self::Super> {
use crate::{
) -> $crate::parse::TransitionResult<Self::Super> {
use $crate::{
parse::{Transition, Transitionable},
xir::{
EleSpan,
@ -710,7 +710,7 @@ macro_rules! ele_parse {
(Attrs(meta, sa), tok) => {
sa.delegate::<Self, _>(
tok,
crate::parse::EmptyContext,
$crate::parse::EmptyContext,
|sa| Transition(Self(Attrs(meta, sa))),
|| Transition(Self(Jmp($ntfirst(meta)))),
)
@ -823,7 +823,7 @@ macro_rules! ele_parse {
Jmp([<$nt ChildNt_>]::ExpectClose_(meta @ (qname, otspan, _))),
unexpected_tok
) => {
use crate::parse::Token;
use $crate::parse::Token;
Transition(Self(
CloseRecoverIgnore(meta, unexpected_tok.span())
)).err(
@ -855,7 +855,7 @@ macro_rules! ele_parse {
}
fn is_accepting(&self, _: &Self::Context) -> bool {
use crate::xir::parse::NtState::*;
use $crate::xir::parse::NtState::*;
matches!(*self, Self(Closed(..) | RecoverEleIgnoreClosed(..)))
}
}
@ -872,11 +872,11 @@ macro_rules! ele_parse {
"."
)]
#[derive(Debug, PartialEq, Eq, Default)]
$vis struct $nt(crate::xir::parse::SumNtState<$nt>);
$vis struct $nt($crate::xir::parse::SumNtState<$nt>);
impl $nt {
fn non_preemptable() -> Self {
Self(crate::xir::parse::SumNtState::NonPreemptableExpecting)
Self($crate::xir::parse::SumNtState::NonPreemptableExpecting)
}
// Whether the given QName would be matched by any of the
@ -894,7 +894,7 @@ macro_rules! ele_parse {
// constant.
// Let a profiler and disassembly guide you.
#[allow(dead_code)] // used by superstate
fn matches(qname: crate::xir::QName) -> bool {
fn matches(qname: $crate::xir::QName) -> bool {
// If we used an array or a trait,
// then we'd need everything to be a similar type;
// this allows for _any_ type provided that it expands
@ -953,7 +953,7 @@ macro_rules! ele_parse {
}
}
impl crate::xir::parse::SumNt for $nt {
impl $crate::xir::parse::SumNt for $nt {
/// Begin formatting using [`Self::fmt_matches`].
///
/// This provides the initial values for the function.
@ -970,21 +970,21 @@ macro_rules! ele_parse {
}
}
impl crate::parse::ParseState for $nt {
type Token = crate::xir::flat::XirfToken<
crate::xir::flat::RefinedText
impl $crate::parse::ParseState for $nt {
type Token = $crate::xir::flat::XirfToken<
$crate::xir::flat::RefinedText
>;
type Object = $objty;
type Error = crate::xir::parse::SumNtError<$nt>;
type Context = crate::xir::parse::SuperStateContext<Self::Super>;
type Error = $crate::xir::parse::SumNtError<$nt>;
type Context = $crate::xir::parse::SuperStateContext<Self::Super>;
type Super = $super;
fn parse_token(
self,
tok: Self::Token,
stack: &mut Self::Context,
) -> crate::parse::TransitionResult<Self::Super> {
use crate::{
) -> $crate::parse::TransitionResult<Self::Super> {
use $crate::{
parse::Transition,
xir::{
flat::XirfToken,
@ -1089,7 +1089,7 @@ macro_rules! ele_parse {
}
fn is_accepting(&self, _: &Self::Context) -> bool {
use crate::xir::parse::SumNtState;
use $crate::xir::parse::SumNtState;
matches!(self, Self(SumNtState::Expecting))
}
}
@ -1195,15 +1195,15 @@ macro_rules! ele_parse {
#[derive(Debug, PartialEq)]
$vis enum [<$super Error_>] {
$(
$nt(<$nt as crate::parse::ParseState>::Error),
$nt(<$nt as $crate::parse::ParseState>::Error),
)*
}
$(
impl From<<$nt as crate::parse::ParseState>::Error>
impl From<<$nt as $crate::parse::ParseState>::Error>
for [<$super Error_>]
{
fn from(e: <$nt as crate::parse::ParseState>::Error) -> Self {
fn from(e: <$nt as $crate::parse::ParseState>::Error) -> Self {
[<$super Error_>]::$nt(e)
}
}
@ -1226,8 +1226,8 @@ macro_rules! ele_parse {
}
}
impl crate::diagnose::Diagnostic for [<$super Error_>] {
fn describe(&self) -> Vec<crate::diagnose::AnnotatedSpan> {
impl $crate::diagnose::Diagnostic for [<$super Error_>] {
fn describe(&self) -> Vec<$crate::diagnose::AnnotatedSpan> {
match self {
$(
Self::$nt(e) => e.describe(),
@ -1236,27 +1236,27 @@ macro_rules! ele_parse {
}
}
impl crate::parse::ParseState for $super {
type Token = crate::xir::flat::XirfToken<
crate::xir::flat::RefinedText
impl $crate::parse::ParseState for $super {
type Token = $crate::xir::flat::XirfToken<
$crate::xir::flat::RefinedText
>;
type Object = $objty;
type Error = [<$super Error_>];
type Context = crate::xir::parse::SuperStateContext<Self>;
type Context = $crate::xir::parse::SuperStateContext<Self>;
fn parse_token(
self,
tok: Self::Token,
stack: &mut Self::Context,
) -> crate::parse::TransitionResult<Self> {
use crate::{
) -> $crate::parse::TransitionResult<Self> {
use $crate::{
parse::Transition,
xir::flat::{XirfToken, RefinedText},
};
// Used only by _some_ expansions.
#[allow(unused_imports)]
use crate::xir::flat::Text;
use $crate::xir::flat::Text;
match (self, tok) {
// [super] {
@ -1365,9 +1365,9 @@ macro_rules! ele_parse {
/// [`ParseState`]: crate::parse::ParseState
fn is_inner_accepting(
&self,
ctx: &<Self as crate::parse::ParseState>::Context
ctx: &<Self as $crate::parse::ParseState>::Context
) -> bool {
use crate::parse::ParseState;
use $crate::parse::ParseState;
match self {
$(
@ -1410,7 +1410,7 @@ macro_rules! ele_parse {
}
}
impl crate::xir::parse::SuperState for $super {}
impl $crate::xir::parse::SuperState for $super {}
}};
(@!ntfirst_init $super:ident, $ntfirst:ident $($nt:ident)*) => {

View File

@ -135,14 +135,14 @@ impl<'s, B: BufRead, S: Escaper> XmlXirReader<'s, B, S> {
QuickXmlEvent::Empty(ele) => Some(
Self::parse_element_open(
&self.escaper,
self.escaper,
&mut self.tokbuf,
ele,
prev_pos,
ctx,
true,
)
.and_then(|open| {
.map(|open| {
let new_pos = self.reader.buffer_position();
// `<tag ... />`
@ -157,12 +157,12 @@ impl<'s, B: BufRead, S: Escaper> XmlXirReader<'s, B, S> {
CloseSpan::empty(span),
));
Ok(open)
open
}),
),
QuickXmlEvent::Start(ele) => Some(Self::parse_element_open(
&self.escaper,
self.escaper,
&mut self.tokbuf,
ele,
prev_pos,
@ -184,14 +184,14 @@ impl<'s, B: BufRead, S: Escaper> XmlXirReader<'s, B, S> {
ele.name()
.try_into()
.map_err(Error::from_with_span(span))
.and_then(|qname| {
Ok(Token::Close(
.map(|qname| {
Token::Close(
Some(qname),
CloseSpan(
span,
name_len.try_into().unwrap_or(0),
),
))
)
})
}),
@ -217,7 +217,7 @@ impl<'s, B: BufRead, S: Escaper> XmlXirReader<'s, B, S> {
.map_err(Into::into)
.and_then(|sym| self.escaper.unescape(sym))
.map_err(Error::from_with_span(span))
.and_then(|unesc| Ok(Token::Text(unesc, span)))
.map(|unesc| Token::Text(unesc, span))
}),
// Comments are _not_ returned escaped.
@ -229,7 +229,7 @@ impl<'s, B: BufRead, S: Escaper> XmlXirReader<'s, B, S> {
bytes
.intern_utf8()
.map_err(Error::from_with_span(span))
.and_then(|comment| Ok(Token::Comment(comment, span)))
.map(|comment| Token::Comment(comment, span))
}),
// TODO: This must appear in the prologue.
@ -368,8 +368,7 @@ impl<'s, B: BufRead, S: Escaper> XmlXirReader<'s, B, S> {
let has_attrs = ele
.attributes_raw()
.iter()
.find(|b| !is_xml_whitespace_u8(**b))
.is_some();
.any(|b| !is_xml_whitespace_u8(*b));
// The tail is anything following the last byte of the QName
// in a non-empty tag with no attributes.
@ -527,8 +526,7 @@ impl<'s, B: BufRead, S: Escaper> XmlXirReader<'s, B, S> {
.intern_utf8()
.map_err(Error::from_with_span(span_value))?,
)
.map_err(Error::from_with_span(span_value))?
.into();
.map_err(Error::from_with_span(span_value))?;
tokbuf.push_front(Token::AttrName(name, span_name));
tokbuf.push_front(Token::AttrValue(value, span_value));
@ -546,20 +544,14 @@ impl<'s, B: BufRead, S: Escaper> XmlXirReader<'s, B, S> {
///
/// [xmlspec-s]: https://www.w3.org/TR/xml/#NT-S
pub fn is_xml_whitespace_u8(b: u8) -> bool {
match b {
b' ' | b'\r' | b'\n' | b'\t' => true,
_ => false,
}
matches!(b, b' ' | b'\r' | b'\n' | b'\t')
}
/// Whether the character represents XML whitespace.
///
/// See [`is_xml_whitespace_u8`].
pub fn is_xml_whitespace_char(c: char) -> bool {
match c {
' ' | '\r' | '\n' | '\t' => true,
_ => false,
}
matches!(c, ' ' | '\r' | '\n' | '\t')
}
impl<'s, B, S> Iterator for XmlXirReader<'s, B, S>

View File

@ -227,21 +227,20 @@ pub enum Tree {
_NonExhaustive,
}
impl Into<Option<Element>> for Tree {
#[inline]
fn into(self) -> Option<Element> {
match self {
Self::Element(ele) => Some(ele),
impl From<Tree> for Option<Element> {
fn from(val: Tree) -> Self {
match val {
Tree::Element(ele) => Some(ele),
_ => None,
}
}
}
impl Into<Option<SymbolId>> for Tree {
impl From<Tree> for Option<SymbolId> {
#[inline]
fn into(self) -> Option<SymbolId> {
match self {
Self::Text(text, _) => Some(text),
fn from(val: Tree) -> Self {
match val {
Tree::Text(text, _) => Some(text),
_ => None,
}
}
@ -251,7 +250,7 @@ impl Tree {
/// Yield a reference to the inner value if it is an [`Element`],
/// otherwise [`None`].
#[inline]
pub fn as_element<'a>(&'a self) -> Option<&'a Element> {
pub fn as_element(&self) -> Option<&Element> {
match self {
Self::Element(ele) => Some(ele),
_ => None,
@ -806,7 +805,7 @@ pub fn parser_from(
/// and the parser in general,
/// see the [module-level documentation](self).
#[inline]
pub fn attr_parser_from<'a>(
pub fn attr_parser_from(
toks: impl TokenStream,
) -> impl Iterator<Item = result::Result<Attr, ParseError<XirToken, StackError>>>
{

View File

@ -103,10 +103,10 @@ impl Default for WriterState {
impl WriterState {
#[inline]
fn close_tag_if_open<W: Write>(&self, sink: &mut W) -> Result<()> {
Ok(match *self {
Self::NodeOpen => sink.write_all(b">")?,
_ => (),
})
match *self {
Self::NodeOpen => Ok(sink.write_all(b">")?),
_ => Ok(()),
}
}
}
@ -150,7 +150,7 @@ where
/// This is intended primarily for testing;
/// it is recommended that you use [`write`](XmlWriter::write) instead,
/// unless you _really_ need a new owned `Vec<u8>`.
#[must_use]
#[cfg(test)]
fn write_new(
self,
prev_state: WriterState,