Skip to content
40 changes: 39 additions & 1 deletion crates/load-cargo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use ide_db::{
use itertools::Itertools;
use proc_macro_api::{
MacroDylib, ProcMacroClient,
bidirectional_protocol::msg::{SubRequest, SubResponse},
bidirectional_protocol::msg::{ParentSpan, SubRequest, SubResponse},
};
use project_model::{CargoConfig, PackageRoot, ProjectManifest, ProjectWorkspace};
use span::{Span, SpanAnchor, SyntaxContext};
Expand Down Expand Up @@ -659,6 +659,44 @@ impl ProcMacroExpander for Expander {
ctx: current_span.ctx.into_u32(),
})
}
SubRequest::SpanParent { file_id, ast_id, start, end, ctx } => {
let span = Span {
range: TextRange::new(TextSize::from(start), TextSize::from(end)),
anchor: SpanAnchor {
file_id: span::EditionedFileId::from_raw(file_id),
ast_id: span::ErasedFileAstId::from_raw(ast_id),
},
// SAFETY: We only receive spans from the server. If someone mess up the communication UB can happen,
// but that will be their problem.
ctx: unsafe { SyntaxContext::from_u32(ctx) },
};

if let Some(macro_call_id) = span.ctx.outer_expn(db) {
let macro_call_loc = db.lookup_intern_macro_call(macro_call_id.into());

let call_site_file = macro_call_loc.kind.file_id();
let call_site_ast_id = macro_call_loc.kind.erased_ast_id();

if let Some(editioned_file_id) = call_site_file.file_id() {
let range = db
.ast_id_map(editioned_file_id.into())
.get_erased(call_site_ast_id)
.text_range();

let parent_span = Some(ParentSpan {
file_id: editioned_file_id.editioned_file_id(db).as_u32(),
ast_id: span::ROOT_ERASED_FILE_AST_ID.into_raw(),
start: u32::from(range.start()),
end: u32::from(range.end()),
ctx: macro_call_loc.ctxt.into_u32(),
});

return Ok(SubResponse::SpanParentResult { parent_span });
}
}

Ok(SubResponse::SpanParentResult { parent_span: None })
}
};
match self.0.expand(
subtree.view(),
Expand Down
13 changes: 13 additions & 0 deletions crates/proc-macro-api/src/bidirectional_protocol/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub enum SubRequest {
LineColumn { file_id: u32, ast_id: u32, offset: u32 },
ByteRange { file_id: u32, ast_id: u32, start: u32, end: u32 },
SpanSource { file_id: u32, ast_id: u32, start: u32, end: u32, ctx: u32 },
SpanParent { file_id: u32, ast_id: u32, start: u32, end: u32, ctx: u32 },
}

#[derive(Debug, Serialize, Deserialize)]
Expand Down Expand Up @@ -50,11 +51,23 @@ pub enum SubResponse {
end: u32,
ctx: u32,
},
SpanParentResult {
parent_span: Option<ParentSpan>,
},
Cancel {
reason: String,
},
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ParentSpan {
pub file_id: u32,
pub ast_id: u32,
pub start: u32,
pub end: u32,
pub ctx: u32,
}

#[derive(Debug, Serialize, Deserialize)]
pub enum BidirectionalMessage {
Request(Request),
Expand Down
34 changes: 34 additions & 0 deletions crates/proc-macro-srv-cli/src/main_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,40 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> {
other => handle_failure(other),
}
}

fn span_parent(
&mut self,
proc_macro_srv::span::Span { range, anchor, ctx }: proc_macro_srv::span::Span,
) -> Option<proc_macro_srv::span::Span> {
let response = self.roundtrip(bidirectional::SubRequest::SpanParent {
file_id: anchor.file_id.as_u32(),
ast_id: anchor.ast_id.into_raw(),
start: range.start().into(),
end: range.end().into(),
ctx: ctx.into_u32(),
});

match response {
Ok(bidirectional::SubResponse::SpanParentResult { parent_span }) => {
parent_span.map(|bidirectional::ParentSpan { file_id, ast_id, start, end, ctx }| {
proc_macro_srv::span::Span {
range: proc_macro_srv::span::TextRange::new(
proc_macro_srv::span::TextSize::new(start),
proc_macro_srv::span::TextSize::new(end),
),
anchor: proc_macro_srv::span::SpanAnchor {
file_id: proc_macro_srv::span::EditionedFileId::from_raw(file_id),
ast_id: proc_macro_srv::span::ErasedFileAstId::from_raw(ast_id),
},
// SAFETY: spans originate from the server. If the protocol is violated,
// undefined behavior is the caller’s responsibility.
ctx: unsafe { proc_macro_srv::span::SyntaxContext::from_u32(ctx) },
}
})
}
other => handle_failure(other),
}
}
}

fn handle_expand_ra(
Expand Down
1 change: 1 addition & 0 deletions crates/proc-macro-srv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ pub trait ProcMacroClientInterface {

fn byte_range(&mut self, span: Span) -> Range<usize>;
fn span_source(&mut self, span: Span) -> Span;
fn span_parent(&mut self, span: Span) -> Option<Span>;
}

const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024;
Expand Down
6 changes: 4 additions & 2 deletions crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,10 @@ impl server::Server for RaSpanServer<'_> {
self.callback.as_mut()?.source_text(span)
}

fn span_parent(&mut self, _span: Self::Span) -> Option<Self::Span> {
// FIXME requires db, looks up the parent call site
fn span_parent(&mut self, span: Self::Span) -> Option<Self::Span> {
if let Some(ref mut callback) = self.callback {
return callback.span_parent(span);
}
None
}
fn span_source(&mut self, span: Self::Span) -> Self::Span {
Expand Down
4 changes: 4 additions & 0 deletions crates/proc-macro-srv/src/tests/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ impl ProcMacroClientInterface for MockCallback<'_> {
fn span_source(&mut self, span: Span) -> Span {
span
}

fn span_parent(&mut self, _span: Span) -> Option<Span> {
None
}
}

pub fn assert_expand_with_callback(
Expand Down