Auto merge of #17220 - Veykril:hov-lit, r=Veykril
fix: Improve confusing literal hovers
This commit is contained in:
commit
6e0c33d1ba
@ -136,15 +136,15 @@ fn from(ast_lit_kind: ast::LiteralKind) -> Self {
|
|||||||
Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty)
|
Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty)
|
||||||
}
|
}
|
||||||
LiteralKind::ByteString(bs) => {
|
LiteralKind::ByteString(bs) => {
|
||||||
let text = bs.value().map(Box::from).unwrap_or_else(Default::default);
|
let text = bs.value().map_or_else(|_| Default::default(), Box::from);
|
||||||
Literal::ByteString(text)
|
Literal::ByteString(text)
|
||||||
}
|
}
|
||||||
LiteralKind::String(s) => {
|
LiteralKind::String(s) => {
|
||||||
let text = s.value().map(Box::from).unwrap_or_else(Default::default);
|
let text = s.value().map_or_else(|_| Default::default(), Box::from);
|
||||||
Literal::String(text)
|
Literal::String(text)
|
||||||
}
|
}
|
||||||
LiteralKind::CString(s) => {
|
LiteralKind::CString(s) => {
|
||||||
let text = s.value().map(Box::from).unwrap_or_else(Default::default);
|
let text = s.value().map_or_else(|_| Default::default(), Box::from);
|
||||||
Literal::CString(text)
|
Literal::CString(text)
|
||||||
}
|
}
|
||||||
LiteralKind::Byte(b) => {
|
LiteralKind::Byte(b) => {
|
||||||
|
@ -441,21 +441,21 @@ fn unquote_str(lit: &tt::Literal) -> Option<(String, Span)> {
|
|||||||
let span = lit.span;
|
let span = lit.span;
|
||||||
let lit = ast::make::tokens::literal(&lit.to_string());
|
let lit = ast::make::tokens::literal(&lit.to_string());
|
||||||
let token = ast::String::cast(lit)?;
|
let token = ast::String::cast(lit)?;
|
||||||
token.value().map(|it| (it.into_owned(), span))
|
token.value().ok().map(|it| (it.into_owned(), span))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unquote_char(lit: &tt::Literal) -> Option<(char, Span)> {
|
fn unquote_char(lit: &tt::Literal) -> Option<(char, Span)> {
|
||||||
let span = lit.span;
|
let span = lit.span;
|
||||||
let lit = ast::make::tokens::literal(&lit.to_string());
|
let lit = ast::make::tokens::literal(&lit.to_string());
|
||||||
let token = ast::Char::cast(lit)?;
|
let token = ast::Char::cast(lit)?;
|
||||||
token.value().zip(Some(span))
|
token.value().ok().zip(Some(span))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unquote_byte_string(lit: &tt::Literal) -> Option<(Vec<u8>, Span)> {
|
fn unquote_byte_string(lit: &tt::Literal) -> Option<(Vec<u8>, Span)> {
|
||||||
let span = lit.span;
|
let span = lit.span;
|
||||||
let lit = ast::make::tokens::literal(&lit.to_string());
|
let lit = ast::make::tokens::literal(&lit.to_string());
|
||||||
let token = ast::ByteString::cast(lit)?;
|
let token = ast::ByteString::cast(lit)?;
|
||||||
token.value().map(|it| (it.into_owned(), span))
|
token.value().ok().map(|it| (it.into_owned(), span))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_error_expand(
|
fn compile_error_expand(
|
||||||
|
@ -2543,6 +2543,20 @@ pub fn is_float(&self) -> bool {
|
|||||||
matches!(self.inner, hir_def::builtin_type::BuiltinType::Float(_))
|
matches!(self.inner, hir_def::builtin_type::BuiltinType::Float(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_f32(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self.inner,
|
||||||
|
hir_def::builtin_type::BuiltinType::Float(hir_def::builtin_type::BuiltinFloat::F32)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_f64(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self.inner,
|
||||||
|
hir_def::builtin_type::BuiltinType::Float(hir_def::builtin_type::BuiltinFloat::F64)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_char(&self) -> bool {
|
pub fn is_char(&self) -> bool {
|
||||||
matches!(self.inner, hir_def::builtin_type::BuiltinType::Char)
|
matches!(self.inner, hir_def::builtin_type::BuiltinType::Char)
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
|
|||||||
if token.is_raw() {
|
if token.is_raw() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let value = token.value()?;
|
let value = token.value().ok()?;
|
||||||
let target = token.syntax().text_range();
|
let target = token.syntax().text_range();
|
||||||
acc.add(
|
acc.add(
|
||||||
AssistId("make_raw_string", AssistKind::RefactorRewrite),
|
AssistId("make_raw_string", AssistKind::RefactorRewrite),
|
||||||
@ -64,7 +64,7 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
|
|||||||
if !token.is_raw() {
|
if !token.is_raw() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let value = token.value()?;
|
let value = token.value().ok()?;
|
||||||
let target = token.syntax().text_range();
|
let target = token.syntax().text_range();
|
||||||
acc.add(
|
acc.add(
|
||||||
AssistId("make_usual_string", AssistKind::RefactorRewrite),
|
AssistId("make_usual_string", AssistKind::RefactorRewrite),
|
||||||
@ -398,12 +398,12 @@ fn f() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_hash_doesnt_work() {
|
fn remove_hash_does_not_work() {
|
||||||
check_assist_not_applicable(remove_hash, r#"fn f() { let s = $0"random string"; }"#);
|
check_assist_not_applicable(remove_hash, r#"fn f() { let s = $0"random string"; }"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_hash_no_hash_doesnt_work() {
|
fn remove_hash_no_hash_does_not_work() {
|
||||||
check_assist_not_applicable(remove_hash, r#"fn f() { let s = $0r"random string"; }"#);
|
check_assist_not_applicable(remove_hash, r#"fn f() { let s = $0r"random string"; }"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
// ```
|
// ```
|
||||||
pub(crate) fn replace_string_with_char(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
pub(crate) fn replace_string_with_char(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||||
let token = ctx.find_token_syntax_at_offset(STRING).and_then(ast::String::cast)?;
|
let token = ctx.find_token_syntax_at_offset(STRING).and_then(ast::String::cast)?;
|
||||||
let value = token.value()?;
|
let value = token.value().ok()?;
|
||||||
let target = token.syntax().text_range();
|
let target = token.syntax().text_range();
|
||||||
|
|
||||||
if value.chars().take(2).count() != 1 {
|
if value.chars().take(2).count() != 1 {
|
||||||
|
@ -123,7 +123,7 @@ fn try_lookup_include_path(
|
|||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let path = token.value()?;
|
let path = token.value().ok()?;
|
||||||
|
|
||||||
let file_id = sema.db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?;
|
let file_id = sema.db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?;
|
||||||
let size = sema.db.file_text(file_id).len().try_into().ok()?;
|
let size = sema.db.file_text(file_id).len().try_into().ok()?;
|
||||||
@ -179,11 +179,11 @@ fn try_filter_trait_item_definition(
|
|||||||
AssocItem::Const(..) | AssocItem::TypeAlias(..) => {
|
AssocItem::Const(..) | AssocItem::TypeAlias(..) => {
|
||||||
let trait_ = assoc.implemented_trait(db)?;
|
let trait_ = assoc.implemented_trait(db)?;
|
||||||
let name = def.name(db)?;
|
let name = def.name(db)?;
|
||||||
let discri_value = discriminant(&assoc);
|
let discriminant_value = discriminant(&assoc);
|
||||||
trait_
|
trait_
|
||||||
.items(db)
|
.items(db)
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|itm| discriminant(*itm) == discri_value)
|
.filter(|itm| discriminant(*itm) == discriminant_value)
|
||||||
.find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten())
|
.find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten())
|
||||||
.map(|it| it.collect())
|
.map(|it| it.collect())
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
FxIndexSet, RootDatabase,
|
FxIndexSet, RootDatabase,
|
||||||
};
|
};
|
||||||
use itertools::{multizip, Itertools};
|
use itertools::{multizip, Itertools};
|
||||||
use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxNode, T};
|
use syntax::{ast, AstNode, SyntaxKind::*, SyntaxNode, T};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
doc_links::token_as_doc_comment,
|
doc_links::token_as_doc_comment,
|
||||||
@ -298,61 +298,8 @@ fn hover_simple(
|
|||||||
})
|
})
|
||||||
// tokens
|
// tokens
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
let mut res = HoverResult::default();
|
render::literal(sema, original_token.clone())
|
||||||
match_ast! {
|
.map(|markup| HoverResult { markup, actions: vec![] })
|
||||||
match original_token {
|
|
||||||
ast::String(string) => {
|
|
||||||
res.markup = Markup::fenced_block_text(format_args!("{}", string.value()?));
|
|
||||||
},
|
|
||||||
ast::ByteString(string) => {
|
|
||||||
res.markup = Markup::fenced_block_text(format_args!("{:?}", string.value()?));
|
|
||||||
},
|
|
||||||
ast::CString(string) => {
|
|
||||||
let val = string.value()?;
|
|
||||||
res.markup = Markup::fenced_block_text(format_args!("{}", std::str::from_utf8(val.as_ref()).ok()?));
|
|
||||||
},
|
|
||||||
ast::Char(char) => {
|
|
||||||
let mut res = HoverResult::default();
|
|
||||||
res.markup = Markup::fenced_block_text(format_args!("{}", char.value()?));
|
|
||||||
},
|
|
||||||
ast::Byte(byte) => {
|
|
||||||
res.markup = Markup::fenced_block_text(format_args!("0x{:X}", byte.value()?));
|
|
||||||
},
|
|
||||||
ast::FloatNumber(num) => {
|
|
||||||
res.markup = if num.suffix() == Some("f32") {
|
|
||||||
match num.value_f32() {
|
|
||||||
Ok(num) => {
|
|
||||||
Markup::fenced_block_text(format_args!("{num} (bits: 0x{:X})", num.to_bits()))
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
Markup::fenced_block_text(format_args!("{e}"))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match num.value() {
|
|
||||||
Ok(num) => {
|
|
||||||
Markup::fenced_block_text(format_args!("{num} (bits: 0x{:X})", num.to_bits()))
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
Markup::fenced_block_text(format_args!("{e}"))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
ast::IntNumber(num) => {
|
|
||||||
res.markup = match num.value() {
|
|
||||||
Ok(num) => {
|
|
||||||
Markup::fenced_block_text(format_args!("{num} (0x{num:X}|0b{num:b})"))
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
Markup::fenced_block_text(format_args!("{e}"))
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
_ => return None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(res)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
result.map(|mut res: HoverResult| {
|
result.map(|mut res: HoverResult| {
|
||||||
|
@ -17,11 +17,7 @@
|
|||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use stdx::format_to;
|
use stdx::format_to;
|
||||||
use syntax::{
|
use syntax::{algo, ast, match_ast, AstNode, AstToken, Direction, SyntaxToken, T};
|
||||||
algo,
|
|
||||||
ast::{self, RecordPat},
|
|
||||||
match_ast, AstNode, Direction, SyntaxToken, T,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
doc_links::{remove_links, rewrite_links},
|
doc_links::{remove_links, rewrite_links},
|
||||||
@ -276,7 +272,7 @@ pub(super) fn keyword(
|
|||||||
pub(super) fn struct_rest_pat(
|
pub(super) fn struct_rest_pat(
|
||||||
sema: &Semantics<'_, RootDatabase>,
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
_config: &HoverConfig,
|
_config: &HoverConfig,
|
||||||
pattern: &RecordPat,
|
pattern: &ast::RecordPat,
|
||||||
) -> HoverResult {
|
) -> HoverResult {
|
||||||
let missing_fields = sema.record_pattern_missing_fields(pattern);
|
let missing_fields = sema.record_pattern_missing_fields(pattern);
|
||||||
|
|
||||||
@ -526,6 +522,60 @@ pub(super) fn definition(
|
|||||||
markup(docs.map(Into::into), desc, mod_path)
|
markup(docs.map(Into::into), desc, mod_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn literal(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Option<Markup> {
|
||||||
|
let lit = token.parent().and_then(ast::Literal::cast)?;
|
||||||
|
let ty = if let Some(p) = lit.syntax().parent().and_then(ast::Pat::cast) {
|
||||||
|
sema.type_of_pat(&p)?
|
||||||
|
} else {
|
||||||
|
sema.type_of_expr(&ast::Expr::Literal(lit))?
|
||||||
|
}
|
||||||
|
.original;
|
||||||
|
|
||||||
|
let value = match_ast! {
|
||||||
|
match token {
|
||||||
|
ast::String(string) => string.value().as_ref().map_err(|e| format!("{e:?}")).map(ToString::to_string),
|
||||||
|
ast::ByteString(string) => string.value().as_ref().map_err(|e| format!("{e:?}")).map(|it| format!("{it:?}")),
|
||||||
|
ast::CString(string) => string.value().as_ref().map_err(|e| format!("{e:?}")).map(|it| std::str::from_utf8(it).map_or_else(|e| format!("{e:?}"), ToOwned::to_owned)),
|
||||||
|
ast::Char(char) => char .value().as_ref().map_err(|e| format!("{e:?}")).map(ToString::to_string),
|
||||||
|
ast::Byte(byte) => byte .value().as_ref().map_err(|e| format!("{e:?}")).map(|it| format!("0x{it:X}")),
|
||||||
|
ast::FloatNumber(num) => {
|
||||||
|
let (text, _) = num.split_into_parts();
|
||||||
|
let text = text.replace('_', "");
|
||||||
|
if ty.as_builtin().map(|it| it.is_f32()).unwrap_or(false) {
|
||||||
|
match text.parse::<f32>() {
|
||||||
|
Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())),
|
||||||
|
Err(e) => Err(e.to_string()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match text.parse::<f64>() {
|
||||||
|
Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())),
|
||||||
|
Err(e) => Err(e.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ast::IntNumber(num) => match num.value() {
|
||||||
|
Ok(num) => Ok(format!("{num} (0x{num:X}|0b{num:b})")),
|
||||||
|
Err(e) => Err(e.to_string()),
|
||||||
|
},
|
||||||
|
_ => return None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let ty = ty.display(sema.db);
|
||||||
|
|
||||||
|
let mut s = format!("```rust\n{ty}\n```\n___\n\n");
|
||||||
|
match value {
|
||||||
|
Ok(value) => {
|
||||||
|
if let Some(newline) = value.find('\n') {
|
||||||
|
format_to!(s, "value of literal (truncated up to newline): {}", &value[..newline])
|
||||||
|
} else {
|
||||||
|
format_to!(s, "value of literal: {value}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(error) => format_to!(s, "invalid literal: {error}"),
|
||||||
|
}
|
||||||
|
Some(s.into())
|
||||||
|
}
|
||||||
|
|
||||||
fn render_notable_trait_comment(
|
fn render_notable_trait_comment(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
notable_traits: &[(Trait, Vec<(Option<Type>, Name)>)],
|
notable_traits: &[(Trait, Vec<(Option<Type>, Name)>)],
|
||||||
|
@ -7790,9 +7790,12 @@ fn main() {
|
|||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
*"🦀\u{1f980}\\\x41"*
|
*"🦀\u{1f980}\\\x41"*
|
||||||
```text
|
```rust
|
||||||
🦀🦀\A
|
&str
|
||||||
```
|
```
|
||||||
|
___
|
||||||
|
|
||||||
|
value of literal: 🦀🦀\A
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
@ -7803,9 +7806,34 @@ fn main() {
|
|||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
*r"🦀\u{1f980}\\\x41"*
|
*r"🦀\u{1f980}\\\x41"*
|
||||||
```text
|
```rust
|
||||||
🦀\u{1f980}\\\x41
|
&str
|
||||||
```
|
```
|
||||||
|
___
|
||||||
|
|
||||||
|
value of literal: 🦀\u{1f980}\\\x41
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
$0r"🦀\u{1f980}\\\x41
|
||||||
|
|
||||||
|
|
||||||
|
fsdghs";
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*r"🦀\u{1f980}\\\x41
|
||||||
|
|
||||||
|
|
||||||
|
fsdghs"*
|
||||||
|
```rust
|
||||||
|
&str
|
||||||
|
```
|
||||||
|
___
|
||||||
|
|
||||||
|
value of literal (truncated up to newline): 🦀\u{1f980}\\\x41
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -7820,9 +7848,12 @@ fn main() {
|
|||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
*c"🦀\u{1f980}\\\x41"*
|
*c"🦀\u{1f980}\\\x41"*
|
||||||
```text
|
```rust
|
||||||
🦀🦀\A
|
&{unknown}
|
||||||
```
|
```
|
||||||
|
___
|
||||||
|
|
||||||
|
value of literal: 🦀🦀\A
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -7837,9 +7868,12 @@ fn main() {
|
|||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
*b"\xF0\x9F\xA6\x80\\"*
|
*b"\xF0\x9F\xA6\x80\\"*
|
||||||
```text
|
```rust
|
||||||
[240, 159, 166, 128, 92]
|
&[u8; 5]
|
||||||
```
|
```
|
||||||
|
___
|
||||||
|
|
||||||
|
value of literal: [240, 159, 166, 128, 92]
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
@ -7850,9 +7884,12 @@ fn main() {
|
|||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
*br"\xF0\x9F\xA6\x80\\"*
|
*br"\xF0\x9F\xA6\x80\\"*
|
||||||
```text
|
```rust
|
||||||
[92, 120, 70, 48, 92, 120, 57, 70, 92, 120, 65, 54, 92, 120, 56, 48, 92, 92]
|
&[u8; 18]
|
||||||
```
|
```
|
||||||
|
___
|
||||||
|
|
||||||
|
value of literal: [92, 120, 70, 48, 92, 120, 57, 70, 92, 120, 65, 54, 92, 120, 56, 48, 92, 92]
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -7867,9 +7904,12 @@ fn main() {
|
|||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
*b'\xF0'*
|
*b'\xF0'*
|
||||||
```text
|
```rust
|
||||||
0xF0
|
u8
|
||||||
```
|
```
|
||||||
|
___
|
||||||
|
|
||||||
|
value of literal: 0xF0
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
@ -7880,9 +7920,12 @@ fn main() {
|
|||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
*b'\\'*
|
*b'\\'*
|
||||||
```text
|
```rust
|
||||||
0x5C
|
u8
|
||||||
```
|
```
|
||||||
|
___
|
||||||
|
|
||||||
|
value of literal: 0x5C
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -7897,7 +7940,12 @@ fn main() {
|
|||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
*'\x41'*
|
*'\x41'*
|
||||||
|
```rust
|
||||||
|
char
|
||||||
|
```
|
||||||
|
___
|
||||||
|
|
||||||
|
value of literal: A
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
@ -7908,7 +7956,12 @@ fn main() {
|
|||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
*'\\'*
|
*'\\'*
|
||||||
|
```rust
|
||||||
|
char
|
||||||
|
```
|
||||||
|
___
|
||||||
|
|
||||||
|
value of literal: \
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
@ -7919,7 +7972,12 @@ fn main() {
|
|||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
*'\u{1f980}'*
|
*'\u{1f980}'*
|
||||||
|
```rust
|
||||||
|
char
|
||||||
|
```
|
||||||
|
___
|
||||||
|
|
||||||
|
value of literal: 🦀
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -7934,9 +7992,12 @@ fn main() {
|
|||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
*1.0*
|
*1.0*
|
||||||
```text
|
```rust
|
||||||
1 (bits: 0x3FF0000000000000)
|
f64
|
||||||
```
|
```
|
||||||
|
___
|
||||||
|
|
||||||
|
value of literal: 1 (bits: 0x3FF0000000000000)
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
@ -7947,9 +8008,12 @@ fn main() {
|
|||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
*1.0f32*
|
*1.0f32*
|
||||||
```text
|
```rust
|
||||||
1 (bits: 0x3F800000)
|
f32
|
||||||
```
|
```
|
||||||
|
___
|
||||||
|
|
||||||
|
value of literal: 1 (bits: 0x3F800000)
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
@ -7960,9 +8024,12 @@ fn main() {
|
|||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
*134e12*
|
*134e12*
|
||||||
```text
|
```rust
|
||||||
134000000000000 (bits: 0x42DE77D399980000)
|
f64
|
||||||
```
|
```
|
||||||
|
___
|
||||||
|
|
||||||
|
value of literal: 134000000000000 (bits: 0x42DE77D399980000)
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
@ -7973,9 +8040,12 @@ fn main() {
|
|||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
*1523527134274733643531312.0*
|
*1523527134274733643531312.0*
|
||||||
```text
|
```rust
|
||||||
1523527134274733600000000 (bits: 0x44F429E9249F629B)
|
f64
|
||||||
```
|
```
|
||||||
|
___
|
||||||
|
|
||||||
|
value of literal: 1523527134274733600000000 (bits: 0x44F429E9249F629B)
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
@ -7986,9 +8056,12 @@ fn main() {
|
|||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
*0.1ea123*
|
*0.1ea123*
|
||||||
```text
|
```rust
|
||||||
invalid float literal
|
f64
|
||||||
```
|
```
|
||||||
|
___
|
||||||
|
|
||||||
|
invalid literal: invalid float literal
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -8003,9 +8076,12 @@ fn main() {
|
|||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
*34325236457856836345234*
|
*34325236457856836345234*
|
||||||
```text
|
```rust
|
||||||
34325236457856836345234 (0x744C659178614489D92|0b111010001001100011001011001000101111000011000010100010010001001110110010010)
|
i32
|
||||||
```
|
```
|
||||||
|
___
|
||||||
|
|
||||||
|
value of literal: 34325236457856836345234 (0x744C659178614489D92|0b111010001001100011001011001000101111000011000010100010010001001110110010010)
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
@ -8016,9 +8092,12 @@ fn main() {
|
|||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
*134_123424_21*
|
*134_123424_21*
|
||||||
```text
|
```rust
|
||||||
13412342421 (0x31F701A95|0b1100011111011100000001101010010101)
|
i32
|
||||||
```
|
```
|
||||||
|
___
|
||||||
|
|
||||||
|
value of literal: 13412342421 (0x31F701A95|0b1100011111011100000001101010010101)
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
@ -8029,9 +8108,12 @@ fn main() {
|
|||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
*0x12423423*
|
*0x12423423*
|
||||||
```text
|
```rust
|
||||||
306328611 (0x12423423|0b10010010000100011010000100011)
|
i32
|
||||||
```
|
```
|
||||||
|
___
|
||||||
|
|
||||||
|
value of literal: 306328611 (0x12423423|0b10010010000100011010000100011)
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
@ -8042,9 +8124,12 @@ fn main() {
|
|||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
*0b1111_1111*
|
*0b1111_1111*
|
||||||
```text
|
```rust
|
||||||
255 (0xFF|0b11111111)
|
i32
|
||||||
```
|
```
|
||||||
|
___
|
||||||
|
|
||||||
|
value of literal: 255 (0xFF|0b11111111)
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
@ -8055,9 +8140,12 @@ fn main() {
|
|||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
*0o12345*
|
*0o12345*
|
||||||
```text
|
```rust
|
||||||
5349 (0x14E5|0b1010011100101)
|
i32
|
||||||
```
|
```
|
||||||
|
___
|
||||||
|
|
||||||
|
value of literal: 5349 (0x14E5|0b1010011100101)
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
@ -8068,9 +8156,12 @@ fn main() {
|
|||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
*0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_F*
|
*0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_F*
|
||||||
```text
|
```rust
|
||||||
number too large to fit in target type
|
i32
|
||||||
```
|
```
|
||||||
|
___
|
||||||
|
|
||||||
|
invalid literal: number too large to fit in target type
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ pub(super) fn highlight_escape_string<T: IsString>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char, start: TextSize) {
|
pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char, start: TextSize) {
|
||||||
if char.value().is_none() {
|
if char.value().is_err() {
|
||||||
// We do not emit invalid escapes highlighting here. The lexer would likely be in a bad
|
// We do not emit invalid escapes highlighting here. The lexer would likely be in a bad
|
||||||
// state and this token contains junks, since `'` is not a reliable delimiter (consider
|
// state and this token contains junks, since `'` is not a reliable delimiter (consider
|
||||||
// lifetimes). Nonetheless, parser errors should already be emitted.
|
// lifetimes). Nonetheless, parser errors should already be emitted.
|
||||||
@ -48,7 +48,7 @@ pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char, start:
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn highlight_escape_byte(stack: &mut Highlights, byte: &Byte, start: TextSize) {
|
pub(super) fn highlight_escape_byte(stack: &mut Highlights, byte: &Byte, start: TextSize) {
|
||||||
if byte.value().is_none() {
|
if byte.value().is_err() {
|
||||||
// See `highlight_escape_char` for why no error highlighting here.
|
// See `highlight_escape_char` for why no error highlighting here.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ pub(super) fn ra_fixture(
|
|||||||
if !active_parameter.ident().map_or(false, |name| name.text().starts_with("ra_fixture")) {
|
if !active_parameter.ident().map_or(false, |name| name.text().starts_with("ra_fixture")) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let value = literal.value()?;
|
let value = literal.value().ok()?;
|
||||||
|
|
||||||
if let Some(range) = literal.open_quote_text_range() {
|
if let Some(range) = literal.open_quote_text_range() {
|
||||||
hl.add(HlRange { range, highlight: HlTag::StringLiteral.into(), binding_hash: None })
|
hl.add(HlRange { range, highlight: HlTag::StringLiteral.into(), binding_hash: None })
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
use rustc_lexer::unescape::{
|
use rustc_lexer::unescape::{
|
||||||
unescape_byte, unescape_char, unescape_mixed, unescape_unicode, MixedUnit, Mode,
|
unescape_byte, unescape_char, unescape_mixed, unescape_unicode, EscapeError, MixedUnit, Mode,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -180,10 +180,7 @@ fn open_quote_text_range(&self) -> Option<TextRange> {
|
|||||||
fn close_quote_text_range(&self) -> Option<TextRange> {
|
fn close_quote_text_range(&self) -> Option<TextRange> {
|
||||||
self.quote_offsets().map(|it| it.quotes.1)
|
self.quote_offsets().map(|it| it.quotes.1)
|
||||||
}
|
}
|
||||||
fn escaped_char_ranges(
|
fn escaped_char_ranges(&self, cb: &mut dyn FnMut(TextRange, Result<char, EscapeError>)) {
|
||||||
&self,
|
|
||||||
cb: &mut dyn FnMut(TextRange, Result<char, rustc_lexer::unescape::EscapeError>),
|
|
||||||
) {
|
|
||||||
let text_range_no_quotes = match self.text_range_between_quotes() {
|
let text_range_no_quotes = match self.text_range_between_quotes() {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => return,
|
None => return,
|
||||||
@ -212,20 +209,17 @@ impl IsString for ast::String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ast::String {
|
impl ast::String {
|
||||||
pub fn value(&self) -> Option<Cow<'_, str>> {
|
pub fn value(&self) -> Result<Cow<'_, str>, EscapeError> {
|
||||||
if self.is_raw() {
|
|
||||||
let text = self.text();
|
|
||||||
let text =
|
|
||||||
&text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
|
|
||||||
return Some(Cow::Borrowed(text));
|
|
||||||
}
|
|
||||||
|
|
||||||
let text = self.text();
|
let text = self.text();
|
||||||
let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
|
let text_range = self.text_range_between_quotes().ok_or(EscapeError::LoneSlash)?;
|
||||||
|
let text = &text[text_range - self.syntax().text_range().start()];
|
||||||
|
if self.is_raw() {
|
||||||
|
return Ok(Cow::Borrowed(text));
|
||||||
|
}
|
||||||
|
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
let mut prev_end = 0;
|
let mut prev_end = 0;
|
||||||
let mut has_error = false;
|
let mut has_error = None;
|
||||||
unescape_unicode(text, Self::MODE, &mut |char_range, unescaped_char| match (
|
unescape_unicode(text, Self::MODE, &mut |char_range, unescaped_char| match (
|
||||||
unescaped_char,
|
unescaped_char,
|
||||||
buf.capacity() == 0,
|
buf.capacity() == 0,
|
||||||
@ -239,13 +233,13 @@ pub fn value(&self) -> Option<Cow<'_, str>> {
|
|||||||
buf.push_str(&text[..prev_end]);
|
buf.push_str(&text[..prev_end]);
|
||||||
buf.push(c);
|
buf.push(c);
|
||||||
}
|
}
|
||||||
(Err(_), _) => has_error = true,
|
(Err(e), _) => has_error = Some(e),
|
||||||
});
|
});
|
||||||
|
|
||||||
match (has_error, buf.capacity() == 0) {
|
match (has_error, buf.capacity() == 0) {
|
||||||
(true, _) => None,
|
(Some(e), _) => Err(e),
|
||||||
(false, true) => Some(Cow::Borrowed(text)),
|
(None, true) => Ok(Cow::Borrowed(text)),
|
||||||
(false, false) => Some(Cow::Owned(buf)),
|
(None, false) => Ok(Cow::Owned(buf)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -256,20 +250,17 @@ impl IsString for ast::ByteString {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ast::ByteString {
|
impl ast::ByteString {
|
||||||
pub fn value(&self) -> Option<Cow<'_, [u8]>> {
|
pub fn value(&self) -> Result<Cow<'_, [u8]>, EscapeError> {
|
||||||
if self.is_raw() {
|
|
||||||
let text = self.text();
|
|
||||||
let text =
|
|
||||||
&text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
|
|
||||||
return Some(Cow::Borrowed(text.as_bytes()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let text = self.text();
|
let text = self.text();
|
||||||
let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
|
let text_range = self.text_range_between_quotes().ok_or(EscapeError::LoneSlash)?;
|
||||||
|
let text = &text[text_range - self.syntax().text_range().start()];
|
||||||
|
if self.is_raw() {
|
||||||
|
return Ok(Cow::Borrowed(text.as_bytes()));
|
||||||
|
}
|
||||||
|
|
||||||
let mut buf: Vec<u8> = Vec::new();
|
let mut buf: Vec<u8> = Vec::new();
|
||||||
let mut prev_end = 0;
|
let mut prev_end = 0;
|
||||||
let mut has_error = false;
|
let mut has_error = None;
|
||||||
unescape_unicode(text, Self::MODE, &mut |char_range, unescaped_char| match (
|
unescape_unicode(text, Self::MODE, &mut |char_range, unescaped_char| match (
|
||||||
unescaped_char,
|
unescaped_char,
|
||||||
buf.capacity() == 0,
|
buf.capacity() == 0,
|
||||||
@ -283,13 +274,13 @@ pub fn value(&self) -> Option<Cow<'_, [u8]>> {
|
|||||||
buf.extend_from_slice(text[..prev_end].as_bytes());
|
buf.extend_from_slice(text[..prev_end].as_bytes());
|
||||||
buf.push(c as u8);
|
buf.push(c as u8);
|
||||||
}
|
}
|
||||||
(Err(_), _) => has_error = true,
|
(Err(e), _) => has_error = Some(e),
|
||||||
});
|
});
|
||||||
|
|
||||||
match (has_error, buf.capacity() == 0) {
|
match (has_error, buf.capacity() == 0) {
|
||||||
(true, _) => None,
|
(Some(e), _) => Err(e),
|
||||||
(false, true) => Some(Cow::Borrowed(text.as_bytes())),
|
(None, true) => Ok(Cow::Borrowed(text.as_bytes())),
|
||||||
(false, false) => Some(Cow::Owned(buf)),
|
(None, false) => Ok(Cow::Owned(buf)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -298,10 +289,7 @@ impl IsString for ast::CString {
|
|||||||
const RAW_PREFIX: &'static str = "cr";
|
const RAW_PREFIX: &'static str = "cr";
|
||||||
const MODE: Mode = Mode::CStr;
|
const MODE: Mode = Mode::CStr;
|
||||||
|
|
||||||
fn escaped_char_ranges(
|
fn escaped_char_ranges(&self, cb: &mut dyn FnMut(TextRange, Result<char, EscapeError>)) {
|
||||||
&self,
|
|
||||||
cb: &mut dyn FnMut(TextRange, Result<char, rustc_lexer::unescape::EscapeError>),
|
|
||||||
) {
|
|
||||||
let text_range_no_quotes = match self.text_range_between_quotes() {
|
let text_range_no_quotes = match self.text_range_between_quotes() {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => return,
|
None => return,
|
||||||
@ -322,20 +310,17 @@ fn escaped_char_ranges(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ast::CString {
|
impl ast::CString {
|
||||||
pub fn value(&self) -> Option<Cow<'_, [u8]>> {
|
pub fn value(&self) -> Result<Cow<'_, [u8]>, EscapeError> {
|
||||||
if self.is_raw() {
|
|
||||||
let text = self.text();
|
|
||||||
let text =
|
|
||||||
&text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
|
|
||||||
return Some(Cow::Borrowed(text.as_bytes()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let text = self.text();
|
let text = self.text();
|
||||||
let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
|
let text_range = self.text_range_between_quotes().ok_or(EscapeError::LoneSlash)?;
|
||||||
|
let text = &text[text_range - self.syntax().text_range().start()];
|
||||||
|
if self.is_raw() {
|
||||||
|
return Ok(Cow::Borrowed(text.as_bytes()));
|
||||||
|
}
|
||||||
|
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
let mut prev_end = 0;
|
let mut prev_end = 0;
|
||||||
let mut has_error = false;
|
let mut has_error = None;
|
||||||
let extend_unit = |buf: &mut Vec<u8>, unit: MixedUnit| match unit {
|
let extend_unit = |buf: &mut Vec<u8>, unit: MixedUnit| match unit {
|
||||||
MixedUnit::Char(c) => buf.extend(c.encode_utf8(&mut [0; 4]).as_bytes()),
|
MixedUnit::Char(c) => buf.extend(c.encode_utf8(&mut [0; 4]).as_bytes()),
|
||||||
MixedUnit::HighByte(b) => buf.push(b),
|
MixedUnit::HighByte(b) => buf.push(b),
|
||||||
@ -353,13 +338,13 @@ pub fn value(&self) -> Option<Cow<'_, [u8]>> {
|
|||||||
buf.extend(text[..prev_end].as_bytes());
|
buf.extend(text[..prev_end].as_bytes());
|
||||||
extend_unit(&mut buf, u);
|
extend_unit(&mut buf, u);
|
||||||
}
|
}
|
||||||
(Err(_), _) => has_error = true,
|
(Err(e), _) => has_error = Some(e),
|
||||||
});
|
});
|
||||||
|
|
||||||
match (has_error, buf.capacity() == 0) {
|
match (has_error, buf.capacity() == 0) {
|
||||||
(true, _) => None,
|
(Some(e), _) => Err(e),
|
||||||
(false, true) => Some(Cow::Borrowed(text.as_bytes())),
|
(None, true) => Ok(Cow::Borrowed(text.as_bytes())),
|
||||||
(false, false) => Some(Cow::Owned(buf)),
|
(None, false) => Ok(Cow::Owned(buf)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -478,34 +463,34 @@ const fn prefix_len(self) -> usize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ast::Char {
|
impl ast::Char {
|
||||||
pub fn value(&self) -> Option<char> {
|
pub fn value(&self) -> Result<char, EscapeError> {
|
||||||
let mut text = self.text();
|
let mut text = self.text();
|
||||||
if text.starts_with('\'') {
|
if text.starts_with('\'') {
|
||||||
text = &text[1..];
|
text = &text[1..];
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return Err(EscapeError::ZeroChars);
|
||||||
}
|
}
|
||||||
if text.ends_with('\'') {
|
if text.ends_with('\'') {
|
||||||
text = &text[0..text.len() - 1];
|
text = &text[0..text.len() - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
unescape_char(text).ok()
|
unescape_char(text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ast::Byte {
|
impl ast::Byte {
|
||||||
pub fn value(&self) -> Option<u8> {
|
pub fn value(&self) -> Result<u8, EscapeError> {
|
||||||
let mut text = self.text();
|
let mut text = self.text();
|
||||||
if text.starts_with("b\'") {
|
if text.starts_with("b\'") {
|
||||||
text = &text[2..];
|
text = &text[2..];
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return Err(EscapeError::ZeroChars);
|
||||||
}
|
}
|
||||||
if text.ends_with('\'') {
|
if text.ends_with('\'') {
|
||||||
text = &text[0..text.len() - 1];
|
text = &text[0..text.len() - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
unescape_byte(text).ok()
|
unescape_byte(text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -559,7 +544,10 @@ fn test_int_number_suffix() {
|
|||||||
|
|
||||||
fn check_string_value<'a>(lit: &str, expected: impl Into<Option<&'a str>>) {
|
fn check_string_value<'a>(lit: &str, expected: impl Into<Option<&'a str>>) {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ast::String { syntax: make::tokens::literal(&format!("\"{lit}\"")) }.value().as_deref(),
|
ast::String { syntax: make::tokens::literal(&format!("\"{lit}\"")) }
|
||||||
|
.value()
|
||||||
|
.as_deref()
|
||||||
|
.ok(),
|
||||||
expected.into()
|
expected.into()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -584,7 +572,8 @@ fn check_byte_string_value<'a, const N: usize>(
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
ast::ByteString { syntax: make::tokens::literal(&format!("b\"{lit}\"")) }
|
ast::ByteString { syntax: make::tokens::literal(&format!("b\"{lit}\"")) }
|
||||||
.value()
|
.value()
|
||||||
.as_deref(),
|
.as_deref()
|
||||||
|
.ok(),
|
||||||
expected.into().map(|value| &value[..])
|
expected.into().map(|value| &value[..])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user