Unnamed repository; edit this file 'description' to name the repository.
Implement `literal_from_str` for proc macro srv
Victor Song 2024-02-13
parent 3770f73 · commit 6836551
-rw-r--r--crates/proc-macro-srv/src/server/rust_analyzer_span.rs67
-rw-r--r--crates/proc-macro-srv/src/server/token_id.rs67
2 files changed, 128 insertions, 6 deletions
diff --git a/crates/proc-macro-srv/src/server/rust_analyzer_span.rs b/crates/proc-macro-srv/src/server/rust_analyzer_span.rs
index c7c7bea994..f7bbbcc09d 100644
--- a/crates/proc-macro-srv/src/server/rust_analyzer_span.rs
+++ b/crates/proc-macro-srv/src/server/rust_analyzer_span.rs
@@ -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,
})
}
diff --git a/crates/proc-macro-srv/src/server/token_id.rs b/crates/proc-macro-srv/src/server/token_id.rs
index edbdc67b48..5c74c15b36 100644
--- a/crates/proc-macro-srv/src/server/token_id.rs
+++ b/crates/proc-macro-srv/src/server/token_id.rs
@@ -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,
})
}