Skip to content

Implement literal_from_str for proc macro server #16446

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/proc-macro-srv/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ paths.workspace = true
base-db.workspace = true
span.workspace = true
proc-macro-api.workspace = true
syntax.workspace = true

[dev-dependencies]
expect-test = "1.4.0"
Expand Down
27 changes: 27 additions & 0 deletions crates/proc-macro-srv/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub mod rust_analyzer_span;
mod symbol;
pub mod token_id;
pub use symbol::*;
use syntax::ast::{self, IsString};
use tt::Spacing;

fn delim_to_internal<S>(d: proc_macro::Delimiter, span: bridge::DelimSpan<S>) -> tt::Delimiter<S> {
Expand Down Expand Up @@ -54,6 +55,32 @@ fn spacing_to_external(spacing: Spacing) -> proc_macro::Spacing {
}
}

fn literal_to_external(literal_kind: ast::LiteralKind) -> Option<proc_macro::bridge::LitKind> {
match literal_kind {
ast::LiteralKind::String(data) => Some(if data.is_raw() {
bridge::LitKind::StrRaw(data.raw_delimiter_count()?)
} else {
bridge::LitKind::Str
}),

ast::LiteralKind::ByteString(data) => Some(if data.is_raw() {
bridge::LitKind::ByteStrRaw(data.raw_delimiter_count()?)
} else {
bridge::LitKind::ByteStr
}),
ast::LiteralKind::CString(data) => Some(if data.is_raw() {
bridge::LitKind::CStrRaw(data.raw_delimiter_count()?)
} else {
bridge::LitKind::CStr
}),
ast::LiteralKind::IntNumber(_) => Some(bridge::LitKind::Integer),
ast::LiteralKind::FloatNumber(_) => Some(bridge::LitKind::Float),
ast::LiteralKind::Char(_) => Some(bridge::LitKind::Char),
ast::LiteralKind::Byte(_) => Some(bridge::LitKind::Byte),
ast::LiteralKind::Bool(_) => None,
}
}

struct LiteralFormatter<S>(bridge::Literal<S, Symbol>);

impl<S> LiteralFormatter<S> {
Expand Down
35 changes: 29 additions & 6 deletions crates/proc-macro-srv/src/server/rust_analyzer_span.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ 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, IsString};

