Merge pull request #5980 from ytmimi/subtree_push_2023_12_12
Subtree push 2023-12-12
This commit is contained in:
commit
20196767d4
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -343,9 +343,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.3"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
|
||||
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
@ -43,7 +43,7 @@ diff = "0.1"
|
||||
dirs = "4.0"
|
||||
getopts = "0.2"
|
||||
ignore = "0.4"
|
||||
itertools = "0.10"
|
||||
itertools = "0.11"
|
||||
lazy_static = "1.4"
|
||||
regex = "1.7"
|
||||
serde = { version = "1.0.160", features = ["derive"] }
|
||||
|
@ -1,3 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "nightly-2023-10-22"
|
||||
channel = "nightly-2023-12-12"
|
||||
components = ["llvm-tools", "rustc-dev"]
|
||||
|
@ -29,7 +29,7 @@ pub(crate) fn rewrite_closure(
|
||||
binder: &ast::ClosureBinder,
|
||||
constness: ast::Const,
|
||||
capture: ast::CaptureBy,
|
||||
is_async: &ast::Async,
|
||||
coroutine_kind: &Option<ast::CoroutineKind>,
|
||||
movability: ast::Movability,
|
||||
fn_decl: &ast::FnDecl,
|
||||
body: &ast::Expr,
|
||||
@ -40,7 +40,16 @@ pub(crate) fn rewrite_closure(
|
||||
debug!("rewrite_closure {:?}", body);
|
||||
|
||||
let (prefix, extra_offset) = rewrite_closure_fn_decl(
|
||||
binder, constness, capture, is_async, movability, fn_decl, body, span, context, shape,
|
||||
binder,
|
||||
constness,
|
||||
capture,
|
||||
coroutine_kind,
|
||||
movability,
|
||||
fn_decl,
|
||||
body,
|
||||
span,
|
||||
context,
|
||||
shape,
|
||||
)?;
|
||||
// 1 = space between `|...|` and body.
|
||||
let body_shape = shape.offset_left(extra_offset)?;
|
||||
@ -188,7 +197,7 @@ fn rewrite_closure_expr(
|
||||
fn allow_multi_line(expr: &ast::Expr) -> bool {
|
||||
match expr.kind {
|
||||
ast::ExprKind::Match(..)
|
||||
| ast::ExprKind::Async(..)
|
||||
| ast::ExprKind::Gen(..)
|
||||
| ast::ExprKind::Block(..)
|
||||
| ast::ExprKind::TryBlock(..)
|
||||
| ast::ExprKind::Loop(..)
|
||||
@ -233,7 +242,7 @@ fn rewrite_closure_fn_decl(
|
||||
binder: &ast::ClosureBinder,
|
||||
constness: ast::Const,
|
||||
capture: ast::CaptureBy,
|
||||
asyncness: &ast::Async,
|
||||
coroutine_kind: &Option<ast::CoroutineKind>,
|
||||
movability: ast::Movability,
|
||||
fn_decl: &ast::FnDecl,
|
||||
body: &ast::Expr,
|
||||
@ -263,8 +272,13 @@ fn rewrite_closure_fn_decl(
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let is_async = if asyncness.is_async() { "async " } else { "" };
|
||||
let mover = if capture == ast::CaptureBy::Value {
|
||||
let coro = match coroutine_kind {
|
||||
Some(ast::CoroutineKind::Async { .. }) => "async ",
|
||||
Some(ast::CoroutineKind::Gen { .. }) => "gen ",
|
||||
Some(ast::CoroutineKind::AsyncGen { .. }) => "async gen ",
|
||||
None => "",
|
||||
};
|
||||
let mover = if matches!(capture, ast::CaptureBy::Value { .. }) {
|
||||
"move "
|
||||
} else {
|
||||
""
|
||||
@ -272,7 +286,7 @@ fn rewrite_closure_fn_decl(
|
||||
// 4 = "|| {".len(), which is overconservative when the closure consists of
|
||||
// a single expression.
|
||||
let nested_shape = shape
|
||||
.shrink_left(binder.len() + const_.len() + immovable.len() + is_async.len() + mover.len())?
|
||||
.shrink_left(binder.len() + const_.len() + immovable.len() + coro.len() + mover.len())?
|
||||
.sub_width(4)?;
|
||||
|
||||
// 1 = |
|
||||
@ -310,7 +324,7 @@ fn rewrite_closure_fn_decl(
|
||||
.tactic(tactic)
|
||||
.preserve_newline(true);
|
||||
let list_str = write_list(&item_vec, &fmt)?;
|
||||
let mut prefix = format!("{binder}{const_}{immovable}{is_async}{mover}|{list_str}|");
|
||||
let mut prefix = format!("{binder}{const_}{immovable}{coro}{mover}|{list_str}|");
|
||||
|
||||
if !ret_str.is_empty() {
|
||||
if prefix.contains('\n') {
|
||||
@ -339,7 +353,7 @@ pub(crate) fn rewrite_last_closure(
|
||||
ref binder,
|
||||
constness,
|
||||
capture_clause,
|
||||
ref asyncness,
|
||||
ref coroutine_kind,
|
||||
movability,
|
||||
ref fn_decl,
|
||||
ref body,
|
||||
@ -360,7 +374,7 @@ pub(crate) fn rewrite_last_closure(
|
||||
binder,
|
||||
constness,
|
||||
capture_clause,
|
||||
asyncness,
|
||||
coroutine_kind,
|
||||
movability,
|
||||
fn_decl,
|
||||
body,
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Formatting and tools for comments.
|
||||
|
||||
use std::{self, borrow::Cow, iter};
|
||||
use std::{borrow::Cow, iter};
|
||||
|
||||
use itertools::{multipeek, MultiPeek};
|
||||
use lazy_static::lazy_static;
|
||||
@ -1847,7 +1847,6 @@ fn remove_comment_header(comment: &str) -> &str {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::shape::{Indent, Shape};
|
||||
|
||||
#[test]
|
||||
fn char_classes() {
|
||||
|
@ -6,7 +6,7 @@ use std::path::PathBuf;
|
||||
use std::{cmp, fmt, iter, str};
|
||||
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_span::{self, SourceFile};
|
||||
use rustc_span::SourceFile;
|
||||
use serde::{ser, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde_json as json;
|
||||
use thiserror::Error;
|
||||
|
@ -1,5 +1,4 @@
|
||||
use std::cell::Cell;
|
||||
use std::default::Default;
|
||||
use std::fs::File;
|
||||
use std::io::{Error, ErrorKind, Read};
|
||||
use std::path::{Path, PathBuf};
|
||||
@ -1018,7 +1017,6 @@ make_backup = false
|
||||
#[cfg(test)]
|
||||
mod partially_unstable_option {
|
||||
use super::mock::{Config, PartiallyUnstableOption};
|
||||
use super::*;
|
||||
|
||||
/// From the command line, we can override with a stable variant.
|
||||
#[test]
|
||||
|
@ -1,3 +1,5 @@
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use std::collections::{hash_set, HashSet};
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
@ -1,7 +1,6 @@
|
||||
use self::xml::XmlEscaped;
|
||||
use super::*;
|
||||
use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch};
|
||||
use std::io::{self, Write};
|
||||
|
||||
mod xml;
|
||||
|
||||
|
@ -51,8 +51,6 @@ impl Emitter for DiffEmitter {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::config::Config;
|
||||
use crate::FileName;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[test]
|
||||
|
@ -2,7 +2,6 @@ use super::*;
|
||||
use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch};
|
||||
use serde::Serialize;
|
||||
use serde_json::to_string as to_json_string;
|
||||
use std::io::{self, Write};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct JsonEmitter {
|
||||
@ -106,7 +105,6 @@ impl JsonEmitter {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::FileName;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[test]
|
||||
|
@ -1,6 +1,5 @@
|
||||
use super::*;
|
||||
use crate::rustfmt_diff::{make_diff, ModifiedLines};
|
||||
use std::io::Write;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct ModifiedLinesEmitter;
|
||||
|
@ -1,6 +1,5 @@
|
||||
use super::*;
|
||||
use crate::config::Verbosity;
|
||||
use std::io::Write;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct StdoutEmitter {
|
||||
|
14
src/expr.rs
14
src/expr.rs
@ -212,7 +212,7 @@ pub(crate) fn format_expr(
|
||||
&cl.binder,
|
||||
cl.constness,
|
||||
cl.capture_clause,
|
||||
&cl.asyncness,
|
||||
&cl.coroutine_kind,
|
||||
cl.movability,
|
||||
&cl.fn_decl,
|
||||
&cl.body,
|
||||
@ -367,15 +367,15 @@ pub(crate) fn format_expr(
|
||||
))
|
||||
}
|
||||
}
|
||||
ast::ExprKind::Async(capture_by, ref block) => {
|
||||
let mover = if capture_by == ast::CaptureBy::Value {
|
||||
ast::ExprKind::Gen(capture_by, ref block, ref kind) => {
|
||||
let mover = if matches!(capture_by, ast::CaptureBy::Value { .. }) {
|
||||
"move "
|
||||
} else {
|
||||
""
|
||||
};
|
||||
if let rw @ Some(_) = rewrite_single_line_block(
|
||||
context,
|
||||
format!("async {mover}").as_str(),
|
||||
format!("{kind} {mover}").as_str(),
|
||||
block,
|
||||
Some(&expr.attrs),
|
||||
None,
|
||||
@ -386,7 +386,7 @@ pub(crate) fn format_expr(
|
||||
// 6 = `async `
|
||||
let budget = shape.width.saturating_sub(6);
|
||||
Some(format!(
|
||||
"async {mover}{}",
|
||||
"{kind} {mover}{}",
|
||||
rewrite_block(
|
||||
block,
|
||||
Some(&expr.attrs),
|
||||
@ -1371,7 +1371,7 @@ pub(crate) fn can_be_overflowed_expr(
|
||||
}
|
||||
|
||||
// Handle always block-like expressions
|
||||
ast::ExprKind::Async(..) | ast::ExprKind::Block(..) | ast::ExprKind::Closure(..) => true,
|
||||
ast::ExprKind::Gen(..) | ast::ExprKind::Block(..) | ast::ExprKind::Closure(..) => true,
|
||||
|
||||
// Handle `[]` and `{}`-like expressions
|
||||
ast::ExprKind::Array(..) | ast::ExprKind::Struct(..) => {
|
||||
@ -1933,7 +1933,7 @@ fn rewrite_unary_op(
|
||||
shape: Shape,
|
||||
) -> Option<String> {
|
||||
// For some reason, an UnOp is not spanned like BinOp!
|
||||
rewrite_unary_prefix(context, ast::UnOp::to_string(op), expr, shape)
|
||||
rewrite_unary_prefix(context, op.as_str(), expr, shape)
|
||||
}
|
||||
|
||||
pub(crate) enum RhsAssignKind<'ast> {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use ignore::{self, gitignore};
|
||||
use ignore::gitignore;
|
||||
|
||||
use crate::config::{FileName, IgnoreList};
|
||||
|
||||
|
@ -1102,7 +1102,6 @@ enum SharedPrefix {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use rustc_span::DUMMY_SP;
|
||||
|
||||
// Parse the path part of an import. This parser is not robust and is only
|
||||
// suitable for use in a test harness.
|
||||
|
@ -287,7 +287,7 @@ pub(crate) struct FnSig<'a> {
|
||||
decl: &'a ast::FnDecl,
|
||||
generics: &'a ast::Generics,
|
||||
ext: ast::Extern,
|
||||
is_async: Cow<'a, ast::Async>,
|
||||
coroutine_kind: Cow<'a, Option<ast::CoroutineKind>>,
|
||||
constness: ast::Const,
|
||||
defaultness: ast::Defaultness,
|
||||
unsafety: ast::Unsafe,
|
||||
@ -302,7 +302,7 @@ impl<'a> FnSig<'a> {
|
||||
) -> FnSig<'a> {
|
||||
FnSig {
|
||||
unsafety: method_sig.header.unsafety,
|
||||
is_async: Cow::Borrowed(&method_sig.header.asyncness),
|
||||
coroutine_kind: Cow::Borrowed(&method_sig.header.coroutine_kind),
|
||||
constness: method_sig.header.constness,
|
||||
defaultness: ast::Defaultness::Final,
|
||||
ext: method_sig.header.ext,
|
||||
@ -328,7 +328,7 @@ impl<'a> FnSig<'a> {
|
||||
generics,
|
||||
ext: fn_sig.header.ext,
|
||||
constness: fn_sig.header.constness,
|
||||
is_async: Cow::Borrowed(&fn_sig.header.asyncness),
|
||||
coroutine_kind: Cow::Borrowed(&fn_sig.header.coroutine_kind),
|
||||
defaultness,
|
||||
unsafety: fn_sig.header.unsafety,
|
||||
visibility: vis,
|
||||
@ -343,7 +343,8 @@ impl<'a> FnSig<'a> {
|
||||
result.push_str(&*format_visibility(context, self.visibility));
|
||||
result.push_str(format_defaultness(self.defaultness));
|
||||
result.push_str(format_constness(self.constness));
|
||||
result.push_str(format_async(&self.is_async));
|
||||
self.coroutine_kind
|
||||
.map(|coroutine_kind| result.push_str(format_coro(&coroutine_kind)));
|
||||
result.push_str(format_unsafety(self.unsafety));
|
||||
result.push_str(&format_extern(
|
||||
self.ext,
|
||||
|
@ -708,7 +708,7 @@ struct MacroArgParser {
|
||||
fn last_tok(tt: &TokenTree) -> Token {
|
||||
match *tt {
|
||||
TokenTree::Token(ref t, _) => t.clone(),
|
||||
TokenTree::Delimited(delim_span, delim, _) => Token {
|
||||
TokenTree::Delimited(delim_span, _, delim, _) => Token {
|
||||
kind: TokenKind::CloseDelim(delim),
|
||||
span: delim_span.close,
|
||||
},
|
||||
@ -925,7 +925,7 @@ impl MacroArgParser {
|
||||
self.add_meta_variable(&mut iter)?;
|
||||
}
|
||||
TokenTree::Token(ref t, _) => self.update_buffer(t),
|
||||
&TokenTree::Delimited(_delimited_span, delimited, ref tts) => {
|
||||
&TokenTree::Delimited(_dspan, _spacing, delimited, ref tts) => {
|
||||
if !self.buf.is_empty() {
|
||||
if next_space(&self.last_tok.kind) == SpaceState::Always {
|
||||
self.add_separator();
|
||||
@ -1167,7 +1167,7 @@ impl<'a> MacroParser<'a> {
|
||||
let tok = self.toks.next()?;
|
||||
let (lo, args_paren_kind) = match tok {
|
||||
TokenTree::Token(..) => return None,
|
||||
&TokenTree::Delimited(delimited_span, d, _) => (delimited_span.open.lo(), d),
|
||||
&TokenTree::Delimited(delimited_span, _, d, _) => (delimited_span.open.lo(), d),
|
||||
};
|
||||
let args = TokenStream::new(vec![tok.clone()]);
|
||||
match self.toks.next()? {
|
||||
|
@ -223,7 +223,7 @@ fn rewrite_match_arm(
|
||||
) -> Option<String> {
|
||||
let (missing_span, attrs_str) = if !arm.attrs.is_empty() {
|
||||
if contains_skip(&arm.attrs) {
|
||||
let (_, body) = flatten_arm_body(context, &arm.body, None);
|
||||
let (_, body) = flatten_arm_body(context, arm.body.as_deref()?, None);
|
||||
// `arm.span()` does not include trailing comma, add it manually.
|
||||
return Some(format!(
|
||||
"{}{}",
|
||||
@ -246,7 +246,7 @@ fn rewrite_match_arm(
|
||||
};
|
||||
|
||||
// Patterns
|
||||
let pat_shape = match &arm.body.kind {
|
||||
let pat_shape = match &arm.body.as_ref()?.kind {
|
||||
ast::ExprKind::Block(_, Some(label)) => {
|
||||
// Some block with a label ` => 'label: {`
|
||||
// 7 = ` => : {`
|
||||
@ -280,10 +280,10 @@ fn rewrite_match_arm(
|
||||
false,
|
||||
)?;
|
||||
|
||||
let arrow_span = mk_sp(arm.pat.span.hi(), arm.body.span().lo());
|
||||
let arrow_span = mk_sp(arm.pat.span.hi(), arm.body.as_ref()?.span().lo());
|
||||
rewrite_match_body(
|
||||
context,
|
||||
&arm.body,
|
||||
arm.body.as_ref()?,
|
||||
&lhs_str,
|
||||
shape,
|
||||
guard_str.contains('\n'),
|
||||
|
@ -339,7 +339,7 @@ impl FlattenPair for ast::Expr {
|
||||
if let Some(pop) = stack.pop() {
|
||||
match pop.kind {
|
||||
ast::ExprKind::Binary(op, _, ref rhs) => {
|
||||
separators.push(op.node.to_string());
|
||||
separators.push(op.node.as_str());
|
||||
node = rhs;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
@ -40,7 +40,9 @@ pub(crate) fn is_short_pattern(pat: &ast::Pat, pat_str: &str) -> bool {
|
||||
|
||||
fn is_short_pattern_inner(pat: &ast::Pat) -> bool {
|
||||
match pat.kind {
|
||||
ast::PatKind::Rest | ast::PatKind::Wild | ast::PatKind::Lit(_) => true,
|
||||
ast::PatKind::Rest | ast::PatKind::Never | ast::PatKind::Wild | ast::PatKind::Lit(_) => {
|
||||
true
|
||||
}
|
||||
ast::PatKind::Ident(_, _, ref pat) => pat.is_none(),
|
||||
ast::PatKind::Struct(..)
|
||||
| ast::PatKind::MacCall(..)
|
||||
@ -193,6 +195,7 @@ impl Rewrite for Pat {
|
||||
None
|
||||
}
|
||||
}
|
||||
PatKind::Never => None,
|
||||
PatKind::Range(ref lhs, ref rhs, ref end_kind) => {
|
||||
let infix = match end_kind.node {
|
||||
RangeEnd::Included(RangeSyntax::DotDotDot) => "...",
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
// FIXME(#2455): Reorder trait items.
|
||||
|
||||
use std::cmp::{Ord, Ordering};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use rustc_ast::{ast, attr};
|
||||
use rustc_span::{symbol::sym, Span};
|
||||
|
@ -97,7 +97,12 @@ impl Spanned for ast::Arm {
|
||||
} else {
|
||||
self.attrs[0].span.lo()
|
||||
};
|
||||
span_with_attrs_lo_hi!(self, lo, self.body.span.hi())
|
||||
let hi = if let Some(body) = &self.body {
|
||||
body.span.hi()
|
||||
} else {
|
||||
self.pat.span.hi()
|
||||
};
|
||||
span_with_attrs_lo_hi!(self, lo, hi)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
use std::iter::ExactSizeIterator;
|
||||
use std::ops::Deref;
|
||||
|
||||
use rustc_ast::ast::{self, FnRetTy, Mutability, Term};
|
||||
@ -546,7 +545,7 @@ impl Rewrite for ast::GenericBound {
|
||||
ast::TraitBoundModifier::Maybe => poly_trait_ref
|
||||
.rewrite(context, shape.offset_left(1)?)
|
||||
.map(|s| format!("?{}", s)),
|
||||
ast::TraitBoundModifier::MaybeConst => poly_trait_ref
|
||||
ast::TraitBoundModifier::MaybeConst(_) => poly_trait_ref
|
||||
.rewrite(context, shape.offset_left(7)?)
|
||||
.map(|s| format!("~const {}", s)),
|
||||
ast::TraitBoundModifier::MaybeConstMaybe => poly_trait_ref
|
||||
|
11
src/utils.rs
11
src/utils.rs
@ -75,10 +75,11 @@ pub(crate) fn format_visibility(
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn format_async(is_async: &ast::Async) -> &'static str {
|
||||
match is_async {
|
||||
ast::Async::Yes { .. } => "async ",
|
||||
ast::Async::No => "",
|
||||
pub(crate) fn format_coro(coroutine_kind: &ast::CoroutineKind) -> &'static str {
|
||||
match coroutine_kind {
|
||||
ast::CoroutineKind::Async { .. } => "async ",
|
||||
ast::CoroutineKind::Gen { .. } => "gen ",
|
||||
ast::CoroutineKind::AsyncGen { .. } => "async gen ",
|
||||
}
|
||||
}
|
||||
|
||||
@ -473,7 +474,7 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr
|
||||
| ast::ExprKind::If(..)
|
||||
| ast::ExprKind::Block(..)
|
||||
| ast::ExprKind::ConstBlock(..)
|
||||
| ast::ExprKind::Async(..)
|
||||
| ast::ExprKind::Gen(..)
|
||||
| ast::ExprKind::Loop(..)
|
||||
| ast::ExprKind::ForLoop(..)
|
||||
| ast::ExprKind::TryBlock(..)
|
||||
|
@ -1,5 +1,5 @@
|
||||
// rustfmt-edition: 2015
|
||||
#![feature(rust_2018_preview, uniform_paths)]
|
||||
#![feature(uniform_paths)]
|
||||
use futures::prelude::*;
|
||||
use http_03::cli::Cli;
|
||||
use hyper::{service::service_fn_ok, Body, Response, Server};
|
||||
|
@ -1,5 +1,5 @@
|
||||
// rustfmt-edition: 2018
|
||||
#![feature(rust_2018_preview, uniform_paths)]
|
||||
#![feature(uniform_paths)]
|
||||
use futures::prelude::*;
|
||||
use http_03::cli::Cli;
|
||||
use hyper::{service::service_fn_ok, Body, Response, Server};
|
||||
|
@ -1,5 +1,5 @@
|
||||
// rustfmt-edition: 2015
|
||||
#![feature(rust_2018_preview, uniform_paths)]
|
||||
#![feature(uniform_paths)]
|
||||
use futures::prelude::*;
|
||||
use http_03::cli::Cli;
|
||||
use hyper::{service::service_fn_ok, Body, Response, Server};
|
||||
|
@ -1,5 +1,5 @@
|
||||
// rustfmt-edition: 2018
|
||||
#![feature(rust_2018_preview, uniform_paths)]
|
||||
#![feature(uniform_paths)]
|
||||
use ::log::{error, info, log};
|
||||
use futures::prelude::*;
|
||||
use http_03::cli::Cli;
|
||||
|
Loading…
x
Reference in New Issue
Block a user