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 c7c7bea9941..f7bbbcc09d4 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 ::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 @@ fn literal_from_str( &mut self, s: &str, ) -> Result, ()> { - // 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: S) -> Option { + 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 edbdc67b482..5c74c15b360 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 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 @@ fn literal_from_str( &mut self, s: &str, ) -> Result, ()> { - // 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: S) -> Option { + 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, }) }