use crate::server::{
delim_to_external, delim_to_internal, token_stream::TokenStreamBuilder, LiteralFormatter,
Symbol, SymbolInternerRef, SYMBOL_INTERNER,
delim_to_external, delim_to_internal, literal_to_external, token_stream::TokenStreamBuilder,
LiteralFormatter, Symbol, SymbolInternerRef, SYMBOL_INTERNER,
};
mod tt {
pub use ::tt::*;
Expand Down Expand Up @@ -70,11 +71,33 @@ impl server::FreeFunctions for RaSpanServer {
&mut self,
s: &str,
) -> Result<bridge::Literal<Self::Span, Self::Symbol>, ()> {
// FIXME: keep track of LitKind and Suffix
let literal = ast::Literal::parse(s).ok_or(())?;
let literal = literal.tree();

let kind = literal_to_external(literal.kind()).ok_or(())?;

// FIXME: handle more than just int and float suffixes
let suffix = match literal.kind() {
ast::LiteralKind::FloatNumber(num) => num.suffix().map(ToString::to_string),
ast::LiteralKind::IntNumber(num) => num.suffix().map(ToString::to_string),
_ => None,
};

let text = match literal.kind() {
ast::LiteralKind::String(data) => data.text_without_quotes().to_string(),
ast::LiteralKind::ByteString(data) => data.text_without_quotes().to_string(),
ast::LiteralKind::CString(data) => data.text_without_quotes().to_string(),
_ => s.to_string(),
};
let text = if let Some(ref suffix) = suffix { text.strip_suffix(suffix) } else { None }
.unwrap_or(&text);

let suffix = suffix.map(|suffix| Symbol::intern(self.interner, &suffix));

Ok(bridge::Literal {
kind: bridge::LitKind::Err,
symbol: Symbol::intern(self.interner, s),
suffix: None,
kind,
symbol: Symbol::intern(self.interner, text),
suffix,
span: self.call_site,
})
}
Expand Down
35 changes: 29 additions & 6 deletions crates/proc-macro-srv/src/server/token_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ use std::{
};

use proc_macro::bridge::{self, server};
use syntax::ast::{self, IsString};

use crate::server::{
delim_to_external, delim_to_internal, token_stream::TokenStreamBuilder, LiteralFormatter,
Symbol, SymbolInternerRef, SYMBOL_INTERNER,
delim_to_external, delim_to_internal, literal_to_external, token_stream::TokenStreamBuilder,
LiteralFormatter, Symbol, SymbolInternerRef, SYMBOL_INTERNER,
};
mod tt {
pub use proc_macro_api::msg::TokenId;
Expand Down Expand Up @@ -62,11 +63,33 @@ impl server::FreeFunctions for TokenIdServer {
&mut self,
s: &str,
) -> Result<bridge::Literal<Self::Span, Self::Symbol>, ()> {
// FIXME: keep track of LitKind and Suffix
let literal = ast::Literal::parse(s).ok_or(())?;
let literal = literal.tree();

let kind = literal_to_external(literal.kind()).ok_or(())?;

// FIXME: handle more than just int and float suffixes
let suffix = match literal.kind() {
ast::LiteralKind::FloatNumber(num) => num.suffix().map(ToString::to_string),
ast::LiteralKind::IntNumber(num) => num.suffix().map(ToString::to_string),
_ => None,
};

let text = match literal.kind() {
ast::LiteralKind::String(data) => data.text_without_quotes().to_string(),
ast::LiteralKind::ByteString(data) => data.text_without_quotes().to_string(),
ast::LiteralKind::CString(data) => data.text_without_quotes().to_string(),
_ => s.to_string(),
};
let text = if let Some(ref suffix) = suffix { text.strip_suffix(suffix) } else { None }
.unwrap_or(&text);

let suffix = suffix.map(|suffix| Symbol::intern(self.interner, &suffix));

Ok(bridge::Literal {
kind: bridge::LitKind::Err,
symbol: Symbol::intern(self.interner, s),
suffix: None,
kind,
symbol: Symbol::intern(self.interner, text),
suffix,
span: self.call_site,
})
}
Expand Down
10 changes: 10 additions & 0 deletions crates/syntax/src/ast/token_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,16 @@ pub trait IsString: AstToken {
assert!(TextRange::up_to(contents_range.len()).contains_range(range));
Some(range + contents_range.start())
}
fn raw_delimiter_count(&self) -> Option<u8> {
let text = self.text();
let quote_range = self.text_range_between_quotes()?;
let range_start = self.syntax().text_range().start();
text[TextRange::up_to((quote_range - range_start).start())]
.matches('#')
.count()
.try_into()
.ok()
}
}

impl IsString for ast::String {
Expand Down
22 changes: 22 additions & 0 deletions crates/syntax/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,28 @@ impl SourceFile {
}
}

impl ast::Literal {
pub fn parse(text: &str) -> Option<Parse<ast::Literal>> {
let lexed = parser::LexedStr::new(text);
let parser_input = lexed.to_input();
let parser_output = parser::TopEntryPoint::Expr.parse(&parser_input);
let (green, mut errors, _) = parsing::build_tree(lexed, parser_output);
let root = SyntaxNode::new_root(green.clone());

errors.extend(validation::validate(&root));

if root.kind() == SyntaxKind::LITERAL {
Some(Parse {
green,
errors: if errors.is_empty() { None } else { Some(errors.into()) },
_ty: PhantomData,
})
} else {
None
}
}
}

impl ast::TokenTree {
pub fn reparse_as_comma_separated_expr(self) -> Parse<ast::MacroEagerInput> {
let tokens = self.syntax().descendants_with_tokens().filter_map(NodeOrToken::into_token);
Expand Down