mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-29 06:23:25 +00:00
Implement literal_from_str
for proc macro srv
This commit is contained in:
parent
3770f73bd6
commit
68365513f3
2 changed files with 128 additions and 6 deletions
|
@ -13,6 +13,7 @@ use std::{
|
|||
use ::tt::{TextRange, TextSize};
|
||||
use proc_macro::bridge::{self, server};
|
||||
use span::{Span, FIXUP_ERASED_FILE_AST_ID_MARKER};
|
||||
use syntax::ast::{self, HasModuleItem, IsString};
|
||||
|
||||
use crate::server::{
|
||||
delim_to_external, delim_to_internal, token_stream::TokenStreamBuilder, LiteralFormatter,
|
||||
|
@ -70,11 +71,71 @@ impl server::FreeFunctions for RaSpanServer {
|
|||
&mut self,
|
||||
s: &str,
|
||||
) -> Result<bridge::Literal<Self::Span, Self::Symbol>, ()> {
|
||||
// FIXME: keep track of LitKind and Suffix
|
||||
let input = s.trim();
|
||||
let source_code = format!("fn f() {{ let _ = {input}; }}");
|
||||
|
||||
let parse = ast::SourceFile::parse(&source_code);
|
||||
let file = parse.tree();
|
||||
|
||||
let Some(ast::Item::Fn(func)) = file.items().next() else { return Err(()) };
|
||||
let Some(ast::Stmt::LetStmt(stmt)) =
|
||||
func.body().ok_or(Err(()))?.stmt_list().ok_or(Err(()))?.statements().next()
|
||||
else {
|
||||
return Err(());
|
||||
};
|
||||
let Some(ast::Expr::Literal(lit)) = stmt.initializer() else { return Err(()) };
|
||||
|
||||
fn raw_delimiter_count<S: IsString>(s: S) -> Option<u8> {
|
||||
let text = s.text();
|
||||
let quote_range = s.text_range_between_quotes()?;
|
||||
let range_start = s.syntax().text_range().start();
|
||||
text[TextRange::up_to((quote_range - range_start).start())]
|
||||
.matches('#')
|
||||
.count()
|
||||
.try_into()
|
||||
.ok()
|
||||
}
|
||||
|
||||
let mut suffix = None;
|
||||
let kind = match lit.kind() {
|
||||
ast::LiteralKind::String(data) => {
|
||||
if data.is_raw() {
|
||||
bridge::LitKind::StrRaw(raw_delimiter_count(data).ok_or(Err(()))?)
|
||||
} else {
|
||||
bridge::LitKind::Str
|
||||
}
|
||||
}
|
||||
ast::LiteralKind::ByteString(data) => {
|
||||
if data.is_raw() {
|
||||
bridge::LitKind::ByteStrRaw(raw_delimiter_count(data).ok_or(Err(()))?)
|
||||
} else {
|
||||
bridge::LitKind::ByteStr
|
||||
}
|
||||
}
|
||||
ast::LiteralKind::CString(data) => {
|
||||
if data.is_raw() {
|
||||
bridge::LitKind::CStrRaw(raw_delimiter_count(data).ok_or(Err(()))?)
|
||||
} else {
|
||||
bridge::LitKind::CStr
|
||||
}
|
||||
}
|
||||
ast::LiteralKind::IntNumber(num) => {
|
||||
suffix = num.suffix();
|
||||
bridge::LitKind::Integer
|
||||
}
|
||||
ast::LiteralKind::FloatNumber(num) => {
|
||||
suffix = num.suffix();
|
||||
bridge::LitKind::Float
|
||||
}
|
||||
ast::LiteralKind::Char(_) => bridge::LitKind::Char,
|
||||
ast::LiteralKind::Byte(_) => bridge::LitKind::Byte,
|
||||
ast::LiteralKind::Bool(_) => unreachable!(),
|
||||
};
|
||||
|
||||
Ok(bridge::Literal {
|
||||
kind: bridge::LitKind::Err,
|
||||
kind,
|
||||
symbol: Symbol::intern(self.interner, s),
|
||||
suffix: None,
|
||||
suffix,
|
||||
span: self.call_site,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use std::{
|
|||
};
|
||||
|
||||
use proc_macro::bridge::{self, server};
|
||||
use syntax::ast::{self, HasModuleItem, IsString};
|
||||
|
||||
use crate::server::{
|
||||
delim_to_external, delim_to_internal, token_stream::TokenStreamBuilder, LiteralFormatter,
|
||||
|
@ -62,11 +63,71 @@ impl server::FreeFunctions for TokenIdServer {
|
|||
&mut self,
|
||||
s: &str,
|
||||
) -> Result<bridge::Literal<Self::Span, Self::Symbol>, ()> {
|
||||
// FIXME: keep track of LitKind and Suffix
|
||||
let input = s.trim();
|
||||
let source_code = format!("fn f() {{ let _ = {input}; }}");
|
||||
|
||||
let parse = ast::SourceFile::parse(&source_code);
|
||||
let file = parse.tree();
|
||||
|
||||
let Some(ast::Item::Fn(func)) = file.items().next() else { return Err(()) };
|
||||
let Some(ast::Stmt::LetStmt(stmt)) =
|
||||
func.body().ok_or(Err(()))?.stmt_list().ok_or(Err(()))?.statements().next()
|
||||
else {
|
||||
return Err(());
|
||||
};
|
||||
let Some(ast::Expr::Literal(lit)) = stmt.initializer() else { return Err(()) };
|
||||
|
||||
fn raw_delimiter_count<S: IsString>(s: S) -> Option<u8> {
|
||||
let text = s.text();
|
||||
let quote_range = s.text_range_between_quotes()?;
|
||||
let range_start = s.syntax().text_range().start();
|
||||
text[TextRange::up_to((quote_range - range_start).start())]
|
||||
.matches('#')
|
||||
.count()
|
||||
.try_into()
|
||||
.ok()
|
||||
}
|
||||
|
||||
let mut suffix = None;
|
||||
let kind = match lit.kind() {
|
||||
ast::LiteralKind::String(data) => {
|
||||
if data.is_raw() {
|
||||
bridge::LitKind::StrRaw(raw_delimiter_count(data).ok_or(Err(()))?)
|
||||
} else {
|
||||
bridge::LitKind::Str
|
||||
}
|
||||
}
|
||||
ast::LiteralKind::ByteString(data) => {
|
||||
if data.is_raw() {
|
||||
bridge::LitKind::ByteStrRaw(raw_delimiter_count(data).ok_or(Err(()))?)
|
||||
} else {
|
||||
bridge::LitKind::ByteStr
|
||||
}
|
||||
}
|
||||
ast::LiteralKind::CString(data) => {
|
||||
if data.is_raw() {
|
||||
bridge::LitKind::CStrRaw(raw_delimiter_count(data).ok_or(Err(()))?)
|
||||
} else {
|
||||
bridge::LitKind::CStr
|
||||
}
|
||||
}
|
||||
ast::LiteralKind::IntNumber(num) => {
|
||||
suffix = num.suffix();
|
||||
bridge::LitKind::Integer
|
||||
}
|
||||
ast::LiteralKind::FloatNumber(num) => {
|
||||
suffix = num.suffix();
|
||||
bridge::LitKind::Float
|
||||
}
|
||||
ast::LiteralKind::Char(_) => bridge::LitKind::Char,
|
||||
ast::LiteralKind::Byte(_) => bridge::LitKind::Byte,
|
||||
ast::LiteralKind::Bool(_) => unreachable!(),
|
||||
};
|
||||
|
||||
Ok(bridge::Literal {
|
||||
kind: bridge::LitKind::Err,
|
||||
kind,
|
||||
symbol: Symbol::intern(self.interner, s),
|
||||
suffix: None,
|
||||
suffix,
|
||||
span: self.call_site,
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue